@@ -9,19 +9,24 @@ import {
9
9
createMatchPath ,
10
10
loadConfig ,
11
11
ConfigLoaderResult ,
12
+ MatchPath ,
12
13
} from 'tsconfig-paths'
13
14
14
15
const IMPORTER_NAME = 'eslint-import-resolver-typescript'
15
16
16
17
const log = debug ( IMPORTER_NAME )
17
18
19
+ /**
20
+ * .mts, .cts, .d.mts, .d.cts, .mjs, .cjs are not included because .cjs and .mjs must be used explicitly.
21
+ */
18
22
const defaultExtensions = [
19
23
'.ts' ,
20
24
'.tsx' ,
21
25
'.d.ts' ,
22
- // eslint-disable-next-line node/no-deprecated-api, sonar/deprecation
23
- ...Object . keys ( require . extensions ) ,
26
+ '.js' ,
24
27
'.jsx' ,
28
+ '.json' ,
29
+ '.node' ,
25
30
]
26
31
27
32
export const interfaceVersion = 2
@@ -69,15 +74,16 @@ export function resolve(
69
74
initMappers ( options )
70
75
const mappedPath = getMappedPath ( source )
71
76
if ( mappedPath ) {
72
- log ( 'matched ts path:' , mappedPath )
77
+ log ( 'matched ts path:' , mappedPath . path )
73
78
}
74
79
75
80
// note that even if we map the path, we still need to do a final resolve
76
81
let foundNodePath : string | null | undefined
77
82
try {
78
- foundNodePath = tsResolve ( mappedPath ?? source , {
83
+ foundNodePath = tsResolve ( mappedPath ?. path ?? source , {
79
84
...options ,
80
- extensions : options . extensions ?? defaultExtensions ,
85
+ extensions :
86
+ mappedPath ?. extensions ?? options . extensions ?? defaultExtensions ,
81
87
basedir : path . dirname ( path . resolve ( file ) ) ,
82
88
packageFilter : options . packageFilter ?? packageFilterDefault ,
83
89
} )
@@ -126,17 +132,46 @@ function packageFilterDefault(pkg: Record<string, string>) {
126
132
return pkg
127
133
}
128
134
135
+ function resolveExtension ( id : string ) {
136
+ const idWithoutJsExt = removeJsExtension ( id )
137
+
138
+ if ( idWithoutJsExt === id ) {
139
+ return
140
+ }
141
+
142
+ if ( id . endsWith ( '.mjs' ) ) {
143
+ return {
144
+ path : idWithoutJsExt ,
145
+ extensions : [ '.mts' , '.d.mts' ] ,
146
+ }
147
+ }
148
+
149
+ if ( id . endsWith ( '.cjs' ) ) {
150
+ return {
151
+ path : idWithoutJsExt ,
152
+ extensions : [ '.cts' , '.d.cts' ] ,
153
+ }
154
+ }
155
+
156
+ return {
157
+ path : idWithoutJsExt ,
158
+ }
159
+ }
160
+
129
161
/**
130
162
* Like `sync` from `resolve` package, but considers that the module id
131
163
* could have a .js or .jsx extension.
132
164
*/
133
- function tsResolve ( id : string , opts ? : SyncOpts ) : string {
165
+ function tsResolve ( id : string , opts : SyncOpts ) : string {
134
166
try {
135
167
return sync ( id , opts )
136
168
} catch ( error ) {
137
- const idWithoutJsExt = removeJsExtension ( id )
138
- if ( idWithoutJsExt !== id ) {
139
- return sync ( idWithoutJsExt , opts )
169
+ const resolved = resolveExtension ( id )
170
+ if ( resolved ) {
171
+ return sync ( resolved . path , {
172
+ ...opts ,
173
+ extensions : resolved . extensions ?? opts . extensions ,
174
+ } )
140
175
}
141
176
throw error
142
177
}
@@ -153,11 +188,20 @@ function removeQuerystring(id: string) {
153
188
154
189
/** Remove .js or .jsx extension from module id. */
155
190
function removeJsExtension ( id : string ) {
156
- return id . replace ( / \. j s x ? $ / , '' )
191
+ return id . replace ( / \. ( [ c m ] j s | j s x ? ) $ / , '' )
157
192
}
158
193
159
194
let mappersBuildForOptions : TsResolverOptions
160
- let mappers : Array < ( source : string ) => string | undefined > | undefined
195
+ let mappers :
196
+ | Array <
197
+ ( source : string ) =>
198
+ | {
199
+ path : string
200
+ extensions ?: string [ ]
201
+ }
202
+ | undefined
203
+ >
204
+ | undefined
161
205
162
206
/**
163
207
* @param {string } source the module to resolve; i.e './some-module'
@@ -176,18 +220,45 @@ function getMappedPath(source: string) {
176
220
177
221
/**
178
222
* Like `createMatchPath` from `tsconfig-paths` package, but considers
179
- * that the module id could have a .js or .jsx extension.
223
+ * that the module id could have a .mjs, .cjs, .js or .jsx extension.
224
+ *
225
+ * The default resolved path does not include the extension, so we need to return it for reusing,
226
+ * otherwise `.mts`, `.cts`, `.d.mts`, `.d.cts` will not be used by default, see also @link {defaultExtensions}.
180
227
*/
181
- const createExtendedMatchPath : typeof createMatchPath = ( ...createArgs ) => {
228
+ const createExtendedMatchPath : (
229
+ ...createArgs : Parameters < typeof createMatchPath >
230
+ ) => ( ...matchArgs : Parameters < MatchPath > ) =>
231
+ | {
232
+ path : string
233
+ extensions ?: string [ ]
234
+ }
235
+ | undefined = ( ...createArgs ) => {
182
236
const matchPath = createMatchPath ( ...createArgs )
183
237
184
- return ( id , ...otherArgs ) => {
185
- const match = matchPath ( id , ...otherArgs )
186
- if ( match != null ) return match
238
+ return ( id , readJson , fileExists , extensions ) => {
239
+ const match = matchPath ( id , readJson , fileExists , extensions )
240
+
241
+ if ( match != null ) {
242
+ return {
243
+ path : match ,
244
+ }
245
+ }
187
246
188
- const idWithoutJsExt = removeJsExtension ( id )
189
- if ( idWithoutJsExt !== id ) {
190
- return matchPath ( idWithoutJsExt , ...otherArgs )
247
+ const resolved = resolveExtension ( id )
248
+
249
+ if ( resolved ) {
250
+ const match = matchPath (
251
+ resolved . path ,
252
+ readJson ,
253
+ fileExists ,
254
+ resolved . extensions ?? extensions ,
255
+ )
256
+ if ( match ) {
257
+ return {
258
+ path : match ,
259
+ extensions : resolved . extensions ,
260
+ }
261
+ }
191
262
}
192
263
}
193
264
}
0 commit comments