UNPKG

ts-migrate-plugins

Version:

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

225 lines 10.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); /* eslint-disable no-use-before-define, @typescript-eslint/no-use-before-define */ const typescript_1 = __importStar(require("typescript")); const type_guards_1 = require("../utils/type-guards"); const updateSourceText_1 = __importDefault(require("../utils/updateSourceText")); const validateOptions_1 = require("../utils/validateOptions"); const optionProperties = { useTsIgnore: { type: 'boolean' }, messageLimit: { type: 'number' }, messagePrefix: { type: 'string' }, }; const tsIgnorePlugin = { name: 'ts-ignore', run({ getLanguageService, fileName, sourceFile, options }) { const diagnostics = getLanguageService() .getSemanticDiagnostics(fileName) .filter(type_guards_1.isDiagnosticWithLinePosition); return getTextWithIgnores(sourceFile, diagnostics, options); }, validate: (0, validateOptions_1.createValidate)(optionProperties), }; exports.default = tsIgnorePlugin; const TS_IGNORE_MESSAGE_LIMIT = 50; function getTextWithIgnores(sourceFile, diagnostics, options) { const { text } = sourceFile; const updates = []; const isIgnored = {}; diagnostics.forEach((diagnostic) => { var _a; const { line: diagnosticLine } = typescript_1.default.getLineAndCharacterOfPosition(sourceFile, diagnostic.start); const { code } = diagnostic; const messageText = typeof diagnostic.messageText === 'string' ? diagnostic.messageText : diagnostic.messageText.messageText; const messageLines = messageText .split('\n') .map((l) => l.trim()) .filter(Boolean); const message = messageLines[messageLines.length - 1]; const errorExpression = options.useTsIgnore ? 'ts-ignore' : `ts-expect-error`; const messageLimit = (_a = options.messageLimit) !== null && _a !== void 0 ? _a : TS_IGNORE_MESSAGE_LIMIT; const messagePrefixInComment = options.messagePrefix ? ` ${options.messagePrefix}` : ''; const tsIgnoreCommentText = `@${errorExpression} TS(${code})${messagePrefixInComment}: ${message.length > messageLimit ? `${message.slice(0, messageLimit)}... Remove this comment to see the full error message` : message}`; if (!isIgnored[diagnosticLine]) { let commentLine = diagnosticLine; let pos = getStartOfLinePos(commentLine, sourceFile); while (commentLine > 0) { const prevLine = commentLine - 1; const prevLinePos = getStartOfLinePos(prevLine, sourceFile); const prevLineText = text.slice(prevLinePos, pos - 1); const prevLineStartsWithEslintComment = /^ *\/\/ *eslint/.test(prevLineText); if (!prevLineStartsWithEslintComment) break; commentLine = prevLine; pos = prevLinePos; } // Include leading whitespace let ws = ''; let i = pos; while (sourceFile.text[i] === ' ') { i += 1; ws += ' '; } if (inTemplateExpressionText(sourceFile, pos)) { const node = findDiagnosticNode(diagnostic, sourceFile); if (node) { updates.push({ kind: 'insert', index: node.pos, text: `${ws}${typescript_1.default.sys.newLine}// ${tsIgnoreCommentText}${text[node.pos] !== typescript_1.default.sys.newLine ? typescript_1.default.sys.newLine : ''}`, }); } else { throw new Error(`Failed to add @${errorExpression} within template expression.`); } } else if (inJsxText(sourceFile, pos)) { updates.push({ kind: 'insert', index: pos, text: `${ws}{/* ${tsIgnoreCommentText} */}${typescript_1.default.sys.newLine}`, }); } else if (onMultilineConditionalTokenLine(sourceFile, diagnostic.start)) { updates.push({ kind: 'insert', index: getConditionalCommentPos(sourceFile, diagnostic.start), text: ` // ${tsIgnoreCommentText}${typescript_1.default.sys.newLine}${ws} `, }); } else { let skip = false; if (commentLine > 1) { const prevLineText = text.slice(getStartOfLinePos(commentLine - 1, sourceFile), getStartOfLinePos(commentLine, sourceFile)); if (/\bwebpackChunkName\b/.test(prevLineText)) { skip = true; } } if (!skip) { updates.push({ kind: 'insert', index: pos, text: `${ws}// ${tsIgnoreCommentText}${typescript_1.default.sys.newLine}`, }); } } isIgnored[diagnosticLine] = true; } }); return (0, updateSourceText_1.default)(text, updates); } function findDiagnosticNode(diagnostic, sourceFile) { const visitor = (node) => isDiagnosticNode(node, diagnostic, sourceFile) ? node : typescript_1.default.forEachChild(node, visitor); return visitor(sourceFile); } function isDiagnosticNode(node, diagnostic, sourceFile) { return (node.getStart(sourceFile) === diagnostic.start && node.getEnd() === diagnostic.start + diagnostic.length); } function inJsxText(sourceFile, pos) { const visitor = (node) => { if (node.pos <= pos && pos < node.end && (typescript_1.default.isJsxElement(node) || typescript_1.default.isJsxFragment(node))) { const isJsxTextChild = node.children.some((child) => typescript_1.default.isJsxText(child) && child.pos <= pos && pos < child.end); const isClosingElement = !(0, typescript_1.isJsxFragment)(node) && node.closingElement.pos === pos; if (isJsxTextChild || isClosingElement) { return true; } } return typescript_1.default.forEachChild(node, visitor); }; return !!typescript_1.default.forEachChild(sourceFile, visitor); } function inTemplateExpressionText(sourceFile, pos) { const visitor = (node) => { if (node.pos <= pos && pos < node.end && typescript_1.default.isTemplateExpression(node)) { const inHead = node.head.pos <= pos && pos < node.head.end; const inMiddleOrTail = node.templateSpans.some((span) => span.literal.pos <= pos && pos < span.literal.end); if (inHead || inMiddleOrTail) { return true; } } return typescript_1.default.forEachChild(node, visitor); }; return !!typescript_1.default.forEachChild(sourceFile, visitor); } function getConditionalExpressionAtPos(sourceFile, pos) { const visitor = (node) => { if (node.pos <= pos && pos < node.end && typescript_1.default.isConditionalExpression(node)) { return node; } return typescript_1.default.forEachChild(node, visitor); }; return typescript_1.default.forEachChild(sourceFile, visitor); } function visitConditionalExpressionWhen(node, pos, visitor) { if (!node) return visitor.otherwise(); const inWhenTrue = node.whenTrue.pos <= pos && pos < node.whenTrue.end; if (inWhenTrue) return visitor.whenTrue(node); const inWhenFalse = node.whenFalse.pos <= pos && pos < node.whenFalse.end; if (inWhenFalse) return visitor.whenFalse(node); return visitor.otherwise(); } function onMultilineConditionalTokenLine(sourceFile, pos) { const conditionalExpression = getConditionalExpressionAtPos(sourceFile, pos); // Not in a conditional expression. if (!conditionalExpression) return false; const { line: questionTokenLine } = typescript_1.default.getLineAndCharacterOfPosition(sourceFile, conditionalExpression.questionToken.end); const { line: colonTokenLine } = typescript_1.default.getLineAndCharacterOfPosition(sourceFile, conditionalExpression.colonToken.end); // Single line conditional expression. if (questionTokenLine === colonTokenLine) return false; const { line } = typescript_1.default.getLineAndCharacterOfPosition(sourceFile, pos); return visitConditionalExpressionWhen(conditionalExpression, pos, { // On question token line of multiline conditional expression. whenTrue: () => line === questionTokenLine, // On colon token line of multiline conditional expression. whenFalse: () => line === colonTokenLine, otherwise: () => false, }); } function getConditionalCommentPos(sourceFile, pos) { return visitConditionalExpressionWhen(getConditionalExpressionAtPos(sourceFile, pos), pos, { whenTrue: (node) => node.questionToken.end, whenFalse: (node) => node.colonToken.end, otherwise: () => pos, }); } /** Get position at start of zero-indexed line number in the given source file. */ function getStartOfLinePos(line, sourceFile) { return typescript_1.default.getPositionOfLineAndCharacter(sourceFile, line, 0); } //# sourceMappingURL=ts-ignore.js.map