-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathresolve.js
182 lines (147 loc) · 4.45 KB
/
resolve.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import 'es6-symbol/implement'
import Map from 'es6-map'
import Set from 'es6-set'
import assign from 'object-assign'
import fs from 'fs'
import { dirname, basename, join } from 'path'
export const CASE_INSENSITIVE = fs.existsSync(join(__dirname, 'reSOLVE.js'))
const fileExistsCache = new Map()
function cachePath(cacheKey, result) {
fileExistsCache.set(cacheKey, { result, lastSeen: Date.now() })
}
function checkCache(cacheKey, { lifetime }) {
if (fileExistsCache.has(cacheKey)) {
const { result, lastSeen } = fileExistsCache.get(cacheKey)
// check fresness
if (Date.now() - lastSeen < (lifetime * 1000)) return result
}
// cache miss
return undefined
}
// http://stackoverflow.com/a/27382838
function fileExistsWithCaseSync(filepath, cacheSettings) {
const dir = dirname(filepath)
let result = checkCache(filepath, cacheSettings)
if (result != null) return result
// base case
if (dir === '/' || dir === '.' || /^[A-Z]:\\$/i.test(dir)) {
result = true
} else {
const filenames = fs.readdirSync(dir)
if (filenames.indexOf(basename(filepath)) === -1) {
result = false
} else {
result = fileExistsWithCaseSync(dir, cacheSettings)
}
}
cachePath(filepath, result)
return result
}
export function relative(modulePath, sourceFile, settings) {
const sourceDir = dirname(sourceFile)
, cacheKey = sourceDir + hashObject(settings) + modulePath
const cacheSettings = assign({
lifetime: 30, // seconds
}, settings['import/cache'])
// parse infinity
if (cacheSettings.lifetime === '∞' || cacheSettings.lifetime === 'Infinity') {
cacheSettings.lifetime = Infinity
}
const cachedPath = checkCache(cacheKey, cacheSettings)
if (cachedPath !== undefined) return cachedPath
function cache(path) {
cachePath(cacheKey, path)
return path
}
function withResolver(resolver, config) {
function v1() {
try {
const path = resolver.resolveImport(modulePath, sourceFile, config)
if (path === undefined) return { found: false }
return { found: true, path }
} catch (err) {
return { found: false }
}
}
function v2() {
return resolver.resolve(modulePath, sourceFile, config)
}
switch (resolver.interfaceVersion) {
case 2:
return v2()
default:
case 1:
return v1()
}
}
const configResolvers = (settings['import/resolver']
|| { 'node': settings['import/resolve'] }) // backward compatibility
const resolvers = resolverReducer(configResolvers, new Map())
for (let [name, config] of resolvers) {
const resolver = requireResolver(name)
let { path: fullPath, found } = withResolver(resolver, config)
// resolvers imply file existence, this double-check just ensures the case matches
if (found && CASE_INSENSITIVE && !fileExistsWithCaseSync(fullPath, cacheSettings)) {
// reject resolved path
fullPath = undefined
}
if (found) return cache(fullPath)
}
return cache(undefined)
}
function resolverReducer(resolvers, map) {
if (resolvers instanceof Array) {
resolvers.forEach(r => resolverReducer(r, map))
return map
}
if (typeof resolvers === 'string') {
map.set(resolvers, null)
return map
}
if (typeof resolvers === 'object') {
for (let key in resolvers) {
map.set(key, resolvers[key])
}
return map
}
throw new Error('invalid resolver config')
}
function requireResolver(name) {
try {
return require(`eslint-import-resolver-${name}`)
} catch (err) {
throw new Error(`unable to load resolver "${name}".`)
}
}
const erroredContexts = new Set()
/**
* Given
* @param {string} p - module path
* @param {object} context - ESLint context
* @return {string} - the full module filesystem path;
* null if package is core;
* undefined if not found
*/
export default function resolve(p, context) {
try {
return relative( p
, context.getFilename()
, context.settings
)
} catch (err) {
if (!erroredContexts.has(context)) {
context.report({
message: `Resolve error: ${err.message}`,
loc: { line: 1, col: 0 },
})
erroredContexts.add(context)
}
}
}
resolve.relative = relative
import { createHash } from 'crypto'
function hashObject(object) {
const settingsShasum = createHash('sha1')
settingsShasum.update(JSON.stringify(object))
return settingsShasum.digest('hex')
}