diff --git a/package.json b/package.json index 9b7c3cc9..c20b702c 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,6 @@ "@typescript-eslint/utils": "^8.28.0", "debug": "^4.4.0", "doctrine": "^3.0.0", - "eslint-import-resolver-node": "^0.3.9", "get-tsconfig": "^4.10.0", "is-glob": "^4.0.3", "minimatch": "^10.0.1", @@ -129,6 +128,7 @@ "eslint": "^9.23.0", "eslint-config-prettier": "^10.1.1", "eslint-doc-generator": "^2.1.2", + "eslint-import-resolver-node": "^0.3.9", "eslint-import-resolver-typescript": "^4.3.1", "eslint-import-resolver-webpack": "^0.13.10", "eslint-import-test-order-redirect": "link:./test/fixtures/order-redirect", diff --git a/src/types.ts b/src/types.ts index ff0175ed..ed6546c5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -34,10 +34,33 @@ export type { export type ImportType = ImportType_ | 'object' | 'type' -export interface NodeResolverOptions { +export interface LegacyNodeResolverOptions { extensions?: readonly string[] - moduleDirectory?: string[] + /** set to `false` to exclude node core modules (e.g.` fs`) from the search */ + includeCoreModules?: boolean + /** directory (or directories) in which to recursively look for modules. Default "node_modules" */ + moduleDirectory?: string + /** if true, doesn't resolve basedir to real path before resolving. This is the way Node resolves dependencies when executed with the --preserve-symlinks flag. Default to true */ + preserveSymlinks?: boolean + /** Noop now, Previously a directory to begin resolving from */ + basedir?: string + /** Noop now. Previously for require.paths array to */ paths?: string[] + + /** Noop now. Previously for how to read files asynchronously */ + readFile?: never + /** Noop now. Previously a function to asynchronously test whether a file exists */ + isFile?: never + /** Noop now. Previously a function to asynchronously whether a file exists and is a directory */ + isDirectory?: never + /** Noop now. Previously a function to asynchronously resolve a potential symlink to its real path */ + realpath?: never + /** Noop now. Previously a function to asynchronously read a package.json file */ + readPackage?: never + /** Noop now. Previously a function to transform the parsed package.json contents before looking at the "main" field */ + packageFilter?: never + /** Noop now. Previously a function to transform the resolved path before returning it */ + pathFilter?: never } export interface WebpackResolverOptions { @@ -98,7 +121,7 @@ export interface ImportSettings { ignore?: string[] internalRegex?: string parsers?: Record - resolve?: NodeResolverOptions + resolve?: LegacyNodeResolverOptions resolver?: LegacyImportResolver 'resolver-legacy'?: LegacyImportResolver 'resolver-next'?: NewResolver[] diff --git a/src/utils/legacy-resolver-settings.ts b/src/utils/legacy-resolver-settings.ts index ca1356a4..d290d599 100644 --- a/src/utils/legacy-resolver-settings.ts +++ b/src/utils/legacy-resolver-settings.ts @@ -7,7 +7,7 @@ import { cjsRequire } from '@pkgr/core' import type { LiteralUnion } from 'type-fest' import type { - NodeResolverOptions, + LegacyNodeResolverOptions, ResolvedResult, TsResolverOptions, WebpackResolverOptions, @@ -48,7 +48,7 @@ export interface LegacyResolverObject { // Options passed to the resolver options?: - | NodeResolverOptions + | LegacyNodeResolverOptions | TsResolverOptions | WebpackResolverOptions | unknown @@ -58,7 +58,7 @@ export interface LegacyResolverObject { } export interface LegacyResolverRecord { - node?: boolean | NodeResolverOptions + node?: boolean | LegacyNodeResolverOptions typescript?: boolean | TsResolverOptions webpack?: WebpackResolverOptions [resolve: string]: unknown diff --git a/src/utils/resolve.ts b/src/utils/resolve.ts index 9dd02ce6..49361baf 100644 --- a/src/utils/resolve.ts +++ b/src/utils/resolve.ts @@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url' import { stableHash } from 'stable-hash' +import { createNodeResolver } from '../node-resolver.js' import type { ImportSettings, LegacyResolver, @@ -110,6 +111,8 @@ function isValidNewResolver(resolver: unknown): resolver is NewResolver { return true } +let fallbackLegacyNodeResolver: NewResolver + function fullResolve( modulePath: string, sourceFile: string, @@ -140,10 +143,7 @@ function fullResolve( return { found: true, path: cachedPath } } - if ( - Object.prototype.hasOwnProperty.call(settings, 'import-x/resolver-next') && - settings['import-x/resolver-next'] - ) { + if (settings['import-x/resolver-next']) { const configResolvers = settings['import-x/resolver-next'] for (let i = 0, len = configResolvers.length; i < len; i++) { @@ -169,33 +169,56 @@ function fullResolve( fileExistsCache.set(cacheKey, resolved.path as string | null) return resolved } - } else { - const configResolvers = settings['import-x/resolver-legacy'] || - settings['import-x/resolver'] || { - node: settings['import-x/resolve'], - } // backward compatibility - - for (const { enable, options, resolver } of normalizeConfigResolvers( - configResolvers, - sourceFile, - )) { - if (!enable) { - continue - } + } else if ( + // backward compatibility for very old `import-x/resolve` options that is no longer supported + settings['import-x/resolve'] + ) { + const resolveSettings = settings['import-x/resolve'] + + fallbackLegacyNodeResolver ||= createNodeResolver({ + extensions: (resolveSettings.extensions || + settings['import-x/extensions']) as string[] | undefined, + builtinModules: resolveSettings.includeCoreModules !== false, + modules: [ + resolveSettings.moduleDirectory, + ...(resolveSettings.paths ?? []), + ].filter(Boolean), + symlinks: resolveSettings.preserveSymlinks ?? true, + }) + + const resolved = fallbackLegacyNodeResolver.resolve(modulePath, sourceFile) + if (resolved.found) { + fileExistsCache.set(cacheKey, resolved.path as string | null) + return resolved + } - const resolved = resolveWithLegacyResolver( - resolver, - options, - modulePath, + // not found, don't do anything + } else { + const configResolvers = + settings['import-x/resolver-legacy'] || settings['import-x/resolver'] + if (configResolvers) { + for (const { enable, options, resolver } of normalizeConfigResolvers( + configResolvers, sourceFile, - ) - if (!resolved.found) { - continue - } + )) { + if (!enable) { + continue + } + + const resolved = resolveWithLegacyResolver( + resolver, + options, + modulePath, + sourceFile, + ) + if (!resolved.found) { + continue + } - // else, counts - fileExistsCache.set(cacheKey, resolved.path as string | null) - return resolved + // else, counts + fileExistsCache.set(cacheKey, resolved.path as string | null) + return resolved + } } }