Uh oh!
There was an error while loading. Please reload this page.
chore: migrate JS/TS tooling to oxlint and oxfmt - #381
Merged
Conversation
The dashboard was formatted by two tools that disagreed. Biome owned `dashboard/` via `yarn lint`, while the pre-commit prettier hook matched `types_or: [javascript, vue, scss]` with no exclude for `dashboard/`, so it also reformatted every Vue file on commit. The result was `.ts` files without semicolons and `.vue` files with them, and 224 `biome check` errors that were just the two tools undoing each other. Biome had no CI job, so nothing caught it. oxlint and oxfmt now cover the whole repo — dashboard, desk scripts and e2e — with one config each, so `yarn lint`, pre-commit and CI all enforce the same thing. `linter.yml` already runs pre-commit, so no new workflow is needed. Notes on the configs: - Both pre-commit mirrors default to `types_or: [javascript, jsx, ts, tsx]`, which silently skips every `.vue` file. Both hooks override it. - `.oxfmtrc.json` sets `useTabs` explicitly because `.editorconfig` covers `*.js,*.vue,*.css,*.scss,*.html` but not `*.ts`. - oxfmt also formats JSON, Markdown, YAML, TOML, HTML and CSS. DocType schemas have Frappe-specific formatting, so `ignorePatterns` restricts oxfmt to JS/TS/Vue, matching the hook's `types_or`. - `eslint:recommended` is not a subset of oxlint's correctness and suspicious categories. The six rules that fall outside them are listed explicitly; all had zero violations. `no-console` is scoped to `buzz/**/*.js`, the only place the old `types_or: [javascript]` hook ever applied it. - The ~90-entry `globals` block in `.eslintrc` existed only to satisfy `no-undef`, which oxlint ships as a nursery rule. It is dropped, not ported. - `unicorn/no-empty-file` is off for `buzz/**/*.js`; those are Frappe's generated doctype stubs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three of these were real bugs rather than style:
- `RegisterInterest.vue` declared a `campaign` prop and a local `campaign` ref
for the fetched document. In `<script setup>` the local binding shadows the
prop, so the template worked by accident. The ref is now `campaignDoc`.
- `FormFieldSections.vue` declared `sections` inside the `sections` computed,
shadowing it.
- `global.d.ts` nested ambient module declarations inside `declare global`,
which is not legal. Dropping `export {}` makes the file script-scope, so the
`Window` interface and the wildcard modules both work at top level. vue-tsc
never surfaced this.
The rest are mechanical: unused catch bindings, unused parameters and imports,
and redundant regex escapes.
Two suppressions rather than fixes. `unicorn/no-array-sort` wants `toSorted()`,
but Vite does not polyfill built-in methods, and both call sites sort an array
they just created, so the mutation is not observable. `CustomFieldInput`'s
`defineModel` is typed `any` because Frappe field values are heterogeneous and
the template binds the model straight into components that expect a string;
typing it honestly is a refactor, not a lint fix.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Pure `oxfmt` output, no hand edits. Settles the two-formatter split: `.ts` files keep the semicolon-free style Biome used, and `.vue` files lose the semicolons prettier had been adding on every commit. Two visible differences from before. `sortImports` replaces Biome's `organizeImports` and uses a different ordering, so import blocks reshuffle. And oxfmt formats `<template>` and `<style>` blocks, which Biome never touched — it only handled `<script>` — so Vue markup reflows to the 100 column width. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
Too many files changed for review (190 files, 100 file limit). Bypass the limit by tagging |
This was referenced Aug 27, 2026
Uh oh!
There was an error while loading. Please reload this page.
Contributor
Backport failed for Please cherry-pick the changes locally and resolve any conflicts. git fetch origin main
git worktree add -d .worktree/backport-381-to-main origin/main
cd .worktree/backport-381-to-main
git switch --create backport-381-to-main
git cherry-pick -x 8fccc2480f6a3287522ec5b3df0ad4ffe73d1fd3 |
harshtandiya added a commit
that referenced
this pull request
Aug 27, 2026
* chore: replace eslint, prettier and biome with oxlint and oxfmt Backport of #381 to main. The upstream squash commit could not be cherry-picked — main is 57 commits behind develop, and a whole-repo reformat conflicts against every line that has moved since, 68 files in total. Roughly 50 of those files do not exist on main at all. So the reformat is regenerated rather than ported: this commit carries only the tooling config, and the formatter is re-run against main's own tree in a following commit. The configs themselves are byte-identical to develop's. The problem being fixed is the same one #381 describes. Biome owned `dashboard/` via `yarn lint`, while the pre-commit prettier hook matched `types_or: [javascript, vue, scss]` with no exclude for `dashboard/`, so it reformatted every Vue file on commit — `.ts` without semicolons, `.vue` with them, and nothing in CI to catch it. Notes carried over from #381: - Both pre-commit mirrors default to `types_or: [javascript, jsx, ts, tsx]`, which silently skips every `.vue` file. Both hooks override it. - `.oxfmtrc.json` sets `useTabs` explicitly because `.editorconfig` covers `*.js,*.vue,*.css,*.scss,*.html` but not `*.ts`. - oxfmt also formats JSON, Markdown, YAML, TOML, HTML and CSS. DocType schemas have Frappe-specific formatting, so `ignorePatterns` restricts oxfmt to JS/TS/Vue, matching the hook's `types_or`. - `eslint:recommended` is not a subset of oxlint's correctness and suspicious categories. The six rules that fall outside them are listed explicitly. `no-console` is scoped to `buzz/**/*.js`, the only place the old `types_or: [javascript]` hook ever applied it. - The ~90-entry `globals` block in `.eslintrc` existed only to satisfy `no-undef`, which oxlint ships as a nursery rule. Dropped, not ported. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: resolve oxlint findings across dashboard, desk scripts and e2e Backport of #381's fix commit. Applied to main's own files rather than cherry-picked, since three of them have diverged. The findings turned out to be identical to develop's minus the files main does not have yet. Three were real bugs rather than style: - `RegisterInterest.vue` declared a `campaign` prop and a local `campaign` ref for the fetched document. In `<script setup>` the local binding shadows the prop, so the template worked by accident. The ref is now `campaignDoc`. - `FormFieldSections.vue` declared `sections` inside the `sections` computed, shadowing it. - `global.d.ts` nested ambient module declarations inside `declare global`, which is not legal. Dropping `export {}` makes the file script-scope, so the `Window` interface and the wildcard modules both work at top level. vue-tsc never surfaced this. The rest are mechanical: unused catch bindings, unused parameters and imports, and redundant regex escapes. Two suppressions rather than fixes. `unicorn/no-array-sort` wants `toSorted()`, but Vite does not polyfill built-in methods, and the call site sorts an array it just created, so the mutation is not observable. `CustomFieldInput`'s `defineModel` is typed `any` because Frappe field values are heterogeneous and the template binds the model straight into components that expect a string. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * style: reformat with oxfmt Pure `oxfmt` output, no hand edits. Regenerated against main rather than ported from #381, so this is main's own reformat — 114 files here against 164 there, the difference being files develop has and main does not. Two visible differences from before. `sortImports` replaces Biome's `organizeImports` and uses a different ordering, so import blocks reshuffle. And oxfmt formats `<template>` and `<style>` blocks, which Biome never touched — it only handled `<script>` — so Vue markup reflows to the 100 column width. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <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 freeto 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.
What changed
Four JS/TS tools become two. oxlint and oxfmt replace ESLint, Prettier and Biome across the dashboard, the desk scripts and
e2e/.The dashboard had two formatters fighting. Biome owned
dashboard/viayarn lint; the pre-commit prettier hook matchedtypes_or: [javascript, vue, scss]with no exclude fordashboard/, so it reformatted every Vue file on commit. Hence.tswithout semicolons,.vuewith them, and 224biome checkerrors that were only the two tools undoing each other. Biome had no CI job, so nothing caught it.linter.ymlalready runspre-commit/action, so the new hooks are enforced in CI with no new workflow.Gotchas worth knowing before you touch this config:
types_or: [javascript, jsx, ts, tsx]— novue. Left alone they silently skip the entire dashboard. Both hooks override it.oxfmt .reformatted 66 DocType schemas, four workflows andpyproject.tomlon the first run.ignorePatternsnow restricts it to JS/TS/Vue, matching the hook..oxfmtrc.jsonsetsuseTabsexplicitly:.editorconfigcovers*.js,*.vue,*.css,*.scss,*.htmlbut not*.ts.eslint:recommendedis not a subset of oxlint'scorrectness+suspicious. The six rules outside them are listed explicitly (all had zero violations).no-consoleis scoped tobuzz/**/*.js, the only place the oldtypes_or: [javascript]hook ever applied it.globalsblock in.eslintrcexisted only forno-undef, which oxlint ships as nursery. Dropped, not ported.Three real bugs surfaced: a prop shadowed by a same-named ref in
RegisterInterest.vue(the template worked by accident), asectionsshadow inside its own computed inFormFieldSections.vue, and ambient modules illegally nested indeclare globalinglobal.d.ts— which vue-tsc never surfaced.Not changed:
@oxlint/migratewas run as an audit only, in scratch. It needs ESLint v9 flat config and hard-errors on our legacy.eslintrc; its output disables the default plugins and pins a frozeneslint:recommendedsnapshot, so the configs here are hand-written. Template linting (vue/require-v-for-key, template a11y) stays impossible — oxlint does not parse Vue templates. We ran zero Vue rules before, so this is still a gain of 44.Read commit by commit. The 164-file reformat is its own commit.
Demo
No visual change — formatting and lint config only. Applying
skip-demo.Testing
npx oxlint --deny-warningsrepo-widenpx oxfmt --check .yarn typecheckyarn test:unityarn buildpre-commit run --all-filesgit status -- 'buzz/**/*.json'Clobber check: dirtied one
.vueand one.ts, ranpre-commit run --filestwice. First run reformats, second is a no-op — and it fixed the.vuetemplate, which Biome never touched. That convergence was the point of the migration.E2E left to CI.
🤖 Generated with Claude Code