Three gaps in the frontend tooling, all in the package.json script surface.
1. No JS/TS linter or formatter existed at all for 274 TS/Svelte files.
Adds an ESLint flat config (typescript-eslint + eslint-plugin-svelte,
Svelte 5 + TS strict) and prettier + prettier-plugin-svelte, plus the
`lint`, `lint:fix`, `format`, `format:check` scripts.
The tree is error-clean (`npx eslint .` exits 0). Getting there needed
seven real one-line fixes (braced switch cases that leaked `const` across
arms, a useless regex escape, two `let`s that never change, a thrown Error
that dropped its `cause`, and two `// eslint-disable-next-line` comments
documenting the Svelte 5 bare-read-for-dependency idiom). Everything else
that fires is set to `warn` with the reason written next to it in
eslint.config.js — notably ~94 dead bindings and `any` at the IPC
boundary. Those are real findings to drive to zero, not noise to delete.
`no-console` is OFF for now: a parallel change is moving all ~468 console
calls onto a logger facade, and turning the rule on today would collide
with it. eslint.config.js says so, and says to flip it to `error` once
that lands.
`prettier --write` is deliberately NOT run here — it would rewrite ~200
files and swamp every other diff in flight. The gate is available; the
sweep is a separate commit. Markdown and CI YAML are in .prettierignore
because both are hand-laid-out (and docs/traceability.md is generated).
2. `bun run test` was bare `vitest`, i.e. watch mode — while CLAUDE.md's
"Before Committing" list tells people to run it. It is now `vitest run`,
with `test:watch` and `test:coverage` (also `--run`-ified) alongside.
scripts/test-all.sh drops the now-redundant `--run`, and
scripts/test-frontend.sh keeps `--watch`/`--ui`/`-w` working by routing
them to a long-running vitest instead of the single-pass one.
3. The webdriverio e2e suite is deleted. It was last touched in January
("First working POC"), has never run since, and is not in CI — five
devDependencies and two scripts of pure decoration. Removes e2e/,
wdio.conf.ts, the two `test:e2e*` scripts, the @wdio/* + webdriverio
devDeps, and the WebdriverIO block in .gitignore.
The package.json diff also carries `hooks:install` and `check:links`, wired
up by the following commits.
172 lines
6.4 KiB
JavaScript
172 lines
6.4 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/",
|
|
"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",
|
|
},
|
|
},
|
|
);
|