/
parse.js
120 lines (102 loc) · 3.69 KB
/
parse.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
'use strict'
exports.__esModule = true
const moduleRequire = require('./module-require').default
const extname = require('path').extname
const fs = require('fs')
const log = require('debug')('eslint-plugin-import:parse')
function getBabelVisitorKeys(parserPath) {
const hypotheticalLocation = parserPath.replace('index.js', 'visitor-keys.js')
if (fs.existsSync(hypotheticalLocation)) {
const keys = moduleRequire(parserPath.replace('index.js', 'visitor-keys.js'))
return keys
} else {
return null
}
}
function keysFromParser(parserPath, parserInstance, parsedResult) {
if (/.*espree.*/.test(parserPath)) {
return parserInstance.VisitorKeys
} else if (/.*babel-eslint.*/.test(parserPath)) {
return getBabelVisitorKeys(parserPath)
} else if (/.*@typescript-eslint\/parser/.test(parserPath)) {
if (parsedResult) {
return parsedResult.visitorKeys
}
}
return null
}
exports.default = function parse(path, content, context) {
if (context == null) throw new Error('need context to parse properly')
let parserOptions = context.parserOptions
const parserPath = getParserPath(path, context)
if (!parserPath) throw new Error('parserPath is required!')
// hack: espree blows up with frozen options
parserOptions = Object.assign({}, parserOptions)
parserOptions.ecmaFeatures = Object.assign({}, parserOptions.ecmaFeatures)
// always include comments and tokens (for doc parsing)
parserOptions.comment = true
parserOptions.attachComment = true // keeping this for backward-compat with older parsers
parserOptions.tokens = true
// attach node locations
parserOptions.loc = true
parserOptions.range = true
// provide the `filePath` like eslint itself does, in `parserOptions`
// https://github.com/eslint/eslint/blob/3ec436ee/lib/linter.js#L637
parserOptions.filePath = path
// @typescript-eslint/parser will parse the entire project with typechecking if you provide
// "project" or "projects" in parserOptions. Removing these options means the parser will
// only parse one file in isolate mode, which is much, much faster.
// https://github.com/benmosher/eslint-plugin-import/issues/1408#issuecomment-509298962
delete parserOptions.project
delete parserOptions.projects
// require the parser relative to the main module (i.e., ESLint)
const parser = moduleRequire(parserPath)
if (typeof parser.parseForESLint === 'function') {
let ast
try {
const parserRaw = parser.parseForESLint(content, parserOptions)
ast = parserRaw.ast
return {
ast,
visitorKeys: keysFromParser(parserPath, parser, parserRaw),
}
} catch (e) {
console.warn()
console.warn('Error while parsing ' + parserOptions.filePath)
console.warn('Line ' + e.lineNumber + ', column ' + e.column + ': ' + e.message)
}
if (!ast || typeof ast !== 'object') {
console.warn(
'`parseForESLint` from parser `' +
parserPath +
'` is invalid and will just be ignored',
path
)
} else {
return {
ast,
visitorKeys: keysFromParser(parserPath, parser, undefined),
}
}
}
const keys = keysFromParser(parserPath, parser, undefined)
return {
ast: parser.parse(content, parserOptions),
visitorKeys: keys,
}
}
function getParserPath(path, context) {
const parsers = context.settings['import/parsers']
if (parsers != null) {
const extension = extname(path)
for (let parserPath in parsers) {
if (parsers[parserPath].indexOf(extension) > -1) {
// use this alternate parser
log('using alt parser:', parserPath)
return parserPath
}
}
}
// default to use ESLint parser
return context.parserPath
}