diff --git a/apps/app/.eslintrc.cjs b/apps/app/.eslintrc.cjs index 3625abb3..4129ea26 100644 --- a/apps/app/.eslintrc.cjs +++ b/apps/app/.eslintrc.cjs @@ -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