Skip to content

Commit e54998d

Browse files
authored
fix(typescript-estree): improve comment parsing code (#1120)
1 parent 366518f commit e54998d

3 files changed

Lines changed: 30 additions & 198 deletions

File tree

packages/typescript-estree/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
"glob": "^7.1.4",
4444
"is-glob": "^4.0.1",
4545
"lodash.unescape": "4.0.1",
46-
"semver": "^6.3.0"
46+
"semver": "^6.3.0",
47+
"tsutils": "^3.17.1"
4748
},
4849
"devDependencies": {
4950
"@babel/code-frame": "7.5.5",
Lines changed: 28 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,8 @@
11
import * as ts from 'typescript'; // leave this as * as ts so people using util package don't need syntheticDefaultImports
2-
import { getLocFor, getNodeContainer } from './node-utils';
2+
import { forEachComment } from 'tsutils';
3+
import { getLocFor } from './node-utils';
34
import { TSESTree } from './ts-estree';
45

5-
/**
6-
* Converts a TypeScript comment to an Esprima comment.
7-
* @param block True if it's a block comment, false if not.
8-
* @param text The text of the comment.
9-
* @param start The index at which the comment starts.
10-
* @param end The index at which the comment ends.
11-
* @param startLoc The location at which the comment starts.
12-
* @param endLoc The location at which the comment ends.
13-
* @returns The comment object.
14-
* @internal
15-
*/
16-
function convertTypeScriptCommentToEsprimaComment(
17-
block: boolean,
18-
text: string,
19-
start: number,
20-
end: number,
21-
startLoc: TSESTree.LineAndColumnData,
22-
endLoc: TSESTree.LineAndColumnData,
23-
): TSESTree.Comment {
24-
const comment: TSESTree.OptionalRangeAndLoc<TSESTree.Comment> = {
25-
type: block ? 'Block' : 'Line',
26-
value: text,
27-
};
28-
29-
if (typeof start === 'number') {
30-
comment.range = [start, end];
31-
}
32-
33-
if (typeof startLoc === 'object') {
34-
comment.loc = {
35-
start: startLoc,
36-
end: endLoc,
37-
};
38-
}
39-
40-
return comment as TSESTree.Comment;
41-
}
42-
43-
/**
44-
* Convert comment from TypeScript Triva Scanner.
45-
* @param triviaScanner TS Scanner
46-
* @param ast the AST object
47-
* @param code TypeScript code
48-
* @returns the converted Comment
49-
* @private
50-
*/
51-
function getCommentFromTriviaScanner(
52-
triviaScanner: ts.Scanner,
53-
ast: ts.SourceFile,
54-
code: string,
55-
): TSESTree.Comment {
56-
const kind = triviaScanner.getToken();
57-
const isBlock = kind === ts.SyntaxKind.MultiLineCommentTrivia;
58-
const range = {
59-
pos: triviaScanner.getTokenPos(),
60-
end: triviaScanner.getTextPos(),
61-
kind: triviaScanner.getToken(),
62-
};
63-
64-
const comment = code.substring(range.pos, range.end);
65-
const text = isBlock
66-
? comment.replace(/^\/\*/, '').replace(/\*\/$/, '')
67-
: comment.replace(/^\/\//, '');
68-
const loc = getLocFor(range.pos, range.end, ast);
69-
70-
return convertTypeScriptCommentToEsprimaComment(
71-
isBlock,
72-
text,
73-
range.pos,
74-
range.end,
75-
loc.start,
76-
loc.end,
77-
);
78-
}
79-
806
/**
817
* Convert all comments for the given AST.
828
* @param ast the AST object
@@ -90,93 +16,33 @@ export function convertComments(
9016
): TSESTree.Comment[] {
9117
const comments: TSESTree.Comment[] = [];
9218

93-
/**
94-
* Create a TypeScript Scanner, with skipTrivia set to false so that
95-
* we can parse the comments
96-
*/
97-
const triviaScanner = ts.createScanner(
98-
ast.languageVersion,
99-
false,
100-
ast.languageVariant,
101-
code,
19+
forEachComment(
20+
ast,
21+
(_, comment) => {
22+
const type =
23+
comment.kind == ts.SyntaxKind.SingleLineCommentTrivia
24+
? 'Line'
25+
: 'Block';
26+
const range: TSESTree.Range = [comment.pos, comment.end];
27+
const loc = getLocFor(range[0], range[1], ast);
28+
29+
// both comments start with 2 characters - /* or //
30+
const textStart = range[0] + 2;
31+
const textEnd =
32+
comment.kind === ts.SyntaxKind.SingleLineCommentTrivia
33+
? // single line comments end at the end
34+
range[1] - textStart
35+
: // multiline comments end 2 characters early
36+
range[1] - textStart - 2;
37+
comments.push({
38+
type,
39+
value: code.substr(textStart, textEnd),
40+
range,
41+
loc,
42+
});
43+
},
44+
ast,
10245
);
10346

104-
let kind = triviaScanner.scan();
105-
while (kind !== ts.SyntaxKind.EndOfFileToken) {
106-
const start = triviaScanner.getTokenPos();
107-
const end = triviaScanner.getTextPos();
108-
109-
let container: ts.Node | null = null;
110-
switch (kind) {
111-
case ts.SyntaxKind.SingleLineCommentTrivia:
112-
case ts.SyntaxKind.MultiLineCommentTrivia: {
113-
const comment = getCommentFromTriviaScanner(triviaScanner, ast, code);
114-
115-
comments.push(comment);
116-
break;
117-
}
118-
case ts.SyntaxKind.GreaterThanToken:
119-
container = getNodeContainer(ast, start, end);
120-
if (
121-
(container.parent &&
122-
container.parent.parent &&
123-
// Rescan after an opening element or fragment
124-
(container.parent.kind === ts.SyntaxKind.JsxOpeningElement &&
125-
// Make sure this is the end of a tag like `<Component<number>>`
126-
container.parent.end === end)) ||
127-
container.parent.kind === ts.SyntaxKind.JsxOpeningFragment ||
128-
// Rescan after a self-closing element if it's inside another JSX element
129-
(container.parent.kind === ts.SyntaxKind.JsxSelfClosingElement &&
130-
(container.parent.parent.kind === ts.SyntaxKind.JsxElement ||
131-
container.parent.parent.kind === ts.SyntaxKind.JsxFragment)) ||
132-
// Rescan after a closing element if it's inside another JSX element
133-
((container.parent.kind === ts.SyntaxKind.JsxClosingElement ||
134-
container.parent.kind === ts.SyntaxKind.JsxClosingFragment) &&
135-
container.parent.parent.parent &&
136-
(container.parent.parent.parent.kind === ts.SyntaxKind.JsxElement ||
137-
container.parent.parent.parent.kind ===
138-
ts.SyntaxKind.JsxFragment))
139-
) {
140-
kind = triviaScanner.reScanJsxToken();
141-
continue;
142-
}
143-
break;
144-
case ts.SyntaxKind.CloseBraceToken:
145-
container = getNodeContainer(ast, start, end);
146-
147-
// Rescan after a JSX expression
148-
if (
149-
container.parent &&
150-
container.parent.kind === ts.SyntaxKind.JsxExpression &&
151-
container.parent.parent &&
152-
container.parent.parent.kind === ts.SyntaxKind.JsxElement
153-
) {
154-
kind = triviaScanner.reScanJsxToken();
155-
continue;
156-
}
157-
158-
if (
159-
container.kind === ts.SyntaxKind.TemplateMiddle ||
160-
container.kind === ts.SyntaxKind.TemplateTail
161-
) {
162-
kind = triviaScanner.reScanTemplateToken();
163-
continue;
164-
}
165-
break;
166-
case ts.SyntaxKind.SlashToken:
167-
case ts.SyntaxKind.SlashEqualsToken:
168-
container = getNodeContainer(ast, start, end);
169-
170-
if (container.kind === ts.SyntaxKind.RegularExpressionLiteral) {
171-
kind = triviaScanner.reScanSlashToken();
172-
continue;
173-
}
174-
break;
175-
default:
176-
break;
177-
}
178-
kind = triviaScanner.scan();
179-
}
180-
18147
return comments;
18248
}

packages/typescript-estree/src/node-utils.ts

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -612,41 +612,6 @@ export function convertTokens(ast: ts.SourceFile): TSESTree.Token[] {
612612
return result;
613613
}
614614

615-
/**
616-
* Get container token node between range
617-
* @param ast the AST object
618-
* @param start The index at which the comment starts.
619-
* @param end The index at which the comment ends.
620-
* @returns typescript container token
621-
* @private
622-
*/
623-
export function getNodeContainer(
624-
ast: ts.SourceFile,
625-
start: number,
626-
end: number,
627-
): ts.Node {
628-
let container: ts.Node | null = null;
629-
630-
/**
631-
* @param node the ts.Node
632-
*/
633-
function walk(node: ts.Node): void {
634-
const nodeStart = node.pos;
635-
const nodeEnd = node.end;
636-
637-
if (start >= nodeStart && end <= nodeEnd) {
638-
if (isToken(node)) {
639-
container = node;
640-
} else {
641-
node.getChildren().forEach(walk);
642-
}
643-
}
644-
}
645-
walk(ast);
646-
647-
return container!;
648-
}
649-
650615
export interface TSError {
651616
index: number;
652617
lineNumber: number;

0 commit comments

Comments
 (0)