ember-codemod-add-component-signatures
Version:
Codemod to add component signatures
99 lines (98 loc) • 3.63 kB
JavaScript
import { AST as ASTJavaScript } from '@codemod-utils/ast-javascript';
import { AST as ASTTemplate } from '@codemod-utils/ast-template';
function analyzeClass(file) {
const args = new Set();
if (file === undefined) {
return args;
}
// We know that the file is in TypeScript
const traverse = ASTJavaScript.traverse(true);
traverse(file, {
visitMemberExpression(node) {
if (node.value.object.type !== 'MemberExpression' ||
node.value.object.property.name !== 'args') {
return false;
}
switch (node.value.property.type) {
// Matches the pattern `this.args.someArgument`
case 'Identifier': {
args.add(node.value.property.name);
break;
}
// Matches the pattern `this.args['someArgument']`
case 'StringLiteral': {
args.add(node.value.property.value);
break;
}
}
return false;
},
visitVariableDeclarator(node) {
const { id: leftHandSide, init: rightHandSide } = node.value;
let isValid = false;
switch (rightHandSide?.type) {
// Matches the pattern `const { someArgument } = this.args;`
case 'MemberExpression': {
if (rightHandSide.object.type !== 'ThisExpression' ||
rightHandSide.property.type !== 'Identifier' ||
rightHandSide.property.name !== 'args') {
break;
}
isValid = true;
break;
}
// Matches the pattern `const { someArgument } = this.args as SomeType;`
case 'TSAsExpression': {
if (rightHandSide.expression.type !== 'MemberExpression' ||
rightHandSide.expression.object.type !== 'ThisExpression' ||
rightHandSide.expression.property.type !== 'Identifier' ||
rightHandSide.expression.property.name !== 'args') {
break;
}
isValid = true;
break;
}
}
if (!isValid) {
return false;
}
// @ts-expect-error: Assume that types from external packages are correct
leftHandSide.properties.forEach((property) => {
switch (property.key.type) {
case 'Identifier': {
args.add(property.key.name);
break;
}
case 'StringLiteral': {
args.add(property.key.value);
break;
}
}
});
return false;
},
});
return args;
}
function analyzeTemplate(file) {
const traverse = ASTTemplate.traverse();
const args = new Set();
traverse(file, {
PathExpression(node) {
if (node.head.type !== 'AtHead') {
return;
}
const value = node.original.replace(/^@/, '');
const arg = value.split('.')[0];
args.add(arg);
},
});
return args;
}
export function findArguments(templateFile, classFile) {
const args = new Set([
...analyzeTemplate(templateFile),
...analyzeClass(classFile),
]);
return Array.from(args).sort();
}