blob: a771544ea79c8f941074ccce825f26f88019b956 [file] [log] [blame]
Tim van der Lippe2c891972021-07-29 16:22:50 +01001'use strict';
2exports.__esModule = true;
Tim van der Lippefdbd42e2020-04-07 15:14:36 +01003
Tim van der Lippe2c891972021-07-29 16:22:50 +01004const moduleRequire = require('./module-require').default;
5const extname = require('path').extname;
Tim van der Lippebc3a0b72021-11-08 15:22:37 +00006const fs = require('fs');
Tim van der Lippefdbd42e2020-04-07 15:14:36 +01007
Tim van der Lippe2c891972021-07-29 16:22:50 +01008const log = require('debug')('eslint-plugin-import:parse');
Tim van der Lippefdbd42e2020-04-07 15:14:36 +01009
Tim van der Lippebc3a0b72021-11-08 15:22:37 +000010function getBabelVisitorKeys(parserPath) {
11 if (parserPath.endsWith('index.js')) {
12 const hypotheticalLocation = parserPath.replace('index.js', 'visitor-keys.js');
13 if (fs.existsSync(hypotheticalLocation)) {
14 const keys = moduleRequire(hypotheticalLocation);
15 return keys.default || keys;
16 }
17 } else if (parserPath.endsWith('index.cjs')) {
18 const hypotheticalLocation = parserPath.replace('index.cjs', 'worker/ast-info.cjs');
19 if (fs.existsSync(hypotheticalLocation)) {
20 const astInfo = moduleRequire(hypotheticalLocation);
21 return astInfo.getVisitorKeys();
22 }
23 }
24 return null;
25}
26
27function keysFromParser(parserPath, parserInstance, parsedResult) {
28 if (/.*espree.*/.test(parserPath)) {
29 return parserInstance.VisitorKeys;
30 }
31 if (/.*(babel-eslint|@babel\/eslint-parser).*/.test(parserPath)) {
32 return getBabelVisitorKeys(parserPath);
33 }
34 if (/.*@typescript-eslint\/parser/.test(parserPath)) {
35 if (parsedResult) {
36 return parsedResult.visitorKeys;
37 }
38 }
39 return null;
40}
41
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010042exports.default = function parse(path, content, context) {
43
Tim van der Lippe2c891972021-07-29 16:22:50 +010044 if (context == null) throw new Error('need context to parse properly');
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010045
Tim van der Lippe2c891972021-07-29 16:22:50 +010046 let parserOptions = context.parserOptions;
47 const parserPath = getParserPath(path, context);
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010048
Tim van der Lippe2c891972021-07-29 16:22:50 +010049 if (!parserPath) throw new Error('parserPath is required!');
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010050
51 // hack: espree blows up with frozen options
Tim van der Lippe2c891972021-07-29 16:22:50 +010052 parserOptions = Object.assign({}, parserOptions);
53 parserOptions.ecmaFeatures = Object.assign({}, parserOptions.ecmaFeatures);
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010054
55 // always include comments and tokens (for doc parsing)
Tim van der Lippe2c891972021-07-29 16:22:50 +010056 parserOptions.comment = true;
57 parserOptions.attachComment = true; // keeping this for backward-compat with older parsers
58 parserOptions.tokens = true;
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010059
60 // attach node locations
Tim van der Lippe2c891972021-07-29 16:22:50 +010061 parserOptions.loc = true;
62 parserOptions.range = true;
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010063
64 // provide the `filePath` like eslint itself does, in `parserOptions`
65 // https://github.com/eslint/eslint/blob/3ec436ee/lib/linter.js#L637
Tim van der Lippe2c891972021-07-29 16:22:50 +010066 parserOptions.filePath = path;
67
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010068 // @typescript-eslint/parser will parse the entire project with typechecking if you provide
69 // "project" or "projects" in parserOptions. Removing these options means the parser will
70 // only parse one file in isolate mode, which is much, much faster.
Tim van der Lippea6619412021-09-13 14:28:55 +020071 // https://github.com/import-js/eslint-plugin-import/issues/1408#issuecomment-509298962
Tim van der Lippe2c891972021-07-29 16:22:50 +010072 delete parserOptions.project;
73 delete parserOptions.projects;
74
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010075 // require the parser relative to the main module (i.e., ESLint)
Tim van der Lippe2c891972021-07-29 16:22:50 +010076 const parser = moduleRequire(parserPath);
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010077
78 if (typeof parser.parseForESLint === 'function') {
Tim van der Lippe2c891972021-07-29 16:22:50 +010079 let ast;
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010080 try {
Tim van der Lippebc3a0b72021-11-08 15:22:37 +000081 const parserRaw = parser.parseForESLint(content, parserOptions);
82 ast = parserRaw.ast;
83 return {
84 ast,
85 visitorKeys: keysFromParser(parserPath, parser, parserRaw),
86 };
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010087 } catch (e) {
Tim van der Lippe2c891972021-07-29 16:22:50 +010088 console.warn();
89 console.warn('Error while parsing ' + parserOptions.filePath);
90 console.warn('Line ' + e.lineNumber + ', column ' + e.column + ': ' + e.message);
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010091 }
92 if (!ast || typeof ast !== 'object') {
Tim van der Lippebc3a0b72021-11-08 15:22:37 +000093 console.warn(
94 '`parseForESLint` from parser `' +
95 parserPath +
96 '` is invalid and will just be ignored'
97 );
Tim van der Lippefdbd42e2020-04-07 15:14:36 +010098 } else {
Tim van der Lippebc3a0b72021-11-08 15:22:37 +000099 return {
100 ast,
101 visitorKeys: keysFromParser(parserPath, parser, undefined),
102 };
Tim van der Lippefdbd42e2020-04-07 15:14:36 +0100103 }
104 }
105
Tim van der Lippebc3a0b72021-11-08 15:22:37 +0000106 const keys = keysFromParser(parserPath, parser, undefined);
107 return {
108 ast: parser.parse(content, parserOptions),
109 visitorKeys: keys,
110 };
Tim van der Lippe2c891972021-07-29 16:22:50 +0100111};
Tim van der Lippefdbd42e2020-04-07 15:14:36 +0100112
113function getParserPath(path, context) {
Tim van der Lippe2c891972021-07-29 16:22:50 +0100114 const parsers = context.settings['import/parsers'];
Tim van der Lippefdbd42e2020-04-07 15:14:36 +0100115 if (parsers != null) {
Tim van der Lippe2c891972021-07-29 16:22:50 +0100116 const extension = extname(path);
117 for (const parserPath in parsers) {
Tim van der Lippefdbd42e2020-04-07 15:14:36 +0100118 if (parsers[parserPath].indexOf(extension) > -1) {
119 // use this alternate parser
Tim van der Lippe2c891972021-07-29 16:22:50 +0100120 log('using alt parser:', parserPath);
121 return parserPath;
Tim van der Lippefdbd42e2020-04-07 15:14:36 +0100122 }
123 }
124 }
125 // default to use ESLint parser
Tim van der Lippe2c891972021-07-29 16:22:50 +0100126 return context.parserPath;
Tim van der Lippefdbd42e2020-04-07 15:14:36 +0100127}