@@ -78,6 +78,9 @@ export interface TsResolverOptions
78
78
79
79
const fileSystem = fs as FileSystem
80
80
81
+ const JS_EXT_PATTERN = / \. (?: [ c m ] j s | j s x ? ) $ /
82
+ const RELATIVE_PATH_PATTERN = / ^ \. { 1 , 2 } (?: \/ .* ) ? $ /
83
+
81
84
let mappersBuildForOptions : TsResolverOptions
82
85
let mappers : Array < ( ( specifier : string ) => string [ ] ) | null > | undefined
83
86
let resolver : Resolver
@@ -95,7 +98,14 @@ export function resolve(
95
98
found : boolean
96
99
path ?: string | null
97
100
} {
98
- const opts : ResolveOptions & TsResolverOptions = {
101
+ const opts : Required <
102
+ Pick <
103
+ ResolveOptions ,
104
+ 'conditionNames' | 'extensions' | 'mainFields' | 'useSyncFileSystemCalls'
105
+ >
106
+ > &
107
+ ResolveOptions &
108
+ TsResolverOptions = {
99
109
...options ,
100
110
extensions : options ?. extensions ?? defaultExtensions ,
101
111
mainFields : options ?. mainFields ?? defaultMainFields ,
@@ -122,7 +132,7 @@ export function resolve(
122
132
123
133
initMappers ( opts )
124
134
125
- const mappedPath = getMappedPath ( source , file , true )
135
+ const mappedPath = getMappedPath ( source , file , opts . extensions , true )
126
136
if ( mappedPath ) {
127
137
log ( 'matched ts path:' , mappedPath )
128
138
}
@@ -140,7 +150,7 @@ export function resolve(
140
150
// naive attempt at @types /* resolution,
141
151
// if path is neither absolute nor relative
142
152
if (
143
- ( / \. j s x ? $ / . test ( foundNodePath ! ) ||
153
+ ( JS_EXT_PATTERN . test ( foundNodePath ! ) ||
144
154
( opts . alwaysTryTypes && ! foundNodePath ) ) &&
145
155
! / ^ @ t y p e s [ / \\ ] / . test ( source ) &&
146
156
! path . isAbsolute ( source ) &&
@@ -179,17 +189,17 @@ function resolveExtension(id: string) {
179
189
return
180
190
}
181
191
182
- if ( id . endsWith ( '.mjs ' ) ) {
192
+ if ( id . endsWith ( '.cjs ' ) ) {
183
193
return {
184
194
path : idWithoutJsExt ,
185
- extensions : [ '.mts ' , '.d.mts ' ] ,
195
+ extensions : [ '.cts ' , '.d.cts ' ] ,
186
196
}
187
197
}
188
198
189
- if ( id . endsWith ( '.cjs ' ) ) {
199
+ if ( id . endsWith ( '.mjs ' ) ) {
190
200
return {
191
201
path : idWithoutJsExt ,
192
- extensions : [ '.cts ' , '.d.cts ' ] ,
202
+ extensions : [ '.mts ' , '.d.mts ' ] ,
193
203
}
194
204
}
195
205
@@ -200,7 +210,7 @@ function resolveExtension(id: string) {
200
210
201
211
/**
202
212
* Like `sync` from `resolve` package, but considers that the module id
203
- * could have a .js or .jsx extension.
213
+ * could have a .cjs, .mjs, . js or .jsx extension.
204
214
*/
205
215
function tsResolve (
206
216
source : string ,
@@ -231,10 +241,7 @@ function removeQuerystring(id: string) {
231
241
return id
232
242
}
233
243
234
- const JS_EXT_PATTERN = / \. (?: [ c m ] j s | j s x ? ) $ /
235
- const RELATIVE_PATH_PATTERN = / ^ \. { 1 , 2 } (?: \/ .* ) ? $ /
236
-
237
- /** Remove .js or .jsx extension from module id. */
244
+ /** Remove .cjs, .mjs, .js or .jsx extension from module id. */
238
245
function removeJsExtension ( id : string ) {
239
246
return id . replace ( JS_EXT_PATTERN , '' )
240
247
}
@@ -250,14 +257,19 @@ const isFile = (path?: string | undefined): path is string => {
250
257
/**
251
258
* @param {string } source the module to resolve; i.e './some-module'
252
259
* @param {string } file the importing file's full path; i.e. '/usr/local/bin/file.js'
260
+ * @param {string[] } extensions the extensions to try
261
+ * @param {boolean } retry should retry on failed to resolve
253
262
* @returns The mapped path of the module or undefined
254
263
*/
255
264
// eslint-disable-next-line sonarjs/cognitive-complexity
256
265
function getMappedPath (
257
266
source : string ,
258
267
file : string ,
268
+ extensions = defaultExtensions ,
259
269
retry ?: boolean ,
260
270
) : string | undefined {
271
+ extensions = [ '' , ...extensions ]
272
+
261
273
let paths : string [ ] | undefined = [ ]
262
274
263
275
if ( RELATIVE_PATH_PATTERN . test ( source ) ) {
@@ -268,43 +280,40 @@ function getMappedPath(
268
280
} else {
269
281
paths = mappers !
270
282
. map ( mapper =>
271
- mapper ?.( source ) . map ( item =>
272
- path . extname ( item )
273
- ? item
274
- : [ 'ts' , 'tsx' , '.d.ts' , 'js' ] . map ( ext => `${ item } .${ ext } ` ) ,
275
- ) ,
283
+ mapper ?.( source ) . map ( item => extensions . map ( ext => `${ item } ${ ext } ` ) ) ,
276
284
)
277
285
. flat ( 2 )
278
286
. filter ( isFile )
279
287
}
280
288
281
289
if ( retry && paths . length === 0 ) {
282
- if ( JS_EXT_PATTERN . test ( source ) ) {
290
+ const isJs = JS_EXT_PATTERN . test ( source )
291
+ if ( isJs ) {
283
292
const jsExt = path . extname ( source )
284
293
const tsExt = jsExt . replace ( 'js' , 'ts' )
285
294
const basename = source . replace ( JS_EXT_PATTERN , '' )
286
- return (
295
+
296
+ const resolved =
287
297
getMappedPath ( basename + tsExt , file ) ||
288
- getMappedPath ( source + '/index.ts' , file ) ||
289
- getMappedPath ( source + '/index.tsx' , file ) ||
290
- getMappedPath ( source + '/index.d.ts' , file ) ||
291
298
getMappedPath (
292
299
basename + '.d' + ( tsExt === '.tsx' ? '.ts' : tsExt ) ,
293
300
file ,
294
- ) ||
295
- getMappedPath ( source + '/index.js' , file )
296
- )
301
+ )
302
+
303
+ if ( resolved ) {
304
+ return resolved
305
+ }
306
+ }
307
+
308
+ for ( const ext of extensions ) {
309
+ const resolved =
310
+ ( isJs ? null : getMappedPath ( source + ext , file ) ) ||
311
+ getMappedPath ( source + `/index${ ext } ` , file )
312
+
313
+ if ( resolved ) {
314
+ return resolved
315
+ }
297
316
}
298
- return (
299
- getMappedPath ( source + '.ts' , file ) ||
300
- getMappedPath ( source + '.tsx' , file ) ||
301
- getMappedPath ( source + '.js' , file ) ||
302
- getMappedPath ( source + '.d.ts' , file ) ||
303
- getMappedPath ( source + '/index.ts' , file ) ||
304
- getMappedPath ( source + '/index.tsx' , file ) ||
305
- getMappedPath ( source + '/index.d.ts' , file ) ||
306
- getMappedPath ( source + '/index.js' , file )
307
- )
308
317
}
309
318
310
319
if ( paths . length > 1 ) {
0 commit comments