@@ -9,19 +9,24 @@ import {
99 createMatchPath ,
1010 loadConfig ,
1111 ConfigLoaderResult ,
12+ MatchPath ,
1213} from 'tsconfig-paths'
1314
1415const IMPORTER_NAME = 'eslint-import-resolver-typescript'
1516
1617const log = debug ( IMPORTER_NAME )
1718
19+ /**
20+ * .mts, .cts, .d.mts, .d.cts, .mjs, .cjs are not included because .cjs and .mjs must be used explicitly.
21+ */
1822const defaultExtensions = [
1923 '.ts' ,
2024 '.tsx' ,
2125 '.d.ts' ,
22- // eslint-disable-next-line node/no-deprecated-api, sonar/deprecation
23- ...Object . keys ( require . extensions ) ,
26+ '.js' ,
2427 '.jsx' ,
28+ '.json' ,
29+ '.node' ,
2530]
2631
2732export const interfaceVersion = 2
@@ -69,15 +74,16 @@ export function resolve(
6974 initMappers ( options )
7075 const mappedPath = getMappedPath ( source )
7176 if ( mappedPath ) {
72- log ( 'matched ts path:' , mappedPath )
77+ log ( 'matched ts path:' , mappedPath . path )
7378 }
7479
7580 // note that even if we map the path, we still need to do a final resolve
7681 let foundNodePath : string | null | undefined
7782 try {
78- foundNodePath = tsResolve ( mappedPath ?? source , {
83+ foundNodePath = tsResolve ( mappedPath ?. path ?? source , {
7984 ...options ,
80- extensions : options . extensions ?? defaultExtensions ,
85+ extensions :
86+ mappedPath ?. extensions ?? options . extensions ?? defaultExtensions ,
8187 basedir : path . dirname ( path . resolve ( file ) ) ,
8288 packageFilter : options . packageFilter ?? packageFilterDefault ,
8389 } )
@@ -126,17 +132,46 @@ function packageFilterDefault(pkg: Record<string, string>) {
126132 return pkg
127133}
128134
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+
129161/**
130162 * Like `sync` from `resolve` package, but considers that the module id
131163 * could have a .js or .jsx extension.
132164 */
133- function tsResolve ( id : string , opts ? : SyncOpts ) : string {
165+ function tsResolve ( id : string , opts : SyncOpts ) : string {
134166 try {
135167 return sync ( id , opts )
136168 } 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+ } )
140175 }
141176 throw error
142177 }
@@ -153,11 +188,20 @@ function removeQuerystring(id: string) {
153188
154189/** Remove .js or .jsx extension from module id. */
155190function removeJsExtension ( id : string ) {
156- return id . replace ( / \. j s x ? $ / , '' )
191+ return id . replace ( / \. ( [ c m ] j s | j s x ? ) $ / , '' )
157192}
158193
159194let 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
161205
162206/**
163207 * @param {string } source the module to resolve; i.e './some-module'
@@ -176,18 +220,45 @@ function getMappedPath(source: string) {
176220
177221/**
178222 * 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}.
180227 */
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 ) => {
182236 const matchPath = createMatchPath ( ...createArgs )
183237
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+ }
187246
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+ }
191262 }
192263 }
193264}
0 commit comments