Skip to content

Commit 9b2b7cd

Browse files
committed
refactor: move all existing resolver to legacy
1 parent bc4de89 commit 9b2b7cd

File tree

3 files changed

+218
-185
lines changed

3 files changed

+218
-185
lines changed

src/types.ts

Lines changed: 25 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
11
import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
22
import type { ResolveOptions } from 'enhanced-resolve'
33
import type { MinimatchOptions } from 'minimatch'
4-
import type { KebabCase, LiteralUnion } from 'type-fest'
4+
import type { KebabCase } from 'type-fest'
55

66
import type { ImportType as ImportType_, PluginName } from './utils'
7+
import type { LegacyImportResolver, LegacyResolver } from './utils/legacy-resolver-settings'
8+
9+
export type {
10+
LegacyResolverName,
11+
LegacyResolverName as ResolverName,
12+
13+
LegacyImportResolver,
14+
LegacyImportResolver as ImportResolver,
15+
16+
LegacyResolverResolve,
17+
LegacyResolverResolve as ResolverResolve,
18+
19+
LegacyResolverResolveImport,
20+
LegacyResolverResolveImport as ResolverResolveImport,
21+
22+
LegacyResolverRecord,
23+
LegacyResolverRecord as ResolverRecord,
24+
25+
LegacyResolverObject,
26+
LegacyResolverObject as ResolverObject,
27+
} from './utils/legacy-resolver-settings'
728

829
export type ImportType = ImportType_ | 'object' | 'type'
930

@@ -42,63 +63,9 @@ export type ResultFound = {
4263
path: string | null
4364
}
4465

45-
export type ResolvedResult = ResultNotFound | ResultFound
66+
export type Resolver = LegacyResolver
4667

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-
95-
export type ImportResolver =
96-
| ResolverName
97-
| ResolverRecord
98-
| ResolverObject
99-
| ResolverName[]
100-
| ResolverRecord[]
101-
| ResolverObject[]
68+
export type ResolvedResult = ResultNotFound | ResultFound
10269

