@@ -5,17 +5,22 @@ import { fileURLToPath } from 'node:url'
55import { setRuleContext } from 'eslint-import-context'
66import { stableHash } from 'stable-hash'
77
8+ import { createNodeResolver } from '../node-resolver.js'
9+ import { cjsRequire } from '../require.js'
810import type {
911 ChildContext ,
1012 ImportSettings ,
1113 LegacyResolver ,
1214 NewResolver ,
15+ NodeResolverOptions ,
1316 PluginSettings ,
1417 RuleContext ,
1518} from '../types.js'
1619
20+ import { arraify } from './arraify.js'
1721import { makeContextCacheKey } from './export-map.js'
1822import {
23+ LEGACY_NODE_RESOLVERS ,
1924 normalizeConfigResolvers ,
2025 resolveWithLegacyResolver ,
2126} from './legacy-resolver-settings.js'
@@ -114,6 +119,106 @@ function isValidNewResolver(resolver: unknown): resolver is NewResolver {
114119 return true
115120}
116121
122+ function legacyNodeResolve (
123+ resolverOptions : NodeResolverOptions ,
124+ context : ChildContext | RuleContext ,
125+ modulePath : string ,
126+ sourceFile : string ,
127+ ) {
128+ const {
129+ extensions,
130+ includeCoreModules,
131+ moduleDirectory,
132+ paths,
133+ preserveSymlinks,
134+ package : packageJson ,
135+ packageFilter,
136+ pathFilter,
137+ packageIterator,
138+ ...rest
139+ } = resolverOptions
140+
141+ const normalizedExtensions = arraify ( extensions )
142+
143+ const modules = arraify ( moduleDirectory )
144+
145+ // TODO: change the default behavior to align node itself
146+ const symlinks = preserveSymlinks === false
147+
148+ const resolver = createNodeResolver ( {
149+ extensions : normalizedExtensions ,
150+ builtinModules : includeCoreModules !== false ,
151+ modules,
152+ symlinks,
153+ ...rest ,
154+ } )
155+
156+ const resolved = setRuleContext ( context , ( ) =>
157+ resolver . resolve ( modulePath , sourceFile ) ,
158+ )
159+
160+ if ( resolved . found ) {
161+ return resolved
162+ }
163+
164+ const normalizedPaths = arraify ( paths )
165+
166+ if ( normalizedPaths ?. length ) {
167+ const paths = modules ?. length
168+ ? normalizedPaths . filter ( p => ! modules . includes ( p ) )
169+ : normalizedPaths
170+
171+ if ( paths . length > 0 ) {
172+ const resolver = createNodeResolver ( {
173+ extensions : normalizedExtensions ,
174+ builtinModules : includeCoreModules !== false ,
175+ modules : paths ,
176+ symlinks,
177+ ...rest ,
178+ } )
179+
180+ const resolved = setRuleContext ( context , ( ) =>
181+ resolver . resolve ( modulePath , sourceFile ) ,
182+ )
183+
184+ if ( resolved . found ) {
185+ return resolved
186+ }
187+ }
188+ }
189+
190+ if (
191+ [ packageJson , packageFilter , pathFilter , packageIterator ] . some (
192+ it => it != null ,
193+ )
194+ ) {
195+ let legacyNodeResolver : LegacyResolver
196+ try {
197+ legacyNodeResolver = cjsRequire < LegacyResolver > (
198+ 'eslint-import-resolver-node' ,
199+ )
200+ } catch {
201+ throw new Error (
202+ [
203+ "You're using legacy resolver options which are not supported by the new resolver." ,
204+ 'Please either:' ,
205+ '1. Install `eslint-import-resolver-node` as a fallback, or' ,
206+ '2. Remove legacy options: `package`, `packageFilter`, `pathFilter`, `packageIterator`' ,
207+ ] . join ( '\n' ) ,
208+ )
209+ }
210+ const resolved = resolveWithLegacyResolver (
211+ legacyNodeResolver ,
212+ resolverOptions ,
213+ modulePath ,
214+ sourceFile ,
215+ )
216+ if ( resolved . found ) {
217+ return resolved
218+ }
219+ }
220+ }
221+
117222function fullResolve (
118223 modulePath : string ,
119224 sourceFile : string ,
@@ -140,11 +245,11 @@ function fullResolve(
140245
141246 const cacheKey =
142247 sourceDir +
143- ', ' +
248+ '\0 ' +
144249 childContextHashKey +
145- ', ' +
250+ '\0 ' +
146251 memoizedHash +
147- ', ' +
252+ '\0 ' +
148253 modulePath
149254
150255 const cacheSettings = ModuleCache . getSettings ( settings )
@@ -154,10 +259,7 @@ function fullResolve(
154259 return { found : true , path : cachedPath }
155260 }
156261
157- if (
158- Object . hasOwn ( settings , 'import-x/resolver-next' ) &&
159- settings [ 'import-x/resolver-next' ]
160- ) {
262+ if ( settings [ 'import-x/resolver-next' ] ) {
161263 let configResolvers = settings [ 'import-x/resolver-next' ]
162264
163265 if ( ! Array . isArray ( configResolvers ) ) {
@@ -196,14 +298,41 @@ function fullResolve(
196298 node : settings [ 'import-x/resolve' ] ,
197299 } // backward compatibility
198300
199- for ( const { enable, options, resolver } of normalizeConfigResolvers (
301+ for ( const { enable, name , options, resolver } of normalizeConfigResolvers (
200302 configResolvers ,
201303 sourceFile ,
202304 ) ) {
203305 if ( ! enable ) {
204306 continue
205307 }
206308
309+ // if the resolver is `eslint-import-resolver-node`, we use the new `node` resolver first
310+ // and try `eslint-import-resolver-node` as fallback instead
311+ if ( LEGACY_NODE_RESOLVERS . has ( name ) ) {
312+ const resolverOptions = ( options || { } ) as NodeResolverOptions
313+ const resolved = legacyNodeResolve (
314+ resolverOptions ,
315+ // TODO: enable the following in the next major
316+ // {
317+ // ...resolverOptions,
318+ // extensions:
319+ // resolverOptions.extensions || settings['import-x/extensions'],
320+ // },
321+ context ,
322+ modulePath ,
323+ sourceFile ,
324+ )
325+
326+ if ( resolved ?. found ) {
327+ fileExistsCache . set ( cacheKey , resolved . path )
328+ return resolved
329+ }
330+
331+ if ( ! resolver ) {
332+ continue
333+ }
334+ }
335+
207336 const resolved = setRuleContext ( context , ( ) =>
208337 resolveWithLegacyResolver ( resolver , options , modulePath , sourceFile ) ,
209338 )
0 commit comments