eslint-plugin-import-next 2.3.6 → 2.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/README.md +76 -65
- package/package.json +7 -3
- package/src/index.js +1 -1
- package/src/rules/enforce-dependency-direction.js +20 -23
- package/src/rules/enforce-import-order.js +5 -10
- package/src/rules/enforce-team-boundaries.js +6 -6
- package/src/rules/first.js +0 -1
- package/src/rules/named.js +4 -7
- package/src/rules/no-amd.js +0 -1
- package/src/rules/no-barrel-import.js +0 -1
- package/src/rules/no-cross-domain-imports.d.ts +8 -0
- package/src/rules/no-cross-domain-imports.js +3 -5
- package/src/rules/no-cycle.d.ts +1 -1
- package/src/rules/no-cycle.js +161 -70
- package/src/rules/no-deprecated.js +20 -20
- package/src/rules/no-duplicates.js +5 -6
- package/src/rules/no-import-module-exports.js +0 -1
- package/src/rules/no-named-as-default-member.js +1 -2
- package/src/rules/no-named-export.js +10 -20
- package/src/rules/no-namespace.js +0 -1
- package/src/rules/no-relative-packages.js +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
## [2.3.6] - 2026-05-03
|
|
2
2
|
|
|
3
|
+
## 2.3.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#252](https://github.com/ofri-peretz/eslint/pull/252) [`d67e395`](https://github.com/ofri-peretz/eslint/commit/d67e3953c2748ad36e6aebe0f24b1d04e518b4d0) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - Fix Codecov badge showing "unknown" — switch from flag to component URL format
|
|
8
|
+
|
|
9
|
+
## 2.3.7
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#141](https://github.com/ofri-peretz/eslint/pull/141) [`38ab670`](https://github.com/ofri-peretz/eslint/commit/38ab670a0221684f4fd3d5dc3c05ddec7458ca2b) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - fix: remove false `meta.fixable: 'code'` declarations from 21 rules that had no `fix()` function
|
|
14
|
+
|
|
15
|
+
Rules that declared `fixable: 'code'` in their ESLint meta without an actual `fix()` implementation would show the ⚡ auto-fix icon in editors and CI formatters but apply no change when `--fix` was run. This patch removes the misleading declaration from:
|
|
16
|
+
- `browser-security/no-clickjacking`
|
|
17
|
+
- `import-next/first`, `named`, `no-barrel-import`, `no-import-module-exports`, `no-namespace`
|
|
18
|
+
- `node-security/no-buffer-overread`, `no-unsafe-dynamic-require`, `no-zip-slip`
|
|
19
|
+
- `react-features/react-no-inline-functions`
|
|
20
|
+
- `reliability/no-jsdoc-terminator-in-example` (uses `suggest`, not auto-fix; corrected to `hasSuggestions: true` only)
|
|
21
|
+
- `secure-coding/no-directive-injection`, `no-electron-security-issues`, `no-graphql-injection`, `no-improper-sanitization`, `no-improper-type-validation`, `no-ldap-injection`, `no-unchecked-loop-condition`, `no-unlimited-resource-allocation`, `no-weak-password-recovery`, `no-xpath-injection`
|
|
22
|
+
|
|
23
|
+
- [#186](https://github.com/ofri-peretz/eslint/pull/186) [`edf208d`](https://github.com/ofri-peretz/eslint/commit/edf208d67ac2357312c97d8964fcf6a462e407eb) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - Consolidation cleanup — no rule behavior change:
|
|
24
|
+
- **react-features**: the README rules table now lists the 8 `componentApi`
|
|
25
|
+
preset rules. The README generator (`sync-readme-rules.ts`) and the
|
|
26
|
+
`plugin-rule-source-drift` validator now recurse into nested
|
|
27
|
+
`docs/rules/<category>/` subfolders, so every documented rule is advertised
|
|
28
|
+
consistently (previously the nested componentApi docs were silently dropped,
|
|
29
|
+
which an earlier `readme` exception had papered over — that exception is now
|
|
30
|
+
removed in favour of the real fix).
|
|
31
|
+
- **node-security**: remove the orphaned `no-pii-in-logs` rule source — the rule
|
|
32
|
+
was migrated to `eslint-plugin-secure-coding` and is no longer exported here;
|
|
33
|
+
the dead source was still compiling into `dist`.
|
|
34
|
+
- **import-next**: restore the `no-cycle` unit test after [#180](https://github.com/ofri-peretz/eslint/issues/180)'s SCC refactor
|
|
35
|
+
(`computeSCCsFromFile` + `findShortestCyclePath` are now bridged in the mock).
|
|
36
|
+
|
|
37
|
+
Also fixes `scripts/ilb-plugin-scope-audit.ts` to stop mis-reading config-preset
|
|
38
|
+
keys (`'recommended-strict': {`) as rules.
|
|
39
|
+
|
|
40
|
+
- Updated dependencies [[`736a5fe`](https://github.com/ofri-peretz/eslint/commit/736a5fed47e673f6157ea900b29fe2a54e4bc7df)]:
|
|
41
|
+
- @interlace/eslint-devkit@1.4.1
|
|
42
|
+
|
|
3
43
|
### Changed
|
|
4
44
|
|
|
5
45
|
- `no-cycle` rewritten to per-import targeted DFS (replaces upfront full-graph BFS+Tarjan SCC). The `nonCyclicFiles` cache provides O(1) rejection after first visit; only files along the actual DFS path are read. Detection parity with the previous algorithm is preserved.
|
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<a href="https://eslint.interlace.tools" target="blank"><img src="https://eslint.interlace.tools/eslint-interlace-logo-light.svg" alt="ESLint Interlace Logo" width="120" /></a>
|
|
2
|
+
<a href="https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next" target="blank"><img src="https://eslint.interlace.tools/eslint-interlace-logo-light.svg" alt="ESLint Interlace Logo" width="120" /></a>
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
@@ -10,10 +10,12 @@
|
|
|
10
10
|
<a href="https://www.npmjs.com/package/eslint-plugin-import-next" target="_blank"><img src="https://img.shields.io/npm/v/eslint-plugin-import-next.svg" alt="NPM Version" /></a>
|
|
11
11
|
<a href="https://www.npmjs.com/package/eslint-plugin-import-next" target="_blank"><img src="https://img.shields.io/npm/dm/eslint-plugin-import-next.svg" alt="NPM Downloads" /></a>
|
|
12
12
|
<a href="https://opensource.org/licenses/MIT" target="_blank"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="Package License" /></a>
|
|
13
|
-
<a href="https://app.codecov.io/gh/ofri-peretz/eslint/components?components%5B0%5D=import-next" target="_blank"><img src="https://codecov.io/gh/ofri-peretz/eslint/graph/badge.svg?component=import-next" alt="Codecov" /></a>
|
|
13
|
+
<a href="https://app.codecov.io/gh/ofri-peretz/eslint/components?components%5B0%5D=eslint-plugin-import-next" target="_blank"><img src="https://codecov.io/gh/ofri-peretz/eslint/graph/badge.svg?component=eslint-plugin-import-next" alt="Codecov" /></a>
|
|
14
14
|
<a href="https://github.com/ofri-peretz/eslint" target="_blank"><img src="https://img.shields.io/badge/Since-Dec_2025-blue?logo=rocket&logoColor=white" alt="Since Dec 2025" /></a>
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
|
+
> ⭐ If this plugin caught a real bug for you, [**star the repo**](https://github.com/ofri-peretz/eslint) — it's the signal that keeps these rules maintained.
|
|
18
|
+
|
|
17
19
|
## Description
|
|
18
20
|
|
|
19
21
|
This plugin provides Next-generation import sorting, validation, and architectural boundaries.
|
|
@@ -24,12 +26,12 @@ This plugin provides Next-generation import sorting, validation, and architectur
|
|
|
24
26
|
|
|
25
27
|
## Getting Started
|
|
26
28
|
|
|
27
|
-
- To check out the [guide](https://eslint.interlace.tools/docs/quality/plugin-import-next), visit [eslint.interlace.tools](https://eslint.interlace.tools). 📚
|
|
28
|
-
- 要查看中文 [指南](https://eslint.interlace.tools/docs/quality/plugin-import-next), 请访问 [eslint.interlace.tools](https://eslint.interlace.tools). 📚
|
|
29
|
-
- [가이드](https://eslint.interlace.tools/docs/quality/plugin-import-next) 문서는 [eslint.interlace.tools](https://eslint.interlace.tools)에서 확인하실 수 있습니다. 📚
|
|
30
|
-
- [ガイド](https://eslint.interlace.tools/docs/quality/plugin-import-next)は [eslint.interlace.tools](https://eslint.interlace.tools)でご確認ください。 📚
|
|
31
|
-
- Para ver la [guía](https://eslint.interlace.tools/docs/quality/plugin-import-next), visita [eslint.interlace.tools](https://eslint.interlace.tools). 📚
|
|
32
|
-
- للاطلاع على [الدليل](https://eslint.interlace.tools/docs/quality/plugin-import-next)، قم بزيارة [eslint.interlace.tools](https://eslint.interlace.tools). 📚
|
|
29
|
+
- To check out the [guide](https://eslint.interlace.tools/docs/quality/plugin-import-next?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next), visit [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next). 📚
|
|
30
|
+
- 要查看中文 [指南](https://eslint.interlace.tools/docs/quality/plugin-import-next?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next), 请访问 [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next). 📚
|
|
31
|
+
- [가이드](https://eslint.interlace.tools/docs/quality/plugin-import-next?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) 문서는 [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next)에서 확인하실 수 있습니다. 📚
|
|
32
|
+
- [ガイド](https://eslint.interlace.tools/docs/quality/plugin-import-next?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next)は [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next)でご確認ください。 📚
|
|
33
|
+
- Para ver la [guía](https://eslint.interlace.tools/docs/quality/plugin-import-next?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next), visita [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next). 📚
|
|
34
|
+
- للاطلاع على [الدليل](https://eslint.interlace.tools/docs/quality/plugin-import-next?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next)، قم بزيارة [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next). 📚
|
|
33
35
|
|
|
34
36
|
```bash
|
|
35
37
|
npm install eslint-plugin-import-next --save-dev
|
|
@@ -81,62 +83,62 @@ See the [ESLint Version Support Policy](../../docs/ESLINT_VERSION_SUPPORT.md) fo
|
|
|
81
83
|
<!-- AUTO-GENERATED:RULES_TABLE:START - Do not edit manually -->
|
|
82
84
|
| Rule | CWE | OWASP | CVSS | Description | 🧠 | 💼 | ⚠️ | 🔧 | 💡 | 🚫 |
|
|
83
85
|
| :--- | :---: | :---: | :---: | :--- | :---: | :---: | :---: | :---: | :---: | :---: |
|
|
84
|
-
| [consistent-type-specifier-style](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/consistent-type-specifier-style) | | | | | 🟢 | | | | | |
|
|
85
|
-
| [default](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/default) | | | | | 🟡 | 💼 | | | | |
|
|
86
|
-
| [dynamic-import-chunkname](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/dynamic-import-chunkname) | | | | | 🟢 | | | | | |
|
|
87
|
-
| [enforce-dependency-direction](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/enforce-dependency-direction) | | | | | 🟢 | | | | | |
|
|
88
|
-
| [enforce-import-order](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/enforce-import-order) | | | | | 🟢 | | | | | |
|
|
89
|
-
| [enforce-team-boundaries](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/enforce-team-boundaries) | | | | | 🟢 | | | | | |
|
|
90
|
-
| [export](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/export) | | | | | 🟢 | 💼 | | | | |
|
|
91
|
-
| [exports-last](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/exports-last) | | | | | 🟢 | | | | | |
|
|
92
|
-
| [extensions](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/extensions) | | | | | 🟢 | | | | | |
|
|
93
|
-
| [first](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/first) | | | | | 🟢 | | ⚠️ | | | |
|
|
94
|
-
| [group-exports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/group-exports) | | | | | 🟢 | | | | | |
|
|
95
|
-
| [max-dependencies](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/max-dependencies) | | | | | 🟢 | | | | | |
|
|
96
|
-
| [named](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/named) | | | | | 🟡 | 💼 | | | | |
|
|
97
|
-
| [namespace](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/namespace) | | | | | 🟡 | | ⚠️ | | | |
|
|
98
|
-
| [newline-after-import](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/newline-after-import) | | | | | 🟢 | | ⚠️ | | | |
|
|
99
|
-
| [no-absolute-path](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-absolute-path) | | | | | 🟢 | | | | | |
|
|
100
|
-
| [no-amd](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-amd) | | | | | 🟢 | | ⚠️ | | | |
|
|
101
|
-
| [no-anonymous-default-export](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-anonymous-default-export) | | | | | 🟢 | | | | | |
|
|
102
|
-
| [no-barrel-file](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-barrel-file) | | | | | 🟢 | | | | | |
|
|
103
|
-
| [no-barrel-import](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-barrel-import) | | | | | 🟢 | | | | | |
|
|
104
|
-
| [no-commonjs](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-commonjs) | | | | | 🟢 | | | | | |
|
|
105
|
-
| [no-cross-domain-imports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-cross-domain-imports) | | | | | 🟢 | | | | | |
|
|
106
|
-
| [no-cycle](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-cycle) | | A05:2021 | | | 🟢 | 💼 | | | | |
|
|
107
|
-
| [no-default-export](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-default-export) | | | | | 🟢 | | | | | |
|
|
108
|
-
| [no-deprecated](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-deprecated) | | A09:2021 | | | 🟢 | | | | | |
|
|
109
|
-
| [no-duplicates](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-duplicates) | | | | | 🟢 | 💼 | | | | |
|
|
110
|
-
| [no-dynamic-require](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-dynamic-require) | | | | | 🟢 | | | | | |
|
|
111
|
-
| [no-empty-named-blocks](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-empty-named-blocks) | | | | | 🟢 | | ⚠️ | | | |
|
|
112
|
-
| [no-extraneous-dependencies](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-extraneous-dependencies) | | | | | 🟢 | | ⚠️ | | | |
|
|
113
|
-
| [no-full-package-import](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-full-package-import) | | | | | 🟢 | | | | | |
|
|
114
|
-
| [no-import-module-exports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-import-module-exports) | | | | | 🟢 | | | | | |
|
|
115
|
-
| [no-internal-modules](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-internal-modules) | | | | | 🟢 | | | | | |
|
|
116
|
-
| [no-legacy-imports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-legacy-imports) | | | | | 🟢 | | | | | |
|
|
117
|
-
| [no-mutable-exports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-mutable-exports) | | | | | 🟢 | | | | | |
|
|
118
|
-
| [no-named-as-default](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-named-as-default) | | | | | 🟢 | | ⚠️ | | | |
|
|
119
|
-
| [no-named-as-default-member](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-named-as-default-member) | | | | | 🟢 | | ⚠️ | | | |
|
|
120
|
-
| [no-named-default](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-named-default) | | | | | 🟢 | | | | | |
|
|
121
|
-
| [no-named-export](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-named-export) | | | | | 🟢 | | | | | |
|
|
122
|
-
| [no-namespace](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-namespace) | | | | | 🟢 | | | | | |
|
|
123
|
-
| [no-nodejs-modules](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-nodejs-modules) | | | | | 🟢 | | | | | |
|
|
124
|
-
| [no-relative-packages](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-relative-packages) | | | | | 🟢 | | | | | |
|
|
125
|
-
| [no-relative-parent-imports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-relative-parent-imports) | | | | | 🟢 | | | | | |
|
|
126
|
-
| [no-restricted-paths](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-restricted-paths) | | | | | 🟢 | | | | | |
|
|
127
|
-
| [no-self-import](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-self-import) | | | | | 🟢 | 💼 | | | | |
|
|
128
|
-
| [no-unassigned-import](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-unassigned-import) | | | | | 🟢 | | | | | |
|
|
129
|
-
| [no-unresolved](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-unresolved) | | A03:2021 | | | 🟢 | 💼 | | | | |
|
|
130
|
-
| [no-unused-modules](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-unused-modules) | | | | | 🟢 | | | | | |
|
|
131
|
-
| [no-useless-path-segments](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-useless-path-segments) | | | | | 🟢 | | | | | |
|
|
132
|
-
| [order](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/order) | | | | | 🟢 | | ⚠️ | | | |
|
|
133
|
-
| [prefer-default-export](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/prefer-default-export) | | | | | 🟢 | | | | | |
|
|
134
|
-
| [prefer-direct-import](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/prefer-direct-import) | | | | | 🟢 | | | | | |
|
|
135
|
-
| [prefer-modern-api](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/prefer-modern-api) | | | | | 🟢 | | | | | |
|
|
136
|
-
| [prefer-node-protocol](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/prefer-node-protocol) | | | | | 🟢 | | | | | |
|
|
137
|
-
| [prefer-tree-shakeable-imports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/prefer-tree-shakeable-imports) | | | | | 🟢 | | | | | |
|
|
138
|
-
| [require-import-approval](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/require-import-approval) | | | | | 🟢 | | | | | |
|
|
139
|
-
| [unambiguous](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/unambiguous) | | | | | 🟢 | | | | | |
|
|
86
|
+
| [consistent-type-specifier-style](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/consistent-type-specifier-style?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
87
|
+
| [default](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/default?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟡 | 💼 | | | | |
|
|
88
|
+
| [dynamic-import-chunkname](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/dynamic-import-chunkname?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
89
|
+
| [enforce-dependency-direction](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/enforce-dependency-direction?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
90
|
+
| [enforce-import-order](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/enforce-import-order?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
91
|
+
| [enforce-team-boundaries](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/enforce-team-boundaries?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
92
|
+
| [export](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/export?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | 💼 | | | | |
|
|
93
|
+
| [exports-last](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/exports-last?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
94
|
+
| [extensions](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/extensions?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
95
|
+
| [first](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/first?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | ⚠️ | | | |
|
|
96
|
+
| [group-exports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/group-exports?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
97
|
+
| [max-dependencies](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/max-dependencies?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
98
|
+
| [named](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/named?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟡 | 💼 | | | | |
|
|
99
|
+
| [namespace](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/namespace?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟡 | | ⚠️ | | | |
|
|
100
|
+
| [newline-after-import](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/newline-after-import?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | ⚠️ | | | |
|
|
101
|
+
| [no-absolute-path](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-absolute-path?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
102
|
+
| [no-amd](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-amd?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | ⚠️ | | | |
|
|
103
|
+
| [no-anonymous-default-export](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-anonymous-default-export?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
104
|
+
| [no-barrel-file](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-barrel-file?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
105
|
+
| [no-barrel-import](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-barrel-import?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
106
|
+
| [no-commonjs](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-commonjs?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
107
|
+
| [no-cross-domain-imports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-cross-domain-imports?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
108
|
+
| [no-cycle](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-cycle?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | A05:2021 | | | 🟢 | 💼 | | | | |
|
|
109
|
+
| [no-default-export](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-default-export?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
110
|
+
| [no-deprecated](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-deprecated?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | A09:2021 | | | 🟢 | | | | | |
|
|
111
|
+
| [no-duplicates](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-duplicates?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | 💼 | | | | |
|
|
112
|
+
| [no-dynamic-require](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-dynamic-require?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
113
|
+
| [no-empty-named-blocks](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-empty-named-blocks?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | ⚠️ | | | |
|
|
114
|
+
| [no-extraneous-dependencies](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-extraneous-dependencies?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | ⚠️ | | | |
|
|
115
|
+
| [no-full-package-import](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-full-package-import?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
116
|
+
| [no-import-module-exports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-import-module-exports?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
117
|
+
| [no-internal-modules](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-internal-modules?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
118
|
+
| [no-legacy-imports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-legacy-imports?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
119
|
+
| [no-mutable-exports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-mutable-exports?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
120
|
+
| [no-named-as-default](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-named-as-default?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | ⚠️ | | | |
|
|
121
|
+
| [no-named-as-default-member](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-named-as-default-member?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | ⚠️ | | | |
|
|
122
|
+
| [no-named-default](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-named-default?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
123
|
+
| [no-named-export](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-named-export?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
124
|
+
| [no-namespace](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-namespace?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
125
|
+
| [no-nodejs-modules](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-nodejs-modules?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
126
|
+
| [no-relative-packages](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-relative-packages?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
127
|
+
| [no-relative-parent-imports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-relative-parent-imports?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
128
|
+
| [no-restricted-paths](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-restricted-paths?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
129
|
+
| [no-self-import](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-self-import?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | 💼 | | | | |
|
|
130
|
+
| [no-unassigned-import](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-unassigned-import?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
131
|
+
| [no-unresolved](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-unresolved?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | A03:2021 | | | 🟢 | 💼 | | | | |
|
|
132
|
+
| [no-unused-modules](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-unused-modules?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
133
|
+
| [no-useless-path-segments](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/no-useless-path-segments?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
134
|
+
| [order](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/order?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | ⚠️ | | | |
|
|
135
|
+
| [prefer-default-export](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/prefer-default-export?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
136
|
+
| [prefer-direct-import](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/prefer-direct-import?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
137
|
+
| [prefer-modern-api](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/prefer-modern-api?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
138
|
+
| [prefer-node-protocol](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/prefer-node-protocol?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
139
|
+
| [prefer-tree-shakeable-imports](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/prefer-tree-shakeable-imports?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
140
|
+
| [require-import-approval](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/require-import-approval?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
141
|
+
| [unambiguous](https://eslint.interlace.tools/docs/quality/plugin-import-next/rules/unambiguous?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next) | | | | | 🟢 | | | | | |
|
|
140
142
|
<!-- AUTO-GENERATED:RULES_TABLE:END -->
|
|
141
143
|
## 🔗 Related ESLint Plugins
|
|
142
144
|
|
|
@@ -156,10 +158,19 @@ Part of the **Interlace ESLint Ecosystem** — AI-native security plugins with L
|
|
|
156
158
|
| [`eslint-plugin-vercel-ai-security`](https://www.npmjs.com/package/eslint-plugin-vercel-ai-security) | [](https://www.npmjs.com/package/eslint-plugin-vercel-ai-security) | Vercel AI SDK security hardening. |
|
|
157
159
|
| [`eslint-plugin-import-next`](https://www.npmjs.com/package/eslint-plugin-import-next) | [](https://www.npmjs.com/package/eslint-plugin-import-next) | Next-gen import sorting & architecture. |
|
|
158
160
|
|
|
161
|
+
<!-- INTERLACE:STAR_CTA:START -->
|
|
162
|
+
## ⭐ Support & follow
|
|
163
|
+
|
|
164
|
+
If this plugin caught a real bug for you, **[star the repo](https://github.com/ofri-peretz/eslint)** — stars are the signal that keeps the Interlace ESLint ecosystem maintained — and **[follow the writeups on Dev.to](https://dev.to/ofri-peretz)** for the benchmarks and security research behind these rules.
|
|
165
|
+
|
|
166
|
+
[](https://github.com/ofri-peretz/eslint)
|
|
167
|
+
|
|
168
|
+
<!-- INTERLACE:STAR_CTA:END -->
|
|
169
|
+
|
|
159
170
|
## 📄 License
|
|
160
171
|
|
|
161
172
|
MIT © [Ofri Peretz](https://github.com/ofri-peretz)
|
|
162
173
|
|
|
163
174
|
<p align="center">
|
|
164
|
-
<a href="https://eslint.interlace.tools/docs/quality/plugin-import-next"><img src="https://eslint.interlace.tools/images/og-import-next.png" alt="ESLint Interlace Plugin" width="100%" /></a>
|
|
175
|
+
<a href="https://eslint.interlace.tools/docs/quality/plugin-import-next?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-import-next"><img src="https://eslint.interlace.tools/images/og-import-next.png" alt="ESLint Interlace Plugin" width="100%" /></a>
|
|
165
176
|
</p>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-import-next",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.8",
|
|
4
4
|
"description": "Drop-in replacement for eslint-plugin-import. 100x faster no-cycle detection, AI-optimized fixes, zero config migration.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"author": "Ofri Peretz <ofriperetzdev@gmail.com>",
|
|
23
23
|
"license": "MIT",
|
|
24
|
-
"homepage": "https://
|
|
24
|
+
"homepage": "https://eslint.interlace.tools/docs/quality/plugin-import-next?utm_source=npm&utm_medium=referral&utm_campaign=eslint-plugin-import-next",
|
|
25
25
|
"repository": {
|
|
26
26
|
"type": "git",
|
|
27
27
|
"url": "https://github.com/ofri-peretz/eslint",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"tslib": "^2.3.0",
|
|
76
|
-
"@interlace/eslint-devkit": "^1.
|
|
76
|
+
"@interlace/eslint-devkit": "^1.4.1"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"@typescript-eslint/parser": "^8.46.2",
|
|
@@ -84,5 +84,9 @@
|
|
|
84
84
|
"test": "vitest run",
|
|
85
85
|
"test:coverage": "vitest run --coverage",
|
|
86
86
|
"typecheck": "tsc -p tsconfig.lib.json --noEmit"
|
|
87
|
+
},
|
|
88
|
+
"funding": {
|
|
89
|
+
"type": "github",
|
|
90
|
+
"url": "https://github.com/ofri-peretz/eslint"
|
|
87
91
|
}
|
|
88
92
|
}
|
package/src/index.js
CHANGED
|
@@ -187,32 +187,27 @@ exports.enforceDependencyDirection = (0, eslint_devkit_2.createRule)({
|
|
|
187
187
|
}
|
|
188
188
|
// For relative imports, check the import path string directly for layer patterns
|
|
189
189
|
// This works for test cases where paths like '../infrastructure/repository' contain the layer
|
|
190
|
+
// The early return above guarantees importPath starts with '.' here.
|
|
190
191
|
let targetLayer = null;
|
|
191
|
-
if
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
192
|
+
// Check if import path contains layer patterns
|
|
193
|
+
// e.g., '../infrastructure/repository' -> extract 'infrastructure'
|
|
194
|
+
const segments = importPath.split(/[/\\]/);
|
|
195
|
+
for (const segment of segments) {
|
|
196
|
+
if (layerPatterns.includes(segment.toLowerCase())) {
|
|
197
|
+
targetLayer = segment.toLowerCase();
|
|
198
|
+
break;
|
|
200
199
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
200
|
+
}
|
|
201
|
+
// Fallback: try regex matching
|
|
202
|
+
if (!targetLayer) {
|
|
203
|
+
for (const pattern of layerPatterns) {
|
|
204
|
+
const regex = new RegExp(`[/\\\\]${pattern}[/\\\\]`, 'i');
|
|
205
|
+
if (regex.test(importPath)) {
|
|
206
|
+
targetLayer = pattern;
|
|
207
|
+
break;
|
|
209
208
|
}
|
|
210
209
|
}
|
|
211
210
|
}
|
|
212
|
-
else {
|
|
213
|
-
// For absolute or external imports, try to extract layer
|
|
214
|
-
targetLayer = extractLayer(importPath, layerPatterns);
|
|
215
|
-
}
|
|
216
211
|
const targetOrder = getLayerOrder(targetLayer, layers);
|
|
217
212
|
// Skip if layers are unknown
|
|
218
213
|
if (sourceOrder < 0 || targetOrder < 0) {
|
|
@@ -229,8 +224,10 @@ exports.enforceDependencyDirection = (0, eslint_devkit_2.createRule)({
|
|
|
229
224
|
node,
|
|
230
225
|
messageId: 'dependencyDirectionViolation',
|
|
231
226
|
data: {
|
|
232
|
-
|
|
233
|
-
|
|
227
|
+
// Both are guaranteed non-null here: sourceOrder/targetOrder >= 0
|
|
228
|
+
// implies the layers were resolved (see the guard above).
|
|
229
|
+
sourceLayer: sourceLayer,
|
|
230
|
+
targetLayer: targetLayer,
|
|
234
231
|
},
|
|
235
232
|
suggest: [
|
|
236
233
|
{
|
|
@@ -170,23 +170,18 @@ exports.enforceImportOrder = (0, eslint_devkit_1.createRule)({
|
|
|
170
170
|
return 'external';
|
|
171
171
|
}
|
|
172
172
|
function getGroupRank(importType) {
|
|
173
|
-
const index = options.groups
|
|
173
|
+
const index = options.groups.indexOf(importType);
|
|
174
174
|
return index !== -1 ? index : 999;
|
|
175
175
|
}
|
|
176
176
|
function getExtendedRange(node) {
|
|
177
177
|
let start = node.range[0];
|
|
178
178
|
let end = node.range[1];
|
|
179
179
|
const commentsBefore = sourceCode.getCommentsBefore(node);
|
|
180
|
-
//
|
|
180
|
+
// getCommentsBefore only returns comments strictly between the previous
|
|
181
|
+
// non-comment token and this node, so they always belong to this import —
|
|
182
|
+
// include them all in the range.
|
|
181
183
|
if (commentsBefore.length > 0) {
|
|
182
|
-
|
|
183
|
-
const tokenBefore = sourceCode.getTokenBefore(node);
|
|
184
|
-
// If comment is between previous token and this node
|
|
185
|
-
if (!tokenBefore || tokenBefore.range[1] <= lastComment.range[0]) {
|
|
186
|
-
// Simple heuristic: include all preceding comments that are contiguous with the import
|
|
187
|
-
// This is tricky, but we'll take all commentsBefore for now to be safe
|
|
188
|
-
start = commentsBefore[0].range[0];
|
|
189
|
-
}
|
|
184
|
+
start = commentsBefore[0].range[0];
|
|
190
185
|
}
|
|
191
186
|
// Include semicolon if present
|
|
192
187
|
const tokenAfter = sourceCode.getTokenAfter(node);
|
|
@@ -170,10 +170,10 @@ exports.enforceTeamBoundaries = (0, eslint_devkit_1.createRule)({
|
|
|
170
170
|
if (!sourceTeam) {
|
|
171
171
|
return {};
|
|
172
172
|
}
|
|
173
|
+
// Narrowed binding: the early return above guarantees sourceTeam is non-null,
|
|
174
|
+
// and closures cannot rely on that narrowing without re-checking.
|
|
175
|
+
const owningTeam = sourceTeam;
|
|
173
176
|
function checkImport(node) {
|
|
174
|
-
// Guard for TypeScript - sourceTeam is checked before this function is registered
|
|
175
|
-
if (!sourceTeam)
|
|
176
|
-
return;
|
|
177
177
|
let importPath = null;
|
|
178
178
|
if (node.type === eslint_devkit_1.AST_NODE_TYPES.ImportDeclaration) {
|
|
179
179
|
importPath = typeof node.source.value === 'string' ? node.source.value : null;
|
|
@@ -207,11 +207,11 @@ exports.enforceTeamBoundaries = (0, eslint_devkit_1.createRule)({
|
|
|
207
207
|
return;
|
|
208
208
|
}
|
|
209
209
|
// Same team - always allowed
|
|
210
|
-
if (targetTeam.team ===
|
|
210
|
+
if (targetTeam.team === owningTeam.team) {
|
|
211
211
|
return;
|
|
212
212
|
}
|
|
213
213
|
// Check if target team is in allowed dependencies
|
|
214
|
-
const allowedDeps =
|
|
214
|
+
const allowedDeps = owningTeam.allowedDependencies || [];
|
|
215
215
|
if (allowedDeps.includes(targetTeam.team)) {
|
|
216
216
|
return;
|
|
217
217
|
}
|
|
@@ -220,7 +220,7 @@ exports.enforceTeamBoundaries = (0, eslint_devkit_1.createRule)({
|
|
|
220
220
|
node,
|
|
221
221
|
messageId: 'unauthorizedTeamImport',
|
|
222
222
|
data: {
|
|
223
|
-
sourceTeam:
|
|
223
|
+
sourceTeam: owningTeam.team,
|
|
224
224
|
targetTeam: targetTeam.team,
|
|
225
225
|
importPath,
|
|
226
226
|
},
|
package/src/rules/first.js
CHANGED
|
@@ -16,7 +16,6 @@ exports.first = (0, eslint_devkit_1.createRule)({
|
|
|
16
16
|
url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/first.md',
|
|
17
17
|
description: 'Ensure all imports appear before other statements',
|
|
18
18
|
},
|
|
19
|
-
fixable: 'code',
|
|
20
19
|
messages: {
|
|
21
20
|
first: (0, eslint_devkit_2.formatLLMMessage)({
|
|
22
21
|
icon: eslint_devkit_2.MessageIcons.ARCHITECTURE,
|
package/src/rules/named.js
CHANGED
|
@@ -18,7 +18,6 @@ exports.named = (0, eslint_devkit_1.createRule)({
|
|
|
18
18
|
url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/named.md',
|
|
19
19
|
description: 'Ensure named imports correspond to a named export in the remote file',
|
|
20
20
|
},
|
|
21
|
-
fixable: 'code',
|
|
22
21
|
messages: {
|
|
23
22
|
named: (0, eslint_devkit_2.formatLLMMessage)({
|
|
24
23
|
icon: eslint_devkit_2.MessageIcons.SECURITY, // Using SECURITY as incorrect imports can be dangerous or break build
|
|
@@ -67,12 +66,10 @@ exports.named = (0, eslint_devkit_1.createRule)({
|
|
|
67
66
|
const moduleSymbol = checker?.getSymbolAtLocation?.(moduleNode);
|
|
68
67
|
if (!moduleSymbol)
|
|
69
68
|
return;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
});
|
|
75
|
-
}
|
|
69
|
+
context.report({
|
|
70
|
+
node: node.imported,
|
|
71
|
+
messageId: 'named',
|
|
72
|
+
});
|
|
76
73
|
}
|
|
77
74
|
},
|
|
78
75
|
};
|
package/src/rules/no-amd.js
CHANGED
|
@@ -63,7 +63,6 @@ exports.noAmd = (0, eslint_devkit_1.createRule)({
|
|
|
63
63
|
function shouldAllow() {
|
|
64
64
|
// Check if the call is in an allowed pattern
|
|
65
65
|
return allow.some((pattern) => {
|
|
66
|
-
/* v8 ignore next -- defensive check, filename always provided by ESLint */
|
|
67
66
|
if (!filename)
|
|
68
67
|
return false;
|
|
69
68
|
// Support glob patterns like **/legacy/**
|
|
@@ -48,7 +48,6 @@ exports.noBarrelImport = (0, eslint_devkit_1.createRule)({
|
|
|
48
48
|
url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-import-next/docs/rules/no-barrel-import.md',
|
|
49
49
|
description: 'Disallow imports from barrel files to improve build performance and tree-shaking',
|
|
50
50
|
},
|
|
51
|
-
fixable: 'code',
|
|
52
51
|
hasSuggestions: true,
|
|
53
52
|
messages: {
|
|
54
53
|
barrelImportDetected: (0, eslint_devkit_1.formatLLMMessage)({
|
|
@@ -27,6 +27,14 @@ export interface Options {
|
|
|
27
27
|
}>;
|
|
28
28
|
}
|
|
29
29
|
type RuleOptions = [Options?];
|
|
30
|
+
/**
|
|
31
|
+
* Check if import violates domain boundaries
|
|
32
|
+
*/
|
|
33
|
+
export declare function violatesDomainBoundary(sourceFile: string, importPath: string, options: Options): {
|
|
34
|
+
violates: boolean;
|
|
35
|
+
sourceDomain: string | null;
|
|
36
|
+
targetDomain: string | null;
|
|
37
|
+
};
|
|
30
38
|
export declare const noCrossDomainImports: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener> & {
|
|
31
39
|
name: string;
|
|
32
40
|
};
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.noCrossDomainImports = void 0;
|
|
9
|
+
exports.violatesDomainBoundary = violatesDomainBoundary;
|
|
9
10
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
11
|
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
11
12
|
const eslint_devkit_3 = require("@interlace/eslint-devkit");
|
|
@@ -51,8 +52,9 @@ function violatesDomainBoundary(sourceFile, importPath, options) {
|
|
|
51
52
|
const sourceDomain = extractDomain(sourceFile, domainPatterns);
|
|
52
53
|
// For relative imports, we need to resolve the path to get the actual target file
|
|
53
54
|
// In tests, we can extract from the import path string directly
|
|
55
|
+
// The early return above guarantees importPath starts with '.' here.
|
|
54
56
|
let targetDomain = null;
|
|
55
|
-
|
|
57
|
+
{
|
|
56
58
|
// Check if import path contains domain patterns
|
|
57
59
|
// e.g., '../../domains/order/service' -> extract 'order'
|
|
58
60
|
// Try multiple patterns to find the domain
|
|
@@ -94,10 +96,6 @@ function violatesDomainBoundary(sourceFile, importPath, options) {
|
|
|
94
96
|
}
|
|
95
97
|
}
|
|
96
98
|
}
|
|
97
|
-
else {
|
|
98
|
-
// For absolute or external imports, try to extract domain
|
|
99
|
-
targetDomain = extractDomain(importPath, domainPatterns);
|
|
100
|
-
}
|
|
101
99
|
// If no domain detected, allow
|
|
102
100
|
if (!sourceDomain || !targetDomain) {
|
|
103
101
|
return { violates: false, sourceDomain, targetDomain };
|
package/src/rules/no-cycle.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export interface IncrementalOptions {
|
|
|
50
50
|
maxCacheAge?: number;
|
|
51
51
|
}
|
|
52
52
|
export interface Options {
|
|
53
|
-
/** Maximum allowed import depth. Default:
|
|
53
|
+
/** Maximum allowed import depth. Default: Infinity (unlimited) */
|
|
54
54
|
maxDepth?: number;
|
|
55
55
|
/** Patterns to ignore when checking for cycles (glob patterns) */
|
|
56
56
|
ignorePatterns?: string[];
|
package/src/rules/no-cycle.js
CHANGED
|
@@ -27,6 +27,7 @@ exports.clearCircularDependencyCache = clearCircularDependencyCache;
|
|
|
27
27
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
28
28
|
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
29
29
|
const eslint_devkit_3 = require("@interlace/eslint-devkit");
|
|
30
|
+
const node_fs_1 = require("node:fs");
|
|
30
31
|
const eslint_devkit_4 = require("@interlace/eslint-devkit");
|
|
31
32
|
/**
|
|
32
33
|
* Module-level shared cache for performance optimization
|
|
@@ -177,7 +178,7 @@ exports.noCycle = (0, eslint_devkit_1.createRule)({
|
|
|
177
178
|
// deeper than 10 hops — verified on next.js's webpack-config.ts
|
|
178
179
|
// (~12 hops). The nonCyclicFiles cache + the depth-limit-truncation
|
|
179
180
|
// fix in eslint-devkit's dependency-analysis make unlimited safe.
|
|
180
|
-
maxDepth:
|
|
181
|
+
maxDepth: Infinity,
|
|
181
182
|
ignorePatterns: [],
|
|
182
183
|
barrelExports: ['index.ts', 'index.tsx', 'index.js', 'index.jsx'],
|
|
183
184
|
reportAllCycles: true,
|
|
@@ -189,7 +190,7 @@ exports.noCycle = (0, eslint_devkit_1.createRule)({
|
|
|
189
190
|
],
|
|
190
191
|
create(context) {
|
|
191
192
|
const options = context.options[0] || {};
|
|
192
|
-
const maxDepth = options.maxDepth ??
|
|
193
|
+
const maxDepth = options.maxDepth ?? Infinity;
|
|
193
194
|
const ignorePatterns = options.ignorePatterns ?? [
|
|
194
195
|
'**/*.test.ts',
|
|
195
196
|
'**/*.test.tsx',
|
|
@@ -205,12 +206,23 @@ exports.noCycle = (0, eslint_devkit_1.createRule)({
|
|
|
205
206
|
'index.js',
|
|
206
207
|
'index.jsx',
|
|
207
208
|
];
|
|
208
|
-
const
|
|
209
|
+
const _reportAllCycles = options.reportAllCycles ?? true;
|
|
209
210
|
const fixStrategy = options.fixStrategy ?? 'auto';
|
|
210
211
|
const moduleNamingConvention = options.moduleNamingConvention ?? 'semantic';
|
|
211
212
|
const coreModuleSuffix = options.coreModuleSuffix ?? 'core';
|
|
212
213
|
const extendedModuleSuffix = options.extendedModuleSuffix ?? 'extended';
|
|
213
214
|
const filename = context.filename;
|
|
215
|
+
// Normalize once per file — on macOS /tmp is a symlink to /private/tmp.
|
|
216
|
+
// ESLint provides the symlink path; the resolver returns real paths.
|
|
217
|
+
// Without normalization, path comparisons always fail on macOS.
|
|
218
|
+
const normalizedFilename = (() => {
|
|
219
|
+
try {
|
|
220
|
+
return (0, node_fs_1.realpathSync)(filename);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return filename;
|
|
224
|
+
}
|
|
225
|
+
})();
|
|
214
226
|
const workspaceRoot = context.cwd;
|
|
215
227
|
// Get resolver settings from ESLint settings (compatible with eslint-plugin-import)
|
|
216
228
|
// eslint-plugin-import uses settings['import/resolver']; allow either the
|
|
@@ -223,6 +235,16 @@ exports.noCycle = (0, eslint_devkit_1.createRule)({
|
|
|
223
235
|
if ((0, eslint_devkit_3.shouldIgnoreFile)(filename, ignorePatterns, sharedCache.compiledPatterns)) {
|
|
224
236
|
return {};
|
|
225
237
|
}
|
|
238
|
+
// Do NOT clear sharedCache.nonCyclicFiles — it is now populated directly
|
|
239
|
+
// from SCC results (singleton SCCs → provably non-cyclic) and persists
|
|
240
|
+
// across all files for the entire lint run. Clearing it per-file would
|
|
241
|
+
// destroy the O(1) fast path for known non-cyclic files.
|
|
242
|
+
sharedCache.pendingCycleReports.clear();
|
|
243
|
+
// sharedCache.sccComputed, sccIndex, sccs, nonCyclicFiles — NOT reset.
|
|
244
|
+
// Per-file dedup set — keyed by import node to ensure each import
|
|
245
|
+
// statement produces at most one error, even when multiple cycle paths
|
|
246
|
+
// exist through the same import.
|
|
247
|
+
const reportedImportNodes = new Set();
|
|
226
248
|
/**
|
|
227
249
|
* Select fix strategy based on cycle characteristics
|
|
228
250
|
*/
|
|
@@ -317,11 +339,6 @@ exports.noCycle = (0, eslint_devkit_1.createRule)({
|
|
|
317
339
|
*/
|
|
318
340
|
function generateMessageData(cycle, strategy, sourceImport) {
|
|
319
341
|
switch (strategy) {
|
|
320
|
-
case 'module-split':
|
|
321
|
-
return {
|
|
322
|
-
messageId: 'moduleSplit',
|
|
323
|
-
data: generateModuleSplitMessage(cycle),
|
|
324
|
-
};
|
|
325
342
|
case 'direct-import':
|
|
326
343
|
return {
|
|
327
344
|
messageId: 'directImport',
|
|
@@ -338,6 +355,7 @@ exports.noCycle = (0, eslint_devkit_1.createRule)({
|
|
|
338
355
|
data: generateDependencyInjectionMessage(cycle),
|
|
339
356
|
};
|
|
340
357
|
default:
|
|
358
|
+
// 'module-split' (and the impossible fall-through) both split.
|
|
341
359
|
return {
|
|
342
360
|
messageId: 'moduleSplit',
|
|
343
361
|
data: generateModuleSplitMessage(cycle),
|
|
@@ -345,11 +363,27 @@ exports.noCycle = (0, eslint_devkit_1.createRule)({
|
|
|
345
363
|
}
|
|
346
364
|
}
|
|
347
365
|
return {
|
|
366
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
367
|
+
'Program'() {
|
|
368
|
+
// Program() is now a no-op for SCC — fully lazy SCC extension.
|
|
369
|
+
// SCC is computed on-demand in ImportDeclaration when source or target
|
|
370
|
+
// is not yet covered. This matches eslint-plugin-import's lazy
|
|
371
|
+
// ExportMap approach: no upfront file reads, defer to actual imports.
|
|
372
|
+
// The global SCC persists across files so each file's SCC is computed
|
|
373
|
+
// at most once per connected component across the whole lint run.
|
|
374
|
+
},
|
|
348
375
|
ImportDeclaration(node) {
|
|
376
|
+
// Type-only imports are erased at compile time — no runtime cycle risk.
|
|
377
|
+
if (node.importKind === 'type')
|
|
378
|
+
return;
|
|
379
|
+
// Each import node should produce at most ONE error regardless of how
|
|
380
|
+
// many distinct cycle paths pass through this import.
|
|
381
|
+
if (reportedImportNodes.has(node))
|
|
382
|
+
return;
|
|
349
383
|
const importSource = node.source.value;
|
|
350
|
-
// Resolve
|
|
384
|
+
// Resolve using normalizedFilename so resolver output paths match.
|
|
351
385
|
const resolved = (0, eslint_devkit_4.resolveImportPath)(importSource, {
|
|
352
|
-
fromFile:
|
|
386
|
+
fromFile: normalizedFilename,
|
|
353
387
|
workspaceRoot,
|
|
354
388
|
barrelExports,
|
|
355
389
|
cache: sharedCache,
|
|
@@ -358,80 +392,137 @@ exports.noCycle = (0, eslint_devkit_1.createRule)({
|
|
|
358
392
|
if (!resolved)
|
|
359
393
|
return;
|
|
360
394
|
// =====================================================================
|
|
361
|
-
//
|
|
395
|
+
// FAST PATH 1: nonCyclicFiles O(1) lookup
|
|
362
396
|
//
|
|
363
|
-
//
|
|
364
|
-
//
|
|
365
|
-
//
|
|
397
|
+
// Files in singleton SCCs are added to nonCyclicFiles during SCC
|
|
398
|
+
// computation. This check is slightly faster than the sccIndex lookup
|
|
399
|
+
// because it short-circuits before any SCC arithmetic.
|
|
400
|
+
// =====================================================================
|
|
401
|
+
if (sharedCache.nonCyclicFiles.has(resolved))
|
|
402
|
+
return;
|
|
403
|
+
// =====================================================================
|
|
404
|
+
// SCC PRE-CHECK (O(1)) — the proven eslint-plugin-import approach
|
|
405
|
+
//
|
|
406
|
+
// If source and target are in DIFFERENT SCCs, no cycle can exist
|
|
407
|
+
// between them — skip immediately without any graph traversal.
|
|
408
|
+
// Eliminates ~99% of imports without reading a single file.
|
|
366
409
|
//
|
|
367
|
-
//
|
|
368
|
-
//
|
|
369
|
-
// - nonCyclicFiles cache provides O(1) rejection after first visit
|
|
370
|
-
// - getFileImports cache reuses parsed results across files
|
|
410
|
+
// If the target file isn't in the SCC yet, extend the SCC from it
|
|
411
|
+
// lazily so we can make the correct determination.
|
|
371
412
|
// =====================================================================
|
|
372
|
-
//
|
|
373
|
-
if (
|
|
413
|
+
// Fully lazy SCC: extend on-demand when source or target not yet covered.
|
|
414
|
+
// Extending from the TARGET is preferred — if target → source (cycle),
|
|
415
|
+
// extending from target will discover source and assign both to the same SCC.
|
|
416
|
+
// Only extend from source as a fallback when target extension doesn't cover it.
|
|
417
|
+
let tgtSCC = sharedCache.sccIndex.get(resolved);
|
|
418
|
+
if (tgtSCC === undefined) {
|
|
419
|
+
(0, eslint_devkit_4.computeSCCsFromFile)(resolved, {
|
|
420
|
+
maxDepth: Infinity,
|
|
421
|
+
workspaceRoot,
|
|
422
|
+
barrelExports,
|
|
423
|
+
cache: sharedCache,
|
|
424
|
+
resolverSettings,
|
|
425
|
+
});
|
|
426
|
+
tgtSCC = sharedCache.sccIndex.get(resolved);
|
|
427
|
+
}
|
|
428
|
+
let srcSCC = sharedCache.sccIndex.get(normalizedFilename);
|
|
429
|
+
if (srcSCC === undefined) {
|
|
430
|
+
// Source still not covered after extending from target — source is in a
|
|
431
|
+
// different connected component. Extend from source to cover it.
|
|
432
|
+
(0, eslint_devkit_4.computeSCCsFromFile)(normalizedFilename, {
|
|
433
|
+
maxDepth: Infinity,
|
|
434
|
+
workspaceRoot,
|
|
435
|
+
barrelExports,
|
|
436
|
+
cache: sharedCache,
|
|
437
|
+
resolverSettings,
|
|
438
|
+
});
|
|
439
|
+
srcSCC = sharedCache.sccIndex.get(normalizedFilename);
|
|
440
|
+
}
|
|
441
|
+
if (srcSCC === undefined || tgtSCC === undefined || srcSCC !== tgtSCC) {
|
|
374
442
|
return;
|
|
375
443
|
}
|
|
376
|
-
//
|
|
377
|
-
|
|
444
|
+
// =====================================================================
|
|
445
|
+
// BFS PATH-FINDING — only runs when SCC guarantees a cycle exists.
|
|
446
|
+
//
|
|
447
|
+
// findShortestCyclePath does BFS from `resolved` back to
|
|
448
|
+
// `normalizedFilename`, only traversing files within the same SCC.
|
|
449
|
+
// This is bounded by the SCC size, not the entire codebase.
|
|
450
|
+
// =====================================================================
|
|
451
|
+
const cyclePath = (0, eslint_devkit_4.findShortestCyclePath)(normalizedFilename, resolved, {
|
|
378
452
|
maxDepth,
|
|
379
|
-
reportAllCycles,
|
|
380
453
|
workspaceRoot,
|
|
381
454
|
barrelExports,
|
|
382
455
|
cache: sharedCache,
|
|
383
456
|
resolverSettings,
|
|
384
457
|
});
|
|
385
|
-
if (
|
|
458
|
+
if (!cyclePath)
|
|
386
459
|
return;
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
//
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
context.report({
|
|
430
|
-
node,
|
|
431
|
-
messageId,
|
|
432
|
-
data,
|
|
460
|
+
// Deduplicate: this import node produces exactly one error.
|
|
461
|
+
reportedImportNodes.add(node);
|
|
462
|
+
// Build the relevant cycle starting from normalizedFilename.
|
|
463
|
+
const cycleStart = cyclePath.indexOf(normalizedFilename);
|
|
464
|
+
const relevantCycle = cycleStart >= 0
|
|
465
|
+
? [...cyclePath.slice(cycleStart), ...cyclePath.slice(0, cycleStart)]
|
|
466
|
+
: cyclePath;
|
|
467
|
+
const strategy = selectFixStrategy(relevantCycle, fixStrategy);
|
|
468
|
+
const sourceImport = { path: resolved, source: importSource };
|
|
469
|
+
const { messageId, data } = generateMessageData(relevantCycle, strategy, sourceImport);
|
|
470
|
+
context.report({ node, messageId, data });
|
|
471
|
+
},
|
|
472
|
+
// Re-export statements (`export { X } from '...'`) create import edges
|
|
473
|
+
// just like `import { X } from '...'`. Without this handler, cycles
|
|
474
|
+
// that close through a re-export are invisible to the rule.
|
|
475
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
476
|
+
ExportNamedDeclaration(node) {
|
|
477
|
+
if (!node.source)
|
|
478
|
+
return; // no 'from' clause → not a re-export
|
|
479
|
+
// Reuse the same logic as ImportDeclaration
|
|
480
|
+
if (reportedImportNodes.has(node))
|
|
481
|
+
return;
|
|
482
|
+
const importSource = node.source.value;
|
|
483
|
+
const resolved = (0, eslint_devkit_4.resolveImportPath)(importSource, {
|
|
484
|
+
fromFile: normalizedFilename,
|
|
485
|
+
workspaceRoot,
|
|
486
|
+
barrelExports,
|
|
487
|
+
cache: sharedCache,
|
|
488
|
+
resolverSettings,
|
|
489
|
+
});
|
|
490
|
+
if (!resolved)
|
|
491
|
+
return;
|
|
492
|
+
if (sharedCache.nonCyclicFiles.has(resolved))
|
|
493
|
+
return;
|
|
494
|
+
let tgtSCC = sharedCache.sccIndex.get(resolved);
|
|
495
|
+
if (tgtSCC === undefined) {
|
|
496
|
+
(0, eslint_devkit_4.computeSCCsFromFile)(resolved, {
|
|
497
|
+
maxDepth: Infinity,
|
|
498
|
+
workspaceRoot,
|
|
499
|
+
barrelExports,
|
|
500
|
+
cache: sharedCache,
|
|
501
|
+
resolverSettings,
|
|
433
502
|
});
|
|
503
|
+
tgtSCC = sharedCache.sccIndex.get(resolved);
|
|
434
504
|
}
|
|
505
|
+
const srcSCC = sharedCache.sccIndex.get(normalizedFilename);
|
|
506
|
+
if (srcSCC === undefined || tgtSCC === undefined || srcSCC !== tgtSCC)
|
|
507
|
+
return;
|
|
508
|
+
const cyclePath = (0, eslint_devkit_4.findShortestCyclePath)(normalizedFilename, resolved, {
|
|
509
|
+
maxDepth,
|
|
510
|
+
workspaceRoot,
|
|
511
|
+
barrelExports,
|
|
512
|
+
cache: sharedCache,
|
|
513
|
+
resolverSettings,
|
|
514
|
+
});
|
|
515
|
+
if (!cyclePath)
|
|
516
|
+
return;
|
|
517
|
+
reportedImportNodes.add(node);
|
|
518
|
+
const cycleStart = cyclePath.indexOf(normalizedFilename);
|
|
519
|
+
const relevantCycle = cycleStart >= 0
|
|
520
|
+
? [...cyclePath.slice(cycleStart), ...cyclePath.slice(0, cycleStart)]
|
|
521
|
+
: cyclePath;
|
|
522
|
+
const strategy = selectFixStrategy(relevantCycle, fixStrategy);
|
|
523
|
+
const sourceImport = { path: resolved, source: importSource };
|
|
524
|
+
const { messageId, data } = generateMessageData(relevantCycle, strategy, sourceImport);
|
|
525
|
+
context.report({ node, messageId, data });
|
|
435
526
|
},
|
|
436
527
|
};
|
|
437
528
|
},
|
|
@@ -159,21 +159,20 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
159
159
|
// Check for @deprecated decorator in TypeScript
|
|
160
160
|
if ('decorators' in node &&
|
|
161
161
|
Array.isArray(node.decorators)) {
|
|
162
|
+
// The Array.isArray guard above proves decorators is an array.
|
|
162
163
|
const decorators = node.decorators;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
break;
|
|
176
|
-
}
|
|
164
|
+
for (const decorator of decorators) {
|
|
165
|
+
const dec = decorator;
|
|
166
|
+
if ((dec.expression.type === 'CallExpression' &&
|
|
167
|
+
dec.expression.callee.type === 'Identifier' &&
|
|
168
|
+
dec.expression.callee.name === 'deprecated') ||
|
|
169
|
+
(dec.expression.type === 'Identifier' &&
|
|
170
|
+
dec.expression.name === 'deprecated')) {
|
|
171
|
+
deprecatedItems.set(name, {
|
|
172
|
+
location: node,
|
|
173
|
+
reason: 'Marked with @deprecated decorator',
|
|
174
|
+
});
|
|
175
|
+
break;
|
|
177
176
|
}
|
|
178
177
|
}
|
|
179
178
|
}
|
|
@@ -184,6 +183,7 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
184
183
|
const deprecationInfo = deprecatedItems.get(name);
|
|
185
184
|
if (!deprecationInfo)
|
|
186
185
|
return;
|
|
186
|
+
const { replacement } = deprecationInfo;
|
|
187
187
|
context.report({
|
|
188
188
|
node,
|
|
189
189
|
messageId: 'deprecatedUsage',
|
|
@@ -197,17 +197,16 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
197
197
|
? `Replace with ${deprecationInfo.replacement}`
|
|
198
198
|
: 'Find modern replacement in documentation',
|
|
199
199
|
},
|
|
200
|
-
suggest:
|
|
200
|
+
suggest: replacement
|
|
201
201
|
? [
|
|
202
202
|
{
|
|
203
203
|
messageId: 'deprecatedUsage',
|
|
204
204
|
fix(fixer) {
|
|
205
|
-
return fixer.replaceText(node,
|
|
206
|
-
'Check documentation for replacement');
|
|
205
|
+
return fixer.replaceText(node, replacement);
|
|
207
206
|
},
|
|
208
207
|
data: {
|
|
209
|
-
replacement
|
|
210
|
-
suggestion: `Replace with ${
|
|
208
|
+
replacement,
|
|
209
|
+
suggestion: `Replace with ${replacement}`,
|
|
211
210
|
},
|
|
212
211
|
},
|
|
213
212
|
]
|
|
@@ -287,8 +286,9 @@ exports.noDeprecated = (0, eslint_devkit_1.createRule)({
|
|
|
287
286
|
},
|
|
288
287
|
// Check for usage of deprecated items
|
|
289
288
|
MemberExpression(node) {
|
|
289
|
+
// No has() pre-check: reportDeprecatedUsage bails out when the
|
|
290
|
+
// object name has no recorded deprecation info.
|
|
290
291
|
if (node.object.type === 'Identifier' &&
|
|
291
|
-
deprecatedItems.has(node.object.name) &&
|
|
292
292
|
node.property.type === 'Identifier') {
|
|
293
293
|
reportDeprecatedUsage(node, node.object.name, `${node.object.name}.${node.property.name}`);
|
|
294
294
|
}
|
|
@@ -35,13 +35,12 @@ exports.noDuplicates = (0, eslint_devkit_1.createRule)({
|
|
|
35
35
|
return {
|
|
36
36
|
ImportDeclaration(node) {
|
|
37
37
|
const source = node.source.value;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (sourceNodes) {
|
|
43
|
-
sourceNodes.push(node);
|
|
38
|
+
let sourceNodes = imports.get(source);
|
|
39
|
+
if (!sourceNodes) {
|
|
40
|
+
sourceNodes = [];
|
|
41
|
+
imports.set(source, sourceNodes);
|
|
44
42
|
}
|
|
43
|
+
sourceNodes.push(node);
|
|
45
44
|
},
|
|
46
45
|
'Program:exit'() {
|
|
47
46
|
for (const nodes of imports.values()) {
|
|
@@ -56,8 +56,7 @@ exports.noNamedAsDefaultMember = (0, eslint_devkit_1.createRule)({
|
|
|
56
56
|
},
|
|
57
57
|
MemberExpression(node) {
|
|
58
58
|
// Check if accessing property of default import
|
|
59
|
-
if (node.object.type !== eslint_devkit_1.AST_NODE_TYPES.Identifier
|
|
60
|
-
!defaultImports.has(node.object.name)) {
|
|
59
|
+
if (node.object.type !== eslint_devkit_1.AST_NODE_TYPES.Identifier) {
|
|
61
60
|
return;
|
|
62
61
|
}
|
|
63
62
|
// Skip computed properties like obj[prop]
|
|
@@ -93,21 +93,15 @@ exports.noNamedExport = (0, eslint_devkit_1.createRule)({
|
|
|
93
93
|
if (pattern.includes('*')) {
|
|
94
94
|
const regexStr = '^' + pattern
|
|
95
95
|
.replace(/[.+?^${}()|[\]\\]/g, (match) => {
|
|
96
|
-
|
|
97
|
-
return '*';
|
|
96
|
+
// The char class above never matches '*', so no special-casing needed.
|
|
98
97
|
return '\\' + match;
|
|
99
98
|
})
|
|
100
99
|
.replace(/\*\*/g, '.*')
|
|
101
100
|
.replace(/\*/g, '[^/]*') + '$';
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return result;
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
101
|
+
// regexStr escapes every regex metacharacter before translating
|
|
102
|
+
// glob tokens, so new RegExp() can never throw here.
|
|
103
|
+
const regex = new RegExp(regexStr);
|
|
104
|
+
return regex.test(filename);
|
|
111
105
|
}
|
|
112
106
|
return filename.includes(pattern);
|
|
113
107
|
});
|
|
@@ -119,19 +113,15 @@ exports.noNamedExport = (0, eslint_devkit_1.createRule)({
|
|
|
119
113
|
if (pattern.includes('*')) {
|
|
120
114
|
const regexStr = '^' + pattern
|
|
121
115
|
.replace(/[.+?^${}()|[\]\\]/g, (match) => {
|
|
122
|
-
|
|
123
|
-
return '*';
|
|
116
|
+
// The char class above never matches '*', so no special-casing needed.
|
|
124
117
|
return '\\' + match;
|
|
125
118
|
})
|
|
126
119
|
.replace(/\*\*/g, '.*')
|
|
127
120
|
.replace(/\*/g, '[^/]*') + '$';
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
catch {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
121
|
+
// regexStr escapes every regex metacharacter before translating
|
|
122
|
+
// glob tokens, so new RegExp() can never throw here.
|
|
123
|
+
const regex = new RegExp(regexStr);
|
|
124
|
+
return regex.test(filename);
|
|
135
125
|
}
|
|
136
126
|
return filename.includes(pattern);
|
|
137
127
|
});
|
|
@@ -101,10 +101,10 @@ exports.noRelativePackages = (0, eslint_devkit_1.createRule)({
|
|
|
101
101
|
},
|
|
102
102
|
fix(fixer) {
|
|
103
103
|
// Calculate the subpath within the package
|
|
104
|
+
// subPath is never '' or '.': the package root is found by walking
|
|
105
|
+
// UP from the resolved path's parent, so it is a strict ancestor.
|
|
104
106
|
const subPath = path.relative(importPackageRoot, resolvedPath);
|
|
105
|
-
const newImport = subPath
|
|
106
|
-
? `${packageName}/${subPath.replace(/\.[^/.]+$/, '')}`
|
|
107
|
-
: packageName;
|
|
107
|
+
const newImport = `${packageName}/${subPath.replace(/\.[^/.]+$/, '')}`;
|
|
108
108
|
return fixer.replaceText(source, `'${newImport}'`);
|
|
109
109
|
},
|
|
110
110
|
});
|