Skip to content

build: adopt oxlint and oxfmt#20

Merged
i11v merged 4 commits into
mainfrom
claude/admiring-hypatia-gd7r2f
Jun 21, 2026
Merged

build: adopt oxlint and oxfmt#20
i11v merged 4 commits into
mainfrom
claude/admiring-hypatia-gd7r2f

Conversation

@i11v

@i11v i11v commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Why

Following the Vite+ assessment, we opted for the lighter, lower-risk path: adopt the two tools Vite+ would have wrapped — oxlint (linter) and oxfmt (formatter) — directly as dev dependencies, leaving the existing Vite 8 / Vitest 4 / Alchemy / Bun stack completely untouched. No vite/vitest aliasing, no overrides.

What

Staged so each tool lands as tooling first, then the resulting code change — four commits:

  1. build(lint): add oxlint with baseline configoxlint dev dep + .oxlintrc.json (typescript/unicorn/oxc/react/vitest plugins, correctness as errors, generated route tree & build output ignored) + lint script.
  2. fix: resolve oxlint correctness violations — fixes the 14 findings:
    • App.tsx: hoist the empty stop list to a stable module constant so the byNode/locationLabel memos don't recompute every render while the index loads (react-hooks/exhaustive-deps).
    • contract tests: assert the specific Schema validation each decode fails on instead of a bare toThrow() (vitest/require-to-throw-message).
    • integration test: narrow the decoded message with a guard and assert the degraded-or-key invariant unconditionally (vitest/no-conditional-expect).
  3. build(fmt): add oxfmt scoped to TS/TSXoxfmt dev dep + .oxfmtrc.json (matches house style: no semicolons, double quotes, printWidth: 100 — the width that reflows current source the least) + fmt/fmt:check scripts. Scoped to TS/TSX via ignorePatterns so it leaves package.json (managed by bun), JSON/YAML config, and Markdown docs alone.
  4. style: format TS/TSX with oxfmt — applies oxfmt. Pure reflow: no semicolons added, quote style unchanged (38 files, +393/−203).

Verification

bun run lint, bun run typecheck, and bun run test (79 tests) all pass on the final tree.

Not included (your call)

  • CI enforcementlint and fmt:check are not wired into deploy.yml / pr-preview.yml yet, since that gates your pipeline. Happy to add it as a follow-up.
  • oxfmt currently skips Markdown/JSON/YAML by design; can be widened later if you want docs/config formatted too.

🤖 Generated with Claude Code

https://claude.ai/code/session_014DWovxbG4A7Ebda7xhG9nA


Generated by Claude Code

claude added 4 commits June 20, 2026 21:46
Adopt oxlint as the linter (dev dependency). Enables the typescript,
unicorn, oxc, react, and vitest plugins with the `correctness` category
as errors, and ignores the generated route tree and build output. Adds a
`lint` npm script. Existing violations are fixed in the following commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DWovxbG4A7Ebda7xhG9nA
- App.tsx: hoist the empty stop list to a stable module constant so the
  byNode/locationLabel memos don't recompute every render while the index
  is loading (react-hooks/exhaustive-deps).
- contract tests: assert the specific Schema validation each decode fails
  on instead of a bare toThrow() (vitest/require-to-throw-message).
- stack integration test: narrow the decoded message with a guard and
  assert the degraded-or-key invariant unconditionally
  (vitest/no-conditional-expect).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DWovxbG4A7Ebda7xhG9nA
Adopt oxfmt as the formatter (dev dependency). Config matches the
existing house style (no semicolons, double quotes) with printWidth 100,
which is the width that reflows the current source the least. Scoped to
TS/TSX via ignorePatterns so it leaves package.json (managed by bun),
JSON/YAML config, and Markdown docs untouched. Adds `fmt` and `fmt:check`
npm scripts. The codebase is reformatted in the following commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DWovxbG4A7Ebda7xhG9nA
Apply oxfmt across the TS/TSX sources. Pure whitespace/line-reflow to
printWidth 100; no semicolons added and quote style is unchanged. Lint,
typecheck, and the full test suite (79 tests) remain green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DWovxbG4A7Ebda7xhG9nA
@github-actions

Copy link
Copy Markdown

🚀 Preview deployed: https://preview-20.tablo.run
fallback: https://tablo-pr-20.i11v.workers.dev

@i11v i11v marked this pull request as ready for review June 21, 2026 08:52
@i11v i11v merged commit f89da7e into main Jun 21, 2026
2 checks passed
i11v added a commit that referenced this pull request Jun 21, 2026
Follow-up to #20: actually *enforce* the linter and formatter, in CI and
locally.

## What

**1. CI gates** (`ci: enforce lint and formatting checks`)
- Adds `bun run lint` (oxlint) and `bun run fmt:check` (oxfmt) right
after `bun install` in **both** workflows — `deploy.yml` (production)
and `pr-preview.yml` (PRs).
- Placed before `typecheck`/`test`/`build` so the cheap static checks
fail fast.

**2. Pre-commit hook via Lefthook** (`build: add pre-commit hook via
lefthook`)
- Uses **[Lefthook](https://lefthook.dev)** (dev dependency) to manage
git hooks — the modern, dependency-light husky alternative (single Go
binary, declarative config, no Node at hook time).
- `lefthook.yml` declares a `pre-commit` hook that runs `oxlint` and
`oxfmt --check` **in parallel**, mirroring the CI static checks. Bypass
a run with `git commit --no-verify`.
- Hooks install automatically via `lefthook install`, run from the
existing `prepare` script on `bun install` (guarded with `|| true` so a
non-git install context can't break `prepare`).

## Verification

- Lefthook hook verified end-to-end: runs lint+format in parallel,
**passes** on a clean tree and **blocks** an unformatted commit (exit
1). It ran live on both commits in this PR.
- `lint`, `fmt:check`, `typecheck`, `test` (79), `build:web`,
`verify:pwa` all green; `bun install --frozen-lockfile` in sync.

## Note

After pulling this, contributors get the hook automatically on their
next `bun install` (via `prepare`) — no manual setup. To enable
immediately without reinstalling: `bunx lefthook install`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_014DWovxbG4A7Ebda7xhG9nA

---------

Co-authored-by: Claude <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.

2 participants