UNPKG

14.1 kBJavaScriptView Raw
1var babylonToEspree = require("./babylon-to-espree");
2var Module = require("module");
3var path = require("path");
4var parse = require("babylon").parse;
5var t = require("@babel/types");
6var tt = require("babylon").tokTypes;
7var traverse = require("@babel/traverse").default;
8var codeFrameColumns = require("@babel/code-frame").codeFrameColumns;
9
10var hasPatched = false;
11var eslintOptions = {};
12
13function getModules() {
14 try {
15 // avoid importing a local copy of eslint, try to find a peer dependency
16 var eslintLoc = Module._resolveFilename("eslint", module.parent);
17 } catch (err) {
18 try {
19 // avoids breaking in jest where module.parent is undefined
20 eslintLoc = require.resolve("eslint");
21 } catch (err) {
22 throw new ReferenceError("couldn't resolve eslint");
23 }
24 }
25
26 // get modules relative to what eslint will load
27 var eslintMod = new Module(eslintLoc);
28 eslintMod.filename = eslintLoc;
29 eslintMod.paths = Module._nodeModulePaths(path.dirname(eslintLoc));
30
31 try {
32 var escope = eslintMod.require("eslint-scope");
33 var Definition = eslintMod.require("eslint-scope/lib/definition")
34 .Definition;
35 var referencer = eslintMod.require("eslint-scope/lib/referencer");
36 } catch (err) {
37 escope = eslintMod.require("escope");
38 Definition = eslintMod.require("escope/lib/definition").Definition;
39 referencer = eslintMod.require("escope/lib/referencer");
40 }
41
42 var estraverse = eslintMod.require("estraverse");
43
44 if (referencer.__esModule) referencer = referencer.default;
45
46 return {
47 Definition,
48 escope,
49 estraverse,
50 referencer,
51 };
52}
53
54function monkeypatch(modules) {
55 var Definition = modules.Definition;
56 var escope = modules.escope;
57 var estraverse = modules.estraverse;
58 var referencer = modules.referencer;
59
60 Object.assign(estraverse.VisitorKeys, t.VISITOR_KEYS);
61 estraverse.VisitorKeys.MethodDefinition.push("decorators");
62 estraverse.VisitorKeys.Property.push("decorators");
63
64 var analyze = escope.analyze;
65 escope.analyze = function(ast, opts) {
66 opts = opts || {};
67 opts.ecmaVersion = eslintOptions.ecmaVersion;
68 opts.sourceType = eslintOptions.sourceType;
69 if (eslintOptions.globalReturn !== undefined) {
70 opts.nodejsScope = eslintOptions.globalReturn;
71 }
72
73 var results = analyze.call(this, ast, opts);
74 return results;
75 };
76
77 // if there are decorators, then visit each
78 function visitDecorators(node) {
79 if (!node.decorators) {
80 return;
81 }
82 for (var i = 0; i < node.decorators.length; i++) {
83 if (node.decorators[i].expression) {
84 this.visit(node.decorators[i]);
85 }
86 }
87 }
88
89 // iterate through part of t.VISITOR_KEYS
90 var flowFlippedAliasKeys = t.FLIPPED_ALIAS_KEYS.Flow.concat([
91 "ArrayPattern",
92 "ClassDeclaration",
93 "ClassExpression",
94 "FunctionDeclaration",
95 "FunctionExpression",
96 "Identifier",
97 "ObjectPattern",
98 "RestElement",
99 ]);
100 var visitorKeysMap = Object.keys(t.VISITOR_KEYS).reduce(function(acc, key) {
101 var value = t.VISITOR_KEYS[key];
102 if (flowFlippedAliasKeys.indexOf(value) === -1) {
103 acc[key] = value;
104 }
105 return acc;
106 }, {});
107
108 var propertyTypes = {
109 // loops
110 callProperties: { type: "loop", values: ["value"] },
111 indexers: { type: "loop", values: ["key", "value"] },
112 properties: { type: "loop", values: ["argument", "value"] },
113 types: { type: "loop" },
114 params: { type: "loop" },
115 // single property
116 argument: { type: "single" },
117 elementType: { type: "single" },
118 qualification: { type: "single" },
119 rest: { type: "single" },
120 returnType: { type: "single" },
121 // others
122 typeAnnotation: { type: "typeAnnotation" },
123 typeParameters: { type: "typeParameters" },
124 id: { type: "id" },
125 };
126
127 function visitTypeAnnotation(node) {
128 // get property to check (params, id, etc...)
129 var visitorValues = visitorKeysMap[node.type];
130 if (!visitorValues) {
131 return;
132 }
133
134 // can have multiple properties
135 for (var i = 0; i < visitorValues.length; i++) {
136 var visitorValue = visitorValues[i];
137 var propertyType = propertyTypes[visitorValue];
138 var nodeProperty = node[visitorValue];
139 // check if property or type is defined
140 if (propertyType == null || nodeProperty == null) {
141 continue;
142 }
143 if (propertyType.type === "loop") {
144 for (var j = 0; j < nodeProperty.length; j++) {
145 if (Array.isArray(propertyType.values)) {
146 for (var k = 0; k < propertyType.values.length; k++) {
147 var loopPropertyNode = nodeProperty[j][propertyType.values[k]];
148 if (loopPropertyNode) {
149 checkIdentifierOrVisit.call(this, loopPropertyNode);
150 }
151 }
152 } else {
153 checkIdentifierOrVisit.call(this, nodeProperty[j]);
154 }
155 }
156 } else if (propertyType.type === "single") {
157 checkIdentifierOrVisit.call(this, nodeProperty);
158 } else if (propertyType.type === "typeAnnotation") {
159 visitTypeAnnotation.call(this, node.typeAnnotation);
160 } else if (propertyType.type === "typeParameters") {
161 for (var l = 0; l < node.typeParameters.params.length; l++) {
162 checkIdentifierOrVisit.call(this, node.typeParameters.params[l]);
163 }
164 } else if (propertyType.type === "id") {
165 if (node.id.type === "Identifier") {
166 checkIdentifierOrVisit.call(this, node.id);
167 } else {
168 visitTypeAnnotation.call(this, node.id);
169 }
170 }
171 }
172 }
173
174 function checkIdentifierOrVisit(node) {
175 if (node.typeAnnotation) {
176 visitTypeAnnotation.call(this, node.typeAnnotation);
177 } else if (node.type === "Identifier") {
178 this.visit(node);
179 } else {
180 visitTypeAnnotation.call(this, node);
181 }
182 }
183
184 function nestTypeParamScope(manager, node) {
185 var parentScope = manager.__currentScope;
186 var scope = new escope.Scope(
187 manager,
188 "type-parameters",
189 parentScope,
190 node,
191 false
192 );
193 manager.__nestScope(scope);
194 for (var j = 0; j < node.typeParameters.params.length; j++) {
195 var name = node.typeParameters.params[j];
196 scope.__define(name, new Definition("TypeParameter", name, name));
197 if (name.typeAnnotation) {
198 checkIdentifierOrVisit.call(this, name);
199 }
200 }
201 scope.__define = function() {
202 return parentScope.__define.apply(parentScope, arguments);
203 };
204 return scope;
205 }
206
207 // visit decorators that are in: ClassDeclaration / ClassExpression
208 var visitClass = referencer.prototype.visitClass;
209 referencer.prototype.visitClass = function(node) {
210 visitDecorators.call(this, node);
211 var typeParamScope;
212 if (node.typeParameters) {
213 typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
214 }
215 // visit flow type: ClassImplements
216 if (node.implements) {
217 for (var i = 0; i < node.implements.length; i++) {
218 checkIdentifierOrVisit.call(this, node.implements[i]);
219 }
220 }
221 if (node.superTypeParameters) {
222 for (var k = 0; k < node.superTypeParameters.params.length; k++) {
223 checkIdentifierOrVisit.call(this, node.superTypeParameters.params[k]);
224 }
225 }
226 visitClass.call(this, node);
227 if (typeParamScope) {
228 this.close(node);
229 }
230 };
231
232 // visit decorators that are in: Property / MethodDefinition
233 var visitProperty = referencer.prototype.visitProperty;
234 referencer.prototype.visitProperty = function(node) {
235 if (node.value && node.value.type === "TypeCastExpression") {
236 visitTypeAnnotation.call(this, node.value);
237 }
238 visitDecorators.call(this, node);
239 visitProperty.call(this, node);
240 };
241
242 function visitClassProperty(node) {
243 if (node.typeAnnotation) {
244 visitTypeAnnotation.call(this, node.typeAnnotation);
245 }
246 this.visitProperty(node);
247 }
248
249 // visit ClassProperty as a Property.
250 referencer.prototype.ClassProperty = visitClassProperty;
251
252 // visit ClassPrivateProperty as a Property.
253 referencer.prototype.ClassPrivateProperty = visitClassProperty;
254
255 // visit flow type in FunctionDeclaration, FunctionExpression, ArrowFunctionExpression
256 var visitFunction = referencer.prototype.visitFunction;
257 referencer.prototype.visitFunction = function(node) {
258 var typeParamScope;
259 if (node.typeParameters) {
260 typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
261 }
262 if (node.returnType) {
263 checkIdentifierOrVisit.call(this, node.returnType);
264 }
265 // only visit if function parameters have types
266 if (node.params) {
267 for (var i = 0; i < node.params.length; i++) {
268 var param = node.params[i];
269 if (param.typeAnnotation) {
270 checkIdentifierOrVisit.call(this, param);
271 } else if (t.isAssignmentPattern(param)) {
272 if (param.left.typeAnnotation) {
273 checkIdentifierOrVisit.call(this, param.left);
274 }
275 }
276 }
277 }
278 // set ArrayPattern/ObjectPattern visitor keys back to their original. otherwise
279 // escope will traverse into them and include the identifiers within as declarations
280 estraverse.VisitorKeys.ObjectPattern = ["properties"];
281 estraverse.VisitorKeys.ArrayPattern = ["elements"];
282 visitFunction.call(this, node);
283 // set them back to normal...
284 estraverse.VisitorKeys.ObjectPattern = t.VISITOR_KEYS.ObjectPattern;
285 estraverse.VisitorKeys.ArrayPattern = t.VISITOR_KEYS.ArrayPattern;
286 if (typeParamScope) {
287 this.close(node);
288 }
289 };
290
291 // visit flow type in VariableDeclaration
292 var variableDeclaration = referencer.prototype.VariableDeclaration;
293 referencer.prototype.VariableDeclaration = function(node) {
294 if (node.declarations) {
295 for (var i = 0; i < node.declarations.length; i++) {
296 var id = node.declarations[i].id;
297 var typeAnnotation = id.typeAnnotation;
298 if (typeAnnotation) {
299 checkIdentifierOrVisit.call(this, typeAnnotation);
300 }
301 }
302 }
303 variableDeclaration.call(this, node);
304 };
305
306 function createScopeVariable(node, name) {
307 this.currentScope().variableScope.__define(
308 name,
309 new Definition("Variable", name, node, null, null, null)
310 );
311 }
312
313 referencer.prototype.InterfaceDeclaration = function(node) {
314 createScopeVariable.call(this, node, node.id);
315 var typeParamScope;
316 if (node.typeParameters) {
317 typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
318 }
319 // TODO: Handle mixins
320 for (var i = 0; i < node.extends.length; i++) {
321 visitTypeAnnotation.call(this, node.extends[i]);
322 }
323 visitTypeAnnotation.call(this, node.body);
324 if (typeParamScope) {
325 this.close(node);
326 }
327 };
328
329 referencer.prototype.TypeAlias = function(node) {
330 createScopeVariable.call(this, node, node.id);
331 var typeParamScope;
332 if (node.typeParameters) {
333 typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
334 }
335 if (node.right) {
336 visitTypeAnnotation.call(this, node.right);
337 }
338 if (typeParamScope) {
339 this.close(node);
340 }
341 };
342
343 referencer.prototype.DeclareModule = referencer.prototype.DeclareFunction = referencer.prototype.DeclareVariable = referencer.prototype.DeclareClass = function(
344 node
345 ) {
346 if (node.id) {
347 createScopeVariable.call(this, node, node.id);
348 }
349
350 var typeParamScope;
351 if (node.typeParameters) {
352 typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node);
353 }
354 if (typeParamScope) {
355 this.close(node);
356 }
357 };
358}
359
360exports.parse = function(code, options) {
361 options = options || {};
362 eslintOptions.ecmaVersion = options.ecmaVersion = options.ecmaVersion || 6;
363 eslintOptions.sourceType = options.sourceType =
364 options.sourceType || "module";
365 eslintOptions.allowImportExportEverywhere = options.allowImportExportEverywhere =
366 options.allowImportExportEverywhere || false;
367 if (options.sourceType === "module") {
368 eslintOptions.globalReturn = false;
369 } else {
370 delete eslintOptions.globalReturn;
371 }
372
373 if (!hasPatched) {
374 hasPatched = true;
375 try {
376 monkeypatch(getModules());
377 } catch (err) {
378 console.error(err.stack);
379 process.exit(1);
380 }
381 }
382
383 return exports.parseNoPatch(code, options);
384};
385
386exports.parseNoPatch = function(code, options) {
387 var opts = {
388 codeFrame: options.hasOwnProperty("codeFrame") ? options.codeFrame : true,
389 sourceType: options.sourceType,
390 allowImportExportEverywhere: options.allowImportExportEverywhere, // consistent with espree
391 allowReturnOutsideFunction: true,
392 allowSuperOutsideMethod: true,
393 ranges: true,
394 tokens: true,
395 plugins: [
396 "flow",
397 "jsx",
398 "estree",
399 "asyncFunctions",
400 "asyncGenerators",
401 "classConstructorCall",
402 "classProperties",
403 "decorators",
404 "doExpressions",
405 "exponentiationOperator",
406 "exportExtensions",
407 "functionBind",
408 "functionSent",
409 "objectRestSpread",
410 "trailingFunctionCommas",
411 "dynamicImport",
412 "numericSeparator",
413 "optionalChaining",
414 "importMeta",
415 "classPrivateProperties",
416 "bigInt",
417 "optionalCatchBinding",
418 ],
419 };
420
421 var ast;
422 try {
423 ast = parse(code, opts);
424 } catch (err) {
425 if (err instanceof SyntaxError) {
426 err.lineNumber = err.loc.line;
427 err.column = err.loc.column;
428
429 if (opts.codeFrame) {
430 err.lineNumber = err.loc.line;
431 err.column = err.loc.column + 1;
432
433 // remove trailing "(LINE:COLUMN)" acorn message and add in esprima syntax error message start
434 err.message =
435 "Line " +
436 err.lineNumber +
437 ": " +
438 err.message.replace(/ \((\d+):(\d+)\)$/, "") +
439 // add codeframe
440 "\n\n" +
441 codeFrameColumns(
442 code,
443 {
444 start: {
445 line: err.lineNumber,
446 column: err.column,
447 },
448 },
449 { highlightCode: true }
450 );
451 }
452 }
453
454 throw err;
455 }
456
457 babylonToEspree(ast, traverse, tt, code);
458
459 return ast;
460};