{ "env": { "es2022": true, "node": true }, "ignorePatterns": [ "**/dist/**", "**/*.d.ts" ], "overrides": [ { "files": [ "**/index.ts", "**/index.tsx" ], "rules": { "no-restricted-syntax": [ "error", { "message": "index.ts files are forbidden. Use explicit file names instead (e.g., UserService.ts, not index.ts).", "selector": "Program" } ] } }, { "files": [ "core/*/application/ports/*/*.ts" ], "rules": { "no-restricted-syntax": [ "error", { "message": "Port interface names should not start with 'Get'. Use descriptive names without the 'Get' prefix.", "selector": "TSInterfaceDeclaration[id.name=/^Get.*Port$/]" } ] } }, { "files": [ "core/**/*.ts" ], "rules": { "no-restricted-syntax": [ "error", { "message": "Blocker classes/interfaces are not allowed in core. Use Guards in backend.", "selector": "TSClassDeclaration[id.name=/Blocker$/], TSInterfaceDeclaration[id.name=/Blocker$/]" }, { "message": "Presenter classes/interfaces are not allowed in core. Presenters belong in API or frontend layers.", "selector": "TSClassDeclaration[id.name=/Presenter$/], TSInterfaceDeclaration[id.name=/Presenter$/]" }, { "message": "DTO classes/interfaces are not allowed in core. DTOs belong in API or frontend layers.", "selector": "TSClassDeclaration[id.name=/Dto$/], TSInterfaceDeclaration[id.name=/Dto$/]" }, { "message": "ViewModel classes/interfaces are not allowed in core. View Models belong in frontend.", "selector": "TSClassDeclaration[id.name=/ViewModel$/], TSInterfaceDeclaration[id.name=/ViewModel$/]" }, { "message": "CommandModel classes/interfaces are not allowed in core. Command Models belong in frontend.", "selector": "TSClassDeclaration[id.name=/CommandModel$/], TSInterfaceDeclaration[id.name=/CommandModel$/]" } ] } }, { "files": [ "core/**/application/dto/**/*.ts", "core/**/application/dtos/**/*.ts" ], "rules": { "no-restricted-syntax": [ "error", { "message": "core/*/application/dto is forbidden. Use application result models + output ports; DTOs belong in API/website layers.", "selector": "Program" } ] } }, { "files": [ "core/**/infrastructure/**/*.ts" ], "rules": { "no-restricted-syntax": [ "error", { "message": "core/*/infrastructure is forbidden. Implementations must live in adapters/ and be wired in apps/.", "selector": "Program" } ] } }, { "files": [ "core/**/domain/ports/**/*.ts" ], "rules": { "no-restricted-syntax": [ "error", { "message": "core/*/domain/ports is forbidden. Ports belong in application/ports (or shared application layer), not domain.", "selector": "Program" } ] } }, { "files": [ "core/**/shared/presentation/**/*.ts" ], "rules": { "no-restricted-syntax": [ "error", { "message": "core/shared/presentation is forbidden. Presentation belongs in API or website layers.", "selector": "Program" } ] } }, { "files": [ "apps/website/**/*.ts" ], "rules": { "no-restricted-syntax": [ "error", { "message": "Guard classes/interfaces are not allowed in frontend. Use Blockers in frontend.", "selector": "TSClassDeclaration[id.name=/Guard$/], TSInterfaceDeclaration[id.name=/Guard$/]" } ] } }, { "files": [ "apps/api/**/*.ts", "apps/website/lib/dtos/**/*.ts" ], "rules": { "no-restricted-syntax": [ "error", { "message": "Transport enums must end with 'Enum'.", "selector": "TSEnumDeclaration[id.name=/^(?!.*Enum$).+/]" } ] } }, { "files": [ "core/*/application/use-cases/*.ts" ], "rules": { "no-restricted-syntax": [ "error", { "message": "Use Case classes must end with 'UseCase'.", "selector": "TSClassDeclaration[id.name=/^(?!.*UseCase$).+/]" } ] } }, { "files": [ "core/*/application/services/*.ts" ], "rules": { "no-restricted-syntax": [ "error", { "message": "Application Service classes must end with 'Service'.", "selector": "TSClassDeclaration[id.name=/^(?!.*Service$).+/]" } ] } }, { "files": [ "apps/website/lib/view-models/*.ts" ], "rules": { "no-restricted-syntax": [ "error", { "message": "View Model classes must end with 'ViewModel'.", "selector": "TSClassDeclaration[id.name=/^(?!.*ViewModel$).+/]" } ] } }, { "files": [ "apps/website/lib/commands/*.ts" ], "rules": { "no-restricted-syntax": [ "error", { "message": "Command Model classes must end with 'CommandModel'.", "selector": "TSClassDeclaration[id.name=/^(?!.*CommandModel$).+/]" } ] } }, { "files": [ "apps/website/app/**/page.tsx", "apps/website/app/**/page.ts", "apps/website/app/**/layout.tsx", "apps/website/app/**/layout.ts", "playwright.*.config.ts", "vitest.*.config.ts", "vitest.config.ts" ], "rules": { "import/no-default-export": "off", "no-restricted-syntax": [ "error", { "message": "Interface names should not start with 'I'. Use descriptive names without the 'I' prefix (e.g., 'LiverCompositor' instead of 'ILiveryCompositor').", "selector": "TSInterfaceDeclaration[id.name=/^I[A-Z]/]" } ], "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-unused-vars": "off" } }, { "extends": [ "plugin:import/recommended", "plugin:import/typescript" ], "files": [ "**/*.ts", "**/*.tsx" ], "excludedFiles": [ "playwright.*.config.ts", "vitest.*.config.ts", "vitest.config.ts" ], "parser": "@typescript-eslint/parser", "plugins": [ "@typescript-eslint", "boundaries", "import", "gridpilot-rules" ], "rules": { "@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/no-unused-vars": "error", "boundaries/element-types": [ 2, { "default": "disallow", "rules": [ { "allow": [ "website" ], "from": [ "website" ] }, { "allow": [ "api", "adapters", "core" ], "from": [ "api" ] }, { "allow": [ "adapters", "core" ], "from": [ "adapters" ] }, { "allow": [ "core" ], "from": [ "core" ] } ] } ], "import/no-default-export": "error", "import/no-useless-path-segments": "error", "no-restricted-syntax": [ "error", { "message": "Default exports are forbidden. Use named exports instead.", "selector": "ExportDefaultDeclaration" }, { "message": "Interface names should not start with 'I'. Use descriptive names without the 'I' prefix (e.g., 'LiverCompositor' instead of 'ILiveryCompositor').", "selector": "TSInterfaceDeclaration[id.name=/^I[A-Z]/]" } ], // GridPilot ESLint Rules "gridpilot-rules/view-model-taxonomy": "error" } }, { "files": [ "core/**/*.ts" ], "rules": { "no-restricted-syntax": [ "error", { "message": "Default exports are forbidden. Use named exports instead.", "selector": "ExportDefaultDeclaration" } ] } }, { "files": [ "apps/website/**/*.tsx", "apps/website/**/*.ts" ], "rules": { "no-restricted-imports": [ "error", { "paths": [ { "message": "Imports from @core are forbidden in website components", "name": "@core/racing" }, { "message": "Imports from @core are forbidden in website components", "name": "@core/analytics" }, { "message": "Imports from @core are forbidden in website components", "name": "@core/identity" }, { "message": "Imports from @core are forbidden in website components", "name": "@core/media" }, { "message": "Imports from @core are forbidden in website components", "name": "@core/notifications" }, { "message": "Imports from @core are forbidden in website components", "name": "@core/payments" }, { "message": "Imports from @core are forbidden in website components", "name": "@core/shared" }, { "message": "Imports from @core are forbidden in website components", "name": "@core/social" }, { "message": "Imports from @adapters are forbidden in website components", "name": "@adapters" }, { "message": "Imports from @api are forbidden in website components", "name": "@api" } ], "patterns": [ { "group": [ "@core/*" ], "message": "Imports from @core are forbidden in website components" }, { "group": [ "@adapters/*" ], "message": "Imports from @adapters are forbidden in website components" }, { "group": [ "@api/*" ], "message": "Imports from @api are forbidden in website components" } ] } ], "no-restricted-syntax": [ "error", { "message": "ViewModel types must be defined in apps/website/lib/view-models, not in components.", "selector": "TSInterfaceDeclaration[id.name=/ViewModel$/], TSTypeAliasDeclaration[id.name=/ViewModel$/], TSClassDeclaration[id.name=/ViewModel$/]" }, { "message": "DTO types are forbidden in website components. Use ViewModels instead.", "selector": "TSInterfaceDeclaration[id.name=/DTO$/], TSTypeAliasDeclaration[id.name=/DTO$/], TSClassDeclaration[id.name=/DTO$/]" } ] } }, { "files": [ "apps/api/**/*.test.ts", "apps/api/**/*.test.tsx" ], "rules": { "@typescript-eslint/no-explicit-any": "error", "no-restricted-syntax": "error" } }, { "files": [ "tests/**/*.ts" ], "rules": { "no-restricted-imports": [ "error", { "paths": [ { "message": "Integration tests must use in-memory adapters, not core directly", "name": "@core/*" }, { "message": "Integration tests must use in-memory adapters only", "name": "@adapters/*" } ] } ] } }, { "files": [ "tests/e2e/**/*.ts" ], "rules": { "no-restricted-imports": [ "error", { "patterns": [ { "group": [ "**/inmemory/**" ], "message": "E2E tests must use TypeORM/PostgreSQL, not in-memory adapters" } ] } ] } }, { "files": [ "core/**/*.ts" ], "rules": { "no-restricted-imports": [ "error", { "paths": [ { "message": "Use @testing/* from adapters/testing", "name": "testing" }, { "message": "Core layer should not depend on testing utilities", "name": "@testing/*" } ] } ] } }, { "files": [ "adapters/**/*.ts" ], "rules": { "no-restricted-imports": [ "error", { "paths": [ { "message": "Use @testing/* from adapters/testing", "name": "testing" } ] } ] } } ], "parserOptions": { "ecmaVersion": 2022, "sourceType": "module" }, "settings": { "boundaries/elements": [ { "pattern": "apps/website/**/*", "type": "website" }, { "pattern": "apps/api/**/*", "type": "api" }, { "pattern": [ "adapters/**/*", "@adapters/**/*" ], "type": "adapters" }, { "pattern": [ "core/**/*", "@core/**/*" ], "type": "core" } ], "import/resolver": { "typescript": {} } } }