gcx/frontend/admin-web/eslint.config.mjs

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;