UNPKG

eslint-import-resolver-typescript

Version:
389 lines (382 loc) 12.4 kB
//#region rolldown:runtime var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion const node_module = __toESM(require("node:module")); const node_path = __toESM(require("node:path")); const eslint_import_context = __toESM(require("eslint-import-context")); const get_tsconfig = __toESM(require("get-tsconfig")); const is_bun_module = __toESM(require("is-bun-module")); const stable_hash_x = __toESM(require("stable-hash-x")); const unrs_resolver = __toESM(require("unrs-resolver")); const node_fs = __toESM(require("node:fs")); const debug = __toESM(require("debug")); const tinyglobby = __toESM(require("tinyglobby")); //#region src/constants.ts const defaultConditionNames = [ "types", "import", "esm2020", "es2020", "es2015", "require", "node", "node-addons", "browser", "default" ]; /** * `.mts`, `.cts`, `.d.mts`, `.d.cts`, `.mjs`, `.cjs` are not included because * `.cjs` and `.mjs` must be used explicitly */ const defaultExtensions = [ ".ts", ".tsx", ".d.ts", ".js", ".jsx", ".json", ".node" ]; const defaultExtensionAlias = { ".js": [ ".ts", ".tsx", ".d.ts", ".js" ], ".ts": [ ".ts", ".d.ts", ".js" ], ".jsx": [ ".tsx", ".d.ts", ".jsx" ], ".tsx": [ ".tsx", ".d.ts", ".jsx", ".js" ], ".cjs": [ ".cts", ".d.cts", ".cjs" ], ".cts": [ ".cts", ".d.cts", ".cjs" ], ".mjs": [ ".mts", ".d.mts", ".mjs" ], ".mts": [ ".mts", ".d.mts", ".mjs" ] }; const defaultMainFields = [ "types", "typings", "fesm2020", "fesm2015", "esm2020", "es2020", "module", "jsnext:main", "main" ]; const JS_EXT_PATTERN = /\.(?:[cm]js|jsx?)$/; const IMPORT_RESOLVER_NAME = "eslint-import-resolver-typescript"; const interfaceVersion = 2; const DEFAULT_TSCONFIG = "tsconfig.json"; const DEFAULT_JSCONFIG = "jsconfig.json"; const DEFAULT_CONFIGS = [DEFAULT_TSCONFIG, DEFAULT_JSCONFIG]; const DEFAULT_TRY_PATHS = ["", ...DEFAULT_CONFIGS]; const MATCH_ALL = "**"; const DEFAULT_IGNORE = [ MATCH_ALL, "node_modules", MATCH_ALL ].join("/"); const TSCONFIG_NOT_FOUND_REGEXP = /^Tsconfig not found\b/; //#endregion //#region src/helpers.ts /** * For a scoped package, we must look in `@types/foo__bar` instead of * `@types/@foo/bar`. */ function mangleScopedPackage(moduleName) { if (moduleName.startsWith("@")) { const replaceSlash = moduleName.replace("/", "__"); if (replaceSlash !== moduleName) return replaceSlash.slice(1); } return moduleName; } /** Remove any trailing querystring from module id. */ function removeQuerystring(id) { const querystringIndex = id.lastIndexOf("?"); if (querystringIndex !== -1) return id.slice(0, querystringIndex); return id; } const tryFile = (filename, includeDir = false, base = process.cwd()) => { if (typeof filename === "string") { const filepath = node_path.default.resolve(base, filename); return node_fs.default.existsSync(filepath) && (includeDir || node_fs.default.statSync(filepath).isFile()) ? filepath : ""; } for (const file of filename ?? []) { const filepath = tryFile(file, includeDir, base); if (filepath) return filepath; } return ""; }; const computeAffinity = (projectDir, targetDir) => { const a = projectDir.split(node_path.default.sep); const b = targetDir.split(node_path.default.sep); let lca = 0; while (lca < a.length && lca < b.length && a[lca] === b[lca]) lca++; return a.length - lca + (b.length - lca); }; const sortProjectsByAffinity = (projects, file) => { const fileDir = node_path.default.dirname(file); return projects.map((project) => ({ project, affinity: computeAffinity(node_path.default.dirname(project), fileDir) })).sort((a, b) => a.affinity - b.affinity).map((item) => item.project); }; const toGlobPath = (pathname) => pathname.replaceAll("\\", "/"); const toNativePath = (pathname) => "/" === node_path.default.sep ? pathname : pathname.replaceAll("/", "\\"); //#endregion //#region src/logger.ts const log = (0, debug.default)(IMPORT_RESOLVER_NAME); //#endregion //#region src/normalize-options.ts let defaultConfigFile; const configFileMapping = /* @__PURE__ */ new Map(); let warned; function normalizeOptions(options, cwd = process.cwd()) { let { project, tsconfig, noWarnOnMultipleProjects } = options ||= {}; let { configFile, references } = tsconfig ?? {}; let ensured; if (configFile) { configFile = tryFile(configFile); ensured = true; } else if (project) { project = Array.isArray(project) ? project : [project]; log("original projects:", ...project); project = project.map(toGlobPath); if (project.some((p) => (0, tinyglobby.isDynamicPattern)(p))) project = (0, tinyglobby.globSync)(project, { absolute: true, cwd, dot: true, expandDirectories: false, onlyFiles: false, ignore: DEFAULT_IGNORE }); log("resolving projects:", ...project); project = project.flatMap((p) => tryFile(DEFAULT_TRY_PATHS, false, toNativePath(p)) || []); log("resolved projects:", ...project); if (project.length === 1) { configFile = project[0]; ensured = true; } if (project.length <= 1) project = void 0; else if (!warned && !noWarnOnMultipleProjects) { warned = true; console.warn("Multiple projects found, consider using a single `tsconfig` with `references` to speed up, or use `noWarnOnMultipleProjects` to suppress this warning"); } } if (!project && !configFile) { configFile = defaultConfigFile ||= tryFile(DEFAULT_CONFIGS); ensured = true; } if (configFile) { const cachedOptions = configFileMapping.get(configFile); if (cachedOptions) { log("using cached options for", configFile); return cachedOptions; } } if (!ensured && configFile && configFile !== defaultConfigFile) configFile = tryFile(DEFAULT_TRY_PATHS, false, configFile); options = { conditionNames: defaultConditionNames, extensions: defaultExtensions, extensionAlias: defaultExtensionAlias, mainFields: defaultMainFields, ...options, project, tsconfig: configFile ? { references: references ?? "auto", configFile } : void 0 }; if (configFile) configFileMapping.set(configFile, options); return options; } //#endregion //#region src/index.ts const resolverCache = /* @__PURE__ */ new Map(); const tsconfigCache = /* @__PURE__ */ new Map(); const matcherCache = /* @__PURE__ */ new Map(); const unrsResolve = (source, file, resolver) => { const result = resolver.sync(node_path.default.dirname(file), source); if (result.path) return { found: true, path: result.path }; if (result.error) { log("unrs-resolver error:", result.error); if (TSCONFIG_NOT_FOUND_REGEXP.test(result.error)) throw new Error(result.error); } return { found: false }; }; const isBun = !!process.versions.bun; const resolve = (source, file, options, resolver) => { options ||= {}; if (isBun || options.bun ? (0, is_bun_module.isBunBuiltin)(source) : (0, node_module.isBuiltin)(source)) { log("matched core:", source); return { found: true, path: null }; } source = removeQuerystring(source); if (!resolver) { const optionsHash = (0, stable_hash_x.stableHash)(options); const context = (0, eslint_import_context.useRuleContext)(); const cwd = context?.cwd || process.cwd(); options = normalizeOptions(options, cwd); const cacheKey = `${optionsHash}\0${cwd}`; let cached = resolverCache.get(cacheKey); if (!cached && !options.project) resolverCache.set(cacheKey, cached = new unrs_resolver.ResolverFactory(options)); resolver = cached; } createResolver: if (!resolver) { const projects = sortProjectsByAffinity(options.project, file); for (const tsconfigPath of projects) { const resolverCached = resolverCache.get(tsconfigPath); if (resolverCached) { resolver = resolverCached; break createResolver; } let tsconfigCached = tsconfigCache.get(tsconfigPath); if (!tsconfigCached) tsconfigCache.set(tsconfigPath, tsconfigCached = (0, get_tsconfig.parseTsconfig)(tsconfigPath)); let matcherCached = matcherCache.get(tsconfigPath); if (!matcherCached) matcherCache.set(tsconfigPath, matcherCached = (0, get_tsconfig.createFilesMatcher)({ config: tsconfigCached, path: tsconfigPath })); const tsconfig = matcherCached(file); if (!tsconfig) { log("tsconfig", tsconfigPath, "does not match", file); continue; } log("matched tsconfig at:", tsconfigPath, "for", file); options = { ...options, tsconfig: { references: "auto", ...options.tsconfig, configFile: tsconfigPath } }; resolver = new unrs_resolver.ResolverFactory(options); const resolved$1 = resolve(source, file, options, resolver); if (resolved$1.found) { resolverCache.set(tsconfigPath, resolver); return resolved$1; } } log("no tsconfig matched", file, "with", ...projects, ", trying from the the nearest one instead"); for (const project of projects) { const resolved$1 = resolve(source, file, { ...options, project }, resolver); if (resolved$1.found) return resolved$1; } } if (!resolver) return { found: false }; const resolved = unrsResolve(source, file, resolver); const foundPath = resolved.path; if ((foundPath && JS_EXT_PATTERN.test(foundPath) || options.alwaysTryTypes !== false && !foundPath) && !/^@types[/\\]/.test(source) && !node_path.default.isAbsolute(source) && !source.startsWith(".")) { const definitelyTyped = unrsResolve("@types/" + mangleScopedPackage(source), file, resolver); if (definitelyTyped.found) return definitelyTyped; } if (foundPath) log("matched path:", foundPath); else log("didn't find", source, "with", options.tsconfig?.configFile || options.project); return resolved; }; const createTypeScriptImportResolver = (options) => { let cwd = process.cwd(); options = normalizeOptions(options, cwd); let resolver = options.project ? void 0 : new unrs_resolver.ResolverFactory(options); return { interfaceVersion: 3, name: IMPORT_RESOLVER_NAME, resolve(source, file) { const context = (0, eslint_import_context.useRuleContext)(); if (context && cwd !== context.cwd) { cwd = context.cwd; options = normalizeOptions(options, cwd); if (options.project) resolver = resolver ? resolver.cloneWithOptions(options) : new unrs_resolver.ResolverFactory(options); } return resolve(source, file, options, resolver); } }; }; //#endregion exports.DEFAULT_CONFIGS = DEFAULT_CONFIGS; exports.DEFAULT_IGNORE = DEFAULT_IGNORE; exports.DEFAULT_JSCONFIG = DEFAULT_JSCONFIG; exports.DEFAULT_TRY_PATHS = DEFAULT_TRY_PATHS; exports.DEFAULT_TSCONFIG = DEFAULT_TSCONFIG; exports.IMPORT_RESOLVER_NAME = IMPORT_RESOLVER_NAME; exports.JS_EXT_PATTERN = JS_EXT_PATTERN; exports.MATCH_ALL = MATCH_ALL; exports.TSCONFIG_NOT_FOUND_REGEXP = TSCONFIG_NOT_FOUND_REGEXP; exports.createTypeScriptImportResolver = createTypeScriptImportResolver; exports.defaultConditionNames = defaultConditionNames; Object.defineProperty(exports, 'defaultConfigFile', { enumerable: true, get: function () { return defaultConfigFile; } }); exports.defaultExtensionAlias = defaultExtensionAlias; exports.defaultExtensions = defaultExtensions; exports.defaultMainFields = defaultMainFields; exports.interfaceVersion = interfaceVersion; exports.mangleScopedPackage = mangleScopedPackage; exports.normalizeOptions = normalizeOptions; exports.removeQuerystring = removeQuerystring; exports.resolve = resolve; exports.sortProjectsByAffinity = sortProjectsByAffinity; exports.toGlobPath = toGlobPath; exports.toNativePath = toNativePath; exports.tryFile = tryFile;