Skip to content

Instantly share code, notes, and snippets.

@JamieMason
Created March 9, 2020 18:08
Show Gist options
  • Save JamieMason/a1f602558b7b2c5b4a9b238b6d8a455b to your computer and use it in GitHub Desktop.
Save JamieMason/a1f602558b7b2c5b4a9b238b6d8a455b to your computer and use it in GitHub Desktop.
[WIP] Attempt to replace this.renderFoo in a React Class with a new <Foo /> Component
export default function transformer(file, api) {
const j = api.jscodeshift;
const root = j(file.source);
const createClassComponent = (className, superClassName, renderMethod) =>
j.classDeclaration(
j.identifier(className),
j.classBody([
j.methodDefinition("method", j.identifier("render"), renderMethod)
]),
j.identifier(superClassName)
);
const createSelfClosingJsxElement = componentName => {
const jsxOpeningElement = j.jsxOpeningElement(
j.jsxIdentifier(componentName)
);
jsxOpeningElement.selfClosing = true;
return j.jsxElement(jsxOpeningElement);
};
const isRenderMethodName = methodName =>
methodName.startsWith("render") &&
methodName !== "render" &&
methodName !== "renderLoaded";
const hasDestructuredArguments = methodDefinition =>
methodDefinition.node.value.params.some(
param => param.type === "ObjectPattern"
);
const replaceRenderMethodWithClassComponent = ({
componentName,
methodDefinition
}) => {
const classDef = methodDefinition.parentPath.parentPath.parentPath;
const superName = classDef.value.superClass.name;
const renderMethod = methodDefinition.node.value;
const nextClass = createClassComponent(
componentName,
superName,
renderMethod
);
j(classDef).insertBefore([nextClass]);
};
const replaceCallsToRenderMethodWithJsx = ({
methodName,
componentName,
methodDefinition
}) => {
root
.find(j.MemberExpression)
.filter(
memberExpression => memberExpression.node.property.name === methodName
)
.forEach(memberExpression => {
const callExpression = memberExpression.parentPath;
const availableParams = methodDefinition.node.value.params;
const usedParams = callExpression.node.arguments;
const jsxAttributes = usedParams.map((param, i) =>
j.jsxAttribute(
j.jsxIdentifier(availableParams[i].name),
j.jsxExpressionContainer(param)
)
);
const jsxExpressionContainer = callExpression.parentPath;
const jsxElement = createSelfClosingJsxElement(componentName);
j(jsxExpressionContainer).replaceWith(jsxElement);
});
};
root.find(j.MethodDefinition).forEach(methodDefinition => {
const methodName = methodDefinition.node.key.name;
const componentName = methodName.replace("render", "");
if (
isRenderMethodName(methodName) &&
!hasDestructuredArguments(methodDefinition)
) {
const options = { methodName, componentName, methodDefinition };
replaceRenderMethodWithClassComponent(options);
replaceCallsToRenderMethodWithJsx(options);
j(methodDefinition).remove();
}
});
return root.toSource();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment