Skip to content

Commit 4da5388

Browse files
GoodbyeNJNSukkaW
andauthored
feat: add support for using resolver object directly in settings (#159)
Co-authored-by: Sukka <[email protected]>
1 parent ab65566 commit 4da5388

File tree

10 files changed

+381
-146
lines changed

10 files changed

+381
-146
lines changed

.changeset/red-rings-pay.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-import-x": minor
3+
---
4+
5+
feat: add support for using resolver object directly in settings

README.md

+34
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,40 @@ module.exports = {
298298
}
299299
```
300300

301+
- use the `import` or `require` syntax to directly import the resolver object:
302+
303+
```js
304+
// .eslintrc.mjs
305+
import tsResolver from 'eslint-import-resolver-typescript'
306+
307+
export default {
308+
settings: {
309+
'import-x/resolver': {
310+
name: 'tsResolver', // required, could be any string you like
311+
// enable: false, // optional, defaults to true
312+
options: { someConfig: value }, // optional, options to pass to the resolver
313+
resolver: tsResolver, // required, the resolver object
314+
},
315+
},
316+
}
317+
```
318+
319+
```js
320+
// .eslintrc.cjs
321+
const tsResolver = require('eslint-import-resolver-typescript')
322+
323+
module.exports = {
324+
settings: {
325+
'import-x/resolver': {
326+
name: 'tsResolver', // required, could be any string you like
327+
// enable: false, // optional, defaults to true
328+
options: { someConfig: value }, // optional, options to pass to the resolver
329+
resolver: tsResolver, // required, the resolver object
330+
},
331+
},
332+
}
333+
```
334+
301335
Relative paths will be resolved relative to the source's nearest `package.json` or
302336
the process's current working directory if no `package.json` is found.
303337

src/types.ts

+67-8
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,73 @@ export type DocStyle = 'jsdoc' | 'tomdoc'
3232

3333
export type Arrayable<T> = T | readonly T[]
3434

35+
export type ResultNotFound = {
36+
found: false
37+
path?: undefined
38+
}
39+
40+
export type ResultFound = {
41+
found: true
42+
path: string | null
43+
}
44+
45+
export type ResolvedResult = ResultNotFound | ResultFound
46+
47+
export type ResolverResolve<T = unknown> = (
48+
modulePath: string,
49+
sourceFile: string,
50+
config: T,
51+
) => ResolvedResult
52+
53+
export type ResolverResolveImport<T = unknown> = (
54+
modulePath: string,
55+
sourceFile: string,
56+
config: T,
57+
) => string | undefined
58+
59+
export type Resolver<T = unknown, U = T> = {
60+
interfaceVersion?: 1 | 2
61+
resolve: ResolverResolve<T>
62+
resolveImport: ResolverResolveImport<U>
63+
}
64+
65+
export type ResolverName = LiteralUnion<
66+
'node' | 'typescript' | 'webpack',
67+
string
68+
>
69+
70+
export type ResolverRecord = {
71+
node?: boolean | NodeResolverOptions
72+
typescript?: boolean | TsResolverOptions
73+
webpack?: WebpackResolverOptions
74+
[resolve: string]: unknown
75+
}
76+
77+
export type ResolverObject = {
78+
// node, typescript, webpack...
79+
name: ResolverName
80+
81+
// Enabled by default
82+
enable?: boolean
83+
84+
// Options passed to the resolver
85+
options?:
86+
| NodeResolverOptions
87+
| TsResolverOptions
88+
| WebpackResolverOptions
89+
| unknown
90+
91+
// Any object satisfied Resolver type
92+
resolver: Resolver
93+
}
94+
3595
export type ImportResolver =
36-
| LiteralUnion<'node' | 'typescript' | 'webpack', string>
37-
| {
38-
node?: boolean | NodeResolverOptions
39-
typescript?: boolean | TsResolverOptions
40-
webpack?: WebpackResolverOptions
41-
[resolve: string]: unknown
42-
}
96+
| ResolverName
97+
| ResolverRecord
98+
| ResolverObject
99+
| ResolverName[]
100+
| ResolverRecord[]
101+
| ResolverObject[]
43102

44103
export type ImportSettings = {
45104
cache?: {
@@ -53,7 +112,7 @@ export type ImportSettings = {
53112
internalRegex?: string
54113
parsers?: Record<string, readonly FileExtension[]>
55114
resolve?: NodeResolverOptions
56-
resolver?: Arrayable<ImportResolver>
115+
resolver?: ImportResolver
57116
}
58117

59118
export type WithPluginName<T extends string | object> = T extends string

src/utils/resolve.ts

+59-56
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,18 @@ import path from 'node:path'
55
import stableHash from 'stable-hash'
66

77
import type {
8-
Arrayable,
9-
ImportResolver,
108
ImportSettings,
119
PluginSettings,
1210
RuleContext,
11+
Resolver,
12+
ImportResolver,
13+
ResolverRecord,
14+
ResolverObject,
1315
} from '../types'
1416

1517
import { ModuleCache } from './module-cache'
1618
import { pkgDir } from './pkg-dir'
1719

18-
export type ResultNotFound = {
19-
found: false
20-
path?: undefined
21-
}
22-
23-
export type ResultFound = {
24-
found: true
25-
path: string | null
26-
}
27-
28-
export type ResolvedResult = ResultNotFound | ResultFound
29-
30-
export type ResolverResolve = (
31-
modulePath: string,
32-
sourceFile: string,
33-
config: unknown,
34-
) => ResolvedResult
35-
36-
export type ResolverResolveImport = (
37-
modulePath: string,
38-
sourceFile: string,
39-
config: unknown,
40-
) => string | undefined
41-
42-
export type Resolver = {
43-
interfaceVersion?: 1 | 2
44-
resolve: ResolverResolve
45-
resolveImport: ResolverResolveImport
46-
}
47-
4820
export const CASE_SENSITIVE_FS = !fs.existsSync(
4921
path.resolve(
5022
__dirname,
@@ -184,11 +156,14 @@ function fullResolve(
184156
node: settings['import-x/resolve'],
185157
} // backward compatibility
186158

187-
const resolvers = resolverReducer(configResolvers, new Map())
159+
const resolvers = normalizeConfigResolvers(configResolvers, sourceFile)
160+
161+
for (const { enable, options, resolver } of resolvers) {
162+
if (!enable) {
163+
continue
164+
}
188165

189-
for (const [name, config] of resolvers) {
190-
const resolver = requireResolver(name, sourceFile)
191-
const resolved = withResolver(resolver, config)
166+
const resolved = withResolver(resolver, options)
192167

193168
if (!resolved.found) {
194169
continue
@@ -212,30 +187,58 @@ export function relative(
212187
return fullResolve(modulePath, sourceFile, settings).path
213188
}
214189

215-
function resolverReducer(
216-
resolvers: Arrayable<ImportResolver>,
217-
map: Map<string, unknown>,
190+
function normalizeConfigResolvers(
191+
resolvers: ImportResolver,
192+
sourceFile: string,
218193
) {
219-
if (Array.isArray(resolvers)) {
220-
for (const r of resolvers as ImportResolver[]) resolverReducer(r, map)
221-
return map
222-
}
223-
224-
if (typeof resolvers === 'string') {
225-
map.set(resolvers, null)
226-
return map
227-
}
228-
229-
if (typeof resolvers === 'object') {
230-
for (const [key, value] of Object.entries(resolvers)) {
231-
map.set(key, value)
194+
const resolverArray = Array.isArray(resolvers) ? resolvers : [resolvers]
195+
const map = new Map<string, Required<ResolverObject>>()
196+
197+
for (const nameOrRecordOrObject of resolverArray) {
198+
if (typeof nameOrRecordOrObject === 'string') {
199+
const name = nameOrRecordOrObject
200+
201+
map.set(name, {
202+
name,
203+
enable: true,
204+
options: undefined,
205+
resolver: requireResolver(name, sourceFile),
206+
})
207+
} else if (typeof nameOrRecordOrObject === 'object') {
208+
if (nameOrRecordOrObject.name && nameOrRecordOrObject.resolver) {
209+
const object = nameOrRecordOrObject as ResolverObject
210+
211+
const { name, enable = true, options, resolver } = object
212+
map.set(name, { name, enable, options, resolver })
213+
} else {
214+
const record = nameOrRecordOrObject as ResolverRecord
215+
216+
for (const [name, enableOrOptions] of Object.entries(record)) {
217+
if (typeof enableOrOptions === 'boolean') {
218+
map.set(name, {
219+
name,
220+
enable: enableOrOptions,
221+
options: undefined,
222+
resolver: requireResolver(name, sourceFile),
223+
})
224+
} else {
225+
map.set(name, {
226+
name,
227+
enable: true,
228+
options: enableOrOptions,
229+
resolver: requireResolver(name, sourceFile),
230+
})
231+
}
232+
}
233+
}
234+
} else {
235+
const err = new Error('invalid resolver config')
236+
err.name = ERROR_NAME
237+
throw err
232238
}
233-
return map
234239
}
235240

236-
const err = new Error('invalid resolver config')
237-
err.name = ERROR_NAME
238-
throw err
241+
return [...map.values()]
239242
}
240243

241244
function getBaseDir(sourceFile: string): string {

test/fixtures/node_modules/eslint-import-resolver-foo-v1/index.js

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/node_modules/eslint-import-resolver-foo-v1/package.json

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/node_modules/eslint-import-resolver-foo-v2/package.json

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/node_modules/eslint-import-resolver-foo/package.json

-3
This file was deleted.

0 commit comments

Comments
 (0)