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

feat: if file has no corresponding mapper function, apply all of them… #372

Merged
merged 3 commits into from
Mar 11, 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/long-insects-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-import-resolver-typescript': patch
---

fix: if file has no corresponding mapper function, apply all of them, starting with the nearest one.
2 changes: 1 addition & 1 deletion .size-limit.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"path": "./lib/index.js",
"limit": "3kB"
"limit": "3.1kB"
}
]
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"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:nearestTsconfig": "eslint --ext ts,tsx tests/nearestTsconfig",
"test:withJsExtension": "node tests/withJsExtension/test.js && eslint --ext ts,tsx tests/withJsExtension",
"test:withJsconfig": "eslint --ext js tests/withJsconfig",
"test:withPaths": "eslint --ext ts,tsx tests/withPaths",
Expand Down
91 changes: 64 additions & 27 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ let prevCwd: string

let mappersCachedOptions: InternalResolverOptions
let mappers: Array<{
path: string
files: Set<string>
mapperFn: NonNullable<ReturnType<typeof createPathsMatcher>>
}> = []
Expand Down Expand Up @@ -311,35 +312,50 @@ function getMappedPaths(
paths = [resolved]
}
} else {
paths = [
...new Set(
mappers
.filter(({ files }) => files.has(file))
.map(({ mapperFn }) =>
mapperFn(source).map(item => [
...extensions.map(ext => `${item}${ext}`),
...originalExtensions.map(ext => `${item}/index${ext}`),
]),
)
.flat(2)
.map(toNativePathSeparator),
),
].filter(mappedPath => {
try {
const stat = fs.statSync(mappedPath, { throwIfNoEntry: false })
if (stat === undefined) return false
if (stat.isFile()) return true

// Maybe this is a module dir?
if (stat.isDirectory()) {
return isModule(mappedPath)
// Filter mapper functions associated with file
let mapperFns: Array<NonNullable<ReturnType<typeof createPathsMatcher>>> =
mappers
.filter(({ files }) => files.has(file))
.map(({ mapperFn }) => mapperFn)
if (mapperFns.length === 0) {
// If empty, try all mapper functions, starting with the nearest one
mapperFns = mappers
.map(mapper => ({
mapperFn: mapper.mapperFn,
counter: equalChars(path.dirname(file), path.dirname(mapper.path)),
}))
.sort(
(a, b) =>
// Sort in descending order where the nearest one has the longest counter
b.counter - a.counter,
)
.map(({ mapperFn }) => mapperFn)
}
paths = mapperFns
.map(mapperFn =>
mapperFn(source).map(item => [
...extensions.map(ext => `${item}${ext}`),
...originalExtensions.map(ext => `${item}/index${ext}`),
]),
)
.flat(2)
.map(toNativePathSeparator)
.filter(mappedPath => {
try {
const stat = fs.statSync(mappedPath, { throwIfNoEntry: false })
if (stat === undefined) return false
if (stat.isFile()) return true

// Maybe this is a module dir?
if (stat.isDirectory()) {
return isModule(mappedPath)
}
} catch {
return false
}
} catch {
return false
}

return false
})
return false
})
}

if (retry && paths.length === 0) {
Expand Down Expand Up @@ -487,6 +503,7 @@ function initMappers(options: InternalResolverOptions) {
}

return {
path: toNativePathSeparator(tsconfigResult.path),
files: new Set(files.map(toNativePathSeparator)),
mapperFn,
}
Expand Down Expand Up @@ -551,3 +568,23 @@ function toNativePathSeparator(p: string) {
function isDefined<T>(value: T | null | undefined): value is T {
return value !== null && value !== undefined
}

/**
* Counts how many characters in strings `a` and `b` are exactly the same and in the same position.
*
* @param {string} a First string
* @param {string} b Second string
* @returns Number of matching characters
*/
function equalChars(a: string, b: string): number {
if (a.length === 0 || b.length === 0) {
return 0
}

let i = 0
const length = Math.min(a.length, b.length)
while (i < length && a.charAt(i) === b.charAt(i)) {
i += 1
}
return i
}
10 changes: 10 additions & 0 deletions tests/nearestTsconfig/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const path = require('node:path')

const project = [
'tsconfig.json',
'a/tsconfig.json',
'a/b/tsconfig.json',
'a/b/c/tsconfig.json',
].map(tsconfig => path.resolve(__dirname, tsconfig))

module.exports = require('../baseEslintConfig.cjs')(project)
2 changes: 2 additions & 0 deletions tests/nearestTsconfig/a/app/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import 'components/a'
import 'components/root'
2 changes: 2 additions & 0 deletions tests/nearestTsconfig/a/b/app/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import 'components/b'
import 'components/root'
2 changes: 2 additions & 0 deletions tests/nearestTsconfig/a/b/c/app/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import 'components/c'
import 'components/root'
1 change: 1 addition & 0 deletions tests/nearestTsconfig/a/b/c/components/c.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'c'
9 changes: 9 additions & 0 deletions tests/nearestTsconfig/a/b/c/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"components/*": ["../../../components/*", "./components/*"]
}
},
"files": ["components/c.ts"]
}
1 change: 1 addition & 0 deletions tests/nearestTsconfig/a/b/components/b.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'b'
9 changes: 9 additions & 0 deletions tests/nearestTsconfig/a/b/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"components/*": ["../../components/*", "./components/*"]
}
},
"files": ["components/b.ts"]
}
1 change: 1 addition & 0 deletions tests/nearestTsconfig/a/components/a.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'a'
9 changes: 9 additions & 0 deletions tests/nearestTsconfig/a/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"components/*": ["../components/*", "./components/*"]
}
},
"files": ["components/a.ts"]
}
1 change: 1 addition & 0 deletions tests/nearestTsconfig/app/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import 'components/root'
1 change: 1 addition & 0 deletions tests/nearestTsconfig/components/root.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'root'
9 changes: 9 additions & 0 deletions tests/nearestTsconfig/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"components/*": ["./components/*"]
}
},
"files": ["components/root.ts"]
}