eslint-plugin-import-boundaries 0.2.0 → 0.2.2
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/README.md +16 -8
- package/eslint-plugin-import-boundaries.js +9 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -116,8 +116,8 @@ Prevent violations of your architecture:
|
|
|
116
116
|
{
|
|
117
117
|
dir: 'application',
|
|
118
118
|
alias: '@application',
|
|
119
|
-
allowImportsFrom: ['@domain'], // Only allow imports from @domain
|
|
120
|
-
|
|
119
|
+
allowImportsFrom: ['@domain'], // Only allow imports from @domain (deny-all by default)
|
|
120
|
+
// Note: denyImportsFrom is redundant here - anything not in allowImportsFrom is already denied
|
|
121
121
|
}
|
|
122
122
|
```
|
|
123
123
|
|
|
@@ -151,7 +151,7 @@ Boundaries can be nested, and each boundary must explicitly declare its import r
|
|
|
151
151
|
dir: 'interface',
|
|
152
152
|
alias: '@interface',
|
|
153
153
|
allowImportsFrom: ['@application', '@public-use-cases'],
|
|
154
|
-
denyImportsFrom: ['@use-cases'], //
|
|
154
|
+
denyImportsFrom: ['@use-cases'], // Deny specific sub-boundary even though parent @application is allowed
|
|
155
155
|
},
|
|
156
156
|
],
|
|
157
157
|
}
|
|
@@ -159,11 +159,19 @@ Boundaries can be nested, and each boundary must explicitly declare its import r
|
|
|
159
159
|
|
|
160
160
|
**Key behaviors:**
|
|
161
161
|
|
|
162
|
-
- Each boundary
|
|
163
|
-
-
|
|
162
|
+
- Each boundary has rules: explicit (via `allowImportsFrom`/`denyImportsFrom`) or implicit "deny all" (if neither is specified)
|
|
163
|
+
- Each boundary uses its own rules directly (no inheritance from parent boundaries)
|
|
164
164
|
- Rules work the same regardless of nesting depth (flat rule checking)
|
|
165
165
|
- You can selectively allow/deny specific nested boundaries
|
|
166
|
-
-
|
|
166
|
+
- Files resolve to their most specific boundary (longest matching path), which determines the rules to apply
|
|
167
|
+
|
|
168
|
+
**Rule semantics:**
|
|
169
|
+
|
|
170
|
+
- If both `allowImportsFrom` and `denyImportsFrom` exist: `allowImportsFrom` takes precedence (items in allow list are allowed even if also in deny list)
|
|
171
|
+
- If only `allowImportsFrom`: deny-all by default (only items in allow list are allowed)
|
|
172
|
+
- If only `denyImportsFrom`: allow-all by default (everything except deny list is allowed)
|
|
173
|
+
- If neither: deny-all by default (strictest)
|
|
174
|
+
- **Important**: When `allowImportsFrom` is specified, `denyImportsFrom` can deny specific sub-boundaries (e.g. deny `@utils` within allowed `@application`), but is otherwise redundant since anything not in the allow list is already denied by default. Note that this works recursively: It is possible to allow a boundary within a denied boundary within an allowed boundary, and so on.
|
|
167
175
|
|
|
168
176
|
### 4. Type-Only Imports
|
|
169
177
|
|
|
@@ -220,8 +228,8 @@ import { something } from "@application"; // When inside @application boundary
|
|
|
220
228
|
{
|
|
221
229
|
dir: 'application',
|
|
222
230
|
alias: '@application',
|
|
223
|
-
allowImportsFrom: ['@domain'], // Application uses domain
|
|
224
|
-
|
|
231
|
+
allowImportsFrom: ['@domain'], // Application uses domain (deny-all by default)
|
|
232
|
+
// Note: denyImportsFrom is redundant here - those boundaries are already denied
|
|
225
233
|
},
|
|
226
234
|
{
|
|
227
235
|
dir: 'infrastructure',
|
|
@@ -87,18 +87,16 @@ function checkAliasSubpath(spec, boundaries) {
|
|
|
87
87
|
return { isSubpath: false };
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
|
-
* Resolve a file to the nearest boundary
|
|
91
|
-
*
|
|
90
|
+
* Resolve a file/path to the nearest boundary (regardless of rules).
|
|
91
|
+
* Used for target boundaries - returns the boundary if it exists, even without rules.
|
|
92
92
|
*
|
|
93
93
|
* @param filename - Absolute filename
|
|
94
94
|
* @param boundaries - Array of all boundaries
|
|
95
|
-
* @returns The nearest boundary
|
|
95
|
+
* @returns The nearest boundary, or null if none found
|
|
96
96
|
*/
|
|
97
|
-
function
|
|
98
|
-
const
|
|
99
|
-
if (
|
|
100
|
-
const ancestors = boundaries.filter((b) => b.allowImportsFrom !== void 0 || b.denyImportsFrom !== void 0 || b.allowTypeImportsFrom !== void 0).filter((b) => isInsideDir(b.absDir, filename));
|
|
101
|
-
if (ancestors.length > 0) return ancestors.sort((a, b) => b.absDir.length - a.absDir.length)[0];
|
|
97
|
+
function resolveToBoundary(filename, boundaries) {
|
|
98
|
+
const matchingBoundaries = boundaries.filter((b) => isInsideDir(b.absDir, filename));
|
|
99
|
+
if (matchingBoundaries.length > 0) return matchingBoundaries.sort((a, b) => b.absDir.length - a.absDir.length)[0];
|
|
102
100
|
return null;
|
|
103
101
|
}
|
|
104
102
|
/**
|
|
@@ -114,7 +112,7 @@ function getFileData(filename, boundaries) {
|
|
|
114
112
|
return {
|
|
115
113
|
isValid: true,
|
|
116
114
|
fileDir: path.dirname(filename),
|
|
117
|
-
fileBoundary:
|
|
115
|
+
fileBoundary: resolveToBoundary(filename, boundaries)
|
|
118
116
|
};
|
|
119
117
|
}
|
|
120
118
|
|
|
@@ -286,7 +284,7 @@ function calculateCorrectImportPath(rawSpec, fileDir, fileBoundary, boundaries,
|
|
|
286
284
|
".cjs"
|
|
287
285
|
]) {
|
|
288
286
|
const { targetAbs, targetDir } = resolveTargetPath(rawSpec, fileDir, boundaries, rootDir, cwd, barrelFileName, fileExtensions);
|
|
289
|
-
const targetBoundary =
|
|
287
|
+
const targetBoundary = resolveToBoundary(targetAbs, boundaries);
|
|
290
288
|
if (!fileBoundary || targetBoundary !== fileBoundary) {
|
|
291
289
|
if (targetBoundary) {
|
|
292
290
|
if (crossBoundaryStyle === "absolute") return path.join(rootDir, targetBoundary.dir).replace(/\\/g, "/");
|
|
@@ -371,7 +369,7 @@ function handleImport(options) {
|
|
|
371
369
|
}
|
|
372
370
|
}
|
|
373
371
|
}
|
|
374
|
-
const targetBoundary =
|
|
372
|
+
const targetBoundary = resolveToBoundary(targetAbs, boundaries);
|
|
375
373
|
if (!skipBoundaryRules && fileBoundary && targetBoundary && fileBoundary !== targetBoundary) {
|
|
376
374
|
const violation = checkBoundaryRules(fileBoundary, targetBoundary, boundaries, isTypeOnly);
|
|
377
375
|
if (violation) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-import-boundaries",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"description": "Enforce architectural boundaries with deterministic import paths",
|
|
6
6
|
"author": "ClassicalMoser",
|
|
7
7
|
"license": "ISC",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"format:check": "prettier --check .",
|
|
56
56
|
"validate:fix": "pnpm run lint:fix && pnpm run format && pnpm run typecheck",
|
|
57
57
|
"validate:check": "pnpm run lint && pnpm run format:check && pnpm run typecheck",
|
|
58
|
-
"prepublishOnly": "npm run build && npm run test && npm run
|
|
58
|
+
"prepublishOnly": "npm run build && npm run test && npm run validate:check"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"eslint": ">=9.0.0"
|