Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: un-ts/eslint-plugin-import-x
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v4.6.1
Choose a base ref
...
head repository: un-ts/eslint-plugin-import-x
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v4.7.0
Choose a head ref
  • 11 commits
  • 89 files changed
  • 6 contributors

Commits on Dec 24, 2024

  1. fix: improve windows support (#214)

    mrginglymus authored Dec 24, 2024
    Copy the full SHA
    091d2da View commit details

Commits on Jan 11, 2025

  1. docs(consistent-type-specifier-style): correct default option (#220)

    privatenumber authored Jan 11, 2025
    Copy the full SHA
    f87804d View commit details

Commits on Jan 17, 2025

  1. docs: add note about no-deprecated with TypeScript (#227)

    rgant authored Jan 17, 2025
    Copy the full SHA
    bf49621 View commit details

Commits on Jan 26, 2025

  1. fix(config): added name to flat presets (#228)

    NishargShah authored Jan 26, 2025
    Copy the full SHA
    fbee5eb View commit details

Commits on Mar 14, 2025

  1. fix: change default conditions and mainFields (#238)

    JounQin authored Mar 14, 2025
    Copy the full SHA
    c8a388d View commit details
  2. feat: migrate enhanced-resolve to oxc-resolver (#237)

    JounQin authored Mar 14, 2025
    Copy the full SHA
    53b316c View commit details
  3. chore: hourcekeeping, bump all (dev) deps, add lint steps (#239)

    JounQin authored Mar 14, 2025
    Copy the full SHA
    240e691 View commit details
  4. ci: corepack is not used

    JounQin committed Mar 14, 2025
    Copy the full SHA
    9730916 View commit details
  5. chore: enable eslint-plugin-mdx for linting Markdown files (#241)

    JounQin authored Mar 14, 2025
    Copy the full SHA
    fb7209b View commit details
  6. chore: ensure only one prettier installed

    JounQin committed Mar 14, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    cfabeb5 View commit details
  7. chore: release eslint-plugin-import-x (#219)

    Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
    github-actions[bot] authored Mar 14, 2025
    Copy the full SHA
    d2512df View commit details
Showing with 14,456 additions and 7,576 deletions.
  1. +1 −2 .codesandbox/ci.json
  2. +3 −0 .commitlintrc
  3. +1 −0 .env.yarn
  4. +2 −0 .eslintignore
  5. +19 −1 .eslintrc.js
  6. +1 −0 .gitattributes
  7. +10 −23 .github/workflows/ci.yml
  8. +4 −0 .github/workflows/codeql.yml
  9. +0 −8 .github/workflows/release.yml
  10. +6 −0 .gitignore
  11. +1 −0 .lintstagedrc.mjs
  12. +1 −0 .prettierignore
  13. +6 −0 .prettierrc.mjs
  14. +19 −0 .remarkrc
  15. +5 −0 .renovaterc
  16. +1 −0 .simple-git-hooks.js
  17. +1 −0 .yarn/plugins/plugin-prepare-lifecycle.cjs
  18. +935 −0 .yarn/releases/yarn-4.7.0.cjs
  19. +10 −0 .yarnrc.yml
  20. +40 −24 CHANGELOG.md
  21. +10 −12 README.md
  22. +4 −4 docs/rules/consistent-type-specifier-style.md
  23. +2 −3 docs/rules/default.md
  24. +2 −2 docs/rules/dynamic-import-chunkname.md
  25. +3 −3 docs/rules/extensions.md
  26. +2 −2 docs/rules/max-dependencies.md
  27. +3 −4 docs/rules/named.md
  28. +3 −4 docs/rules/namespace.md
  29. +2 −2 docs/rules/no-default-export.md
  30. +7 −0 docs/rules/no-deprecated.md
  31. +3 −3 docs/rules/no-mutable-exports.md
  32. +2 −2 docs/rules/no-named-as-default-member.md
  33. +2 −2 docs/rules/no-named-default.md
  34. +2 −2 docs/rules/no-named-export.md
  35. +5 −1 docs/rules/order.md
  36. +1 −0 jest.config.ts
  37. +71 −61 package.json
  38. +0 −40 patches/eslint-compat-utils+0.5.0.patch
  39. +92 −0 resolvers/README.md
  40. +4 −4 src/index.ts
  41. +27 −27 src/node-resolver.ts
  42. +2 −2 src/rules/consistent-type-specifier-style.ts
  43. +3 −3 src/rules/no-deprecated.ts
  44. +10 −11 src/rules/no-duplicates.ts
  45. +3 −1 src/rules/no-extraneous-dependencies.ts
  46. +5 −3 src/rules/no-restricted-paths.ts
  47. +3 −2 src/rules/no-unassigned-import.ts
  48. +5 −4 src/rules/no-unused-modules.ts
  49. +3 −3 src/types.ts
  50. +1 −2 src/utils/declared-scope.ts
  51. +7 −7 src/utils/export-map.ts
  52. +1 −1 src/utils/parse.ts
  53. +1 −1 src/utils/resolve.ts
  54. +4 −1 src/utils/visit.ts
  55. +207 −0 test/__snapshots__/node-resolver.spec.ts.snap
  56. +14 −13 test/fixtures/foo-bar-resolver-v3.js
  57. +1 −1 test/fixtures/just-json-files/.eslintrc.json
  58. +1 −1 test/fixtures/no-rename-default/anonymous-arrow-async.js
  59. +1 −1 test/fixtures/no-rename-default/anonymous-arrow.js
  60. +1 −1 test/fixtures/no-rename-default/anonymous-class.js
  61. +1 −1 test/fixtures/no-rename-default/anonymous-object.js
  62. +1 −1 test/fixtures/no-rename-default/anonymous-primitive.js
  63. +1 −1 test/fixtures/no-rename-default/assign-arrow-async.js
  64. +1 −1 test/fixtures/no-rename-default/assign-arrow.js
  65. +1 −1 test/fixtures/no-rename-default/assign-fn-named.js
  66. +1 −1 test/fixtures/no-rename-default/assign-fn.js
  67. +1 −1 test/fixtures/no-rename-default/assign-generator-named.js
  68. +1 −1 test/fixtures/no-rename-default/assign-generator.js
  69. +1 −1 test/fixtures/no-rename-default/class-user.js
  70. +4 −4 test/fixtures/no-rename-default/const-bar.js
  71. +4 −4 test/fixtures/no-rename-default/const-foo.js
  72. +2 −2 test/fixtures/no-rename-default/pr-3006-feedback/binding-const-rename-fn.js
  73. +1 −1 test/fixtures/no-rename-default/pr-3006-feedback/binding-fn-rename.js
  74. +3 −3 test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-foo.js
  75. +3 −3 test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-get-users.js
  76. +4 −4 test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-with-auth-for-get-users.js
  77. +2 −2 test/fixtures/no-rename-default/pr-3006-feedback/hoc-with-auth.js
  78. +2 −2 test/fixtures/no-rename-default/pr-3006-feedback/hoc-with-logger.js
  79. +2 −2 test/fixtures/no-rename-default/typescript-default.d.ts
  80. +0 −1 test/fixtures/node_modules/eslint-import-resolver-foo-v1/index.js
  81. +1 −0 test/fixtures/node_modules/eslint-import-resolver-foo-v1/index.js
  82. +0 −1 test/fixtures/node_modules/eslint-import-resolver-foo-v2/index.js
  83. +1 −0 test/fixtures/node_modules/eslint-import-resolver-foo-v2/index.js
  84. +6 −0 test/jest.serializer.ts
  85. +17 −10 test/node-resolver.spec.ts
  86. +22 −26 test/rules/no-absolute-path.spec.ts
  87. +1 −1 test/utils/import-type.spec.ts
  88. +2 −1 tsconfig.base.json
  89. +12,784 −7,206 yarn.lock
3 changes: 1 addition & 2 deletions .codesandbox/ci.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"node": "18",
"installCommand": "codesandbox:install",
"node": "20",
"sandboxes": []
}
3 changes: 3 additions & 0 deletions .commitlintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "@1stg"
}
1 change: 1 addition & 0 deletions .env.yarn
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ESLINT_USE_FLAT_CONFIG=false
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.yarn
lib
coverage
node_modules
@@ -8,3 +9,4 @@ test/fixtures/typescript-d-ts/
# we want to ignore "test/fixtures" here, but unfortunately doing so would
# interfere with unit test and fail it for some reason.
# test/fixtures
CHANGELOG.md
20 changes: 19 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -9,8 +9,12 @@ module.exports = {
'plugin:@typescript-eslint/recommended',
'plugin:eslint-plugin/recommended',
'plugin:import-x/recommended',
'plugin:json/recommended-legacy',
'plugin:mdx/recommended',
'plugin:n/recommended',
'plugin:unicorn/recommended',
'plugin:yml/standard',
'plugin:yml/prettier',
'plugin:prettier/recommended',
],
env: {
@@ -42,6 +46,13 @@ module.exports = {
'n/no-missing-import': 'off',
'n/no-missing-require': 'off',
'n/no-unsupported-features/es-syntax': 'off',
'unicorn/filename-case': [
'error',
{
case: 'kebabCase',
ignore: [String.raw`^(CONTRIBUTING|README)\.md$`],
},
],
'unicorn/no-array-callback-reference': 'off',
'unicorn/no-array-reduce': 'off',
'unicorn/no-null': 'off',
@@ -115,7 +126,14 @@ module.exports = {
{
files: 'global.d.ts',
rules: {
'import-x/no-extraneous-dependencies': 0,
'import-x/no-extraneous-dependencies': 'off',
},
},
{
files: 'README.md',
rules: {
// https://github.com/bmish/eslint-doc-generator/issues/655
'no-irregular-whitespace': 'off',
},
},
],
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
33 changes: 10 additions & 23 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ on:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
cancel-in-progress: true

jobs:
ci:
@@ -16,7 +16,7 @@ jobs:
os:
- macos-latest
- ubuntu-latest
# - windows-latest
- windows-latest
node:
- 18
- 20
@@ -30,52 +30,39 @@ jobs:
- executeLint: true
node: 20
os: ubuntu-latest
fail-fast: false

runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repo
uses: actions/checkout@v4

- name: Setup target Node.js to enable Corepack
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}

- name: Enable Corepack
run: corepack enable

- name: Setup Node.js ${{ matrix.node }} with cache
- name: Setup Node.js ${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: yarn
env:
# https://github.com/actions/setup-node/issues/531#issuecomment-1819151412
SKIP_YARN_COREPACK_CHECK: 1

- name: Install ESLint ${{ matrix.eslint }}
run: |
yarn add -D --ignore-engines eslint@${{ matrix.eslint }}
yarn add -D eslint@${{ matrix.eslint }}
- name: Install Dependencies
run: yarn --ignore-engines
env:
SKIP_YARN_COREPACK_CHECK: 1
run: yarn --immutable

- name: Build and Test
run: |
yarn test-compiled
yarn test
env:
SKIP_YARN_COREPACK_CHECK: 1
- name: Lint
run: yarn lint
if: ${{ matrix.executeLint }}
env:
EFF_NO_LINK_RULES: true
PARSER_NO_WATCH: true
SKIP_YARN_COREPACK_CHECK: 1
if: ${{ matrix.executeLint }}

- name: Codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
4 changes: 4 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -6,6 +6,10 @@ on:
schedule:
- cron: '41 19 * * 6'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
analyze:
name: Analyze
8 changes: 0 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -16,9 +16,6 @@ jobs:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0

- name: Enable Corepack
run: corepack enable

- name: Setup Node.js LTS
uses: actions/setup-node@v4
with:
@@ -30,14 +27,10 @@ jobs:

- name: Install Dependencies
run: yarn --immutable
env:
SKIP_YARN_COREPACK_CHECK: 1

# required for linting before commit
- name: Build
run: yarn build
env:
SKIP_YARN_COREPACK_CHECK: 1

- name: Create Release Pull Request or Publish to npm
id: changesets
@@ -49,4 +42,3 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
SKIP_YARN_COREPACK_CHECK: 1
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -38,3 +38,9 @@ lib/
# macOS
.DS_Store
.*cache

.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
1 change: 1 addition & 0 deletions .lintstagedrc.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@1stg/lint-staged/tsc'
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.yarn
test/fixtures/just-json-files/invalid.json
test/fixtures/malformed.js
test/fixtures/with-syntax-error/package.json
6 changes: 6 additions & 0 deletions .prettierrc.mjs
Original file line number Diff line number Diff line change
@@ -10,5 +10,11 @@ export default {
parser: 'babel-flow',
},
},
{
files: ['**/.changeset/*.md'],
options: {
singleQuote: false,
},
},
],
}
19 changes: 19 additions & 0 deletions .remarkrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"plugins": [
"@1stg/preset",
[
"lint-no-shortcut-reference-link",
false
],
[
"lint-no-undefined-references",
{
"allow": [
"!NOTE",
"!TIP",
"!WARNING"
]
}
]
]
}
5 changes: 5 additions & 0 deletions .renovaterc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": [
"github>1stG/configs"
]
}
1 change: 1 addition & 0 deletions .simple-git-hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@1stg/simple-git-hooks')
1 change: 1 addition & 0 deletions .yarn/plugins/plugin-prepare-lifecycle.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports={name:"plugin-prepare-lifecycle",factory:e=>({hooks:{afterAllInstalled(r){if(!r.topLevelWorkspace.manifest.scripts.get("prepare"))return;e("@yarnpkg/shell").execute("yarn prepare")}}})};
935 changes: 935 additions & 0 deletions .yarn/releases/yarn-4.7.0.cjs

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
enableTelemetry: false

nodeLinker: node-modules

plugins:
- checksum: 37b2361b1502b2054e6779788c0e9bdd6a90ce49852a8cad2feda79b0614ec94f06fb6e78951f5f95429c610d7934dd077caa47413a0227378a102c55161616d
path: .yarn/plugins/plugin-prepare-lifecycle.cjs
spec: 'https://github.com/un-es/yarn-plugin-prepare-lifecycle/releases/download/v0.0.1/index.js'

yarnPath: .yarn/releases/yarn-4.7.0.cjs
64 changes: 40 additions & 24 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# eslint-plugin-import-x

## 4.7.0

### Minor Changes

- [#237](https://github.com/un-ts/eslint-plugin-import-x/pull/237) [`53b316c`](https://github.com/un-ts/eslint-plugin-import-x/commit/53b316cb0901e328f1b6529a05012dc67a17df46) Thanks [@JounQin](https://github.com/JounQin)! - feat: migrate `enhanced-resolve` to `oxc-resolver`

- [#214](https://github.com/un-ts/eslint-plugin-import-x/pull/214) [`091d2da`](https://github.com/un-ts/eslint-plugin-import-x/commit/091d2da9ee668e90ac7ee9aeb08437bc96871296) Thanks [@mrginglymus](https://github.com/mrginglymus)! - Improve windows support

### Patch Changes

- [#238](https://github.com/un-ts/eslint-plugin-import-x/pull/238) [`c8a388d`](https://github.com/un-ts/eslint-plugin-import-x/commit/c8a388dc4a0f801d697e6279043cfe6f81e512d5) Thanks [@JounQin](https://github.com/JounQin)! - fix: change default `conditions` and `mainFields`

`default` should be last matched, `module` should be in `mainFields`.

Reference https://github.com/isaacs/resolve-import/blob/03daf0a9649d183bea40975a7777ae72955f44b8/src/resolve-conditional-value.ts#L15

## 4.6.1

### Patch Changes
@@ -167,24 +183,24 @@

```ts
export interface NewResolver {
interfaceVersion: 3;
name?: string; // This will be included in the debug log
resolve: (modulePath: string, sourceFile: string) => ResolvedResult;
interfaceVersion: 3
name?: string // This will be included in the debug log
resolve: (modulePath: string, sourceFile: string) => ResolvedResult
}

// The `ResultNotFound` (returned when not resolved) is the same, no changes
export interface ResultNotFound {
found: false;
path?: undefined;
found: false
path?: undefined
}

// The `ResultFound` (returned resolve result) is also the same, no changes
export interface ResultFound {
found: true;
path: string | null;
found: true
path: string | null
}

export type ResolvedResult = ResultNotFound | ResultFound;
export type ResolvedResult = ResultNotFound | ResultFound
```
You will be able to import `NewResolver` from `eslint-plugin-import-x/types`.
@@ -231,33 +247,33 @@
resolve(mod, source) {
// every time the `resolve` function is called, a new instance is created
// This is very slow
const resolverInstance = ResolverFactory.createResolver({});
const found = resolverInstance.resolve(mod, {});
const resolverInstance = ResolverFactory.createResolver({})
const found = resolverInstance.resolve(mod, {})
},
};
}
```

With the factory function pattern, you can create a resolver instance beforehand:

```js
exports.createCustomResolver = (options) => {
exports.createCustomResolver = options => {
// `enhance-resolve` allows you to create a reusable instance:
const resolverInstance = ResolverFactory.createResolver({});
const resolverInstance = enhanceResolve.create({});
const resolverInstance = ResolverFactory.createResolver({})
const resolverInstance = enhanceResolve.create({})

// `oxc-resolver` also allows you to create a reusable instance:
const resolverInstance = new ResolverFactory({});
const resolverInstance = new ResolverFactory({})

return {
name: "custom-resolver",
name: 'custom-resolver',
interfaceVersion: 3,
resolve(mod, source) {
// the same re-usable instance is shared across `resolve` invocations.
// more performant
const found = resolverInstance.resolve(mod, {});
const found = resolverInstance.resolve(mod, {})
},
};
};
}
}
```

### Patch Changes
@@ -359,12 +375,12 @@
- [#122](https://github.com/un-ts/eslint-plugin-import-x/pull/122) [`cd52e86`](https://github.com/un-ts/eslint-plugin-import-x/commit/cd52e86f44754b4dd0c1aae1e9fd5e952e90938f) Thanks [@michaelfaith](https://github.com/michaelfaith)! - Add ESLint flat configuration presets. You can access them with:

```ts
import eslintPluginImportX from "eslint-plugin-import-x";
import eslintPluginImportX from 'eslint-plugin-import-x'

eslintPluginImportX.flatConfigs.recommended;
eslintPluginImportX.flatConfigs.react;
eslintPluginImportX.flatConfigs.typescript;
eslintPluginImportX.flatConfigs.electron;
eslintPluginImportX.flatConfigs.recommended
eslintPluginImportX.flatConfigs.react
eslintPluginImportX.flatConfigs.typescript
eslintPluginImportX.flatConfigs.electron
```

- [#132](https://github.com/un-ts/eslint-plugin-import-x/pull/132) [`9948c78`](https://github.com/un-ts/eslint-plugin-import-x/commit/9948c7894758dd461f6d75b89c6425fee304789a) Thanks [@SukkaW](https://github.com/SukkaW)! - Added `no-rename-default` that forbid importing a default export by a different name. Originally created by @whitneyit, ported by @SukkaW
22 changes: 10 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -160,9 +160,6 @@ settings:
node: true
```
[`@typescript-eslint/parser`]: https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser
[`eslint-import-resolver-typescript`]: https://github.com/import-js/eslint-import-resolver-typescript

## Configuration (new: `eslint.config.js`)

From [`v8.21.0`](https://github.com/eslint/eslint/releases/tag/v8.21.0), ESLint announced a new config system.
@@ -337,11 +334,6 @@ the process's current working directory if no `package.json` is found.

If you are interesting in writing a resolver, see the [spec](./resolvers/README.md) for more details.

[`resolve`]: https://www.npmjs.com/package/resolve
[`externals`]: https://webpack.github.io/docs/library-and-externals.html
[Node]: https://www.npmjs.com/package/eslint-import-resolver-node
[webpack]: https://www.npmjs.com/package/eslint-import-resolver-webpack

## Settings

You may set the following settings in your `.eslintrc`:
@@ -505,14 +497,11 @@ settings:
lifetime: 5 # 30 is the default
```

[`eslint_d`]: https://www.npmjs.com/package/eslint_d
[`eslint-loader`]: https://www.npmjs.com/package/eslint-loader

### `import-x/internal-regex`

A regex for packages should be treated as internal. Useful when you are utilizing a monorepo setup or developing a set of packages that depend on each other.

By default, any package referenced from [`import-x/external-module-folders`](#importexternal-module-folders) will be considered as "external", including packages in a monorepo like yarn workspace or lerna environment. If you want to mark these packages as "internal" this will be useful.
By default, any package referenced from [`import-x/external-module-folders`](#import-xexternal-module-folders) will be considered as "external", including packages in a monorepo like yarn workspace or lerna environment. If you want to mark these packages as "internal" this will be useful.

For example, if your packages in a monorepo are all in `@scope`, you can configure `import-x/internal-regex` like this

@@ -593,3 +582,12 @@ In Package Settings / SublimeLinter / User Settings:

I believe this defaults to `3`, so you may not need to alter it depending on your
project folder max depth.

[`@typescript-eslint/parser`]: https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser
[`eslint-import-resolver-typescript`]: https://github.com/import-js/eslint-import-resolver-typescript
[`resolve`]: https://www.npmjs.com/package/resolve
[`externals`]: https://webpack.github.io/docs/library-and-externals.html
[Node]: https://www.npmjs.com/package/eslint-import-resolver-node
[webpack]: https://www.npmjs.com/package/eslint-import-resolver-webpack
[`eslint_d`]: https://www.npmjs.com/package/eslint_d
[`eslint-loader`]: https://www.npmjs.com/package/eslint-loader
8 changes: 4 additions & 4 deletions docs/rules/consistent-type-specifier-style.md
Original file line number Diff line number Diff line change
@@ -31,16 +31,14 @@ This rule either enforces or bans the use of inline type-only markers for named

This rule includes a fixer that will automatically convert your specifiers to the correct form - however the fixer will not respect your preferences around de-duplicating imports. If this is important to you, consider using the [`import-x/no-duplicates`] rule.

[`import-x/no-duplicates`]: ./no-duplicates.md

## Options

The rule accepts a single string option which may be one of:

- `'prefer-inline'` - enforces that named type-only specifiers are only ever written with an inline marker; and never as part of a top-level, type-only import.
- `'prefer-top-level'` - enforces that named type-only specifiers only ever written as part of a top-level, type-only import; and never with an inline marker.
- `'prefer-inline'` - enforces that named type-only specifiers are only ever written with an inline marker; and never as part of a top-level, type-only import.

By default the rule will use the `prefer-inline` option.
By default the rule will use the `prefer-top-level` option.

## Examples

@@ -89,3 +87,5 @@ import {typeof Foo} from 'Foo';
If you aren't using Flow or TypeScript 4.5+, then this rule does not apply and need not be used.

If you don't care about, and don't want to standardize how named specifiers are imported then you should not use this rule.

[`import-x/no-duplicates`]: ./no-duplicates.md
5 changes: 2 additions & 3 deletions docs/rules/default.md
Original file line number Diff line number Diff line change
@@ -16,9 +16,6 @@ Redux's npm module includes this key, and thereby is lintable, for example.

A module path that is [ignored] or not [unambiguously an ES module] will not be reported when imported.

[ignored]: ../README.md#importignore
[unambiguously an ES module]: https://github.com/bmeck/UnambiguousJavaScriptGrammar

## Rule Details

Given:
@@ -78,3 +75,5 @@ either, so such a situation will be reported in the importing module.
[ES7]: https://github.com/leebyron/ecmascript-more-export-from
[`import-x/ignore`]: ../../README.md#importignore
[`jsnext:main`]: https://github.com/rollup/rollup/wiki/jsnext:main
[ignored]: ../../README.md#importignore
[unambiguously an ES module]: https://github.com/bmeck/UnambiguousJavaScriptGrammar
4 changes: 2 additions & 2 deletions docs/rules/dynamic-import-chunkname.md
Original file line number Diff line number Diff line change
@@ -103,7 +103,7 @@ If you want to allow dynamic imports without a webpackChunkName, you can set `al

Given `{ "allowEmpty": true }`:

<!-- markdownlint-disable-next-line MD024 -- duplicate header -->
<!-- lint disable no-duplicate-headings-in-section -->

### valid

@@ -118,7 +118,7 @@ import(
)
```

<!-- markdownlint-disable-next-line MD024 -- duplicate header -->
<!-- lint disable no-duplicate-headings-in-section -->

### invalid

6 changes: 3 additions & 3 deletions docs/rules/extensions.md
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ By providing an object you can configure each extension separately.
}]
```

For example `{ "js": "always", "json": "never" }` would always enforce the use of the `.js` extension but never allow the use of the `.json` extension.
For example `{ "js": "always", "json": "never" }` would always enforce the use of the `.js` extension but never allow the use of the `.json` extension.

By providing both a string and an object, the string will set the default setting for all extensions, and the object can be used to set granular overrides for specific extensions.

@@ -66,8 +66,8 @@ For example, given the following folder structure:

```pt
├── foo
   ├── bar.js
   ├── bar.json
├── bar.js
├── bar.json
```

and this import statement:
4 changes: 2 additions & 2 deletions docs/rules/max-dependencies.md
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ Ignores `type` imports. Type imports are a feature released in TypeScript 3.8, y

Given `{"max": 2, "ignoreTypeImports": true}`:

<!-- markdownlint-disable-next-line MD024 -- duplicate header -->
<!-- lint disable no-duplicate-headings-in-section -->

### Fail

@@ -57,7 +57,7 @@ import b from './b'
import c from './c'
```

<!-- markdownlint-disable-next-line MD024 -- duplicate header -->
<!-- lint disable no-duplicate-headings-in-section -->

### Pass

7 changes: 3 additions & 4 deletions docs/rules/named.md
Original file line number Diff line number Diff line change
@@ -14,10 +14,6 @@ Redux's npm module includes this key, and thereby is lintable, for example.

A module path that is [ignored] or not [unambiguously an ES module] will not be reported when imported. Note that type imports and exports, as used by [Flow], are always ignored.

[ignored]: ../../README.md#importignore
[unambiguously an ES module]: https://github.com/bmeck/UnambiguousJavaScriptGrammar
[Flow]: https://flow.org/

## Rule Details

Given:
@@ -100,3 +96,6 @@ runtime, you will likely see false positives with this rule.
[`jsnext:main`]: https://github.com/jsforum/jsforum/issues/5
[`pkg.module`]: https://github.com/rollup/rollup/wiki/pkg.module
[`import-x/ignore`]: ../../README.md#importignore
[ignored]: ../../README.md#importignore
[unambiguously an ES module]: https://github.com/bmeck/UnambiguousJavaScriptGrammar
[Flow]: https://flow.org/
7 changes: 3 additions & 4 deletions docs/rules/namespace.md
Original file line number Diff line number Diff line change
@@ -18,9 +18,6 @@ Redux's npm module includes this key, and thereby is lintable, for example.

A module path that is [ignored] or not [unambiguously an ES module] will not be reported when imported.

[ignored]: ../README.md#importignore
[unambiguously an ES module]: https://github.com/bmeck/UnambiguousJavaScriptGrammar

## Rule Details

Currently, this rule does not check for possible
@@ -98,8 +95,10 @@ still can't be statically analyzed any further.

- Lee Byron's [ES7] export proposal
- [`import-x/ignore`] setting
- [`jsnext:main`](Rollup)
- [`jsnext:main`]

[ES7]: https://github.com/leebyron/ecmascript-more-export-from
[`import-x/ignore`]: ../../README.md#importignore
[`jsnext:main`]: https://github.com/rollup/rollup/wiki/jsnext:main
[ignored]: ../../README.md#importignore
[unambiguously an ES module]: https://github.com/bmeck/UnambiguousJavaScriptGrammar
4 changes: 2 additions & 2 deletions docs/rules/no-default-export.md
Original file line number Diff line number Diff line change
@@ -4,8 +4,6 @@

Prohibit default exports. Mostly an inverse of [`prefer-default-export`].

[`prefer-default-export`]: ./prefer-default-export.md

## Rule Details

The following patterns are considered warnings:
@@ -63,3 +61,5 @@ export * from './other-module'
## When Not To Use It

If you don't care if default imports are used, or if you prefer default imports over named imports.

[`prefer-default-export`]: ./prefer-default-export.md
7 changes: 7 additions & 0 deletions docs/rules/no-deprecated.md
Original file line number Diff line number Diff line change
@@ -51,6 +51,13 @@ settings:
import-x/docstyle: ['jsdoc', 'tomdoc']
```
## When Not To Use It
TypeScript supports [multiple function signatures](https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads)
where only some of signatures can be deprecated. This rule does not support this
configuration. Consider using [`@typescript-eslint/no-deprecated`](https://typescript-eslint.io/rules/no-deprecated/)
instead.

## Worklist

- [x] report explicit imports on the import node
6 changes: 3 additions & 3 deletions docs/rules/no-mutable-exports.md
Original file line number Diff line number Diff line change
@@ -46,9 +46,9 @@ want to enable the following core ESLint rules:
- [no-func-assign]
- [no-class-assign]

[no-func-assign]: https://eslint.org/docs/rules/no-func-assign
[no-class-assign]: https://eslint.org/docs/rules/no-class-assign

## When Not To Use It

If your environment correctly implements mutable export bindings.

[no-func-assign]: https://eslint.org/docs/rules/no-func-assign
[no-class-assign]: https://eslint.org/docs/rules/no-class-assign
4 changes: 2 additions & 2 deletions docs/rules/no-named-as-default-member.md
Original file line number Diff line number Diff line change
@@ -17,8 +17,6 @@ Furthermore, [in Babel 5 this is actually how things worked][blog]. This was
fixed in Babel 6. Before upgrading an existing codebase to Babel 6, it can be
useful to run this lint rule.

[blog]: https://kentcdodds.com/blog/misunderstanding-es6-modules-upgrading-babel-tears-and-a-solution

## Rule Details

Given:
@@ -50,3 +48,5 @@ const bar = foo.bar
import foo from './foo.js'
const { bar } = foo
```

[blog]: https://kentcdodds.com/blog/misunderstanding-es6-modules-upgrading-babel-tears-and-a-solution
4 changes: 2 additions & 2 deletions docs/rules/no-named-default.md
Original file line number Diff line number Diff line change
@@ -8,8 +8,6 @@ Rationale: the syntax exists to import default exports expressively, let's use i

Note that type imports, as used by [Flow], are always ignored.

[Flow]: https://flow.org/

## Rule Details

Given:
@@ -34,3 +32,5 @@ import foo, { bar } from './foo.js'
import { default as foo } from './foo.js'
import { default as foo, bar } from './foo.js'
```

[Flow]: https://flow.org/
4 changes: 2 additions & 2 deletions docs/rules/no-named-export.md
Original file line number Diff line number Diff line change
@@ -4,8 +4,6 @@

Prohibit named exports. Mostly an inverse of [`no-default-export`].

[`no-default-export`]: ./no-default-export.md

## Rule Details

The following patterns are considered warnings:
@@ -77,3 +75,5 @@ export default from './other-module'
## When Not To Use It

If you don't care if named imports are used, or if you prefer named imports over default imports.

[`no-default-export`]: ./no-default-export.md
6 changes: 5 additions & 1 deletion docs/rules/order.md
Original file line number Diff line number Diff line change
@@ -102,7 +102,7 @@ This rule supports the following options:

### `groups: [array]`

How groups are defined, and the order to respect. `groups` must be an array of `string` or [`string`]. The only allowed `string`s are:
How groups are defined, and the order to respect. `groups` must be an array of `string` or `string[]`. The only allowed `string`s are:
`"builtin"`, `"external"`, `"internal"`, `"unknown"`, `"parent"`, `"sibling"`, `"index"`, `"object"`, `"type"`.
The enforced order is the same as the order of each element in a group. Omitted types are implicitly grouped together as the last element. Example:

@@ -224,6 +224,8 @@ Example:

[Import Type](https://github.com/un-ts/eslint-plugin-import-x/blob/ea7c13eb9b18357432e484b25dfa4451eca69c5b/src/utils/import-type.ts#L145) is resolved as a fixed string in predefined set, it can't be a `patterns` (e.g., `react`, `react-router-dom`, etc).

<!-- lint disable maximum-heading-length -->

### `newlines-between: [ignore|always|always-and-inside-groups|never]`

Enforces or forbids new lines between import groups:
@@ -295,6 +297,8 @@ import index from './'
import sibling from './foo'
```

<!-- lint disable maximum-heading-length -->

### `alphabetize: {order: asc|desc|ignore, orderImportKind: asc|desc|ignore, caseInsensitive: true|false}`

Sort the order within each group in alphabetical manner based on **import path**:
1 change: 1 addition & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ export default {
'^eslint-plugin-import-x/package.json$': `<rootDir>/package.json`,
'^eslint-plugin-import-x/(.+)$': `<rootDir>/${srcDir}/$1`,
},
snapshotSerializers: ['<rootDir>/test/jest.serializer.ts'],
testMatch: ['<rootDir>/test/**/*.spec.ts'],
transform: {
'^.+\\.(t|j)sx?$': ['@swc-node/jest', {} satisfies SwcOptions],
132 changes: 71 additions & 61 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "eslint-plugin-import-x",
"version": "4.6.1",
"version": "4.7.0",
"description": "Import with sanity.",
"repository": "git+https://github.com/un-ts/eslint-plugin-import-x",
"author": "JounQin <admin@1stg.me> (https://www.1stG.me)",
"license": "MIT",
"packageManager": "yarn@1.22.22",
"packageManager": "yarn@4.7.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -32,12 +32,11 @@
"scripts": {
"build": "tsc -p src",
"clean": "rimraf lib",
"codesandbox:install": "yarn --ignore-engines",
"lint": "run-p lint:*",
"lint": "run-p 'lint:*'",
"lint:docs": "yarn update:eslint-docs --check",
"lint:es": "cross-env ESLINT_USE_FLAT_CONFIG=false eslint . --cache",
"lint:es": "eslint . --cache",
"lint:tsc": "tsc -p tsconfig.base.json --noEmit",
"prepare": "patch-package",
"prepare": "patch-package && simple-git-hooks && yarn-berry-deduplicate || exit 0",
"release": "changeset publish",
"test": "jest",
"test-compiled": "yarn build && cross-env TEST_COMPILED=1 jest",
@@ -49,81 +48,92 @@
},
"dependencies": {
"@types/doctrine": "^0.0.9",
"@typescript-eslint/scope-manager": "^8.1.0",
"@typescript-eslint/utils": "^8.1.0",
"debug": "^4.3.4",
"@typescript-eslint/utils": "^8.26.1",
"debug": "^4.4.0",
"doctrine": "^3.0.0",
"eslint-import-resolver-node": "^0.3.9",
"get-tsconfig": "^4.7.3",
"get-tsconfig": "^4.10.0",
"is-glob": "^4.0.3",
"minimatch": "^9.0.3",
"enhanced-resolve": "^5.17.1",
"semver": "^7.6.3",
"stable-hash": "^0.0.4",
"tslib": "^2.6.3"
"minimatch": "^10.0.1",
"oxc-resolver": "^5.0.0",
"semver": "^7.7.1",
"stable-hash": "^0.0.5",
"tslib": "^2.8.1"
},
"devDependencies": {
"@1stg/prettier-config": "^4.0.1",
"@1stg/tsconfig": "^2.3.3",
"@angular-eslint/template-parser": "^17.5.2",
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.25.1",
"@babel/plugin-proposal-decorators": "^7.24.7",
"@babel/plugin-proposal-export-default-from": "^7.24.7",
"@babel/preset-env": "^7.25.3",
"@babel/preset-flow": "^7.24.7",
"@babel/preset-react": "^7.24.7",
"@babel/preset-typescript": "^7.24.7",
"@babel/register": "^7.24.6",
"@changesets/changelog-github": "^0.5.0",
"@changesets/cli": "^2.27.1",
"@1stg/commitlint-config": "^5.0.0",
"@1stg/lint-staged": "^4.0.4",
"@1stg/prettier-config": "^4.0.4",
"@1stg/remark-preset": "^3.0.0",
"@1stg/simple-git-hooks": "^0.2.4",
"@1stg/tsconfig": "^3.0.0",
"@angular-eslint/template-parser": "^19.2.1",
"@babel/core": "^7.26.10",
"@babel/eslint-parser": "^7.26.10",
"@babel/plugin-proposal-decorators": "^7.25.9",
"@babel/plugin-proposal-export-default-from": "^7.25.9",
"@babel/preset-env": "^7.26.9",
"@babel/preset-flow": "^7.25.9",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@babel/register": "^7.25.9",
"@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.28.1",
"@commitlint/cli": "^19.8.0",
"@eslint/import-test-order-redirect-scoped": "link:./test/fixtures/order-redirect-scoped",
"@swc-node/jest": "^1.8.12",
"@swc/core": "^1.7.6",
"@swc/helpers": "^0.5.12",
"@swc-node/jest": "^1.8.13",
"@swc/core": "^1.11.9",
"@swc/helpers": "^0.5.15",
"@test-scope/some-module": "link:./test/fixtures/symlinked-module",
"@total-typescript/ts-reset": "^0.5.1",
"@total-typescript/ts-reset": "^0.6.1",
"@types/debug": "^4.1.12",
"@types/eslint": "^9.6.1",
"@types/eslint8.56": "npm:@types/eslint@^8.56.11",
"@types/eslint9": "npm:@types/eslint@^9.6.1",
"@types/eslint8.56": "npm:@types/eslint@~8.56.0",
"@types/is-glob": "^4.0.4",
"@types/jest": "^29.5.12",
"@types/jest": "^29.5.14",
"@types/json-schema": "^7.0.15",
"@types/klaw-sync": "^6.0.5",
"@types/node": "^20.11.30",
"@typescript-eslint/eslint-plugin": "^8.15.0",
"@typescript-eslint/parser": "^8.15.0",
"@typescript-eslint/rule-tester": "^8.15.0",
"@unts/patch-package": "^8.0.0",
"@types/node": "^20.17.24",
"@typescript-eslint/eslint-plugin": "^8.26.1",
"@typescript-eslint/parser": "^8.26.1",
"@typescript-eslint/rule-tester": "^8.26.1",
"@unts/patch-package": "^8.1.1",
"cross-env": "^7.0.3",
"escope": "^4.0.0",
"eslint": "^9.15.0",
"eslint-config-prettier": "^9.1.0",
"eslint-doc-generator": "^1.7.1",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-import-resolver-webpack": "^0.13.8",
"eslint": "^9.22.0",
"eslint-config-prettier": "^10.1.1",
"eslint-doc-generator": "^2.1.1",
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-import-resolver-webpack": "^0.13.10",
"eslint-import-test-order-redirect": "link:./test/fixtures/order-redirect",
"eslint-plugin-eslint-plugin": "^5.4.1",
"eslint-plugin-eslint-plugin": "^6.4.0",
"eslint-plugin-import-x": "link:.",
"eslint-plugin-jest": "^27.9.0",
"eslint-plugin-json": "^3.1.0",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-jest": "^28.11.0",
"eslint-plugin-json": "^4.0.1",
"eslint-plugin-mdx": "^3.2.0",
"eslint-plugin-n": "^17.16.2",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-unicorn": "^56.0.1",
"eslint8.56": "npm:eslint@^8.56.0",
"eslint9": "npm:eslint@^9.15.0",
"hermes-eslint": "^0.23.1",
"eslint-plugin-yml": "^1.17.0",
"eslint8.56": "npm:eslint@~8.56.0",
"eslint9": "npm:eslint@^9.22.0",
"hermes-eslint": "^0.26.0",
"jest": "^29.7.0",
"klaw-sync": "^6.0.0",
"npm-run-all2": "^6.1.2",
"prettier": "^3.2.5",
"lint-staged": "^15.5.0",
"npm-run-all2": "^7.0.2",
"path-serializer": "^0.3.4",
"prettier": "^3.5.3",
"redux": "^5.0.1",
"rimraf": "^5.0.10",
"svelte": "^4.2.12",
"rimraf": "^6.0.1",
"simple-git-hooks": "^2.11.1",
"ts-node": "^10.9.2",
"type-fest": "^4.14.0",
"typescript": "^5.5.4",
"zod": "^3.23.8"
"type-fest": "^4.37.0",
"typescript": "^5.8.2",
"yarn-berry-deduplicate": "^6.1.1",
"zod": "^3.24.2"
},
"resolutions": {
"prettier": "^3.5.3"
}
}
40 changes: 0 additions & 40 deletions patches/eslint-compat-utils+0.5.0.patch

This file was deleted.

92 changes: 92 additions & 0 deletions resolvers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Resolver spec (v2)

Resolvers must export two names:

## `interfaceVersion => Number`

This document currently describes version 2 of the resolver interface. As such, a resolver implementing this version should

```js
export const interfaceVersion = 2
```

or

```js
exports.interfaceVersion = 2
```

To the extent it is feasible, trailing versions of the resolvers will continue to be supported, at least until a major version bump on the plugin proper.

Currently, version 1 is assumed if no `interfaceVersion` is available. (didn't think to define it until v2, heh. 😅)

<!-- lint disable maximum-heading-length -->

## `resolve(source, file, config) => { found: Boolean, path: String? }`

Given:

```js
// /some/path/to/module.js
import ... from './imported-file'
```

and

```yaml
# .eslintrc.yml
---
settings:
import/resolver:
my-cool-resolver: [some, stuff]
node: { paths: [a, b, c] }
```
### Arguments
The arguments provided will be:
#### `source`

the module identifier (`./imported-file`).

#### `file`

the absolute path to the file making the import (`/some/path/to/module.js`)

#### `config`

an object provided via the `import/resolver` setting. `my-cool-resolver` will get `["some", "stuff"]` as its `config`, while
`node` will get `{ "paths": ["a", "b", "c"] }` provided as `config`.

### Return value

The first resolver to return `{found: true}` is considered the source of truth. The returned object has:

- `found`: `true` if the `source` module can be resolved relative to `file`, else `false`
- `path`: an absolute path `String` if the module can be located on the filesystem; else, `null`.

An example of a `null` path is a Node core module, such as `fs` or `crypto`. These modules can always be resolved, but the path need not be provided as the plugin will not attempt to parse core modules at this time.

If the resolver cannot resolve `source` relative to `file`, it should just return `{ found: false }`. No `path` key is needed in this case.

## Example

Here is most of the [Node resolver] at the time of this writing. It is just a wrapper around substack/Browserify's synchronous [`resolve`]:

```js
var resolve = require('resolve/sync')
var isCoreModule = require('is-core-module')
exports.resolve = function (source, file, config) {
if (isCoreModule(source)) return { found: true, path: null }
try {
return { found: true, path: resolve(source, opts(file, config)) }
} catch (err) {
return { found: false }
}
}
```

[Node resolver]: https://github.com/import-js/eslint-plugin-import/blob/main/resolvers/node/index.js
[`resolve`]: https://www.npmjs.com/package/resolve
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -173,10 +173,10 @@ const flatConfigs = {
'stage-0': createFlatConfig(stage0Flat, 'stage-0'),

// useful stuff for folks using various environments
react: reactFlat,
'react-native': reactNativeFlat,
electron: electronFlat,
typescript: typescriptFlat,
react: createFlatConfig(reactFlat, 'react'),
'react-native': createFlatConfig(reactNativeFlat, 'react-native'),
electron: createFlatConfig(electronFlat, 'electron'),
typescript: createFlatConfig(typescriptFlat, 'typescript'),
} satisfies Record<string, PluginFlatConfig>

export = {
54 changes: 27 additions & 27 deletions src/node-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,45 @@
import fs from 'node:fs'
import { isBuiltin } from 'node:module'
import path from 'node:path'

import { ResolverFactory, CachedInputFileSystem } from 'enhanced-resolve'
import type { ResolveOptions } from 'enhanced-resolve'
import { ResolverFactory } from 'oxc-resolver'
import type { NapiResolveOptions as ResolveOptions } from 'oxc-resolver'

import type { NewResolver } from './types'

type NodeResolverOptions = {
export type NodeResolverOptions = {
/**
* The allowed extensions the resolver will attempt to find when resolving a module
* @type {string[] | undefined}
* Attempt to resolve these extensions in order.
* If multiple files share the same name but have different extensions,
* will resolve the one with the extension listed first in the array and skip the rest.
*
* @default ['.mjs', '.cjs', '.js', '.json', '.node']
*/
extensions?: string[]
/**
* The import conditions the resolver will used when reading the exports map from "package.json"
* @type {string[] | undefined}
* @default ['default', 'module', 'import', 'require']
* Condition names for exports field which defines entry points of a package.
* The key order in the exports field is significant. During condition matching, earlier entries have higher priority and take precedence over later entries.
*
* @default ['import', 'require', 'default']
*/
conditionNames?: string[]
} & Omit<ResolveOptions, 'useSyncFileSystemCalls'>
/**
* A list of main fields in description files
*
* @default ['module', 'main']
*/
mainFields?: string[]
} & ResolveOptions

export function createNodeResolver({
extensions = ['.mjs', '.cjs', '.js', '.json', '.node'],
conditionNames = ['default', 'module', 'import', 'require'],
mainFields: _mainFields = ['main'],
exportsFields: _exportsFields = ['exports'],
mainFiles: _mainFiles = ['index'],
fileSystem = new CachedInputFileSystem(fs, 4 * 1000),
conditionNames = ['import', 'require', 'default'],
mainFields = ['module', 'main'],
...restOptions
}: Partial<NodeResolverOptions> = {}): NewResolver {
const resolver = ResolverFactory.createResolver({
}: NodeResolverOptions = {}): NewResolver {
const resolver = new ResolverFactory({
extensions,
fileSystem,
conditionNames,
useSyncFileSystemCalls: true,
mainFields,
...restOptions,
})

@@ -44,7 +48,7 @@ export function createNodeResolver({
return {
interfaceVersion: 3,
name: 'eslint-plugin-import-x built-in node resolver',
resolve: (modulePath, sourceFile) => {
resolve(modulePath, sourceFile) {
if (isBuiltin(modulePath)) {
return { found: true, path: null }
}
@@ -54,13 +58,9 @@ export function createNodeResolver({
}

try {
const resolved = resolver.resolveSync(
{},
path.dirname(sourceFile),
modulePath,
)
if (resolved) {
return { found: true, path: resolved }
const resolved = resolver.sync(path.dirname(sourceFile), modulePath)
if (resolved.path) {
return { found: true, path: resolved.path }
}
return { found: false }
} catch {
4 changes: 2 additions & 2 deletions src/rules/consistent-type-specifier-style.ts
Original file line number Diff line number Diff line change
@@ -61,8 +61,8 @@ export = createRule<[Options?], MessageId>({
schema: [
{
type: 'string',
enum: ['prefer-inline', 'prefer-top-level'],
default: 'prefer-inline',
enum: ['prefer-top-level', 'prefer-inline'],
default: 'prefer-top-level',
},
],
messages: {
6 changes: 3 additions & 3 deletions src/rules/no-deprecated.ts
Original file line number Diff line number Diff line change
@@ -40,8 +40,8 @@ export = createRule({
},
defaultOptions: [],
create(context) {
const deprecated = new Map()
const namespaces = new Map()
const deprecated = new Map<string, Tag>()
const namespaces = new Map<string, ExportMap | null>()

return {
Program({ body }) {
@@ -154,7 +154,7 @@ export = createRule({
}
context.report({
node,
...message(deprecated.get(node.name)),
...message(deprecated.get(node.name)!),
})
},

21 changes: 10 additions & 11 deletions src/rules/no-duplicates.ts
Original file line number Diff line number Diff line change
@@ -395,6 +395,14 @@ function hasCommentInsideNonSpecifiers(
)
}

type ModuleMap = {
imported: Map<string, TSESTree.ImportDeclaration[]>
nsImported: Map<string, TSESTree.ImportDeclaration[]>
defaultTypesImported: Map<string, TSESTree.ImportDeclaration[]>
namespaceTypesImported: Map<string, TSESTree.ImportDeclaration[]>
namedTypesImported: Map<string, TSESTree.ImportDeclaration[]>
}

export = createRule<[Options?], MessageId>({
name: 'no-duplicates',
meta: {
@@ -441,20 +449,11 @@ export = createRule<[Options?], MessageId>({
}
: defaultResolver

const moduleMaps = new Map<
TSESTree.Node,
{
imported: Map<string, TSESTree.ImportDeclaration[]>
nsImported: Map<string, TSESTree.ImportDeclaration[]>
defaultTypesImported: Map<string, TSESTree.ImportDeclaration[]>
namespaceTypesImported: Map<string, TSESTree.ImportDeclaration[]>
namedTypesImported: Map<string, TSESTree.ImportDeclaration[]>
}
>()
const moduleMaps = new Map<TSESTree.Node, ModuleMap>()

function getImportMap(n: TSESTree.ImportDeclaration) {
const parent = n.parent!
let map
let map: ModuleMap
if (moduleMaps.has(parent)) {
map = moduleMaps.get(parent)!
} else {
4 changes: 3 additions & 1 deletion src/rules/no-extraneous-dependencies.ts
Original file line number Diff line number Diff line change
@@ -335,7 +335,9 @@ function testConfig(config: string[] | boolean | undefined, filename: string) {
}
// Array of globs.
return config.some(
c => minimatch(filename, c) || minimatch(filename, path.resolve(c)),
c =>
minimatch(filename, c) ||
minimatch(filename, path.resolve(c), { windowsPathsNoEscape: true }),
)
}

8 changes: 5 additions & 3 deletions src/rules/no-restricted-paths.ts
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ const containsPath = (filepath: string, target: string) => {

function isMatchingTargetPath(filename: string, targetPath: string) {
if (isGlob(targetPath)) {
const mm = new Minimatch(targetPath)
const mm = new Minimatch(targetPath, { windowsPathsNoEscape: true })
return mm.match(filename)
}

@@ -171,13 +171,15 @@ export = createRule<[Options?], MessageId>({
) {
let isPathException: ((absoluteImportPath: string) => boolean) | undefined

const mm = new Minimatch(absoluteFrom)
const mm = new Minimatch(absoluteFrom, { windowsPathsNoEscape: true })
const isPathRestricted = (absoluteImportPath: string) =>
mm.match(absoluteImportPath)
const hasValidExceptions = zoneExcept.every(it => isGlob(it))

if (hasValidExceptions) {
const exceptionsMm = zoneExcept.map(except => new Minimatch(except))
const exceptionsMm = zoneExcept.map(
except => new Minimatch(except, { windowsPathsNoEscape: true }),
)
isPathException = (absoluteImportPath: string) =>
exceptionsMm.some(mm => mm.match(absoluteImportPath))
}
5 changes: 3 additions & 2 deletions src/rules/no-unassigned-import.ts
Original file line number Diff line number Diff line change
@@ -15,13 +15,14 @@ function testIsAllow(

const filePath =
// a node module
source[0] !== '.' && source[0] !== '/'
source[0] !== '.' && source[0] !== path.sep
? source
: path.resolve(path.dirname(filename), source) // get source absolute path

return globs.some(
glob =>
minimatch(filePath, glob) || minimatch(filePath, path.resolve(glob)),
minimatch(filePath, glob) ||
minimatch(filePath, path.resolve(glob), { windowsPathsNoEscape: true }),
)
}

9 changes: 5 additions & 4 deletions src/rules/no-unused-modules.ts
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
import path from 'node:path'

import { TSESTree } from '@typescript-eslint/utils'
import type { TSESLint } from '@typescript-eslint/utils'
import { FileEnumerator } from 'eslint/use-at-your-own-risk'

import type { FileExtension, RuleContext } from '../types'
@@ -118,7 +119,7 @@ const importList = new Map<string, Map<string, Set<string>>>()
*/
const exportList = new Map<string, Map<string, { whereUsed: Set<string> }>>()

const visitorKeyMap = new Map()
const visitorKeyMap = new Map<string, TSESLint.SourceCode.VisitorKeys | null>()

const ignoredFiles = new Set()
const filesOutsideSrc = new Set()
@@ -707,8 +708,8 @@ export = createRule<Options[], MessageId>({
const oldDefaultImports = new Set<string>()
const newDefaultImports = new Set<string>()

const oldImports = new Map()
const newImports = new Map()
const oldImports = new Map<string, string>()
const newImports = new Map<string, string>()
for (const [key, value] of oldImportPaths.entries()) {
if (value.has(AST_NODE_TYPES.ExportAllDeclaration)) {
oldExportAll.add(key)
@@ -770,7 +771,7 @@ export = createRule<Options[], MessageId>({
if (name === DEFAULT) {
newDefaultImports.add(resolvedPath!)
} else {
newImports.set(name, resolvedPath)
newImports.set(name, resolvedPath!)
}
}
}
6 changes: 3 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
import type { ResolveOptions } from 'enhanced-resolve'
import type { MinimatchOptions } from 'minimatch'
import type { NapiResolveOptions as ResolveOptions } from 'oxc-resolver'
import type { KebabCase } from 'type-fest'

import type { ImportType as ImportType_, PluginName } from './utils'
@@ -40,7 +40,7 @@ export type NodeResolverOptions = {
}

export type WebpackResolverOptions = {
config?: string | { resolve: Omit<ResolveOptions, 'fileSystem'> }
config?: string | { resolve: ResolveOptions }
'config-index'?: number
env?: Record<string, unknown>
argv?: Record<string, unknown>
@@ -50,7 +50,7 @@ export type TsResolverOptions = {
alwaysTryTypes?: boolean
project?: string[] | string
extensions?: string[]
} & Omit<ResolveOptions, 'fileSystem' | 'useSyncFileSystemCalls'>
} & ResolveOptions

// TODO: remove prefix New in the next major version
export type NewResolverResolve = (
3 changes: 1 addition & 2 deletions src/utils/declared-scope.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { ScopeType } from '@typescript-eslint/scope-manager'
import type { TSESTree } from '@typescript-eslint/utils'

import type { RuleContext } from '../types'
@@ -7,7 +6,7 @@ export function declaredScope(
context: RuleContext,
node: TSESTree.Node,
name: string,
): ScopeType | undefined {
) {
const references = context.sourceCode.getScope(node).references
const reference = references.find(x => x.identifier.name === name)
if (!reference || !reference.resolved) {
14 changes: 7 additions & 7 deletions src/utils/export-map.ts
Original file line number Diff line number Diff line change
@@ -7,9 +7,9 @@ import type { Annotation } from 'doctrine'
import doctrine from 'doctrine'
import type { AST } from 'eslint'
import { SourceCode } from 'eslint'
import type { TsConfigJsonResolved } from 'get-tsconfig'
import type { TsConfigJsonResolved, TsConfigResult } from 'get-tsconfig'
import { getTsconfig } from 'get-tsconfig'
import stableHash from 'stable-hash'
import { stableHash } from 'stable-hash'

import type {
ChildContext,
@@ -32,7 +32,7 @@ const log = debug('eslint-plugin-import-x:ExportMap')

const exportCache = new Map<string, ExportMap | null>()

const tsconfigCache = new Map<string, TsConfigJsonResolved | null>()
const tsconfigCache = new Map<string, TsConfigJsonResolved | null | undefined>()

export type DocStyleParsers = Record<
DocStyle,
@@ -410,13 +410,13 @@ export class ExportMap {
let tsconfigRootDir = parserOptions.tsconfigRootDir
const project = parserOptions.project
const cacheKey = stableHash({ tsconfigRootDir, project })
let tsConfig: TsConfigJsonResolved | null
let tsConfig: TsConfigJsonResolved | null | undefined

if (tsconfigCache.has(cacheKey)) {
tsConfig = tsconfigCache.get(cacheKey)!
} else {
tsconfigRootDir = tsconfigRootDir || process.cwd()
let tsconfigResult
let tsconfigResult: TsConfigResult | null | undefined
if (project) {
const projects = Array.isArray(project) ? project : [project]
for (const project of projects) {
@@ -432,7 +432,7 @@ export class ExportMap {
} else {
tsconfigResult = getTsconfig(tsconfigRootDir)
}
tsConfig = (tsconfigResult && tsconfigResult.config) || null
tsConfig = tsconfigResult?.config
tsconfigCache.set(cacheKey, tsConfig)
}

@@ -729,7 +729,7 @@ export class ExportMap {

declare visitorKeys: TSESLint.SourceCode.VisitorKeys | null

private declare mtime: number
declare private mtime: number

declare doc: Annotation | undefined

2 changes: 1 addition & 1 deletion src/utils/parse.ts
Original file line number Diff line number Diff line change
@@ -146,7 +146,7 @@ export function parse(
if (!ast || typeof ast !== 'object') {
console.warn(
// Can only be invalid for custom parser per imports/parser
`\`parseForESLint\` from parser \`${typeof parserOrPath === 'string' ? parserOrPath : '`context.languageOptions.parser`'}\` is invalid and will just be ignored`,
`\`parseForESLint\` from parser \`${typeof parserOrPath === 'string' ? parserOrPath : 'context.languageOptions.parser'}\` is invalid and will just be ignored`,
{ content, parserMeta: parser.meta },
)
} else {
2 changes: 1 addition & 1 deletion src/utils/resolve.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'node:fs'
import path from 'node:path'

import stableHash from 'stable-hash'
import { stableHash } from 'stable-hash'

import type {
ImportSettings,
5 changes: 4 additions & 1 deletion src/utils/visit.ts
Original file line number Diff line number Diff line change
@@ -2,7 +2,10 @@ import type { TSESTree } from '@typescript-eslint/utils'

export function visit(
node: TSESTree.Node,
keys: { [k in TSESTree.Node['type']]?: Array<keyof TSESTree.Node> } | null,
keys:
| { [k in TSESTree.Node['type']]?: Array<keyof TSESTree.Node> }
| undefined
| null,
visitorSpec: {
[k in TSESTree.Node['type'] | `${TSESTree.Node['type']}:Exit`]?: (
node: TSESTree.Node,
207 changes: 207 additions & 0 deletions test/__snapshots__/node-resolver.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`builtin node:path => true 1`] = `
{
"expected": true,
"requireResolve": "node:path",
"source": "node:path",
}
`;

exports[`builtin node:path => true 2`] = `
{
"expected": true,
"result": {
"found": true,
"path": null,
},
"source": "node:path",
}
`;

exports[`builtin path => true 1`] = `
{
"expected": true,
"requireResolve": "path",
"source": "path",
}
`;

exports[`builtin path => true 2`] = `
{
"expected": true,
"result": {
"found": true,
"path": null,
},
"source": "path",
}
`;

exports[`modules @sukka/does-not-exists => false 1`] = `
{
"expected": false,
"requireResolve": undefined,
"source": "@sukka/does-not-exists",
}
`;

exports[`modules @sukka/does-not-exists => false 2`] = `
{
"expected": false,
"result": {
"found": false,
},
"source": "@sukka/does-not-exists",
}
`;

exports[`modules jest => true 1`] = `
{
"expected": true,
"requireResolve": "<ROOT>/node_modules/jest/build/index.js",
"source": "jest",
}
`;

exports[`modules jest => true 2`] = `
{
"expected": true,
"result": {
"found": true,
"path": "<ROOT>/node_modules/jest/build/index.js",
},
"source": "jest",
}
`;

exports[`relative ../.github/dependabot.yml => false 1`] = `
{
"expected": false,
"requireResolve": undefined,
"source": "../.github/dependabot.yml",
}
`;

exports[`relative ../.github/dependabot.yml => false 2`] = `
{
"expected": false,
"result": {
"found": false,
},
"source": "../.github/dependabot.yml",
}
`;

exports[`relative ../babel.config.js => babel.config.js 1`] = `
{
"expected": "babel.config.js",
"requireResolve": "<ROOT>/babel.config.js",
"source": "../babel.config.js",
}
`;

exports[`relative ../babel.config.js => babel.config.js 2`] = `
{
"expected": "babel.config.js",
"result": {
"found": true,
"path": "<ROOT>/babel.config.js",
},
"source": "../babel.config.js",
}
`;

exports[`relative ../inexistent.js => false 1`] = `
{
"expected": false,
"requireResolve": undefined,
"source": "../inexistent.js",
}
`;

exports[`relative ../inexistent.js => false 2`] = `
{
"expected": false,
"result": {
"found": false,
},
"source": "../inexistent.js",
}
`;

exports[`relative ../package.json => package.json 1`] = `
{
"expected": "package.json",
"requireResolve": "<ROOT>/package.json",
"source": "../package.json",
}
`;

exports[`relative ../package.json => package.json 2`] = `
{
"expected": "package.json",
"result": {
"found": true,
"path": "<ROOT>/package.json",
},
"source": "../package.json",
}
`;

exports[`relative ../test => test/index.js 1`] = `
{
"expected": "test/index.js",
"requireResolve": "<ROOT>/test/index.js",
"source": "../test",
}
`;

exports[`relative ../test => test/index.js 2`] = `
{
"expected": "test/index.js",
"result": {
"found": true,
"path": "<ROOT>/test/index.js",
},
"source": "../test",
}
`;

exports[`relative ../test/ => test/index.js 1`] = `
{
"expected": "test/index.js",
"requireResolve": "<ROOT>/test/index.js",
"source": "../test/",
}
`;

exports[`relative ../test/ => test/index.js 2`] = `
{
"expected": "test/index.js",
"result": {
"found": true,
"path": "<ROOT>/test/index.js",
},
"source": "../test/",
}
`;

exports[`relative ../test/index.js => test/index.js 1`] = `
{
"expected": "test/index.js",
"requireResolve": "<ROOT>/test/index.js",
"source": "../test/index.js",
}
`;

exports[`relative ../test/index.js => test/index.js 2`] = `
{
"expected": "test/index.js",
"result": {
"found": true,
"path": "<ROOT>/test/index.js",
},
"source": "../test/index.js",
}
`;
27 changes: 14 additions & 13 deletions test/fixtures/foo-bar-resolver-v3.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
var path = require('path')

exports.foobarResolver = (/** @type {import('eslint-plugin-import-x/types').NewResolver} */ {
name: 'resolver-foo-bar',
interfaceVersion: 3,
resolve: function (modulePath, sourceFile) {
var sourceFileName = path.basename(sourceFile)
if (sourceFileName === 'foo.js') {
return { found: true, path: path.join(__dirname, 'bar.jsx') }
}
if (sourceFileName === 'exception.js') {
throw new Error('foo-bar-resolver-v3 resolve test exception')
}
return { found: false }
exports.foobarResolver =
/** @type {import('eslint-plugin-import-x/types').NewResolver} */ {
name: 'resolver-foo-bar',
interfaceVersion: 3,
resolve: function (modulePath, sourceFile) {
var sourceFileName = path.basename(sourceFile)
if (sourceFileName === 'foo.js') {
return { found: true, path: path.join(__dirname, 'bar.jsx') }
}
if (sourceFileName === 'exception.js') {
throw new Error('foo-bar-resolver-v3 resolve test exception')
}
return { found: false }
},
}
})
2 changes: 1 addition & 1 deletion test/fixtures/just-json-files/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@
"overrides": [
{
"files": "*.json",
"extends": "plugin:json/recommended"
"extends": "plugin:json/recommended-legacy"
}
]
}
2 changes: 1 addition & 1 deletion test/fixtures/no-rename-default/anonymous-arrow-async.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default async () => {};
export default async () => {}
2 changes: 1 addition & 1 deletion test/fixtures/no-rename-default/anonymous-arrow.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default () => {};
export default () => {}
2 changes: 1 addition & 1 deletion test/fixtures/no-rename-default/anonymous-class.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default class {};
export default class {}
2 changes: 1 addition & 1 deletion test/fixtures/no-rename-default/anonymous-object.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default {};
export default {}
2 changes: 1 addition & 1 deletion test/fixtures/no-rename-default/anonymous-primitive.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default 123;
export default 123
2 changes: 1 addition & 1 deletion test/fixtures/no-rename-default/assign-arrow-async.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default arrowAsync = async () => {};
export default arrowAsync = async () => {}
2 changes: 1 addition & 1 deletion test/fixtures/no-rename-default/assign-arrow.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default arrow = () => {};
export default arrow = () => {}
2 changes: 1 addition & 1 deletion test/fixtures/no-rename-default/assign-fn-named.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default fn = function myFn() {};
export default fn = function myFn() {}
2 changes: 1 addition & 1 deletion test/fixtures/no-rename-default/assign-fn.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default fn = function () {};
export default fn = function () {}
2 changes: 1 addition & 1 deletion test/fixtures/no-rename-default/assign-generator-named.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default generator = function* myGenerator() {};
export default generator = function* myGenerator() {}
2 changes: 1 addition & 1 deletion test/fixtures/no-rename-default/assign-generator.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default generator = function* () {};
export default generator = function* () {}
2 changes: 1 addition & 1 deletion test/fixtures/no-rename-default/class-user.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default class User {};
export default class User {}
8 changes: 4 additions & 4 deletions test/fixtures/no-rename-default/const-bar.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const barNamed1 = 'bar-named-1';
export const barNamed2 = 'bar-named-2';
export const barNamed1 = 'bar-named-1'
export const barNamed2 = 'bar-named-2'

const bar = 'bar';
const bar = 'bar'

export default bar;
export default bar
8 changes: 4 additions & 4 deletions test/fixtures/no-rename-default/const-foo.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const fooNamed1 = 'foo-named-1';
export const fooNamed2 = 'foo-named-2';
export const fooNamed1 = 'foo-named-1'
export const fooNamed2 = 'foo-named-2'

const foo = 'foo';
const foo = 'foo'

export default foo;
export default foo
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
const foo = function bar() {};
const foo = function bar() {}

export default foo;
export default foo
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
function bar() {}

export default bar;
export default bar
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import foo from '../default-const-foo';
import withLogger from './hoc-with-logger';
import foo from '../default-const-foo'
import withLogger from './hoc-with-logger'

export default withLogger(foo);
export default withLogger(foo)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import getUsers from '../default-fn-get-users';
import withLogger from './hoc-with-logger';
import getUsers from '../default-fn-get-users'
import withLogger from './hoc-with-logger'

export default withLogger(getUsers);
export default withLogger(getUsers)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import getUsers from '../default-fn-get-users';
import withAuth from './hoc-with-auth';
import withLogger from './hoc-with-logger';
import getUsers from '../default-fn-get-users'
import withAuth from './hoc-with-auth'
import withLogger from './hoc-with-logger'

export default withLogger(withAuth(getUsers));
export default withLogger(withAuth(getUsers))
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default function withAuth(fn) {
return function innerAuth(...args) {
const auth = {};
return fn.call(null, auth, ...args);
const auth = {}
return fn.call(null, auth, ...args)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default function withLogger(fn) {
return function innerLogger(...args) {
console.log(`${fn.name} called`);
return fn.apply(null, args);
console.log(`${fn.name} called`)
return fn.apply(null, args)
}
}
4 changes: 2 additions & 2 deletions test/fixtures/no-rename-default/typescript-default.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
declare const foo: {};
declare const foo: {}

export default foo;
export default foo

This file was deleted.

This file was deleted.

6 changes: 6 additions & 0 deletions test/jest.serializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createSnapshotSerializer } from 'path-serializer'

const serializer: ReturnType<typeof createSnapshotSerializer> =
createSnapshotSerializer()

export = serializer
27 changes: 17 additions & 10 deletions test/node-resolver.spec.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
import path from 'node:path'
import { cwd } from 'node:process'

import { createNodeResolver } from '../src/node-resolver'
import { createNodeResolver } from 'eslint-plugin-import-x/node-resolver'

const resolver = createNodeResolver()

function expectResolve(source: string, expected: boolean | string) {
it(`${source} => ${expected}`, () => {
let requireResolve: string | undefined
try {
console.log({
source,
expected,
requireResolve: require.resolve(source, { paths: [__dirname] }),
})
requireResolve = require.resolve(source, { paths: [__dirname] })
} catch {
console.log({ source, expected, requireResolve: null })
// ignore
}

expect({
source,
expected,
requireResolve,
}).toMatchSnapshot()

const result = resolver.resolve(source, __filename)
console.log({ source, expected, result })
expect({
source,
expected,
result,
}).toMatchSnapshot()

if (typeof expected === 'string') {
expect(result.path).toBe(path.resolve(cwd(), expected))
expect(result.path).toBe(path.resolve(expected))
} else {
expect(result.found).toBe(expected)
}
48 changes: 22 additions & 26 deletions test/rules/no-absolute-path.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import path from 'node:path'

import { RuleTester as TSESLintRuleTester } from '@typescript-eslint/rule-tester'
import type { TestCaseError as TSESLintTestCaseError } from '@typescript-eslint/rule-tester'

@@ -18,8 +16,6 @@ const ABSOLUTE_ERROR: TSESLintTestCaseError<
messageId: 'absolute',
}

const absolutePath = (testPath: string) => path.join(__dirname, testPath)

ruleTester.run('no-absolute-path', rule, {
valid: [
tValid({ code: 'import _ from "lodash"' }),
@@ -66,72 +62,72 @@ ruleTester.run('no-absolute-path', rule, {
],
invalid: [
tInvalid({
code: `import f from "${absolutePath('/foo')}"`,
filename: absolutePath('/foo/bar/index.js'),
code: `import f from "/foo"`,
filename: '/foo/bar/index.js',
errors: [ABSOLUTE_ERROR],
output: 'import f from ".."',
}),
tInvalid({
code: `import f from "${absolutePath('/foo/bar/baz.js')}"`,
filename: absolutePath('/foo/bar/index.js'),
code: `import f from "/foo/bar/baz.js"`,
filename: '/foo/bar/index.js',
errors: [ABSOLUTE_ERROR],
output: 'import f from "./baz.js"',
}),
tInvalid({
code: `import f from "${absolutePath('/foo/path')}"`,
filename: absolutePath('/foo/bar/index.js'),
code: `import f from "/foo/path"`,
filename: '/foo/bar/index.js',
errors: [ABSOLUTE_ERROR],
output: 'import f from "../path"',
}),
tInvalid({
code: `import f from "${absolutePath('/some/path')}"`,
filename: absolutePath('/foo/bar/index.js'),
code: `import f from "/some/path"`,
filename: '/foo/bar/index.js',
errors: [ABSOLUTE_ERROR],
output: 'import f from "../../some/path"',
}),
tInvalid({
code: `import f from "${absolutePath('/some/path')}"`,
filename: absolutePath('/foo/bar/index.js'),
code: `import f from "/some/path"`,
filename: '/foo/bar/index.js',
options: [{ amd: true }],
errors: [ABSOLUTE_ERROR],
output: 'import f from "../../some/path"',
}),
tInvalid({
code: `var f = require("${absolutePath('/foo')}")`,
filename: absolutePath('/foo/bar/index.js'),
code: `var f = require("/foo")`,
filename: '/foo/bar/index.js',
errors: [ABSOLUTE_ERROR],
output: 'var f = require("..")',
}),
tInvalid({
code: `var f = require("${absolutePath('/foo/path')}")`,
filename: absolutePath('/foo/bar/index.js'),
code: `var f = require("/foo/path")`,
filename: '/foo/bar/index.js',
errors: [ABSOLUTE_ERROR],
output: 'var f = require("../path")',
}),
tInvalid({
code: `var f = require("${absolutePath('/some/path')}")`,
filename: absolutePath('/foo/bar/index.js'),
code: `var f = require("/some/path")`,
filename: '/foo/bar/index.js',
errors: [ABSOLUTE_ERROR],
output: 'var f = require("../../some/path")',
}),
tInvalid({
code: `var f = require("${absolutePath('/some/path')}")`,
filename: absolutePath('/foo/bar/index.js'),
code: `var f = require("/some/path")`,
filename: '/foo/bar/index.js',
options: [{ amd: true }],
errors: [ABSOLUTE_ERROR],
output: 'var f = require("../../some/path")',
}),
// validate amd
tInvalid({
code: `require(["${absolutePath('/some/path')}"], function (f) { /* ... */ })`,
filename: absolutePath('/foo/bar/index.js'),
code: `require(["/some/path"], function (f) { /* ... */ })`,
filename: '/foo/bar/index.js',
options: [{ amd: true }],
errors: [ABSOLUTE_ERROR],
output: 'require(["../../some/path"], function (f) { /* ... */ })',
}),
tInvalid({
code: `define(["${absolutePath('/some/path')}"], function (f) { /* ... */ })`,
filename: absolutePath('/foo/bar/index.js'),
code: `define(["/some/path"], function (f) { /* ... */ })`,
filename: '/foo/bar/index.js',
languageOptions: { parser: require(parsers.ESPREE) },
options: [{ amd: true }],
errors: [ABSOLUTE_ERROR],
2 changes: 1 addition & 1 deletion test/utils/import-type.spec.ts
Original file line number Diff line number Diff line change
@@ -106,7 +106,7 @@ describe('importType(name)', () => {
// `@` for internal modules is a common alias and is different from scoped names.
// Scoped names are prepended with `@` (e.g. `@scoped/some-file.js`) whereas `@`
// as an alias by itelf is the full root name (e.g. `@/some-file.js`).
const alias = { '@': testFilePath('internal-modules') }
const alias = { '@': [testFilePath('internal-modules')] }
const webpackConfig = { resolve: { alias } }
const pathContext = testContext({
'import-x/resolver': { webpack: { config: webpackConfig } },
3 changes: 2 additions & 1 deletion tsconfig.base.json
Original file line number Diff line number Diff line change
@@ -6,7 +6,8 @@
"eslint-plugin-import-x": ["./src"],
"eslint-plugin-import-x/package.json": ["./package.json"],
"eslint-plugin-import-x/*": ["./src/*"]
}
},
"verbatimModuleSyntax": false
},
"exclude": ["test/fixtures"]
}
19,990 changes: 12,784 additions & 7,206 deletions yarn.lock

Large diffs are not rendered by default.