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