UNPKG

eslint-import-resolver-typescript

Version:
252 lines 7.69 kB
import fs from 'node:fs'; import path from 'node:path'; import debug from 'debug'; import enhancedResolve from 'enhanced-resolve'; import { hashObject } from 'eslint-module-utils/hash.js'; import fg from 'fast-glob'; import { createPathsMatcher, getTsconfig } from 'get-tsconfig'; import isCore from 'is-core-module'; import isGlob from 'is-glob'; const { globSync } = fg; const IMPORTER_NAME = 'eslint-import-resolver-typescript'; const log = debug(IMPORTER_NAME); export const defaultConditionNames = [ 'types', 'import', 'esm2020', 'es2020', 'es2015', 'require', 'node', 'node-addons', 'browser', 'default', ]; export const defaultExtensions = [ '.ts', '.tsx', '.d.ts', '.js', '.jsx', '.json', '.node', ]; export const defaultExtensionAlias = { '.js': [ '.ts', '.tsx', '.d.ts', '.js', ], '.jsx': ['.tsx', '.d.ts', '.jsx'], '.cjs': ['.cts', '.d.cts', '.cjs'], '.mjs': ['.mts', '.d.mts', '.mjs'], }; export const defaultMainFields = [ 'types', 'typings', 'fesm2020', 'fesm2015', 'esm2020', 'es2020', 'module', 'jsnext:main', 'main', ]; export const interfaceVersion = 2; const fileSystem = fs; const JS_EXT_PATTERN = /\.(?:[cm]js|jsx?)$/; const RELATIVE_PATH_PATTERN = /^\.{1,2}(?:\/.*)?$/; let previousOptionsHash; let optionsHash; let cachedOptions; let prevCwd; let mappersCachedOptions; let mappers; let resolverCachedOptions; let resolver; const digestHashObject = (value) => hashObject(value ?? {}).digest('hex'); export function resolve(source, file, options) { if (!cachedOptions || previousOptionsHash !== (optionsHash = digestHashObject(options))) { previousOptionsHash = optionsHash; cachedOptions = { ...options, conditionNames: options?.conditionNames ?? defaultConditionNames, extensions: options?.extensions ?? defaultExtensions, extensionAlias: options?.extensionAlias ?? defaultExtensionAlias, mainFields: options?.mainFields ?? defaultMainFields, fileSystem: new enhancedResolve.CachedInputFileSystem(fileSystem, 5 * 1000), useSyncFileSystemCalls: true, }; } if (!resolver || resolverCachedOptions !== cachedOptions) { resolver = enhancedResolve.ResolverFactory.createResolver(cachedOptions); resolverCachedOptions = cachedOptions; } log('looking for:', source); source = removeQuerystring(source); if (isCore(source)) { log('matched core:', source); return { found: true, path: null, }; } initMappers(cachedOptions); const mappedPath = getMappedPath(source, file, cachedOptions.extensions, true); if (mappedPath) { log('matched ts path:', mappedPath); } let foundNodePath; try { foundNodePath = resolver.resolveSync({}, path.dirname(path.resolve(file)), mappedPath ?? source) || null; } catch { foundNodePath = null; } if ((JS_EXT_PATTERN.test(foundNodePath) || (cachedOptions.alwaysTryTypes && !foundNodePath)) && !/^@types[/\\]/.test(source) && !path.isAbsolute(source) && !source.startsWith('.')) { const definitelyTyped = resolve('@types' + path.sep + mangleScopedPackage(source), file, options); if (definitelyTyped.found) { return definitelyTyped; } } if (foundNodePath) { log('matched node path:', foundNodePath); return { found: true, path: foundNodePath, }; } log("didn't find ", source); return { found: false, }; } function removeQuerystring(id) { const querystringIndex = id.lastIndexOf('?'); if (querystringIndex >= 0) { return id.slice(0, querystringIndex); } return id; } const isFile = (path) => { try { return !!(path && fs.statSync(path, { throwIfNoEntry: false })?.isFile()); } catch { return false; } }; const isModule = (modulePath) => { return !!modulePath && isFile(path.resolve(modulePath, 'package.json')); }; function getMappedPath(source, file, extensions = defaultExtensions, retry) { const originalExtensions = extensions; extensions = ['', ...extensions]; let paths = []; if (RELATIVE_PATH_PATTERN.test(source)) { const resolved = path.resolve(path.dirname(file), source); if (isFile(resolved)) { paths = [resolved]; } } else { paths = mappers .map(mapper => mapper?.(source).map(item => [ ...extensions.map(ext => `${item}${ext}`), ...originalExtensions.map(ext => `${item}/index${ext}`), ])) .flat(2) .filter(mappedPath => { if (mappedPath === undefined) { return false; } try { const stat = fs.statSync(mappedPath, { throwIfNoEntry: false }); if (stat === undefined) return false; if (stat.isFile()) return true; if (stat.isDirectory()) { return isModule(mappedPath); } } catch { return false; } return false; }); } if (retry && paths.length === 0) { const isJs = JS_EXT_PATTERN.test(source); if (isJs) { const jsExt = path.extname(source); const tsExt = jsExt.replace('js', 'ts'); const basename = source.replace(JS_EXT_PATTERN, ''); const resolved = getMappedPath(basename + tsExt, file) || getMappedPath(basename + '.d' + (tsExt === '.tsx' ? '.ts' : tsExt), file); if (resolved) { return resolved; } } for (const ext of extensions) { const resolved = (isJs ? null : getMappedPath(source + ext, file)) || getMappedPath(source + `/index${ext}`, file); if (resolved) { return resolved; } } } if (paths.length > 1) { log('found multiple matching ts paths:', paths); } return paths[0]; } function initMappers(options) { if (mappers && mappersCachedOptions === options && prevCwd === process.cwd()) { return; } prevCwd = process.cwd(); const configPaths = typeof options.project === 'string' ? [options.project] : Array.isArray(options.project) ? options.project : [process.cwd()]; const ignore = ['!**/node_modules/**']; const projectPaths = [ ...new Set([ ...configPaths.filter(path => !isGlob(path)), ...globSync([...configPaths.filter(path => isGlob(path)), ...ignore]), ]), ]; mappers = projectPaths.map(projectPath => { let tsconfigResult; if (isFile(projectPath)) { const { dir, base } = path.parse(projectPath); tsconfigResult = getTsconfig(dir, base); } else { tsconfigResult = getTsconfig(projectPath); } return tsconfigResult && createPathsMatcher(tsconfigResult); }); mappersCachedOptions = options; } function mangleScopedPackage(moduleName) { if (moduleName.startsWith('@')) { const replaceSlash = moduleName.replace(path.sep, '__'); if (replaceSlash !== moduleName) { return replaceSlash.slice(1); } } return moduleName; } //# sourceMappingURL=index.js.map