Skip to content

Commit 2fd7c2e

Browse files
authored
feat: rewrite, speed up by using rspack-resolver under the hood (#368)
1 parent 31e3592 commit 2fd7c2e

13 files changed

+517
-596
lines changed

.changeset/friendly-weeks-act.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"eslint-import-resolver-typescript": major
3+
---
4+
5+
feat!: rewrite, speed up by using [`rspack-resolver`](https://github.com/unrs/rspack-resolver) which supports `references` natively under the hood
6+
7+
BREAKING CHANGES:
8+
9+
- drop Node 14 support, Node `^16.17.0 || >=18.6` is now required
10+
- `alwaysTryTypes` is enabled by default, you can set it as `true` to opt-out
11+
- array type of `project` is discouraged but still supported, single `project` with `references` are encouraged for better performance, you can enable `noWarnOnMultipleProjects` option to supress the warning message
12+
- root `tsconfig.json` or `jsconfig.json` will be used automatically if no `project` provided

.size-limit.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[
22
{
33
"path": "./lib/index.js",
4-
"limit": "3.1kB"
4+
"limit": "1.5kB"
55
}
66
]

eslint.config.js

+7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ const config = [
1919
},
2020
},
2121
},
22+
{
23+
files: ['src/*'],
24+
rules: {
25+
'prefer-const': ['error', { destructuring: 'all' }],
26+
'sonarjs/no-nested-assignment': 'off',
27+
},
28+
},
2229
]
2330

2431
export default config

package.json

+8-16
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,21 @@
1212
"license": "ISC",
1313
"packageManager": "yarn@4.7.0",
1414
"engines": {
15-
"node": "^14.18.0 || >=16.0.0"
15+
"node": "^16.17.0 || >=18.6.0"
1616
},
1717
"main": "lib/index.cjs",
1818
"module": "lib/index.js",
1919
"exports": {
2020
".": {
2121
"types": "./lib/index.d.ts",
22-
"es2020": "./lib/index.es2020.mjs",
23-
"fesm2020": "./lib/index.es2020.mjs",
24-
"import": "./lib/index.js",
25-
"require": "./lib/index.cjs"
22+
"require": "./lib/index.cjs",
23+
"default": "./lib/index.js"
2624
},
2725
"./package.json": "./package.json"
2826
},
29-
"es2020": "lib/index.es2020.mjs",
30-
"fesm2020": "lib/index.es2020.mjs",
3127
"types": "lib/index.d.ts",
3228
"files": [
3329
"lib",
34-
"shim.d.ts",
3530
"!**/*.tsbuildinfo"
3631
],
3732
"keywords": [
@@ -42,10 +37,10 @@
4237
"plugin"
4338
],
4439
"scripts": {
45-
"build": "run-p 'build:*'",
46-
"build:r": "r -f cjs,es2020",
40+
"build": "run-p -c 'build:*'",
41+
"build:r": "r -f cjs",
4742
"build:ts": "tsc -b",
48-
"eslint": "cross-env ESLINT_USE_FLAT_CONFIG=false eslint --cache",
43+
"eslint": "ESLINT_USE_FLAT_CONFIG=false eslint",
4944
"lint": "run-p 'lint:*'",
5045
"lint:es": "eslint . --cache",
5146
"lint:tsc": "tsc --noEmit",
@@ -55,7 +50,7 @@
5550
"test:dotInclude": "yarn eslint --ext ts,tsx tests/dotInclude --ignore-pattern \"!.dot\"",
5651
"test:dotPaths": "yarn eslint --ext ts,tsx tests/dotPaths --ignore-pattern \"!.dot\"",
5752
"test:dotProject": "yarn eslint --ext ts,tsx tests/dotProject --ignore-pattern \"!.dot\"",
58-
"test:importXResolverV3": "eslint --cache --config=tests/importXResolverV3/eslint.config.cjs tests/importXResolverV3",
53+
"test:importXResolverV3": "eslint --config=tests/importXResolverV3/eslint.config.cjs tests/importXResolverV3",
5954
"test:multipleEslintrcs": "yarn eslint --ext ts,tsx tests/multipleEslintrcs",
6055
"test:multipleTsconfigs": "yarn eslint --ext ts,tsx tests/multipleTsconfigs",
6156
"test:nearestTsconfig": "yarn eslint --ext ts,tsx tests/nearestTsconfig",
@@ -81,11 +76,10 @@
8176
}
8277
},
8378
"dependencies": {
84-
"@nolyfill/is-core-module": "1.0.39",
8579
"debug": "^4.4.0",
8680
"get-tsconfig": "^4.10.0",
8781
"is-bun-module": "^1.3.0",
88-
"rspack-resolver": "^1.1.0",
82+
"rspack-resolver": "^1.1.2",
8983
"stable-hash": "^0.0.5",
9084
"tinyglobby": "^0.2.12"
9185
},
@@ -102,7 +96,6 @@
10296
"@types/pnpapi": "^0.0.5",
10397
"@types/unist": "^3.0.3",
10498
"clean-pkg-json": "^1.2.1",
105-
"cross-env": "^7.0.3",
10699
"dummy.js": "link:dummy.js",
107100
"eslint": "^9.22.0",
108101
"eslint-import-resolver-typescript": "link:.",
@@ -115,7 +108,6 @@
115108
"size-limit": "^11.2.0",
116109
"size-limit-preset-node-lib": "^0.3.0",
117110
"type-coverage": "^2.29.7",
118-
"type-fest": "^4.37.0",
119111
"typescript": "~5.8.2",
120112
"yarn-berry-deduplicate": "^6.1.1"
121113
},

