8
8
} from 'tsconfig-paths'
9
9
import { sync as globSync } from 'glob'
10
10
import isGlob from 'is-glob'
11
- import { isCore , sync } from 'resolve'
11
+ import { isCore , sync , SyncOpts } from 'resolve'
12
12
import debug from 'debug'
13
13
14
14
const IMPORTER_NAME = 'eslint-import-resolver-typescript'
@@ -70,7 +70,7 @@ export function resolve(
70
70
// note that even if we map the path, we still need to do a final resolve
71
71
let foundNodePath : string | null | undefined
72
72
try {
73
- foundNodePath = sync ( mappedPath || source , {
73
+ foundNodePath = tsResolve ( mappedPath || source , {
74
74
extensions : options . extensions || defaultExtensions ,
75
75
basedir : path . dirname ( path . resolve ( file ) ) ,
76
76
packageFilter : options . packageFilter || packageFilterDefault ,
@@ -120,6 +120,27 @@ function packageFilterDefault(pkg: Record<string, string>) {
120
120
return pkg
121
121
}
122
122
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
+
123
144
let mappersBuildForOptions : TsResolverOptions
124
145
let mappers :
125
146
| Array < ( source : string , file : string ) => string | undefined >
@@ -142,6 +163,24 @@ function getMappedPath(source: string, file: string) {
142
163
return paths [ 0 ]
143
164
}
144
165
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
+
145
184
function initMappers ( options : TsResolverOptions ) {
146
185
if ( mappers && mappersBuildForOptions === options ) {
147
186
return
@@ -175,7 +214,7 @@ function initMappers(options: TsResolverOptions) {
175
214
// eslint-disable-next-line unicorn/no-fn-reference-in-iterator
176
215
. filter ( isConfigLoaderSuccessResult )
177
216
. map ( configLoaderResult => {
178
- const matchPath = createMatchPath (
217
+ const matchPath = createExtendedMatchPath (
179
218
configLoaderResult . absoluteBaseUrl ,
180
219
configLoaderResult . paths ,
181
220
)
0 commit comments