Lumen/eslint.config.js

214 lines
7.4 KiB
JavaScript

import js from "@eslint/js";
import tseslint from "typescript-eslint";
import boundaries from "eslint-plugin-boundaries";
import globals from "globals";
// eslint-disable-next-line @typescript-eslint/no-deprecated
export default tseslint.config(
{
ignores: [
"dist/**",
"node_modules/**",
"coverage/**",
"experiments/**",
"instance/**",
"scripts/serve-demo.mjs",
"public/sw.js",
"share/**",
],
},
js.configs.recommended,
...tseslint.configs.strictTypeChecked,
...tseslint.configs.stylisticTypeChecked,
{
languageOptions: {
parserOptions: {
projectService: {
allowDefaultProject: [
"eslint.config.js",
"vitest.config.ts",
"vite.config.ts",
"public/sw.js",
],
},
tsconfigRootDir: import.meta.dirname,
},
globals: {
...globals.browser,
...globals.node,
},
},
plugins: {
boundaries,
},
settings: {
"boundaries/elements": [
{ type: "platform", pattern: "src/platform/**" },
{ type: "storage", pattern: "src/storage/**" },
{ type: "data", pattern: "src/data/**" },
{ type: "sync-verifier", pattern: "src/sync/verifier/**" },
{ type: "sync-transport", pattern: "src/sync/transport/**" },
{ type: "sync", pattern: "src/sync/**" },
{ type: "domain", pattern: "src/domain/**" },
{ type: "ui", pattern: "src/ui/**" },
{ type: "app", pattern: "src/app/**" },
{ type: "emergency-baseline", pattern: "src/emergency-baseline/**" },
{ type: "tests", pattern: "tests/**" },
{ type: "scripts", pattern: "scripts/**" },
{ type: "pipeline", pattern: "pipeline/**" },
],
"boundaries/ignore": ["**/*.test.ts", "**/*.spec.ts"],
},
rules: {
// Architectural invariants — dependency rules B-1 … B-7
// B-1: ui never touches platform/storage/sync/transport/fetch/idb/caches
// Implemented via boundaries + no-restricted-globals where applicable
"boundaries/element-types": [
"error",
{
default: "disallow",
rules: [
// platform: low-level — may not import domain/ui/sync
{ from: "platform", allow: [] },
// storage: may only import platform
{ from: "storage", allow: ["platform"] },
// data: may import platform only
{ from: "data", allow: ["platform"] },
// sync-verifier: may import platform (hash) but not data/domain/ui/sync
{ from: "sync-verifier", allow: ["platform"] },
// sync-transport: byte pipe — may not import data/domain/ui
{ from: "sync-transport", allow: [] },
// sync: orchestration — may import data + sync sublayers + platform (via verifier)
{ from: "sync", allow: ["data", "sync-verifier", "sync-transport", "platform"] },
// domain: may import data + clock (domain/clock) — clock is domain, so allow domain internal
{ from: "domain", allow: ["data", "domain", "emergency-baseline"] },
// ui: may import domain + app helpers, but NOT platform/storage/sync
{ from: "ui", allow: ["domain", "emergency-baseline"] },
// app: composition root may import via public APIs — allow all for Stage 1, will tighten later
{
from: "app",
allow: [
"platform",
"storage",
"data",
"sync",
"sync-verifier",
"sync-transport",
"domain",
"ui",
"emergency-baseline",
],
},
// emergency-baseline: generated, no deps
{ from: "emergency-baseline", allow: [] },
// tests/scripts may import anything for validation
{
from: "tests",
allow: [
"platform",
"storage",
"data",
"sync",
"sync-verifier",
"sync-transport",
"domain",
"ui",
"app",
"emergency-baseline",
],
},
{
from: "scripts",
allow: [
"platform",
"storage",
"data",
"sync",
"sync-verifier",
"sync-transport",
"domain",
"ui",
"app",
"emergency-baseline",
],
},
{
from: "pipeline",
allow: ["platform", "storage", "data", "domain", "emergency-baseline", "pipeline"],
},
],
},
],
// Explicit forbidden globals in ui (B-1 core)
"no-restricted-globals": [
"error",
{
name: "indexedDB",
message: "B-1: ui must not touch indexedDB — use data/ read APIs only.",
},
{
name: "caches",
message: "B-1: ui must not touch caches — use platform/cache only via data/sync.",
},
],
// General strictness
"no-console": ["warn", { allow: ["warn", "error"] }],
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unnecessary-condition": "warn",
},
},
{
// B-7: no innerHTML with festival content — forbid assignment to innerHTML in src
files: ["src/**/*.ts"],
rules: {
"no-restricted-syntax": [
"error",
{
selector: "AssignmentExpression[left.property.name='innerHTML']",
message:
"B-7: Do not render festival content via innerHTML of raw strings — use ui/render safe DOM (C-22).",
},
{
selector: "CallExpression[callee.property.name='insertAdjacentHTML']",
message:
"B-7: Do not use insertAdjacentHTML with festival content — use ui/render safe DOM (C-22).",
},
],
},
},
{
// platform/idb and cache/sw are the ONLY places allowed to touch indexedDB/caches (B-1 — ui forbidden)
files: ["src/platform/**/*.ts"],
rules: {
"no-restricted-globals": "off",
},
},
{
// pipeline is build-time Node code — relax some strict app rules
files: ["pipeline/**/*.ts"],
rules: {
"no-restricted-globals": "off",
"no-restricted-syntax": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/no-unnecessary-type-assertion": "off",
"@typescript-eslint/no-unnecessary-condition": "off",
"@typescript-eslint/no-empty-object-type": "off",
"@typescript-eslint/consistent-type-imports": "off",
"@typescript-eslint/no-require-imports": "off",
"@typescript-eslint/array-type": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-unnecessary-type-conversion": "off",
"prefer-const": "off",
},
},
{
// Allow explicit forbidden-globals only where justified; tests may use them
files: ["tests/**/*.ts", "scripts/**/*.ts"],
rules: {
"no-restricted-globals": "off",
"no-restricted-syntax": "off",
},
},
);