Skip to content
This repository was archived by the owner on Jan 19, 2019. It is now read-only.

Commit 2044025

Browse files
committed
Breaking: Allow comment scanner to rescan tokens (fixes #216)
Template strings and regex patterns need to be rescaned to not scan tokens as comments. Fix how comment tokens are removed.
1 parent f836bb9 commit 2044025

File tree

45 files changed

+2531
-107
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2531
-107
lines changed

Diff for: lib/ast-converter.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//------------------------------------------------------------------------------
1313

1414
const convert = require("./convert"),
15+
convertComments = require("./convert-comments").convertComments,
1516
nodeUtils = require("./node-utils");
1617

1718
//------------------------------------------------------------------------------
@@ -67,10 +68,10 @@ module.exports = (ast, extra) => {
6768
}
6869

6970
/**
70-
* Add the comment nodes to the AST (that were parsed separately in parser.js)
71+
* Optionally convert and include all comments in the AST
7172
*/
72-
if (extra.comment || extra.attachComment) {
73-
estree.comments = extra.comments || [];
73+
if (extra.comment) {
74+
estree.comments = convertComments(ast, extra.code);
7475
}
7576

7677
return estree;

Diff for: lib/convert-comments.js

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/**
2+
* @fileoverview Convert comment using TypeScript token scanner
3+
* @author James Henry
4+
* @copyright jQuery Foundation and other contributors, https://jquery.org/
5+
* MIT License
6+
*/
7+
8+
"use strict";
9+
10+
//------------------------------------------------------------------------------
11+
// Requirements
12+
//------------------------------------------------------------------------------
13+
14+
const ts = require("typescript"),
15+
nodeUtils = require("./node-utils");
16+
17+
//------------------------------------------------------------------------------
18+
// Private
19+
//------------------------------------------------------------------------------
20+
21+
/**
22+
* Converts a TypeScript comment to an Esprima comment.
23+
* @param {boolean} block True if it's a block comment, false if not.
24+
* @param {string} text The text of the comment.
25+
* @param {int} start The index at which the comment starts.
26+
* @param {int} end The index at which the comment ends.
27+
* @param {Location} startLoc The location at which the comment starts.
28+
* @param {Location} endLoc The location at which the comment ends.
29+
* @returns {Object} The comment object.
30+
* @private
31+
*/
32+
function convertTypeScriptCommentToEsprimaComment(block, text, start, end, startLoc, endLoc) {
33+
const comment = {
34+
type: block ? "Block" : "Line",
35+
value: text
36+
};
37+
38+
if (typeof start === "number") {
39+
comment.range = [start, end];
40+
}
41+
42+
if (typeof startLoc === "object") {
43+
comment.loc = {
44+
start: startLoc,
45+
end: endLoc
46+
};
47+
}
48+
49+
return comment;
50+
}
51+
52+
/**
53+
* Convert comment from TypeScript Triva Scanner.
54+
* @param {Object} triviaScanner TS Scanner
55+
* @param {Object} ast the AST object
56+
* @param {string} code TypeScript code
57+
* @returns {ESTreeComment} the converted ESTreeComment
58+
* @private
59+
*/
60+
function getCommentFromTriviaScanner(triviaScanner, ast, code) {
61+
const kind = triviaScanner.getToken();
62+
const isBlock = (kind === ts.SyntaxKind.MultiLineCommentTrivia);
63+
const range = {
64+
pos: triviaScanner.getTokenPos(),
65+
end: triviaScanner.getTextPos(),
66+
kind: triviaScanner.getToken()
67+
};
68+
69+
const comment = code.substring(range.pos, range.end);
70+
const text = (isBlock) ? comment.replace(/^\/\*/, "").replace(/\*\/$/, "") : comment.replace(/^\/\//, "");
71+
const loc = nodeUtils.getLocFor(range.pos, range.end, ast);
72+
73+
const esprimaComment = convertTypeScriptCommentToEsprimaComment(isBlock, text, range.pos, range.end, loc.start, loc.end);
74+
75+
return esprimaComment;
76+
}
77+
78+
//------------------------------------------------------------------------------
79+
// Public
80+
//------------------------------------------------------------------------------
81+
82+
/* eslint-disable no-use-before-define */
83+
module.exports = {
84+
convertComments
85+
};
86+
87+
88+
/**
89+
* Convert all comments for the given AST.
90+
* @param {Object} ast the AST object
91+
* @param {string} code the TypeScript code
92+
* @returns {ESTreeComment[]} the converted ESTreeComment
93+
* @private
94+
*/
95+
function convertComments(ast, code) {
96+
const comments = [];
97+
98+
/**
99+
* Create a TypeScript Scanner, with skipTrivia set to false so that
100+
* we can parse the comments
101+
*/
102+
const triviaScanner = ts.createScanner(ast.languageVersion, false, 0, code);
103+
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 = 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.CloseBraceToken:
119+
container = nodeUtils.getNodeContainer(ast, start, end);
120+
121+
if (
122+
container.kind === ts.SyntaxKind.TemplateMiddle ||
123+
container.kind === ts.SyntaxKind.TemplateTail
124+
) {
125+
kind = triviaScanner.reScanTemplateToken();
126+
continue;
127+
}
128+
break;
129+
case ts.SyntaxKind.SlashToken:
130+
case ts.SyntaxKind.SlashEqualsToken:
131+
container = nodeUtils.getNodeContainer(ast, start, end);
132+
133+
if (
134+
container.kind === ts.SyntaxKind.RegularExpressionLiteral
135+
) {
136+
kind = triviaScanner.reScanSlashToken();
137+
continue;
138+
}
139+
break;
140+
default:
141+
break;
142+
}
143+
kind = triviaScanner.scan();
144+
}
145+
146+
return comments;
147+
}

Diff for: lib/node-utils.js

+34-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ module.exports = {
187187
fixExports,
188188
getTokenType,
189189
convertToken,
190-
convertTokens
190+
convertTokens,
191+
getNodeContainer
191192
};
192193
/* eslint-enable no-use-before-define */
193194

@@ -633,3 +634,35 @@ function convertTokens(ast) {
633634
walk(ast);
634635
return result;
635636
}
637+
638+
/**
639+
* Get container token node between range
640+
* @param {Object} ast the AST object
641+
* @param {int} start The index at which the comment starts.
642+
* @param {int} end The index at which the comment ends.
643+
* @returns {TSToken} typescript container token
644+
* @private
645+
*/
646+
function getNodeContainer(ast, start, end) {
647+
let container = null;
648+
649+
/**
650+
* @param {TSNode} node the TSNode
651+
* @returns {undefined}
652+
*/
653+
function walk(node) {
654+
const nodeStart = node.pos;
655+
const nodeEnd = node.end;
656+
657+
if (start >= nodeStart && end <= nodeEnd) {
658+
if (isToken(node)) {
659+
container = node;
660+
} else {
661+
node.getChildren().forEach(walk);
662+
}
663+
}
664+
}
665+
walk(ast);
666+
667+
return container;
668+
}

Diff for: parser.js

+2-98
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
const astNodeTypes = require("./lib/ast-node-types"),
1111
ts = require("typescript"),
12+
convert = require("./lib/ast-converter"),
1213
semver = require("semver");
1314

1415
const SUPPORTED_TYPESCRIPT_VERSIONS = require("./package.json").devDependencies.typescript;
@@ -51,61 +52,6 @@ function resetExtra() {
5152
};
5253
}
5354