src/constants.ts

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
export const defaultConditionNames = [
2+
'types',
3+
'import',
4+
5+
// APF: https://angular.io/guide/angular-package-format
6+
'esm2020',
7+
'es2020',
8+
'es2015',
9+
10+
'require',
11+
'node',
12+
'node-addons',
13+
'browser',
14+
'default',
15+
]
16+
17+
/**
18+
* `.mts`, `.cts`, `.d.mts`, `.d.cts`, `.mjs`, `.cjs` are not included because `.cjs` and `.mjs` must be used explicitly
19+
*/
20+
export const defaultExtensions = [
21+
'.ts',
22+
'.tsx',
23+
'.d.ts',
24+
'.js',
25+
'.jsx',
26+
'.json',
27+
'.node',
28+
]
29+
30+
export const defaultExtensionAlias = {
31+
'.js': [
32+
'.ts',
33+
// `.tsx` can also be compiled as `.js`
34+
'.tsx',
35+
'.d.ts',
36+
'.js',
37+
],
38+
'.jsx': ['.tsx', '.d.ts', '.jsx'],
39+
'.cjs': ['.cts', '.d.cts', '.cjs'],
40+
'.mjs': ['.mts', '.d.mts', '.mjs'],
41+
}
42+
43+
export const defaultMainFields = [
44+
'types',
45+
'typings',
46+
47+
// APF: https://angular.io/guide/angular-package-format
48+
'fesm2020',
49+
'fesm2015',
50+
'esm2020',
51+
'es2020',
52+
53+
'module',
54+
'jsnext:main',
55+
56+
'main',
57+
]
58+
59+
export const JS_EXT_PATTERN = /\.(?:[cm]js|jsx?)$/
60+
61+
export const IMPORT_RESOLVER_NAME = 'eslint-import-resolver-typescript'
62+
63+
export const interfaceVersion = 2
64+
65+
export const DEFAULT_TSCONFIG = 'tsconfig.json'
66+
67+
export const DEFAULT_JSCONFIG = 'jsconfig.json'
68+
69+
export const DEFAULT_CONFIGS = [DEFAULT_TSCONFIG, DEFAULT_JSCONFIG]
70+
71+
export const DEFAULT_TRY_PATHS = ['', ...DEFAULT_CONFIGS]
72+
73+
export const MATCH_ALL = '**'
74+
75+
export const DEFAULT_IGNORE = [MATCH_ALL, 'node_modules', MATCH_ALL].join('/')

src/helpers.ts

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import fs from 'node:fs'
2+
import path from 'node:path'
3+
4+
/**
5+
* For a scoped package, we must look in `@types/foo__bar` instead of `@types/@foo/bar`.
6+
*/
7+
export function mangleScopedPackage(moduleName: string) {
8+
if (moduleName.startsWith('@')) {
9+
const replaceSlash = moduleName.replace('/', '__')
10+
if (replaceSlash !== moduleName) {
11+
return replaceSlash.slice(1) // Take off the "@"
12+
}
13+
}
14+
return moduleName
15+
}
16+
17+
/** Remove any trailing querystring from module id. */
18+
export function removeQuerystring(id: string) {
19+
const querystringIndex = id.lastIndexOf('?')
20+
if (querystringIndex !== -1) {
21+
return id.slice(0, querystringIndex)
22+
}
23+
return id
24+
}
25+
26+
export const tryFile = (
27+
filename?: string[] | string,
28+
includeDir = false,
29+
base = process.cwd(),
30+
): string => {
31+
if (typeof filename === 'string') {
32+
const filepath = path.resolve(base, filename)
33+
return fs.existsSync(filepath) &&
34+
(includeDir || fs.statSync(filepath).isFile())
35+
? filepath
36+
: ''
37+
}
38+
39+
for (const file of filename ?? []) {
40+
const filepath = tryFile(file, includeDir, base)
41+
if (filepath) {
42+
return filepath
43+
}
44+
}
45+
46+
return ''
47+
}
48+
49+
const computeAffinity = (projectDir: string, targetDir: string): number => {
50+
const a = projectDir.split(path.sep)
51+
const b = targetDir.split(path.sep)
52+
let lca = 0
53+
while (lca < a.length && lca < b.length && a[lca] === b[lca]) {
54+
lca++
55+
}
56+
return a.length - lca + (b.length - lca)
57+
}
58+
59+
export const sortProjectsByAffinity = (projects: string[], file: string) => {
60+
const fileDir = path.dirname(file)
61+
return projects
62+
.map(project => ({
63+
project,
64+
affinity: computeAffinity(path.dirname(project), fileDir),
65+
}))
66+
.sort((a, b) => a.affinity - b.affinity)
67+
.map(item => item.project)
68+
}
69+
70+
export const toGlobPath = (pathname: string) => pathname.replaceAll('\\', '/')
71+
72+
export const toNativePath = (pathname: string) =>
73+
'/' === path.sep ? pathname : pathname.replaceAll('/', '\\')

0 commit comments

Comments
 (0)