-
Notifications
You must be signed in to change notification settings - Fork 220
/
strip-ts-ignore.ts
92 lines (76 loc) · 3.21 KB
/
strip-ts-ignore.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/* eslint-disable no-use-before-define, @typescript-eslint/no-use-before-define */
import ts from 'typescript';
import { Plugin } from 'ts-migrate-server';
import updateSourceText, { SourceTextUpdate } from '../utils/updateSourceText';
const stripTSIgnorePlugin: Plugin = {
name: 'strip-ts-ignore',
run({ text, fileName }) {
const sourceFile = ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest);
return getTextWithoutIgnores(sourceFile);
},
};
export default stripTSIgnorePlugin;
function getTextWithoutIgnores(sourceFile: ts.SourceFile): string {
const updates: SourceTextUpdate[] = [];
const printerWithoutComments = ts.createPrinter({ removeComments: true });
const printWithoutComments = (node: ts.Node) =>
printerWithoutComments.printNode(ts.EmitHint.Unspecified, node, sourceFile);
const { text } = sourceFile;
const regExp = /\/\/ *@ts-ignore\b/g;
let result: RegExpExecArray | null;
// eslint-disable-next-line no-cond-assign
while ((result = regExp.exec(text)) != null) {
const matchPos = result.index;
const { line } = ts.getLineAndCharacterOfPosition(sourceFile, matchPos);
const lineStart = ts.getPositionOfLineAndCharacter(sourceFile, line, 0);
const lineEnd = ts.getPositionOfLineAndCharacter(sourceFile, line + 1, 0);
const lineText = sourceFile.text.slice(lineStart, lineEnd);
const node = findNodeAtPos(sourceFile, matchPos);
if (node && !ts.isJsxText(node)) {
const commentRanges = getCommentRanges(text, node.pos).filter((range) =>
isInRange(matchPos, range),
);
if (commentRanges.length > 0) {
commentRanges.forEach((range) => {
const { pos, end } = expandToWhitespace(text, range);
updates.push({ kind: 'delete', index: pos, length: end - pos });
});
} else {
const printedWithoutComments = printWithoutComments(node);
const inTemplate = ts.isTemplateLiteralToken(node);
if (ts.isJsxExpression(node) && printedWithoutComments === '') {
const { pos, end } = expandToWhitespace(text, node);
updates.push({ kind: 'delete', index: pos, length: end - pos });
} else if (!inTemplate && /^ *\/\/ *@ts-ignore\b/.test(lineText)) {
updates.push({ kind: 'delete', index: lineStart, length: lineEnd - lineStart });
}
}
}
}
return updateSourceText(text, updates);
}
function findNodeAtPos(sourceFile: ts.SourceFile, pos: number): ts.Node | undefined {
const visitor = (node: ts.Node): ts.Node | undefined =>
ts.forEachChild(node, visitor) || (isInRange(pos, node) ? node : undefined);
return ts.forEachChild(sourceFile, visitor);
}
function isInRange(pos: number, range: ts.TextRange): boolean {
return range.pos <= pos && pos < range.end;
}
function expandToWhitespace(text: string, range: ts.TextRange): ts.TextRange {
let { pos } = range;
while (pos > 0 && text[pos - 1] === ' ') {
pos -= 1;
}
let { end } = range;
if (end < text.length && text[end] === ts.sys.newLine) {
end += 1;
}
return { pos, end };
}
function getCommentRanges(text: string, pos: number): ts.CommentRange[] {
return [
...(ts.getLeadingCommentRanges(text, pos) || []),
...(ts.getTrailingCommentRanges(text, pos) || []),
];
}