UNPKG

@xtrek/ts-migrate-plugins

Version:

Set of codemods, which are doing transformation of js/jsx to ts/tsx

96 lines 4.93 kB
"use strict"; 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