Add eslint-plugin-boundaries with layered dependency rules

This commit is contained in:
2026-04-14 22:47:26 +03:00
parent 7bbda35041
commit f1acf7827d
3 changed files with 177 additions and 0 deletions
+54
View File
@@ -3,6 +3,7 @@
import js from "@eslint/js";
import tseslint from "typescript-eslint";
import unusedImports from "eslint-plugin-unused-imports";
import boundaries from "eslint-plugin-boundaries";
export default [
{
@@ -52,4 +53,57 @@ export default [
"no-console": ["warn", { allow: ["warn", "error"] }],
},
},
{
files: ["src/**/*.{ts,tsx}"],
plugins: {
boundaries,
},
settings: {
"boundaries/elements": [
{ type: "routes", pattern: "src/routes/*" },
{ type: "mf", pattern: "src/mf/*" },
{ type: "features", pattern: "src/features/*", capture: ["feature"] },
{ type: "ui", pattern: "src/ui/*" },
{ type: "shared", pattern: "src/shared/*" },
{ type: "observability", pattern: "src/observability/*" },
{ type: "i18n", pattern: "src/i18n/*" },
{ type: "env", pattern: "src/env/*" },
],
},
rules: {
// Design spec §1.2 layered dependency direction:
// features/ cannot import routes/ or mf/
// ui/ cannot import features/
// shared/ cannot import features/, routes/, mf/, observability/
// observability/ cannot import features/, routes/, mf/
"boundaries/element-types": [
"error",
{
default: "allow",
rules: [
{
from: "features",
disallow: ["routes", "mf"],
message: "Features must not import from routes/ or mf/. Use the HostContract or a shared module instead.",
},
{
from: "ui",
disallow: ["features", "routes", "mf"],
message: "UI layer must not import from features/, routes/, or mf/. UI is consumed by features, not the other way around.",
},
{
from: "shared",
disallow: ["features", "routes", "mf", "observability"],
message: "Shared modules must not import from features/, routes/, mf/, or observability/.",
},
{
from: "observability",
disallow: ["features", "routes", "mf"],
message: "Observability modules must not import from features/, routes/, or mf/.",
},
],
},
],
},
},
];
+1
View File
@@ -37,6 +37,7 @@
"@typescript-eslint/parser": "^8.0.0",
"@vitest/ui": "^3.0.0",
"eslint": "^9.0.0",
"eslint-plugin-boundaries": "^5.0.0",
"eslint-plugin-unused-imports": "^4.0.0",
"typescript": "^5.5.0",
"typescript-eslint": "^8.58.2",
+122
View File
@@ -51,6 +51,9 @@ importers:
eslint:
specifier: ^9.0.0
version: 9.39.4(jiti@2.6.1)
eslint-plugin-boundaries:
specifier: ^5.0.0
version: 5.4.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))
eslint-plugin-unused-imports:
specifier: ^4.0.0
version: 4.4.1(@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))
@@ -666,6 +669,10 @@ packages:
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
engines: {node: '>=6.9.0'}
'@boundaries/elements@1.2.0':
resolution: {integrity: sha512-W65Gum02liMd3hmNrLmDBX1u5BmRMcunouFjLXyhxHnNY4YlK1kTxsgfflZ5XBGSnPnO0MkiUzAcoGzYrlx0RQ==}
engines: {node: '>=18.18'}
'@bufbuild/protobuf@2.11.0':
resolution: {integrity: sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==}
@@ -2976,6 +2983,14 @@ packages:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
debug@3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
@@ -3150,6 +3165,36 @@ packages:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
eslint-import-resolver-node@0.3.9:
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
eslint-module-utils@2.12.1:
resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
eslint: '*'
eslint-import-resolver-node: '*'
eslint-import-resolver-typescript: '*'
eslint-import-resolver-webpack: '*'
peerDependenciesMeta:
'@typescript-eslint/parser':
optional: true
eslint:
optional: true
eslint-import-resolver-node:
optional: true
eslint-import-resolver-typescript:
optional: true
eslint-import-resolver-webpack:
optional: true
eslint-plugin-boundaries@5.4.0:
resolution: {integrity: sha512-6SQmEhXCqGrrxm9YiM24SC95CqrVi2MUOm5SDrfquceh/os8MIAvZYsDU69zvtCSb1S6UbNEmdioi1gCDc8+VQ==}
engines: {node: '>=18.18'}
peerDependencies:
eslint: '>=6.0.0'
eslint-plugin-unused-imports@4.4.1:
resolution: {integrity: sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==}
peerDependencies:
@@ -3433,6 +3478,11 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
handlebars@4.7.8:
resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
engines: {node: '>=0.4.7'}
hasBin: true
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
@@ -5267,6 +5317,11 @@ packages:
ufo@1.6.3:
resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==}
uglify-js@3.19.3:
resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==}
engines: {node: '>=0.8.0'}
hasBin: true
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
@@ -5470,6 +5525,9 @@ packages:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
wordwrap@1.0.0:
resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
@@ -6317,6 +6375,20 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5
'@boundaries/elements@1.2.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))':
dependencies:
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1))
handlebars: 4.7.8
is-core-module: 2.16.1
micromatch: 4.0.8
transitivePeerDependencies:
- '@typescript-eslint/parser'
- eslint
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
'@bufbuild/protobuf@2.11.0': {}
'@colordx/core@5.0.3': {}
@@ -9139,6 +9211,10 @@ snapshots:
data-uri-to-buffer@4.0.1: {}
debug@3.2.7:
dependencies:
ms: 2.1.3
debug@4.4.3:
dependencies:
ms: 2.1.3
@@ -9363,6 +9439,38 @@ snapshots:
escape-string-regexp@4.0.0: {}
eslint-import-resolver-node@0.3.9:
dependencies:
debug: 3.2.7
is-core-module: 2.16.1
resolve: 1.22.12
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.4(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
- supports-color
eslint-plugin-boundaries@5.4.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)):
dependencies:
'@boundaries/elements': 1.2.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))
chalk: 4.1.2
eslint: 9.39.4(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.4(jiti@2.6.1))
micromatch: 4.0.8
transitivePeerDependencies:
- '@typescript-eslint/parser'
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)):
dependencies:
eslint: 9.39.4(jiti@2.6.1)
@@ -9674,6 +9782,15 @@ snapshots:
graceful-fs@4.2.11: {}
handlebars@4.7.8:
dependencies:
minimist: 1.2.8
neo-async: 2.6.2
source-map: 0.6.1
wordwrap: 1.0.0
optionalDependencies:
uglify-js: 3.19.3
has-flag@4.0.0: {}
has-property-descriptors@1.0.2:
@@ -11503,6 +11620,9 @@ snapshots:
ufo@1.6.3: {}
uglify-js@3.19.3:
optional: true
undici-types@7.16.0: {}
undici@7.24.7: {}
@@ -11734,6 +11854,8 @@ snapshots:
word-wrap@1.2.5: {}
wordwrap@1.0.0: {}
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0