1 | "use strict";
|
2 |
|
3 |
|
4 | const TokenTranslator = require("./token-translator");
|
5 |
|
6 | const DEFAULT_ECMA_VERSION = 5;
|
7 | const STATE = Symbol("espree's internal state");
|
8 | const ESPRIMA_FINISH_NODE = Symbol("espree's esprimaFinishNode");
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | function normalizeEcmaVersion(ecmaVersion = DEFAULT_ECMA_VERSION) {
|
17 | if (typeof ecmaVersion !== "number") {
|
18 | throw new Error(`ecmaVersion must be a number. Received value of type ${typeof ecmaVersion} instead.`);
|
19 | }
|
20 |
|
21 | let version = ecmaVersion;
|
22 |
|
23 |
|
24 |
|
25 | if (version >= 2015) {
|
26 | version -= 2009;
|
27 | }
|
28 |
|
29 | switch (version) {
|
30 | case 3:
|
31 | case 5:
|
32 | case 6:
|
33 | case 7:
|
34 | case 8:
|
35 | case 9:
|
36 | case 10:
|
37 | case 11:
|
38 | return version;
|
39 |
|
40 |
|
41 | }
|
42 |
|
43 | throw new Error("Invalid ecmaVersion.");
|
44 | }
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 | function normalizeSourceType(sourceType = "script") {
|
53 | if (sourceType === "script" || sourceType === "module") {
|
54 | return sourceType;
|
55 | }
|
56 | throw new Error("Invalid sourceType.");
|
57 | }
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 | function normalizeOptions(options) {
|
66 | const ecmaVersion = normalizeEcmaVersion(options.ecmaVersion);
|
67 | const sourceType = normalizeSourceType(options.sourceType);
|
68 | const ranges = options.range === true;
|
69 | const locations = options.loc === true;
|
70 |
|
71 | if (sourceType === "module" && ecmaVersion < 6) {
|
72 | throw new Error("sourceType 'module' is not supported when ecmaVersion < 2015. Consider adding `{ ecmaVersion: 2015 }` to the parser options.");
|
73 | }
|
74 | return Object.assign({}, options, { ecmaVersion, sourceType, ranges, locations });
|
75 | }
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 | function convertAcornCommentToEsprimaComment(block, text, start, end, startLoc, endLoc) {
|
89 | const comment = {
|
90 | type: block ? "Block" : "Line",
|
91 | value: text
|
92 | };
|
93 |
|
94 | if (typeof start === "number") {
|
95 | comment.start = start;
|
96 | comment.end = end;
|
97 | comment.range = [start, end];
|
98 | }
|
99 |
|
100 | if (typeof startLoc === "object") {
|
101 | comment.loc = {
|
102 | start: startLoc,
|
103 | end: endLoc
|
104 | };
|
105 | }
|
106 |
|
107 | return comment;
|
108 | }
|
109 |
|
110 | module.exports = () => Parser => {
|
111 | const tokTypes = Object.assign({}, Parser.acorn.tokTypes);
|
112 |
|
113 | if (Parser.acornJsx) {
|
114 | Object.assign(tokTypes, Parser.acornJsx.tokTypes);
|
115 | }
|
116 |
|
117 | return class Espree extends Parser {
|
118 | constructor(opts, code) {
|
119 | if (typeof opts !== "object" || opts === null) {
|
120 | opts = {};
|
121 | }
|
122 | if (typeof code !== "string" && !(code instanceof String)) {
|
123 | code = String(code);
|
124 | }
|
125 |
|
126 | const options = normalizeOptions(opts);
|
127 | const ecmaFeatures = options.ecmaFeatures || {};
|
128 | const tokenTranslator =
|
129 | options.tokens === true
|
130 | ? new TokenTranslator(tokTypes, code)
|
131 | : null;
|
132 |
|
133 |
|
134 | super({
|
135 |
|
136 |
|
137 | ecmaVersion: options.ecmaVersion,
|
138 | sourceType: options.sourceType,
|
139 | ranges: options.ranges,
|
140 | locations: options.locations,
|
141 |
|
142 |
|
143 | allowReturnOutsideFunction: Boolean(ecmaFeatures.globalReturn),
|
144 |
|
145 |
|
146 | onToken: token => {
|
147 | if (tokenTranslator) {
|
148 |
|
149 |
|
150 | tokenTranslator.onToken(token, this[STATE]);
|
151 | }
|
152 | if (token.type !== tokTypes.eof) {
|
153 | this[STATE].lastToken = token;
|
154 | }
|
155 | },
|
156 |
|
157 |
|
158 | onComment: (block, text, start, end, startLoc, endLoc) => {
|
159 | if (this[STATE].comments) {
|
160 | const comment = convertAcornCommentToEsprimaComment(block, text, start, end, startLoc, endLoc);
|
161 |
|
162 | this[STATE].comments.push(comment);
|
163 | }
|
164 | }
|
165 | }, code);
|
166 |
|
167 |
|
168 | this[STATE] = {
|
169 | tokens: tokenTranslator ? [] : null,
|
170 | comments: options.comment === true ? [] : null,
|
171 | impliedStrict: ecmaFeatures.impliedStrict === true && this.options.ecmaVersion >= 5,
|
172 | ecmaVersion: this.options.ecmaVersion,
|
173 | jsxAttrValueToken: false,
|
174 | lastToken: null
|
175 | };
|
176 | }
|
177 |
|
178 | tokenize() {
|
179 | do {
|
180 | this.next();
|
181 | } while (this.type !== tokTypes.eof);
|
182 |
|
183 |
|
184 | this.next();
|
185 |
|
186 | const extra = this[STATE];
|
187 | const tokens = extra.tokens;
|
188 |
|
189 | if (extra.comments) {
|
190 | tokens.comments = extra.comments;
|
191 | }
|
192 |
|
193 | return tokens;
|
194 | }
|
195 |
|
196 | finishNode(...args) {
|
197 | const result = super.finishNode(...args);
|
198 |
|
199 | return this[ESPRIMA_FINISH_NODE](result);
|
200 | }
|
201 |
|
202 | finishNodeAt(...args) {
|
203 | const result = super.finishNodeAt(...args);
|
204 |
|
205 | return this[ESPRIMA_FINISH_NODE](result);
|
206 | }
|
207 |
|
208 | parse() {
|
209 | const extra = this[STATE];
|
210 | const program = super.parse();
|
211 |
|
212 | program.sourceType = this.options.sourceType;
|
213 |
|
214 | if (extra.comments) {
|
215 | program.comments = extra.comments;
|
216 | }
|
217 | if (extra.tokens) {
|
218 | program.tokens = extra.tokens;
|
219 | }
|
220 |
|
221 | |
222 |
|
223 |
|
224 |
|
225 |
|
226 |
|
227 |
|
228 | if (program.range) {
|
229 | program.range[0] = program.body.length ? program.body[0].range[0] : program.range[0];
|
230 | program.range[1] = extra.lastToken ? extra.lastToken.range[1] : program.range[1];
|
231 | }
|
232 | if (program.loc) {
|
233 | program.loc.start = program.body.length ? program.body[0].loc.start : program.loc.start;
|
234 | program.loc.end = extra.lastToken ? extra.lastToken.loc.end : program.loc.end;
|
235 | }
|
236 |
|
237 | return program;
|
238 | }
|
239 |
|
240 | parseTopLevel(node) {
|
241 | if (this[STATE].impliedStrict) {
|
242 | this.strict = true;
|
243 | }
|
244 | return super.parseTopLevel(node);
|
245 | }
|
246 |
|
247 | |
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 | raise(pos, message) {
|
255 | const loc = Parser.acorn.getLineInfo(this.input, pos);
|
256 | const err = new SyntaxError(message);
|
257 |
|
258 | err.index = pos;
|
259 | err.lineNumber = loc.line;
|
260 | err.column = loc.column + 1;
|
261 | throw err;
|
262 | }
|
263 |
|
264 | |
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 | raiseRecoverable(pos, message) {
|
272 | this.raise(pos, message);
|
273 | }
|
274 |
|
275 | |
276 |
|
277 |
|
278 |
|
279 |
|
280 |
|
281 | unexpected(pos) {
|
282 | let message = "Unexpected token";
|
283 |
|
284 | if (pos !== null && pos !== void 0) {
|
285 | this.pos = pos;
|
286 |
|
287 | if (this.options.locations) {
|
288 | while (this.pos < this.lineStart) {
|
289 | this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1;
|
290 | --this.curLine;
|
291 | }
|
292 | }
|
293 |
|
294 | this.nextToken();
|
295 | }
|
296 |
|
297 | if (this.end > this.start) {
|
298 | message += ` ${this.input.slice(this.start, this.end)}`;
|
299 | }
|
300 |
|
301 | this.raise(this.start, message);
|
302 | }
|
303 |
|
304 | |
305 |
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 | jsx_readString(quote) {
|
312 | const result = super.jsx_readString(quote);
|
313 |
|
314 | if (this.type === tokTypes.string) {
|
315 | this[STATE].jsxAttrValueToken = true;
|
316 | }
|
317 | return result;
|
318 | }
|
319 |
|
320 | |
321 |
|
322 |
|
323 |
|
324 |
|
325 | [ESPRIMA_FINISH_NODE](result) {
|
326 |
|
327 |
|
328 |
|
329 | if (result.type === "TemplateElement") {
|
330 |
|
331 |
|
332 | const terminalDollarBraceL = this.input.slice(result.end, result.end + 2) === "${";
|
333 |
|
334 | if (result.range) {
|
335 | result.range[0]--;
|
336 | result.range[1] += (terminalDollarBraceL ? 2 : 1);
|
337 | }
|
338 |
|
339 | if (result.loc) {
|
340 | result.loc.start.column--;
|
341 | result.loc.end.column += (terminalDollarBraceL ? 2 : 1);
|
342 | }
|
343 | }
|
344 |
|
345 | if (result.type.indexOf("Function") > -1 && !result.generator) {
|
346 | result.generator = false;
|
347 | }
|
348 |
|
349 | return result;
|
350 | }
|
351 | };
|
352 | };
|