Files
gridpilot.gg/.eslintrc.json
2026-01-03 16:51:40 +01:00

424 lines
13 KiB
JSON

{
"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"
}
]
}]
}
}
]
}