112 lines
3.5 KiB
JavaScript
112 lines
3.5 KiB
JavaScript
import { dirname } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import { FlatCompat } from '@eslint/eslintrc';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
const compat = new FlatCompat({ baseDirectory: __dirname });
|
|
|
|
// ── Clean Architecture layer boundary rules ──────────────────
|
|
// Domain → nothing from this project
|
|
// Infrastructure → domain only
|
|
// Application → domain + infrastructure
|
|
// Store → domain only
|
|
// Views/Components → application + store + domain (NOT infrastructure)
|
|
|
|
const LAYERS = {
|
|
domain: ['@/domain/*', '*/domain/*'],
|
|
infrastructure: ['@/infrastructure/*', '*/infrastructure/*'],
|
|
application: ['@/application/*', '*/application/*'],
|
|
store: ['@/store/*', '*/store/*'],
|
|
views: ['@/views/*', '*/views/*'],
|
|
};
|
|
|
|
function noImport(patterns, message) {
|
|
return patterns.map((group) => ({ group: [group], message }));
|
|
}
|
|
|
|
const config = [
|
|
...compat.extends('next/core-web-vitals', 'next/typescript'),
|
|
|
|
// ── Domain: pure entities + interfaces, zero outward deps ──
|
|
{
|
|
files: ['src/domain/**/*.ts', 'src/domain/**/*.tsx'],
|
|
rules: {
|
|
'no-restricted-imports': ['error', {
|
|
patterns: [
|
|
...noImport(LAYERS.infrastructure, 'Domain must not import Infrastructure'),
|
|
...noImport(LAYERS.application, 'Domain must not import Application'),
|
|
...noImport(LAYERS.store, 'Domain must not import Store'),
|
|
...noImport(LAYERS.views, 'Domain must not import Views'),
|
|
],
|
|
}],
|
|
},
|
|
},
|
|
|
|
// ── Infrastructure: repos + HTTP client, depends on domain only ──
|
|
{
|
|
files: ['src/infrastructure/**/*.ts', 'src/infrastructure/**/*.tsx'],
|
|
rules: {
|
|
'no-restricted-imports': ['error', {
|
|
patterns: [
|
|
...noImport(LAYERS.application, 'Infrastructure must not import Application'),
|
|
...noImport(LAYERS.store, 'Infrastructure must not import Store'),
|
|
...noImport(LAYERS.views, 'Infrastructure must not import Views'),
|
|
],
|
|
}],
|
|
},
|
|
},
|
|
|
|
// ── Application (use cases): depends on domain + infrastructure ──
|
|
{
|
|
files: ['src/application/**/*.ts', 'src/application/**/*.tsx'],
|
|
rules: {
|
|
'no-restricted-imports': ['error', {
|
|
patterns: [
|
|
...noImport(LAYERS.store, 'Application must not import Store'),
|
|
...noImport(LAYERS.views, 'Application must not import Views'),
|
|
],
|
|
}],
|
|
},
|
|
},
|
|
|
|
// ── Store (Redux + Zustand): UI state only, depends on domain ──
|
|
{
|
|
files: ['src/store/**/*.ts', 'src/store/**/*.tsx'],
|
|
rules: {
|
|
'no-restricted-imports': ['error', {
|
|
patterns: [
|
|
...noImport(LAYERS.infrastructure, 'Store must not import Infrastructure'),
|
|
...noImport(LAYERS.application, 'Store must not import Application'),
|
|
...noImport(LAYERS.views, 'Store must not import Views'),
|
|
],
|
|
}],
|
|
},
|
|
},
|
|
|
|
// ── Presentation (views + components + layouts): no direct infra access ──
|
|
{
|
|
files: [
|
|
'src/views/**/*.ts',
|
|
'src/views/**/*.tsx',
|
|
'src/components/**/*.ts',
|
|
'src/components/**/*.tsx',
|
|
'src/layouts/**/*.ts',
|
|
'src/layouts/**/*.tsx',
|
|
],
|
|
rules: {
|
|
'no-restricted-imports': ['error', {
|
|
patterns: [
|
|
...noImport(
|
|
LAYERS.infrastructure,
|
|
'Presentation must not import Infrastructure directly — use Application use-cases',
|
|
),
|
|
],
|
|
}],
|
|
},
|
|
},
|
|
];
|
|
|
|
export default config;
|