Created
March 9, 2020 18:08
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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