Skip to content

Commit 56775b3

Browse files
authored
refactor: support custom extensions on resolving (#128)
* refactor: support custom extensions on resolving * chore: do not try extensions which already has js extension * feat: try extensionless file by default
1 parent ca59b47 commit 56775b3

File tree

3 files changed

+54
-35
lines changed

3 files changed

+54
-35
lines changed

.changeset/late-papayas-smile.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-import-resolver-typescript": minor
3+
---
4+
5+
refactor: support custom extensions on resolving

.changeset/yellow-mice-build.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-import-resolver-typescript": minor
3+
---
4+
5+
feat: try extensionless file by default

src/index.ts

+44-35
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ export interface TsResolverOptions
7878

7979
const fileSystem = fs as FileSystem
8080

81+
const JS_EXT_PATTERN = /\.(?:[cm]js|jsx?)$/
82+
const RELATIVE_PATH_PATTERN = /^\.{1,2}(?:\/.*)?$/
83+
8184
let mappersBuildForOptions: TsResolverOptions
8285
let mappers: Array<((specifier: string) => string[]) | null> | undefined
8386
let resolver: Resolver
@@ -95,7 +98,14 @@ export function resolve(
9598
found: boolean
9699
path?: string | null
97100
} {
98-
const opts: ResolveOptions & TsResolverOptions = {
101+
const opts: Required<
102+
Pick<
103+
ResolveOptions,
104+
'conditionNames' | 'extensions' | 'mainFields' | 'useSyncFileSystemCalls'
105+
>
106+
> &
107+
ResolveOptions &
108+
TsResolverOptions = {
99109
...options,
100110
extensions: options?.extensions ?? defaultExtensions,
101111
mainFields: options?.mainFields ?? defaultMainFields,
@@ -122,7 +132,7 @@ export function resolve(
122132

123133
initMappers(opts)
124134

125-
const mappedPath = getMappedPath(source, file, true)
135+
const mappedPath = getMappedPath(source, file, opts.extensions, true)
126136
if (mappedPath) {
127137
log('matched ts path:', mappedPath)
128138
}
@@ -140,7 +150,7 @@ export function resolve(
140150
// naive attempt at @types/* resolution,
141151
// if path is neither absolute nor relative
142152
if (
143-
(/\.jsx?$/.test(foundNodePath!) ||
153+
(JS_EXT_PATTERN.test(foundNodePath!) ||
144154
(opts.alwaysTryTypes && !foundNodePath)) &&
145155
!/^@types[/\\]/.test(source) &&
146156
!path.isAbsolute(source) &&
@@ -179,17 +189,17 @@ function resolveExtension(id: string) {
179189
return
180190
}
181191

182-
if (id.endsWith('.mjs')) {
192+
if (id.endsWith('.cjs')) {
183193
return {
184194
path: idWithoutJsExt,
185-
extensions: ['.mts', '.d.mts'],
195+
extensions: ['.cts', '.d.cts'],
186196
}
187197
}
188198

189-
if (id.endsWith('.cjs')) {
199+
if (id.endsWith('.mjs')) {
190200
return {
191201
path: idWithoutJsExt,
192-
extensions: ['.cts', '.d.cts'],
202+
extensions: ['.mts', '.d.mts'],
193203
}
194204
}
195205

@@ -200,7 +210,7 @@ function resolveExtension(id: string) {
200210

201211
/**
202212
* 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.
204214
*/
205215
function tsResolve(
206216
source: string,
@@ -231,10 +241,7 @@ function removeQuerystring(id: string) {
231241
return id
232242
}
233243

234-
const JS_EXT_PATTERN = /\.(?:[cm]js|jsx?)$/
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. */
238245
function removeJsExtension(id: string) {
239246
return id.replace(JS_EXT_PATTERN, '')
240247
}
@@ -250,14 +257,19 @@ const isFile = (path?: string | undefined): path is string => {
250257
/**
251258
* @param {string} source the module to resolve; i.e './some-module'
252259
* @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
253262
* @returns The mapped path of the module or undefined
254263
*/
255264
// eslint-disable-next-line sonarjs/cognitive-complexity
256265
function getMappedPath(
257266
source: string,
258267
file: string,
268+
extensions = defaultExtensions,
259269
retry?: boolean,
260270
): string | undefined {
271+
extensions = ['', ...extensions]
272+
261273
let paths: string[] | undefined = []
262274

263275
if (RELATIVE_PATH_PATTERN.test(source)) {
@@ -268,43 +280,40 @@ function getMappedPath(
268280
} else {
269281
paths = mappers!
270282
.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}`)),
276284
)
277285
.flat(2)
278286
.filter(isFile)
279287
}
280288

281289
if (retry && paths.length === 0) {
282-
if (JS_EXT_PATTERN.test(source)) {
290+
const isJs = JS_EXT_PATTERN.test(source)
291+
if (isJs) {
283292
const jsExt = path.extname(source)
284293
const tsExt = jsExt.replace('js', 'ts')
285294
const basename = source.replace(JS_EXT_PATTERN, '')
286-
return (
295+
296+
const resolved =
287297
getMappedPath(basename + tsExt, file) ||
288-
getMappedPath(source + '/index.ts', file) ||
289-
getMappedPath(source + '/index.tsx', file) ||
290-
getMappedPath(source + '/index.d.ts', file) ||
291298
getMappedPath(
292299
basename + '.d' + (tsExt === '.tsx' ? '.ts' : tsExt),
293300
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+
}
297316
}
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-
)
308317
}
309318

310319
if (paths.length > 1) {

0 commit comments

Comments
 (0)