@xtrek/ts-migrate-plugins
Version:
Set of codemods, which are doing transformation of js/jsx to ts/tsx
131 lines • 5.57 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 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