.claude/worktrees holds full checkouts of this repo, generated .svelte-kit trees included, so 'eslint .' was linting every in-flight branch — 410 errors, none of them ours. Same root cause the doc-link checker hit.
176 lines
6.7 KiB
JavaScript
176 lines
6.7 KiB
JavaScript
// ESLint flat config for the JellyTau frontend (Svelte 5 + TypeScript strict).
|
|
//
|
|
// TRACES: | DR-205
|
|
//
|
|
// Scope: `src/` (the presentation layer), `scripts/` (build tooling), and the
|
|
// root config files. The Rust backend is linted by clippy, not by this config.
|
|
//
|
|
// Formatting is NOT ESLint's job here — `eslint-config-prettier` is applied last
|
|
// and switches off every stylistic rule that would fight `prettier`. Run
|
|
// `bun run format` / `bun run format:check` for layout.
|
|
import js from "@eslint/js";
|
|
import ts from "typescript-eslint";
|
|
import svelte from "eslint-plugin-svelte";
|
|
import globals from "globals";
|
|
import prettier from "eslint-config-prettier";
|
|
import svelteConfig from "./svelte.config.js";
|
|
|
|
export default ts.config(
|
|
{
|
|
// Kept in one place so `npx eslint .` and editor integrations agree.
|
|
ignores: [
|
|
"node_modules/",
|
|
".svelte-kit/",
|
|
// Scratch worktrees (git-ignored) hold full checkouts of this repo,
|
|
// including their own generated .svelte-kit trees. Without this, `eslint .`
|
|
// lints every in-flight branch and reports its generated code as ours.
|
|
".claude/",
|
|
"build/",
|
|
"dist/",
|
|
"coverage/",
|
|
"package/",
|
|
"src-tauri/",
|
|
// Generated by tauri-specta on every Rust build — never hand-edited, and
|
|
// its shape is dictated by the Rust command definitions.
|
|
"src/lib/api/bindings.ts",
|
|
],
|
|
},
|
|
|
|
js.configs.recommended,
|
|
...ts.configs.recommended,
|
|
...svelte.configs.recommended,
|
|
prettier,
|
|
...svelte.configs.prettier,
|
|
|
|
{
|
|
languageOptions: {
|
|
globals: {
|
|
...globals.browser,
|
|
...globals.es2021,
|
|
},
|
|
},
|
|
rules: {
|
|
// 🔴 TEMPORARILY OFF. A parallel migration is moving all ~468 `console.*`
|
|
// calls in `src/` onto a logger facade. Turning this on before that lands
|
|
// would paint the tree red and collide with that work.
|
|
//
|
|
// 👉 Switch this to "error" (allowing nothing, or at most
|
|
// `{ allow: ["warn", "error"] }`) once the logger-facade migration is
|
|
// merged — that is the whole point of the rule being listed here.
|
|
"no-console": "off",
|
|
|
|
// Unused values are a real signal, but `_`-prefixed args are the
|
|
// established way to say "this parameter exists for the signature".
|
|
//
|
|
// ⚠️ warn, not error: the tree carries ~94 genuinely dead bindings (stale
|
|
// imports, `$state` left over from refactors, unused `catch (e)`). Every
|
|
// one is a real finding, but fixing them here would mean ~50 unrelated
|
|
// files in this tooling commit. Clear the backlog, then promote to
|
|
// "error".
|
|
"@typescript-eslint/no-unused-vars": [
|
|
"warn",
|
|
{
|
|
argsIgnorePattern: "^_",
|
|
varsIgnorePattern: "^_",
|
|
caughtErrorsIgnorePattern: "^_",
|
|
destructuredArrayIgnorePattern: "^_",
|
|
},
|
|
],
|
|
|
|
// Warn-only rules: each flags something real, but the existing tree has
|
|
// more instances than can be fixed without swamping unrelated diffs.
|
|
// Drive these to zero and promote them to "error" — do not delete them.
|
|
//
|
|
// `any` at the Tauri IPC boundary, mostly in code predating the
|
|
// tauri-specta bindings (~25 sites outside tests).
|
|
"@typescript-eslint/no-explicit-any": "warn",
|
|
// Empty catch/if bodies that swallow an error.
|
|
"no-empty": ["warn", { allowEmptyCatch: true }],
|
|
|
|
// Prefer `import type` so type-only imports are erased cleanly by the
|
|
// bundler instead of pulling a module in at run time.
|
|
"@typescript-eslint/consistent-type-imports": "off",
|
|
|
|
// Not applicable to this app (~130 hits, all no-ops). SvelteKit's
|
|
// `resolve()` exists so hrefs keep working under a non-empty
|
|
// `kit.paths.base`; JellyTau is an adapter-static SPA served from the
|
|
// Tauri webview root and svelte.config.js sets no `base`. Re-enable this
|
|
// the day a base path is introduced — the rule is otherwise correct.
|
|
// (Declared here, not in the *.svelte block: `goto()` is also called from
|
|
// plain .ts modules such as src/lib/utils/navigation.ts.)
|
|
"svelte/no-navigation-without-resolve": "off",
|
|
},
|
|
},
|
|
|
|
{
|
|
// Svelte components: the parser needs the project's svelte.config.js so it
|
|
// resolves preprocessors and Svelte 5 runes the same way the build does.
|
|
files: ["**/*.svelte", "**/*.svelte.ts", "**/*.svelte.js"],
|
|
languageOptions: {
|
|
parserOptions: {
|
|
parser: ts.parser,
|
|
svelteConfig,
|
|
},
|
|
},
|
|
rules: {
|
|
// Warn-only — real findings, but each fix is a behavioural refactor that
|
|
// does not belong in a tooling commit:
|
|
// require-each-key keyed {#each} changes DOM reuse semantics
|
|
// prefer-svelte-reactivity Set/Map -> SvelteSet/SvelteMap changes
|
|
// reactivity, not just syntax
|
|
// prefer-writable-derived $state + $effect -> writable $derived
|
|
// no-at-html-tags {@html} sites need an XSS review each
|
|
"svelte/require-each-key": "warn",
|
|
"svelte/prefer-svelte-reactivity": "warn",
|
|
"svelte/prefer-writable-derived": "warn",
|
|
"svelte/no-at-html-tags": "warn",
|
|
|
|
// Warn-only: this rule cannot see the Svelte *compiler's* warning set, so
|
|
// it reports `<!-- svelte-ignore a11y_… -->` as unused when the compiler
|
|
// may still be emitting the warning it suppresses. Verify against a real
|
|
// `bun run check` before deleting any of them.
|
|
"svelte/no-unused-svelte-ignore": "warn",
|
|
},
|
|
},
|
|
|
|
{
|
|
// Node-side tooling: build/test scripts and root config files run under
|
|
// Bun/Node, not in the webview.
|
|
files: [
|
|
"scripts/**/*.{ts,js}",
|
|
"*.config.{ts,js}",
|
|
"*.config.*.{ts,js}",
|
|
"svelte.config.js",
|
|
"eslint.config.js",
|
|
],
|
|
languageOptions: {
|
|
globals: {
|
|
...globals.node,
|
|
},
|
|
},
|
|
},
|
|
|
|
{
|
|
// Test files: vitest globals are enabled in vitest.config.ts.
|
|
files: ["**/*.{test,spec}.{ts,js}", "src/test/**/*.{ts,js}"],
|
|
languageOptions: {
|
|
globals: {
|
|
...globals.node,
|
|
...globals.vitest,
|
|
},
|
|
},
|
|
rules: {
|
|
// Test doubles legitimately use `any` for partial mocks.
|
|
"@typescript-eslint/no-explicit-any": "off",
|
|
// `vi.mock` factories are hoisted above the import graph, so a lazy
|
|
// `require()` inside one is the documented escape hatch.
|
|
"@typescript-eslint/no-require-imports": "off",
|
|
// Several tests deliberately replay a production assignment sequence
|
|
// (`currentStreamUrl = newStreamUrl; hasSeeked = false;`) to document the
|
|
// `$effect` they stand in for. The "useless" write is the subject under
|
|
// test, not dead code.
|
|
"no-useless-assignment": "off",
|
|
},
|
|
},
|
|
);
|