@xtrek/ts-migrate-plugins
Version:
Set of codemods, which are doing transformation of js/jsx to ts/tsx
96 lines • 4.93 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const typescript_1 = __importDefault(require("typescript"));
const react_1 = require("./utils/react");
const identifiers_1 = require("./utils/identifiers");
const updateSourceText_1 = __importDefault(require("../utils/updateSourceText"));
const validateOptions_1 = require("../utils/validateOptions");
const reactClassStatePlugin = {
name: 'react-class-state',
async run({ fileName, sourceFile, options }) {
if (!fileName.endsWith('.tsx'))
return undefined;
const updates = [];
const printer = typescript_1.default.createPrinter();
const reactClassDeclarations = sourceFile.statements
.filter(typescript_1.default.isClassDeclaration)
.filter(react_1.isReactClassComponent);
if (reactClassDeclarations.length === 0)
return undefined;
const numComponentsInFile = react_1.getNumComponentsInSourceFile(sourceFile);
const usedIdentifiers = identifiers_1.collectIdentifiers(sourceFile);
reactClassDeclarations.forEach((classDeclaration) => {
const componentName = (classDeclaration.name && classDeclaration.name.text) || 'Component';
const heritageType = react_1.getReactComponentHeritageType(classDeclaration);
const heritageTypeArgs = heritageType.typeArguments || [];
const propsType = heritageTypeArgs[0];
const stateType = heritageTypeArgs[1];
const getStateTypeName = () => {
let name = '';
if (propsType && typescript_1.default.isTypeReferenceNode(propsType) && typescript_1.default.isIdentifier(propsType.typeName)) {
name = propsType.typeName.text.replace('Props', 'State');
}
else if (numComponentsInFile > 1) {
name = `${componentName}State`;
}
else {
name = 'State';
}
if (!usedIdentifiers.has(name)) {
return name;
}
// Ensure name is unused.
let i = 1;
while (usedIdentifiers.has(name + i)) {
i += 1;
}
return name + i;
};
if (!stateType && usesState(classDeclaration)) {
const stateTypeName = getStateTypeName();
const anyType = options.anyAlias != null
? typescript_1.default.factory.createTypeReferenceNode(options.anyAlias, undefined)
: typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.AnyKeyword);
const newStateType = typescript_1.default.factory.createTypeAliasDeclaration(undefined, undefined, stateTypeName, undefined, anyType);
updates.push({
kind: 'insert',
index: classDeclaration.pos,
text: `\n\n${printer.printNode(typescript_1.default.EmitHint.Unspecified, newStateType, sourceFile)}`,
});
updates.push({
kind: 'replace',
index: heritageType.pos,
length: heritageType.end - heritageType.pos,
text: ` ${printer.printNode(typescript_1.default.EmitHint.Unspecified, typescript_1.default.factory.updateExpressionWithTypeArguments(heritageType, heritageType.expression, [
propsType || typescript_1.default.factory.createTypeLiteralNode([]),
typescript_1.default.factory.createTypeReferenceNode(stateTypeName, undefined),
]), sourceFile)}`,
});
}
});
return updateSourceText_1.default(sourceFile.text, updates);
},
validate: validateOptions_1.validateAnyAliasOptions,
};
exports.default = reactClassStatePlugin;
function usesState(classDeclaration) {
const visitor = (node) => {
if (typescript_1.default.isPropertyAccessExpression(node) &&
node.expression.kind === typescript_1.default.SyntaxKind.ThisKeyword &&
node.name.text === 'state') {
return true;
}
if (typescript_1.default.isCallExpression(node) &&
typescript_1.default.isPropertyAccessExpression(node.expression) &&
node.expression.expression.kind === typescript_1.default.SyntaxKind.ThisKeyword &&
node.expression.name.text === 'setState') {
return true;
}
return typescript_1.default.forEachChild(node, visitor);
};
return !!typescript_1.default.forEachChild(classDeclaration, visitor);
}
//# sourceMappingURL=react-class-state.js.map