Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: test import-x and flat config support correctly #370

Merged
merged 3 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/beige-gorillas-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-import-resolver-typescript": patch
---

fix: support multiple matching ts paths
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"node": true
},
"parserOptions": {
"ecmaVersion": "latest"
"ecmaVersion": "latest",
"sourceType": "module"
},
"extends": [
"plugin:prettier/recommended",
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
18.20.6
18.20.7
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"test:dotInclude": "eslint --ext ts,tsx tests/dotInclude --ignore-pattern \"!.dot\"",
"test:dotPaths": "eslint --ext ts,tsx tests/dotPaths --ignore-pattern \"!.dot\"",
"test:dotProject": "eslint --ext ts,tsx tests/dotProject --ignore-pattern \"!.dot\"",
"test:importXResolverV3": "eslint --config=tests/importXResolverV3/eslint.config.js tests/importXResolverV3",
"test:importXResolverV3": "cross-env ESLINT_USE_FLAT_CONFIG=true eslint --config=tests/importXResolverV3/eslint.config.js tests/importXResolverV3",
"test:multipleEslintrcs": "eslint --ext ts,tsx tests/multipleEslintrcs",
"test:multipleTsconfigs": "eslint --ext ts,tsx tests/multipleTsconfigs",
"test:withJsExtension": "node tests/withJsExtension/test.js && eslint --ext ts,tsx tests/withJsExtension",
Expand Down Expand Up @@ -98,6 +98,7 @@
"@types/debug": "^4.1.12",
"@types/node": "^18.19.78",
"@types/unist": "^2.0.11",
"cross-env": "^7.0.3",
"dummy.js": "link:dummy.js",
"eslint": "^8.57.1",
"eslint-import-resolver-typescript": "link:.",
Expand All @@ -111,7 +112,7 @@
"size-limit": "^11.0.0",
"size-limit-preset-node-lib": "^0.3.0",
"type-coverage": "^2.27.0",
"typescript": "^5.3.2"
"typescript": "~5.1.0"
},
"resolutions": {
"eslint-import-resolver-typescript": "link:.",
Expand Down
67 changes: 38 additions & 29 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,22 +181,30 @@ export function resolve(

initMappers(cachedOptions)

const mappedPath = getMappedPath(source, file, cachedOptions.extensions, true)
if (mappedPath) {
log('matched ts path:', mappedPath)
let mappedPaths = getMappedPaths(source, file, cachedOptions.extensions, true)

if (mappedPaths.length > 0) {
log('matched ts path:', ...mappedPaths)
} else {
mappedPaths = [source]
}

// note that even if we map the path, we still need to do a final resolve
let foundNodePath: string | null
try {
foundNodePath =
resolver.resolveSync(
let foundNodePath: string | undefined
for (const mappedPath of mappedPaths) {
try {
const resolved = resolver.resolveSync(
{},
path.dirname(path.resolve(file)),
mappedPath ?? source,
) || null
} catch {
foundNodePath = null
mappedPath,
)
if (resolved) {
foundNodePath = resolved
break
}
} catch {
log('failed to resolve with', mappedPath)
}
}

// naive attempt at `@types/*` resolution,
Expand Down Expand Up @@ -286,16 +294,16 @@ const isModule = (modulePath?: string | undefined): modulePath is string => {
* @returns The mapped path of the module or undefined
*/
// eslint-disable-next-line sonarjs/cognitive-complexity
function getMappedPath(
function getMappedPaths(
source: string,
file: string,
extensions: string[] = defaultExtensions,
retry?: boolean,
): string | undefined {
): string[] {
const originalExtensions = extensions
extensions = ['', ...extensions]

let paths: Array<string | undefined> | undefined = []
let paths: string[] = []

if (RELATIVE_PATH_PATTERN.test(source)) {
const resolved = path.resolve(path.dirname(file), source)
Expand Down Expand Up @@ -341,34 +349,35 @@ function getMappedPath(
const tsExt = jsExt.replace('js', 'ts')
const basename = source.replace(JS_EXT_PATTERN, '')

const resolved =
getMappedPath(basename + tsExt, file) ||
getMappedPath(
basename + '.d' + (tsExt === '.tsx' ? '.ts' : tsExt),
file,
)
const mappedPaths = getMappedPaths(basename + tsExt, file)

if (resolved) {
const resolved =
mappedPaths.length > 0
? mappedPaths
: getMappedPaths(
basename + '.d' + (tsExt === '.tsx' ? '.ts' : tsExt),
file,
)

if (resolved.length > 0) {
return resolved
}
}

for (const ext of extensions) {
const mappedPaths = isJs ? [] : getMappedPaths(source + ext, file)
const resolved =
(isJs ? null : getMappedPath(source + ext, file)) ||
getMappedPath(source + `/index${ext}`, file)
mappedPaths.length > 0
? mappedPaths
: getMappedPaths(source + `/index${ext}`, file)

if (resolved) {
if (resolved.length > 0) {
return resolved
}
}
}

if (paths.length > 1) {
log('found multiple matching ts paths:', paths)
}

return paths[0]
return paths
}

// eslint-disable-next-line sonarjs/cognitive-complexity
Expand Down
37 changes: 24 additions & 13 deletions tests/importXResolverV3/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
const path = require('path')
const { createTypeScriptImportResolver } = require('../../lib/index.cjs')
const importX = require('eslint-plugin-import-x')

const { createTypeScriptImportResolver } = require('../..')

const globPattern = './packages/*/tsconfig.json'

// in normal cases this is not needed because the __dirname would be the root
const absoluteGlobPath = path.join(__dirname, globPattern)

module.exports = {
...require('eslint-plugin-import-x').flatConfigs.typescript,
settings: {
...require('eslint-plugin-import-x').flatConfigs.typescript.settings,
'import-x/resolver-next': [
createTypeScriptImportResolver({
project: absoluteGlobPath,
alwaysTryTypes: true,
}),
],
},
}
const base = require('../baseEslintConfig.cjs')()

module.exports =
// don't run on node 16 because lacking of `structuredClone`
+process.versions.node.split('.')[0] <= 16
? {}
: {
files: ['**/*.ts', '**/*.tsx'],
plugins: {
import: importX,
},
settings: {
...importX.flatConfigs.typescript.settings,
'import-x/resolver-next': [
createTypeScriptImportResolver({
project: absoluteGlobPath,
}),
],
},
rules: base.rules,
}
57 changes: 37 additions & 20 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2268,14 +2268,7 @@ __metadata:
languageName: node
linkType: hard

"@changesets/types@npm:^6.0.0":
version: 6.0.0
resolution: "@changesets/types@npm:6.0.0"
checksum: 214c58ff3e3da019c578b94815ec6748729a38b665d950acddf53f3a23073ac7a57dce45812c4bec0cbcd6902c84a482c804457d4c903602005b2399de8a4021
languageName: node
linkType: hard

"@changesets/types@npm:^6.1.0":
"@changesets/types@npm:^6.0.0, @changesets/types@npm:^6.1.0":
version: 6.1.0
resolution: "@changesets/types@npm:6.1.0"
checksum: 2dcd00712cb85d0c53afdd8d0e856b4bf9c0ce8dc36c838c918d44799aacd9ba8659b9ff610ff92b94fc03c8fd2b52c5b05418fcf8a1bd138cd9182414ede373
Expand Down Expand Up @@ -3731,16 +3724,7 @@ __metadata:
languageName: node
linkType: hard

"@types/node@npm:^18.0.0":
version: 18.19.74
resolution: "@types/node@npm:18.19.74"
dependencies:
undici-types: "npm:~5.26.4"
checksum: 2306bd0b41cdd528b890b210b96f287a5b5035c128f62636057d6616bd612b3f53d32d77f7e76ef41a9f130ea691e6980e6d5942dd625df05d3a641764fddb78
languageName: node
linkType: hard

"@types/node@npm:^18.19.78":
"@types/node@npm:^18.0.0, @types/node@npm:^18.19.78":
version: 18.19.78
resolution: "@types/node@npm:18.19.78"
dependencies:
Expand Down Expand Up @@ -5373,6 +5357,18 @@ __metadata:
languageName: node
linkType: hard

"cross-env@npm:^7.0.3":
version: 7.0.3
resolution: "cross-env@npm:7.0.3"
dependencies:
cross-spawn: "npm:^7.0.1"
bin:
cross-env: src/bin/cross-env.js
cross-env-shell: src/bin/cross-env-shell.js
checksum: e99911f0d31c20e990fd92d6fd001f4b01668a303221227cc5cb42ed155f086351b1b3bd2699b200e527ab13011b032801f8ce638e6f09f854bdf744095e604c
languageName: node
linkType: hard

"cross-spawn@npm:^6.0.5":
version: 6.0.5
resolution: "cross-spawn@npm:6.0.5"
Expand All @@ -5386,7 +5382,7 @@ __metadata:
languageName: node
linkType: hard

"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.5":
"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.5":
version: 7.0.6
resolution: "cross-spawn@npm:7.0.6"
dependencies:
Expand Down Expand Up @@ -6169,6 +6165,7 @@ __metadata:
"@types/debug": "npm:^4.1.12"
"@types/node": "npm:^18.19.78"
"@types/unist": "npm:^2.0.11"
cross-env: "npm:^7.0.3"
debug: "npm:^4.3.7"
dummy.js: "link:dummy.js"
enhanced-resolve: "npm:^5.15.0"
Expand All @@ -6188,7 +6185,7 @@ __metadata:
stable-hash: "npm:^0.0.4"
tinyglobby: "npm:^0.2.12"
type-coverage: "npm:^2.27.0"
typescript: "npm:^5.3.2"
typescript: "npm:~5.1.0"
peerDependencies:
eslint: "*"
eslint-plugin-import: "*"
Expand Down Expand Up @@ -13873,6 +13870,16 @@ __metadata:
languageName: node
linkType: hard

"typescript@npm:~5.1.0":
version: 5.1.6
resolution: "typescript@npm:5.1.6"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: f347cde665cf43dc4c1c7d9821c7d9bbec3c3914f4bdd82ee490e9fb9f6d99036ed8666463b6a192dd005eeef333c5087d5931bdd51ec853436ff9a670a7417e
languageName: node
linkType: hard

"typescript@patch:typescript@npm%3A^4.6.4 || ^5.2.2#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.3.2#optional!builtin<compat/typescript>":
version: 5.3.2
resolution: "typescript@patch:typescript@npm%3A5.3.2#optional!builtin<compat/typescript>::version=5.3.2&hash=e012d7"
Expand All @@ -13883,6 +13890,16 @@ __metadata:
languageName: node
linkType: hard

"typescript@patch:typescript@npm%3A~5.1.0#optional!builtin<compat/typescript>":
version: 5.1.6
resolution: "typescript@patch:typescript@npm%3A5.1.6#optional!builtin<compat/typescript>::version=5.1.6&hash=5da071"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: f5481fa3ba0eee8970f46708d13c05650a865ad093b586fc9573f425c64c57ca97e3308e110bb528deb3ccebe83f6fd7b5a8ac90018038da96326a9ccdf8e77c
languageName: node
linkType: hard

"unassert@npm:^2.0.0, unassert@npm:^2.0.2":
version: 2.0.2
resolution: "unassert@npm:2.0.2"
Expand Down