10370
export type ImportSettings = {
10471
cache?: {
@@ -112,7 +79,7 @@ export type ImportSettings = {
11279
internalRegex?: string
11380
parsers?: Record<string, readonly FileExtension[]>
11481
resolve?: NodeResolverOptions
115-
resolver?: ImportResolver
82+
resolver?: LegacyImportResolver
11683
}
11784

11885
export type WithPluginName<T extends string | object> = T extends string
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
// Although the new import resolver settings is still `import-x/resolver-next`, but it won't stop us from calling existing ones legacy~
2+
3+
import type { LiteralUnion } from "type-fest"
4+
5+
import { createRequire } from 'node:module'
6+
import type { NodeResolverOptions, ResolvedResult, TsResolverOptions, WebpackResolverOptions } from "../types"
7+
import path from "path"
8+
import { IMPORT_RESOLVE_ERROR_NAME } from "./resolve"
9+
import { pkgDir } from './pkg-dir'
10+
11+
export type LegacyResolverName = LiteralUnion<
12+
'node' | 'typescript' | 'webpack',
13+
string
14+
>
15+
16+
export type LegacyResolverResolveImport<T = unknown> = (
17+
modulePath: string,
18+
sourceFile: string,
19+
config: T,
20+
) => string | undefined
21+
22+
export type LegacyResolverResolve<T = unknown> = (
23+
modulePath: string,
24+
sourceFile: string,
25+
config: T,
26+
) => ResolvedResult
27+
28+
export type LegacyResolver<T = unknown, U = T> = {
29+
interfaceVersion?: 1 | 2
30+
resolve: LegacyResolverResolve<T>
31+
resolveImport: LegacyResolverResolveImport<U>
32+
}
33+
34+
export type LegacyResolverObject = {
35+
// node, typescript, webpack...
36+
name: LegacyResolverName
37+
38+
// Enabled by default
39+
enable?: boolean
40+
41+
// Options passed to the resolver
42+
options?:
43+
| NodeResolverOptions
44+
| TsResolverOptions
45+
| WebpackResolverOptions
46+
| unknown
47+
48+
// Any object satisfied Resolver type
49+
resolver: LegacyResolver
50+
}
51+
52+
export type LegacyResolverRecord = {
53+
node?: boolean | NodeResolverOptions
54+
typescript?: boolean | TsResolverOptions
55+
webpack?: WebpackResolverOptions
56+
[resolve: string]: unknown
57+
}
58+
59+
export type LegacyImportResolver =
60+
| LegacyResolverName
61+
| LegacyResolverRecord
62+
| LegacyResolverObject
63+
| LegacyResolverName[]
64+
| LegacyResolverRecord[]
65+
| LegacyResolverObject[];
66+
67+
export function normalizeConfigResolvers(
68+
resolvers: LegacyImportResolver,
69+
sourceFile: string,
70+
) {
71+
const resolverArray = Array.isArray(resolvers) ? resolvers : [resolvers]
72+
const map = new Map<string, Required<LegacyResolverObject>>()
73+
74+
for (const nameOrRecordOrObject of resolverArray) {
75+
if (typeof nameOrRecordOrObject === 'string') {
76+
const name = nameOrRecordOrObject
77+
78+
map.set(name, {
79+
name,
80+
enable: true,
81+
options: undefined,
82+
resolver: requireResolver(name, sourceFile),
83+
})
84+
} else if (typeof nameOrRecordOrObject === 'object') {
85+
if (nameOrRecordOrObject.name && nameOrRecordOrObject.resolver) {
86+
const object = nameOrRecordOrObject as LegacyResolverObject
87+
88+
const { name, enable = true, options, resolver } = object
89+
map.set(name, { name, enable, options, resolver })
90+
} else {
91+
const record = nameOrRecordOrObject as LegacyResolverRecord
92+
93+
for (const [name, enableOrOptions] of Object.entries(record)) {
94+
if (typeof enableOrOptions === 'boolean') {
95+
map.set(name, {
96+
name,
97+
enable: enableOrOptions,
98+
options: undefined,
99+
resolver: requireResolver(name, sourceFile),
100+
})
101+
} else {
102+
map.set(name, {
103+
name,
104+
enable: true,
105+
options: enableOrOptions,
106+
resolver: requireResolver(name, sourceFile),
107+
})
108+
}
109+
}
110+
}
111+
} else {
112+
const err = new Error('invalid resolver config')
113+
err.name = IMPORT_RESOLVE_ERROR_NAME
114+
throw err
115+
}
116+
}
117+
118+
return [...map.values()]
119+
}
120+
121+
function requireResolver(name: string, sourceFile: string) {
122+
// Try to resolve package with conventional name
123+
const resolver =
124+
tryRequire(`eslint-import-resolver-${name}`, sourceFile) ||
125+
tryRequire(name, sourceFile) ||
126+
tryRequire(path.resolve(getBaseDir(sourceFile), name))
127+
128+
if (!resolver) {
129+
const err = new Error(`unable to load resolver "${name}".`)
130+
err.name = IMPORT_RESOLVE_ERROR_NAME
131+
throw err
132+
}
133+
if (!isLegacyResolverValid(resolver)) {
134+
const err = new Error(`${name} with invalid interface loaded as resolver`)
135+
err.name = IMPORT_RESOLVE_ERROR_NAME
136+
throw err
137+
}
138+
139+
return resolver
140+
}
141+
142+
function isLegacyResolverValid(resolver: object): resolver is LegacyResolver {
143+
if ('interfaceVersion' in resolver && resolver.interfaceVersion === 2) {
144+
return (
145+
'resolve' in resolver &&
146+
!!resolver.resolve &&
147+
typeof resolver.resolve === 'function'
148+
)
149+
}
150+
return (
151+
'resolveImport' in resolver &&
152+
!!resolver.resolveImport &&
153+
typeof resolver.resolveImport === 'function'
154+
)
155+
}
156+
157+
function tryRequire<T>(
158+
target: string,
159+
sourceFile?: string | null,
160+
): undefined | T {
161+
let resolved
162+
try {
163+
// Check if the target exists
164+
if (sourceFile == null) {
165+
resolved = require.resolve(target)
166+
} else {
167+
try {
168+
resolved = createRequire(path.resolve(sourceFile)).resolve(target)
169+
} catch {
170+
resolved = require.resolve(target)
171+
}
172+
}
173+
} catch {
174+
// If the target does not exist then just return undefined
175+
return undefined
176+
}
177+
178+
// If the target exists then return the loaded module
179+
return require(resolved)
180+
}
181+
182+
183+
function getBaseDir(sourceFile: string): string {
184+
return pkgDir(sourceFile) || process.cwd()
185+
}

0 commit comments

Comments
 (0)