Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/eslint-plugin-import
Path: blob/main/utils/resolve.js
828 views
1
'use strict';
2
exports.__esModule = true;
3
4
const pkgDir = require('pkg-dir');
5
6
const fs = require('fs');
7
const Module = require('module');
8
const path = require('path');
9
10
const hashObject = require('./hash').hashObject;
11
const ModuleCache = require('./ModuleCache').default;
12
13
const CASE_SENSITIVE_FS = !fs.existsSync(path.join(__dirname.toUpperCase(), 'reSOLVE.js'));
14
exports.CASE_SENSITIVE_FS = CASE_SENSITIVE_FS;
15
16
const ERROR_NAME = 'EslintPluginImportResolveError';
17
18
const fileExistsCache = new ModuleCache();
19
20
// Polyfill Node's `Module.createRequireFromPath` if not present (added in Node v10.12.0)
21
// Use `Module.createRequire` if available (added in Node v12.2.0)
22
const createRequire = Module.createRequire || Module.createRequireFromPath || function (filename) {
23
const mod = new Module(filename, null);
24
mod.filename = filename;
25
mod.paths = Module._nodeModulePaths(path.dirname(filename));
26
27
mod._compile(`module.exports = require;`, filename);
28
29
return mod.exports;
30
};
31
32
function tryRequire(target, sourceFile) {
33
let resolved;
34
try {
35
// Check if the target exists
36
if (sourceFile != null) {
37
try {
38
resolved = createRequire(path.resolve(sourceFile)).resolve(target);
39
} catch (e) {
40
resolved = require.resolve(target);
41
}
42
} else {
43
resolved = require.resolve(target);
44
}
45
} catch (e) {
46
// If the target does not exist then just return undefined
47
return undefined;
48
}
49
50
// If the target exists then return the loaded module
51
return require(resolved);
52
}
53
54
// https://stackoverflow.com/a/27382838
55
exports.fileExistsWithCaseSync = function fileExistsWithCaseSync(filepath, cacheSettings, strict) {
56
// don't care if the FS is case-sensitive
57
if (CASE_SENSITIVE_FS) return true;
58
59
// null means it resolved to a builtin
60
if (filepath === null) return true;
61
if (filepath.toLowerCase() === process.cwd().toLowerCase() && !strict) return true;
62
const parsedPath = path.parse(filepath);
63
const dir = parsedPath.dir;
64
65
let result = fileExistsCache.get(filepath, cacheSettings);
66
if (result != null) return result;
67
68
// base case
69
if (dir === '' || parsedPath.root === filepath) {
70
result = true;
71
} else {
72
const filenames = fs.readdirSync(dir);
73
if (filenames.indexOf(parsedPath.base) === -1) {
74
result = false;
75
} else {
76
result = fileExistsWithCaseSync(dir, cacheSettings, strict);
77
}
78
}
79
fileExistsCache.set(filepath, result);
80
return result;
81
};
82
83
function relative(modulePath, sourceFile, settings) {
84
return fullResolve(modulePath, sourceFile, settings).path;
85
}
86
87
function fullResolve(modulePath, sourceFile, settings) {
88
// check if this is a bonus core module
89
const coreSet = new Set(settings['import/core-modules']);
90
if (coreSet.has(modulePath)) return { found: true, path: null };
91
92
const sourceDir = path.dirname(sourceFile);
93
const cacheKey = sourceDir + hashObject(settings).digest('hex') + modulePath;
94
95
const cacheSettings = ModuleCache.getSettings(settings);
96
97
const cachedPath = fileExistsCache.get(cacheKey, cacheSettings);
98
if (cachedPath !== undefined) return { found: true, path: cachedPath };
99
100
function cache(resolvedPath) {
101
fileExistsCache.set(cacheKey, resolvedPath);
102
}
103
104
function withResolver(resolver, config) {
105
106
function v1() {
107
try {
108
const resolved = resolver.resolveImport(modulePath, sourceFile, config);
109
if (resolved === undefined) return { found: false };
110
return { found: true, path: resolved };
111
} catch (err) {
112
return { found: false };
113
}
114
}
115
116
function v2() {
117
return resolver.resolve(modulePath, sourceFile, config);
118
}
119
120
switch (resolver.interfaceVersion) {
121
case 2:
122
return v2();
123
124
default:
125
case 1:
126
return v1();
127
}
128
}
129
130
const configResolvers = (settings['import/resolver']
131
|| { 'node': settings['import/resolve'] }); // backward compatibility
132
133
const resolvers = resolverReducer(configResolvers, new Map());
134
135
for (const pair of resolvers) {
136
const name = pair[0];
137
const config = pair[1];
138
const resolver = requireResolver(name, sourceFile);
139
const resolved = withResolver(resolver, config);
140
141
if (!resolved.found) continue;
142
143
// else, counts
144
cache(resolved.path);
145
return resolved;
146
}
147
148
// failed
149
// cache(undefined)
150
return { found: false };
151
}
152
exports.relative = relative;
153
154
function resolverReducer(resolvers, map) {
155
if (Array.isArray(resolvers)) {
156
resolvers.forEach(r => resolverReducer(r, map));
157
return map;
158
}
159
160
if (typeof resolvers === 'string') {
161
map.set(resolvers, null);
162
return map;
163
}
164
165
if (typeof resolvers === 'object') {
166
for (const key in resolvers) {
167
map.set(key, resolvers[key]);
168
}
169
return map;
170
}
171
172
const err = new Error('invalid resolver config');
173
err.name = ERROR_NAME;
174
throw err;
175
}
176
177
function getBaseDir(sourceFile) {
178
return pkgDir.sync(sourceFile) || process.cwd();
179
}
180
function requireResolver(name, sourceFile) {
181
// Try to resolve package with conventional name
182
const resolver = tryRequire(`eslint-import-resolver-${name}`, sourceFile) ||
183
tryRequire(name, sourceFile) ||
184
tryRequire(path.resolve(getBaseDir(sourceFile), name));
185
186
if (!resolver) {
187
const err = new Error(`unable to load resolver "${name}".`);
188
err.name = ERROR_NAME;
189
throw err;
190
}
191
if (!isResolverValid(resolver)) {
192
const err = new Error(`${name} with invalid interface loaded as resolver`);
193
err.name = ERROR_NAME;
194
throw err;
195
}
196
197
return resolver;
198
}
199
200
function isResolverValid(resolver) {
201
if (resolver.interfaceVersion === 2) {
202
return resolver.resolve && typeof resolver.resolve === 'function';
203
} else {
204
return resolver.resolveImport && typeof resolver.resolveImport === 'function';
205
}
206
}
207
208
const erroredContexts = new Set();
209
210
/**
211
* Given
212
* @param {string} p - module path
213
* @param {object} context - ESLint context
214
* @return {string} - the full module filesystem path;
215
* null if package is core;
216
* undefined if not found
217
*/
218
function resolve(p, context) {
219
try {
220
return relative(p, context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(), context.settings);
221
} catch (err) {
222
if (!erroredContexts.has(context)) {
223
// The `err.stack` string starts with `err.name` followed by colon and `err.message`.
224
// We're filtering out the default `err.name` because it adds little value to the message.
225
let errMessage = err.message;
226
if (err.name !== ERROR_NAME && err.stack) {
227
errMessage = err.stack.replace(/^Error: /, '');
228
}
229
context.report({
230
message: `Resolve error: ${errMessage}`,
231
loc: { line: 1, column: 0 },
232
});
233
erroredContexts.add(context);
234
}
235
}
236
}
237
resolve.relative = relative;
238
exports.default = resolve;
239
240