UNPKG

4.5 kBJavaScriptView Raw
1"use strict"
2exports.__esModule = true
3
4const fs = require('fs')
5const path = require('path')
6
7const hashObject = require('./hash').hashObject
8
9const CASE_INSENSITIVE = fs.existsSync(path.join(__dirname, 'reSOLVE.js'))
10exports.CASE_INSENSITIVE = CASE_INSENSITIVE
11
12const fileExistsCache = new Map()
13
14function cachePath(cacheKey, result) {
15 fileExistsCache.set(cacheKey, { result, lastSeen: Date.now() })
16}
17
18function checkCache(cacheKey, settings) {
19 if (fileExistsCache.has(cacheKey)) {
20 const f = fileExistsCache.get(cacheKey)
21 // check fresness
22 if (Date.now() - f.lastSeen < (settings.lifetime * 1000)) return f.result
23 }
24 // cache miss
25 return undefined
26}
27
28// http://stackoverflow.com/a/27382838
29function fileExistsWithCaseSync(filepath, cacheSettings) {
30 const dir = path.dirname(filepath)
31
32 let result = checkCache(filepath, cacheSettings)
33 if (result != null) return result
34
35 // base case
36 if (dir === '/' || dir === '.' || /^[A-Z]:\\$/i.test(dir)) {
37 result = true
38 } else {
39 const filenames = fs.readdirSync(dir)
40 if (filenames.indexOf(path.basename(filepath)) === -1) {
41 result = false
42 } else {
43 result = fileExistsWithCaseSync(dir, cacheSettings)
44 }
45 }
46 cachePath(filepath, result)
47 return result
48}
49
50function relative(modulePath, sourceFile, settings) {
51
52 const sourceDir = path.dirname(sourceFile)
53 , cacheKey = sourceDir + hashObject(settings).digest('hex') + modulePath
54
55 const cacheSettings = Object.assign({
56 lifetime: 30, // seconds
57 }, settings['import/cache'])
58
59 // parse infinity
60 if (cacheSettings.lifetime === '∞' || cacheSettings.lifetime === 'Infinity') {
61 cacheSettings.lifetime = Infinity
62 }
63
64 const cachedPath = checkCache(cacheKey, cacheSettings)
65 if (cachedPath !== undefined) return cachedPath
66
67 function cache(p) {
68 cachePath(cacheKey, p)
69 return p
70 }
71
72 function withResolver(resolver, config) {
73
74 function v1() {
75 try {
76 const path = resolver.resolveImport(modulePath, sourceFile, config)
77 if (path === undefined) return { found: false }
78 return { found: true, path }
79 } catch (err) {
80 return { found: false }
81 }
82 }
83
84 function v2() {
85 return resolver.resolve(modulePath, sourceFile, config)
86 }
87
88 switch (resolver.interfaceVersion) {
89 case 2:
90 return v2()
91
92 default:
93 case 1:
94 return v1()
95 }
96 }
97
98 const configResolvers = (settings['import/resolver']
99 || { 'node': settings['import/resolve'] }) // backward compatibility
100
101 const resolvers = resolverReducer(configResolvers, new Map())
102
103 for (let pair of resolvers) {
104 let name = pair[0]
105 , config = pair[1]
106
107 const resolver = requireResolver(name)
108
109 const resolved = withResolver(resolver, config)
110 let resolvedPath = resolved.path
111
112 // resolvers imply file existence, this double-check just ensures the case matches
113 if (resolved.found && CASE_INSENSITIVE && !fileExistsWithCaseSync(resolvedPath, cacheSettings)) {
114 // reject resolved path
115 resolvedPath = undefined
116 }
117
118 if (resolved.found) return cache(resolvedPath)
119 }
120
121 return cache(undefined)
122}
123exports.relative = relative
124
125function resolverReducer(resolvers, map) {
126 if (resolvers instanceof Array) {
127 resolvers.forEach(r => resolverReducer(r, map))
128 return map
129 }
130
131 if (typeof resolvers === 'string') {
132 map.set(resolvers, null)
133 return map
134 }
135
136 if (typeof resolvers === 'object') {
137 for (let key in resolvers) {
138 map.set(key, resolvers[key])
139 }
140 return map
141 }
142
143 throw new Error('invalid resolver config')
144}
145
146function requireResolver(name) {
147 try {
148 return require(`eslint-import-resolver-${name}`)
149 } catch (err) {
150 throw new Error(`unable to load resolver "${name}".`)
151 }
152}
153
154const erroredContexts = new Set()
155
156/**
157 * Given
158 * @param {string} p - module path
159 * @param {object} context - ESLint context
160 * @return {string} - the full module filesystem path;
161 * null if package is core;
162 * undefined if not found
163 */
164function resolve(p, context) {
165 try {
166 return relative( p
167 , context.getFilename()
168 , context.settings
169 )
170 } catch (err) {
171 if (!erroredContexts.has(context)) {
172 context.report({
173 message: `Resolve error: ${err.message}`,
174 loc: { line: 1, col: 0 },
175 })
176 erroredContexts.add(context)
177 }
178 }
179}
180resolve.relative = relative
181exports.default = resolve