chore(tooling): enable eslint-plugin-boundaries in apps/app

Adds 'boundaries' to plugins, the layered-architecture matrix to
rules, and the boundaries/elements + boundaries/ignore + boundaries/
include settings per the WS-3 1c audit (Phase A:
dev-docs/WS-3-SESSION-1C-AUDIT.md). Phase B sign-off (Bert):

- Q1=B — `lib → stores` is DISALLOWED in the matrix; lib/axios.ts is
  refactored in the next commit.
- Q2 — src/views/** added to boundaries/ignore (dead Vuexy file;
  TECH-DELETE-DEAD-VIEWS backlog item lands with the docs commit).
- Q3 — `navigation` allowed to import `types`, `utils` (forward
  headroom).
- Q4 — sub-zone enforcement deferred to TECH-WS3-BOUNDARIES-SUBZONES
  (lands when WS-3 PR-B brings the §4.2 components/{organizer,portal,
  shared} + pages/{(auth),portal,…} structure).

Forward-flag carried into the inline comment: when src/plugins/1.router/
migrates to a top-level src/router/ in a later WS-3 PR, add a
{ type: 'router', pattern: 'src/router/**' } element and a
{ from: 'router', allow: ['types','utils','lib','plugins','stores'] }
rule. Doc-side flag also lands in the ARCH-CONSOLIDATION 1c entry.

Boundaries plugin v6 emits a deprecation warning that the
'element-types' selector format is legacy (v5 syntax); the rule
still works on v5-compatible config and migrating to v6 object-
selector syntax is out of scope per the prompt's "only the two
.eslintrc.cjs changes listed are permitted" constraint. Filing a
TECH-BOUNDARIES-V6-SELECTOR-MIGRATION backlog item (in the docs
commit) so the migration happens deliberately.

Lint count after this commit: 4 errors, all in lib/axios.ts (lines
3, 4, 61, 72 — the 2 static + 2 dynamic store imports). The plugin
treats both static AND dynamic `await import('@/stores/...')` as
boundary edges; this is a deliberate intermediate state. The next
commit (refactor) resolves all 4 to land at lint = 0.

Tests + typecheck verified green (boundary errors are lint-only).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-04-30 23:16:57 +02:00
parent af1c54967f
commit de71d31a2b

View File

@@ -32,6 +32,7 @@ module.exports = {
'@typescript-eslint',
'regex',
'regexp',
'boundaries',
],
ignorePatterns: [
'src/plugins/iconify/*.js',
@@ -200,12 +201,68 @@ module.exports = {
],
'\\.eslintrc\\.cjs',
],
// Architectural import boundaries (WS-3 1c, audit:
// dev-docs/WS-3-SESSION-1C-AUDIT.md). The matrix is layered:
// types → utils → lib → composables → stores → components → layouts → pages.
// The `lib → stores` edge is intentionally disallowed; lib/axios.ts
// uses dynamic `await import('@/stores/...')` for its 4 store reads
// so the static-import surface stays clean.
// Sub-zone enforcement (components/{organizer,portal,shared}) is a
// backlog item (TECH-WS3-BOUNDARIES-SUBZONES); it lands after the
// §4.2 consolidation directory layout.
// FORWARD-FLAG: when src/plugins/1.router/ migrates to src/router/
// in a later WS-3 PR, add `{ type: 'router', pattern: 'src/router/**' }`
// to boundaries/elements and `{ from: 'router', allow: ['types',
// 'utils', 'lib', 'plugins', 'stores'] }` to the rules.
'boundaries/element-types': ['error', {
default: 'disallow',
rules: [
{ from: 'types', allow: ['types'] },
{ from: 'utils', allow: ['types', 'utils'] },
{ from: 'lib', allow: ['types', 'utils', 'lib'] },
{ from: 'plugins', allow: ['types', 'utils', 'lib', 'plugins', 'stores'] },
{ from: 'composables', allow: ['types', 'utils', 'lib', 'composables', 'stores'] },
{ from: 'stores', allow: ['types', 'utils', 'lib', 'composables', 'stores'] },
{ from: 'navigation', allow: ['types', 'utils', 'navigation'] },
{ from: 'components', allow: ['types', 'utils', 'lib', 'composables', 'stores', 'components'] },
{ from: 'layouts', allow: ['types', 'utils', 'lib', 'composables', 'stores', 'navigation', 'components', 'layouts'] },
{ from: 'pages', allow: ['types', 'utils', 'lib', 'composables', 'stores', 'navigation', 'components', 'layouts'] },
],
}],
'boundaries/no-unknown': 'off', // External packages are fine.
'boundaries/no-unknown-files': 'off', // The ignore list handles vendor/generated.
},
settings: {
'import/resolver': {
node: true,
typescript: {},
},
// Element-type assignment: first-match-wins. Order matters.
'boundaries/elements': [
{ type: 'types', pattern: 'src/types/**' },
{ type: 'utils', pattern: 'src/utils/**' },
{ type: 'lib', pattern: 'src/lib/**' },
{ type: 'plugins', pattern: 'src/plugins/**' },
{ type: 'composables', pattern: 'src/composables/**' },
{ type: 'stores', pattern: 'src/stores/**' },
{ type: 'navigation', pattern: 'src/navigation/**' },
{ type: 'components', pattern: 'src/components/**' },
{ type: 'layouts', pattern: 'src/layouts/**' },
{ type: 'pages', pattern: 'src/pages/**' },
],
'boundaries/ignore': [
'src/@core/**', // vendored Vuexy
'src/@layouts/**', // vendored Vuexy
'src/views/**', // single dead Vuexy file (zero importers); see TECH-DELETE-DEAD-VIEWS
'src/App.vue', // orchestration root
'src/main.ts', // orchestration root
'src/assets/**', // static media
'src/styles/**', // SCSS
'**/*.d.ts', // generated declarations
],
'boundaries/include': ['src/**/*.{ts,vue,tsx}'],
},
overrides: [
// Vue SFCs: the base lines-around-comment rule conflicts with