54-
/**
55-
* Converts a TypeScript comment to an Esprima comment.
56-
* @param {boolean} block True if it's a block comment, false if not.
57-
* @param {string} text The text of the comment.
58-
* @param {int} start The index at which the comment starts.
59-
* @param {int} end The index at which the comment ends.
60-
* @param {Location} startLoc The location at which the comment starts.
61-
* @param {Location} endLoc The location at which the comment ends.
62-
* @returns {Object} The comment object.
63-
* @private
64-
*/
65-
function convertTypeScriptCommentToEsprimaComment(block, text, start, end, startLoc, endLoc) {
66-
const comment = {
67-
type: block ? "Block" : "Line",
68-
value: text
69-
};
70-
71-
if (typeof start === "number") {
72-
comment.range = [start, end];
73-
}
74-
75-
if (typeof startLoc === "object") {
76-
comment.loc = {
77-
start: startLoc,
78-
end: endLoc
79-
};
80-
}
81-
82-
return comment;
83-
}
84-
85-
/**
86-
* Returns line and column data for the given start and end positions,
87-
* for the given AST
88-
* @param {Object} start start data
89-
* @param {Object} end end data
90-
* @param {Object} ast the AST object
91-
* @returns {Object} the loc data
92-
*/
93-
function getLocFor(start, end, ast) {
94-
const startLoc = ast.getLineAndCharacterOfPosition(start),
95-
endLoc = ast.getLineAndCharacterOfPosition(end);
96-
97-
return {
98-
start: {
99-
line: startLoc.line + 1,
100-
column: startLoc.character
101-
},
102-
end: {
103-
line: endLoc.line + 1,
104-
column: endLoc.character
105-
}
106-
};
107-
}
108-
10955
//------------------------------------------------------------------------------
11056
// Parser
11157
//------------------------------------------------------------------------------
@@ -129,7 +75,6 @@ function parse(code, options) {
12975
if (typeof options !== "undefined") {
13076
extra.range = (typeof options.range === "boolean") && options.range;
13177
extra.loc = (typeof options.loc === "boolean") && options.loc;
132-
extra.attachComment = (typeof options.attachComment === "boolean") && options.attachComment;
13378

13479
if (extra.loc && options.source !== null && options.source !== undefined) {
13580
extra.source = toString(options.source);
@@ -145,10 +90,6 @@ function parse(code, options) {
14590
if (typeof options.tolerant === "boolean" && options.tolerant) {
14691
extra.errors = [];
14792
}
148-
if (extra.attachComment) {
149-
extra.range = true;
150-
extra.comments = [];
151-
}
15293

15394
if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") {
15495
// pass through jsx option
@@ -209,45 +150,8 @@ function parse(code, options) {
209150

210151
const ast = program.getSourceFile(FILENAME);
211152

212-
if (extra.attachComment || extra.comment) {
213-
/**
214-
* Create a TypeScript Scanner, with skipTrivia set to false so that
215-
* we can parse the comments
216-
*/
217-
const triviaScanner = ts.createScanner(ast.languageVersion, false, 0, code);
218-
219-
let kind = triviaScanner.scan();
220-
221-
while (kind !== ts.SyntaxKind.EndOfFileToken) {
222-
if (kind !== ts.SyntaxKind.SingleLineCommentTrivia && kind !== ts.SyntaxKind.MultiLineCommentTrivia) {
223-
kind = triviaScanner.scan();
224-
continue;
225-
}
226-
227-
const isBlock = (kind === ts.SyntaxKind.MultiLineCommentTrivia);
228-
const range = {
229-
pos: triviaScanner.getTokenPos(),
230-
end: triviaScanner.getTextPos(),
231-
kind: triviaScanner.getToken()
232-
};
233-
234-
const comment = code.substring(range.pos, range.end);
235-
const text = comment.replace("//", "").replace("/*", "").replace("*/", "");
236-
const loc = getLocFor(range.pos, range.end, ast);
237-
238-
const esprimaComment = convertTypeScriptCommentToEsprimaComment(isBlock, text, range.pos, range.end, loc.start, loc.end);
239-
240-
extra.comments.push(esprimaComment);
241-
242-
kind = triviaScanner.scan();
243-
}
244-
245-
}
246-
247-
const convert = require("./lib/ast-converter");
248-
153+
extra.code = code;
249154
return convert(ast, extra);
250-
251155
}
252156

253157
//------------------------------------------------------------------------------

0 commit comments

Comments
 (0)