chore(lint): migrate to Biome (ultracite) + lefthook#65
Merged
Conversation
…thook
Replace the ESLint/airbnb + Prettier + (dead) Husky stack with Biome via
the Ultracite preset, and add lefthook for git hooks.
Tooling:
- biome.jsonc extends ultracite/biome/{core,vitest}; formatter matched to
the old Prettier style (single quotes, trailing-comma all, arrow parens
as-needed) so there is no quote/spacing churn.
- Scripts: `lint` -> `biome check ./src`, add `lint:fix`, `format` ->
`biome format --write ./src`, `prepare` -> `lefthook install`.
- lefthook.yml: pre-commit runs `biome check --write` on staged files
with stage_fixed. Replaces the never-wired husky + lint-staged setup.
- Removed: eslint, @eslint/*, @typescript-eslint/*, eslint-config-airbnb-base,
eslint-config-prettier, eslint-plugin-import, eslint-import-resolver-typescript,
eslint-plugin-prettier, prettier, husky, lint-staged, globals; deleted
eslint.config.js, .prettierrc, .prettierignore. Dropped the now-unused
unrs-resolver from allowBuilds.
Code improvements applied (all behavior-preserving, 110 tests still green):
- Number namespace (parseInt -> Number.parseInt, etc.), numeric separators,
explicit block statements.
- forEach -> for...of in validators and tests.
- Hoisted regex literals to module scope (useTopLevelRegex).
- Explicit type on an evolving `let` (am.ts).
Rules turned off with justification (see biome.jsonc): noSubstr (substring
is intentional and not deprecated), useFilenamingConvention (file names are
the public API), noBarrelFile (index barrels are the entry points),
useAtIndex (.at() is es2022; tsconfig targets es2016), and namespace/dynamic
access for the ie/index.ts validator registry.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review follow-ups: - Remove @types/jest: the suite uses Vitest with explicit imports (no jest globals/types referenced anywhere), so the stubs were dead. - Make the lefthook pre-commit glob explicit (**/*) for readability; behaviour is unchanged (lefthook already matches at any depth). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the ESLint (airbnb) + Prettier + (dead) Husky stack with Biome via the Ultracite preset, and adds lefthook for git hooks. Not a 1:1 port — adopts Ultracite's stricter ruleset and applies its safe improvements.
Tooling
biome.jsoncextendsultracite/biome/core+ultracite/biome/vitest. Formatter is matched to the old Prettier config (single quotes, trailing-commaall, arrow parens as-needed) → no quote/spacing churn.lint→biome check ./src, newlint:fix,format→biome format --write ./src,prepare→lefthook install.lefthook.yml: pre-commit runsbiome check --writeon staged files withstage_fixed: true— verified it auto-formats and re-stages. Replaces the never-wired husky + lint-staged.@eslint/*,@typescript-eslint/*, airbnb-base, eslint-config-prettier, eslint-plugin-import, eslint-import-resolver-typescript, eslint-plugin-prettier, prettier, husky, lint-staged, globals. Deletedeslint.config.js,.prettierrc,.prettierignore. Dropped the now-unusedunrs-resolverfromallowBuilds.Code improvements (behavior-preserving — 110 tests green)
parseInt→Number.parseInt, identical fn), numeric separators, explicit block statements.forEach→for...ofin validators and tests.let.Rules disabled (with inline justification in
biome.jsonc)noSubstrString.substring(not deprecatedsubstr);slicewould change edge-case semantics in checksum logicuseFilenamingConventionvalidateCPF.ts) are the public import APInoBarrelFileindex.tsbarrels are the package entry pointsuseAtIndex.at()is es2022; tsconfig targets es2016noNamespaceImport/noDynamicNamespaceImportAccess(ie/index.ts only)Verification (Node 22)
✅
pnpm build· ✅pnpm coverage(110 tests) · ✅pnpm lint(Biome) · ✅tsc --noEmit· ✅pnpm test:e2e· ✅ lefthook pre-commit hook tested live🤖 Generated with Claude Code