{ "$schema": "./node_modules/oxlint/configuration_schema.json", "plugins": ["import"], "categories": { "correctness": "error", "suspicious": "warn", "perf": "warn", // style/restriction off: opt-in, contradictory grab-bags. Wanted rules enabled individually below. "style": "off", "restriction": "off" }, "env": { "browser": true, "es2021": true }, "ignorePatterns": [ "node_modules", "dist", "build", ".svelte-kit", ".vercel", "*.config.js", "*.config.ts" ], "rules": { "no-console": "warn", "no-debugger": "error", "no-alert": "warn", // no-cycle resolves $-aliases via tsconfig auto-discovery (no resolver config in oxlint) "import/no-cycle": "error", "import/no-duplicates": "warn", "import/no-unassigned-import": "off", // CSS/side-effect imports are intentional "no-sequences": "error", "no-underscore-dangle": "off", "no-shadow": "warn", "no-implicit-coercion": "warn", "no-await-in-loop": "warn", "no-return-assign": "warn", "no-new": "warn", "no-unneeded-ternary": "warn" }, // FSD boundaries. oxlint has no zone rule, so layer/segment direction is enforced // with no-restricted-imports patterns scoped per glob. Layer order (high->low): // app(exempt top shell) > routes > widgets > features > entities > shared. // A layer bans imports from itself (cross-slice via alias) and every layer above. // Overrides are LAST-WINS, not merged: a file matching two overrides keeps only the // last rule config. So the domain override (below) is a self-contained superset, and // the test/story override (last) fully disables boundary checks for those files. "overrides": [ // shared = lowest layer: imports nothing above it { "files": ["src/shared/**"], "rules": { "no-restricted-imports": ["error", { "patterns": [ { "group": [ "$app", "$app/*", "$routes", "$routes/*", "$widgets", "$widgets/*", "$features", "$features/*", "$entities", "$entities/*" ], "message": "FSD layer violation: `shared` is the lowest layer and may not import from any layer above it." } ] }] } }, // entities: import shared only; no other entity via alias; interior ui<-only-ui { "files": ["src/entities/**"], "rules": { "no-restricted-imports": ["error", { "patterns": [ { "group": ["$app", "$app/*", "$routes", "$routes/*", "$widgets", "$widgets/*", "$features", "$features/*"], "message": "FSD layer violation: `entities` may only import from `shared`." }, { "group": ["$entities", "$entities/*"], "message": "FSD cross-slice violation: do not import another entity via its alias. Use relative imports inside your own slice; invert the dependency through a higher layer for cross-slice needs." }, { "group": ["../ui", "../ui/*", "../../ui/*"], "message": "FSD segment violation: only `ui` may import `ui`. Interior direction is ui -> model -> domain." } ] }] } }, // features: import entities/shared only; no other feature via alias { "files": ["src/features/**"], "rules": { "no-restricted-imports": ["error", { "patterns": [ { "group": ["$app", "$app/*", "$routes", "$routes/*", "$widgets", "$widgets/*"], "message": "FSD layer violation: `features` may only import from `entities` and `shared`." }, { "group": ["$features", "$features/*"], "message": "FSD cross-slice violation: do not import another feature via its alias. Invert the dependency through a higher layer (widget/route)." }, { "group": ["../ui", "../ui/*", "../../ui/*"], "message": "FSD segment violation: only `ui` may import `ui`. Interior direction is ui -> model -> domain." } ] }] } }, // widgets: import features/entities/shared only; no other widget via alias { "files": ["src/widgets/**"], "rules": { "no-restricted-imports": ["error", { "patterns": [ { "group": ["$app", "$app/*", "$routes", "$routes/*"], "message": "FSD layer violation: `widgets` may only import from `features`, `entities`, and `shared`." }, { "group": ["$widgets", "$widgets/*"], "message": "FSD cross-slice violation: do not import another widget via its alias. Invert the dependency through the route layer." }, { "group": ["../ui", "../ui/*", "../../ui/*"], "message": "FSD segment violation: only `ui` may import `ui`. Interior direction is ui -> model -> domain." } ] }] } }, // routes: top of the FSD list, imports any layer below; only app is above it { "files": ["src/routes/**"], "rules": { "no-restricted-imports": ["error", { "patterns": [ { "group": ["$app", "$app/*"], "message": "FSD layer violation: `routes` may not import from `app`." } ] }] } }, // domain (FSD+): pure logic. Imports NO layer (not even shared) and no sibling // model/ui segment. Superset: wins over the layer override above for these files. { "files": ["src/**/domain/**"], "rules": { "no-restricted-imports": ["error", { "patterns": [ { "group": [ "$app", "$app/*", "$routes", "$routes/*", "$widgets", "$widgets/*", "$features", "$features/*", "$entities", "$entities/*", "$shared", "$shared/*" ], "message": "FSD+ domain isolation: `domain` is pure business logic and may not import any layer (including `shared`). Allowed: relative imports within `domain` and framework-agnostic npm packages." }, { "group": ["../model", "../model/*", "../../model/*", "../ui", "../ui/*", "../../ui/*"], "message": "FSD+ domain isolation: `domain` may not import sibling `model` or `ui` segments. Dependency flows ui -> model -> domain, never back." } ] }] } }, // tests/stories/fixtures legitimately cross-import (e.g. $entities/Font/testing). // Must be LAST so last-wins disables boundary checks for them. { "files": ["**/*.test.ts", "**/*.spec.ts", "**/*.stories.svelte", "src/**/testing/**"], "rules": { "no-restricted-imports": "off" } } ] }