UNPKG

@xtrek/ts-migrate-plugins

Version:

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

131 lines 5.57 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 type_guards_1 = require("../utils/type-guards"); const token_pos_1 = __importDefault(require("./utils/token-pos")); const validateOptions_1 = require("../utils/validateOptions"); const update_1 = __importDefault(require("./utils/update")); const supportedDiagnostics = new Set([ // TS2339: Property '{0}' does not exist on type '{1}'. 2339, // TS2571: Object is of type 'unknown'. 2571, ]); const addConversionsPlugin = { name: 'add-conversions', run({ fileName, sourceFile, options, getLanguageService }) { // Filter out diagnostics we care about. const diags = getLanguageService() .getSemanticDiagnostics(fileName) .filter(type_guards_1.isDiagnosticWithLinePosition) .filter((diag) => supportedDiagnostics.has(diag.code)); const updates = new update_1.default(sourceFile); typescript_1.default.transform(sourceFile, [addConversionsTransformerFactory(updates, diags, options)]); return updates.apply(); }, validate: validateOptions_1.validateAnyAliasOptions, }; exports.default = addConversionsPlugin; const addConversionsTransformerFactory = (updates, diags, { anyAlias }) => (context) => { const { factory } = context; const anyType = anyAlias ? factory.createTypeReferenceNode(anyAlias) : factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.AnyKeyword); let nodesToConvert; return (file) => { nodesToConvert = new Set(diags .map((diag) => { const token = token_pos_1.default(file, diag.start); switch (diag.code) { case 2339: if (!typescript_1.default.isPropertyAccessExpression(token.parent)) { return null; } return token.parent.expression; case 2571: return token; default: // Should be impossible. return null; } }) .filter((node) => node !== null)); visit(file); return file; }; function visit(origNode) { const needsConversion = nodesToConvert.has(origNode); let node = typescript_1.default.visitEachChild(origNode, visit, context); if (node === origNode && !needsConversion) { return origNode; } if (needsConversion) { node = factory.createAsExpression(node, anyType); } if (shouldReplace(node)) { replaceNode(origNode, node); return origNode; } return node; } /** * For nodes that contain both expression and statement children, only * replace the direct expression children. The statements have already * been replaced at a lower level and replacing them again can produce * duplicate statements or invalid syntax. */ function replaceNode(origNode, newNode) { switch (origNode.kind) { case typescript_1.default.SyntaxKind.DoStatement: case typescript_1.default.SyntaxKind.IfStatement: case typescript_1.default.SyntaxKind.SwitchStatement: case typescript_1.default.SyntaxKind.WithStatement: case typescript_1.default.SyntaxKind.WhileStatement: updates.replaceNode(origNode.expression, newNode.expression); break; case typescript_1.default.SyntaxKind.ForStatement: updates.replaceNode(origNode.initializer, newNode.initializer); updates.replaceNode(origNode.condition, newNode.condition); updates.replaceNode(origNode.incrementor, newNode.incrementor); break; case typescript_1.default.SyntaxKind.ForInStatement: case typescript_1.default.SyntaxKind.ForOfStatement: updates.replaceNode(origNode.expression, newNode.expression); updates.replaceNode(origNode.initializer, newNode.initializer); break; default: updates.replaceNode(origNode, newNode); break; } } }; /** * Determines whether a node is eligible to be replaced. * * Replacing only the expression may produce invalid syntax due to missing parentheses. * There is still some risk of losing whitespace if the expression is contained within * an if statement condition or other construct that can contain blocks. */ function shouldReplace(node) { if (isStatement(node)) { return true; } switch (node.kind) { case typescript_1.default.SyntaxKind.CaseClause: case typescript_1.default.SyntaxKind.ClassDeclaration: case typescript_1.default.SyntaxKind.EnumMember: case typescript_1.default.SyntaxKind.HeritageClause: case typescript_1.default.SyntaxKind.PropertyDeclaration: case typescript_1.default.SyntaxKind.SourceFile: // In case we missed any other case. return true; default: return false; } } function isStatement(node) { return typescript_1.default.SyntaxKind.FirstStatement <= node.kind && node.kind <= typescript_1.default.SyntaxKind.LastStatement; } //# sourceMappingURL=add-conversions.js.map