Skip to content

fix(client): restore runnable lint after TS 6 + ESLint 10 bumps#563

Merged
Detair merged 1 commit into
mainfrom
fix/lint-chain
May 12, 2026
Merged

fix(client): restore runnable lint after TS 6 + ESLint 10 bumps#563
Detair merged 1 commit into
mainfrom
fix/lint-chain

Conversation

@Detair

@Detair Detair commented May 12, 2026

Copy link
Copy Markdown
Owner

Summary

bun run lint has been silently broken on main since PR #555 (TypeScript 6 bump). Three distinct bugs in the chain — fixing all three so lint becomes useful again. Build and tests unaffected (581/581 passing).

What was broken

  1. eslint.config.mjs subpath importeslint-plugin-solid/configs/typescript.js failed Node's stricter ESM resolution because the package's exports map only exposes ./configs/typescript (no .js suffix). Removed the suffix.

  2. brace-expansion override union^1.1.13 || ^5.0.6 was deliberately a union (per dep-update spec note: "do not drop the 1.x bound"), but the only consumer in the tree is minimatch ^5.0.2 — the 1.x bound was vestigial. Bun resolved to 1.x, which doesn't expose the named { expand } export minimatch's compiled CJS expects, crashing @eslint/config-array with (0, brace_expansion_1.expand) is not a function. Tightened to ^5.0.6 only.

  3. no-unassigned-vars rule (ESLint 10 recommended) fires false positives on SolidJS's ref pattern: let myRef: T | undefined; <div ref={myRef}>. The JSX ref={} binding form assigns at render time, which ESLint's static analyzer can't trace. Disabled — accounts for ~40 of the 62 errors that surfaced once lint became runnable.

After this PR

bun run lint → 29 errors + 233 warnings (all real code findings)

Open follow-ups (real lint findings, deliberately not bundled here)

Count Rule Severity
14 preserve-caught-error Important — missing cause on re-thrown errors
8 solid/prefer-for Important — Array#map should be <For> (perf)
3 solid/no-innerhtml Critical — potential XSS, security review needed
2 no-case-declarations Minor
1 no-useless-assignment Minor
1 no-control-regex Likely intentional
233 (various warnings) Mostly solid/style-prop, solid/reactivity, @typescript-eslint/no-explicit-any

Test plan

  • bun run lint runs without crashing
  • bun run build clean
  • bun run test:run — 581/581 passing
  • CI Frontend passes
  • Follow-up PR opens for the 3 solid/no-innerhtml errors (security)

🤖 Generated with Claude Code

`bun run lint` has been silently broken on main since PR #555
(TypeScript 6 bump). Three distinct bugs in the chain — fixing all
three here so lint becomes useful again. Build and tests unaffected
(581/581 passing).

1. eslint.config.mjs — eslint-plugin-solid subpath
   `eslint-plugin-solid/configs/typescript.js` failed Node's stricter
   ESM resolution because the package's exports map only exposes
   `./configs/typescript` (no `.js` suffix). Removed the suffix.

2. brace-expansion override
   The override `^1.1.13 || ^5.0.6` was deliberately a union (per the
   dep-update spec, "do not drop the 1.x bound"). But the only
   consumer in the tree is `minimatch ^5.0.2` — the 1.x bound was
   vestigial. The union let bun resolve to 1.x, which doesn't expose
   the named `{ expand }` export minimatch's compiled CJS expects,
   crashing `@eslint/config-array` with
   `(0, brace_expansion_1.expand) is not a function`.
   Tightened to `^5.0.6` only.

3. no-unassigned-vars rule (ESLint 10 recommended)
   This rule was added to `@eslint/js` recommended in ESLint 10
   (Phase 6a) and fires false positives on SolidJS's ref pattern:
   `let myRef: T | undefined; <div ref={myRef}>`. The JSX ref={}
   binding form assigns the variable at render time, which ESLint's
   static analysis can't trace. Disabled — accounts for ~40 of the
   62 errors that were appearing once lint became runnable.

After this PR:
  bun run lint → 29 errors + 233 warnings, all real code findings.

Remaining errors are deliberately left for separate per-area PRs
(they touch specific files / call sites):

  14× preserve-caught-error  — missing `cause` on re-thrown errors
   8× solid/prefer-for       — Array#map should be <For>
   3× solid/no-innerhtml     — potential XSS, security review needed
   2× no-case-declarations
   1× no-useless-assignment
   1× no-control-regex

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Detair Detair merged commit 0f87c93 into main May 12, 2026
18 checks passed
Detair added a commit that referenced this pull request May 12, 2026
…tml (#564)

Three JSX innerHTML usages that the solid/no-innerhtml rule flags as
potential XSS. After tracing the data flow at each site, all three
are safely sanitized — adding eslint-disable-next-line directives
with per-site justification pointing at the sanitization call, so
the rule stays active for the rest of the codebase to catch new
unsafe innerHTML while documenting the existing safety inline.

1. MarkdownPreview.tsx:387
   html() signal is only ever set via
     DOMPurify.sanitize(rawHtml, MARKDOWN_PURIFY_CONFIG)
   in the setHtml(...) call above (strict allowlist, no scripts).

2. MessageItem.tsx:740
   TextBlock.html is produced by sanitizeHtml() (defined at file
   top as DOMPurify.sanitize(html, PURIFY_CONFIG)) on every path
   in the contentBlocks createMemo. The fallback path also goes
   through DOMPurify with ALLOWED_TAGS: [], ALLOWED_ATTR: [].

3. SearchPanel.tsx:535
   Wrapped in sanitizeHeadline() defined immediately above, which
   runs DOMPurify with ALLOWED_TAGS: ["mark"] only — exactly the
   tags ts_headline emits from PostgreSQL full-text-search.

No behavioral change. Build clean, 581/581 tests pass.

Depends conceptually on #563 (which makes `bun run lint` actually
run again so these directives are exercised) — but does not depend
on it merging first since the directive syntax is valid regardless.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant