88} from 'tsconfig-paths'
99import { sync as globSync } from 'glob'
1010import isGlob from 'is-glob'
11- import { isCore , sync } from 'resolve'
11+ import { isCore , sync , SyncOpts } from 'resolve'
1212import debug from 'debug'
1313
1414const IMPORTER_NAME = 'eslint-import-resolver-typescript'
@@ -70,7 +70,7 @@ export function resolve(
7070 // note that even if we map the path, we still need to do a final resolve
7171 let foundNodePath : string | null | undefined
7272 try {
73- foundNodePath = sync ( mappedPath || source , {
73+ foundNodePath = tsResolve ( mappedPath || source , {
7474 extensions : options . extensions || defaultExtensions ,
7575 basedir : path . dirname ( path . resolve ( file ) ) ,
7676 packageFilter : options . packageFilter || packageFilterDefault ,
@@ -120,6 +120,27 @@ function packageFilterDefault(pkg: Record<string, string>) {
120120 return pkg
121121}
122122
123+ /**
124+ * Like `sync` from `resolve` package, but considers that the module id
125+ * could have a .js or .jsx extension.
126+ */
127+ function tsResolve ( id : string , opts ?: SyncOpts ) : string {
128+ try {
129+ return sync ( id , opts )
130+ } catch ( error ) {
131+ const idWithoutJsExt = removeJsExtension ( id )
132+ if ( idWithoutJsExt !== id ) {
133+ return sync ( idWithoutJsExt , opts )
134+ }
135+ throw error
136+ }
137+ }
138+
139+ /** Remove .js or .jsx extension from module id. */
140+ function removeJsExtension ( id : string ) {
141+ return id . replace ( / \. j s x ? $ / , '' )
142+ }
143+
123144let mappersBuildForOptions : TsResolverOptions
124145let mappers :
125146 | Array < ( source : string , file : string ) => string | undefined >
@@ -142,6 +163,24 @@ function getMappedPath(source: string, file: string) {
142163 return paths [ 0 ]
143164}
144165
166+ /**
167+ * Like `createMatchPath` from `tsconfig-paths` package, but considers
168+ * that the module id could have a .js or .jsx extension.
169+ */
170+ const createExtendedMatchPath : typeof createMatchPath = ( ...createArgs ) => {
171+ const matchPath = createMatchPath ( ...createArgs )
172+
173+ return ( id , ...otherArgs ) => {
174+ const match = matchPath ( id , ...otherArgs )
175+ if ( match != null ) return match
176+
177+ const idWithoutJsExt = removeJsExtension ( id )
178+ if ( idWithoutJsExt !== id ) {
179+ return matchPath ( idWithoutJsExt , ...otherArgs )
180+ }
181+ }
182+ }
183+
145184function initMappers ( options : TsResolverOptions ) {
146185 if ( mappers && mappersBuildForOptions === options ) {
147186 return
@@ -175,7 +214,7 @@ function initMappers(options: TsResolverOptions) {
175214 // eslint-disable-next-line unicorn/no-fn-reference-in-iterator
176215 . filter ( isConfigLoaderSuccessResult )
177216 . map ( configLoaderResult => {
178- const matchPath = createMatchPath (
217+ const matchPath = createExtendedMatchPath (
179218 configLoaderResult . absoluteBaseUrl ,
180219 configLoaderResult . paths ,
181220 )
0 commit comments