'use strict'; exports.__esModule = true; const fs = require('fs'); const Module = require('module'); const path = require('path'); const hashObject = require('./hash').hashObject; const ModuleCache = require('./ModuleCache').default; const pkgDir = require('./pkgDir').default; const CASE_SENSITIVE_FS = !fs.existsSync(path.join(__dirname.toUpperCase(), 'reSOLVE.js')); exports.CASE_SENSITIVE_FS = CASE_SENSITIVE_FS; const ERROR_NAME = 'EslintPluginImportResolveError'; const fileExistsCache = new ModuleCache(); // Polyfill Node's `Module.createRequireFromPath` if not present (added in Node v10.12.0) // Use `Module.createRequire` if available (added in Node v12.2.0) const createRequire = Module.createRequire || Module.createRequireFromPath || function (filename) { const mod = new Module(filename, null); mod.filename = filename; mod.paths = Module._nodeModulePaths(path.dirname(filename)); mod._compile(`module.exports = require;`, filename); return mod.exports; }; function tryRequire(target, sourceFile) { let resolved; try { // Check if the target exists if (sourceFile != null) { try { resolved = createRequire(path.resolve(sourceFile)).resolve(target); } catch (e) { resolved = require.resolve(target); } } else { resolved = require.resolve(target); } } catch (e) { // If the target does not exist then just return undefined return undefined; } // If the target exists then return the loaded module return require(resolved); } // https://stackoverflow.com/a/27382838 exports.fileExistsWithCaseSync = function fileExistsWithCaseSync(filepath, cacheSettings, strict) { // don't care if the FS is case-sensitive if (CASE_SENSITIVE_FS) return true; // null means it resolved to a builtin if (filepath === null) return true; if (filepath.toLowerCase() === process.cwd().toLowerCase() && !strict) return true; const parsedPath = path.parse(filepath); const dir = parsedPath.dir; let result = fileExistsCache.get(filepath, cacheSettings); if (result != null) return result; // base case if (dir === '' || parsedPath.root === filepath) { result = true; } else { const filenames = fs.readdirSync(dir); if (filenames.indexOf(parsedPath.base) === -1) { result = false; } else { result = fileExistsWithCaseSync(dir, cacheSettings, strict); } } fileExistsCache.set(filepath, result); return result; }; function relative(modulePath, sourceFile, settings) { return fullResolve(modulePath, sourceFile, settings).path; } function fullResolve(modulePath, sourceFile, settings) { // check if this is a bonus core module const coreSet = new Set(settings['import/core-modules']); if (coreSet.has(modulePath)) return { found: true, path: null }; const sourceDir = path.dirname(sourceFile); const cacheKey = sourceDir + hashObject(settings).digest('hex') + modulePath; const cacheSettings = ModuleCache.getSettings(settings); const cachedPath = fileExistsCache.get(cacheKey, cacheSettings); if (cachedPath !== undefined) return { found: true, path: cachedPath }; function cache(resolvedPath) { fileExistsCache.set(cacheKey, resolvedPath); } function withResolver(resolver, config) { function v1() { try { const resolved = resolver.resolveImport(modulePath, sourceFile, config); if (resolved === undefined) return { found: false }; return { found: true, path: resolved }; } 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 (const pair of resolvers) { const name = pair[0]; const config = pair[1]; const resolver = requireResolver(name, sourceFile); const resolved = withResolver(resolver, config); if (!resolved.found) continue; // else, counts cache(resolved.path); return resolved; } // failed // cache(undefined) return { found: false }; } exports.relative = relative; function resolverReducer(resolvers, map) { if (Array.isArray(resolvers)) { resolvers.forEach(r => resolverReducer(r, map)); return map; } if (typeof resolvers === 'string') { map.set(resolvers, null); return map; } if (typeof resolvers === 'object') { for (const key in resolvers) { map.set(key, resolvers[key]); } return map; } const err = new Error('invalid resolver config'); err.name = ERROR_NAME; throw err; } function getBaseDir(sourceFile) { return pkgDir(sourceFile) || process.cwd(); } function requireResolver(name, sourceFile) { // Try to resolve package with conventional name const resolver = tryRequire(`eslint-import-resolver-${name}`, sourceFile) || tryRequire(name, sourceFile) || tryRequire(path.resolve(getBaseDir(sourceFile), name)); if (!resolver) { const err = new Error(`unable to load resolver "${name}".`); err.name = ERROR_NAME; throw err; } if (!isResolverValid(resolver)) { const err = new Error(`${name} with invalid interface loaded as resolver`); err.name = ERROR_NAME; throw err; } return resolver; } function isResolverValid(resolver) { if (resolver.interfaceVersion === 2) { return resolver.resolve && typeof resolver.resolve === 'function'; } else { return resolver.resolveImport && typeof resolver.resolveImport === 'function'; } } 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 */ function resolve(p, context) { try { return relative(p, context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(), context.settings); } catch (err) { if (!erroredContexts.has(context)) { // The `err.stack` string starts with `err.name` followed by colon and `err.message`. // We're filtering out the default `err.name` because it adds little value to the message. let errMessage = err.message; if (err.name !== ERROR_NAME && err.stack) { errMessage = err.stack.replace(/^Error: /, ''); } context.report({ message: `Resolve error: ${errMessage}`, loc: { line: 1, column: 0 }, }); erroredContexts.add(context); } } } resolve.relative = relative; exports.default = resolve;