Skip to content

Move linting to oxlint and oxfmt, and enable @shadcn/lint - #47

Merged
thebuilder merged 2 commits into
mainfrom
feat/shadcn-lint-oxlint-migration-3cc3c3
Sep 19, 2026
Merged

thebuilder merged 2 commits into
mainfrom
feat/shadcn-lint-oxlint-migration-3cc3c3

Conversation

@thebuilder

Copy link
Copy Markdown
Owner

What changed

Biome is gone. Ultracite now drives oxlint and oxfmt. pnpm lint and pnpm fix are unchanged on the outside. The config lives in oxlint.config.ts and oxfmt.config.ts; the three file-level overrides from biome.jsonc moved across under the same comments, and the inline Biome suppressions became oxlint ones.

Ultracite's oxlint preset is stricter than its Biome one. It produced about 800 findings on the existing tree. The mechanical ones are fixed (unicode regex flags, toSorted, path default imports, import.meta.dirname, apostrophe escapes). Twelve rules are turned off in the config, each with its reason beside it. The largest was the pair that wanted every component rewritten as an arrow function; the payload stays as function declarations to match shadcn upstream. sort-keys was reordering keyframe percentages in theme.mjs.

Two formatter defaults are overridden. Ultracite's proseWrap: "never" unwrapped every hand-wrapped paragraph in the docs, and embedded-code formatting was closing an intentionally open <html> snippet and adding semicolons to JSX examples. Both are off. The fallow-managed files and vendored skills are excluded from the formatter.

@shadcn/lint is on, all six rules at error. The site and the examples are held to the registry as a consumer would be. The registry itself is exempt from the four consumer-facing rules, since it is the system being protected. Twelve no-restyle contracts open one more category where a part is a box the page fills (Content, Header, Footer, Title, table cells) or an unstyled Base UI trigger; each says why. The unknown-classes allow list names the hook classes declared in app/site.css, by Table, and by sonner.

Things the rules changed in the payload

Running the rules moved three things into the registry rather than around it. Reviewers should look at these first.

  • CommandDialog now carries the three cmdk spacing classes that both the site search and the command palette example were repeating on Command. public/r/command.json changes accordingly.
  • ComboboxTrigger drops Button's uppercase label treatment itself, since it shows a value. The combobox example no longer restyles Button.
  • A shadow-glow-current theme token (0 0 8px currentColor) replaces three hand-written swatch glows. This adds one line to globals.css and the theme item.

Small behaviour changes on the site: the GitHub source link uses rel="noreferrer", the hero terminal's scroll effect depends on the transcript length rather than the array, the root OG image re-exports size and contentType directly, and the build scripts need Node 20 or later for toSorted.

Verification

pnpm check passes end to end (lint, typecheck, dead code, registry build, Next build), and fallow audit passes. I checked the changed parts in the browser: both command dialogs keep their spacing, the combobox trigger renders at normal weight and case, and all 28 swatches render their colours with the glow. The branch includes the three commits main gained meanwhile (the cn package, CodeBlock, card options); their new code needed a suppression comment ported, one hook class allowed, and Body added to the divider contract.

Known follow-up

Every lint run prints four informational lines from the plugin about allow entries that match a class by exact name. That is its documented behaviour for hook classes. Underneath it is a real gap: the theme's tracking-terminal and ease-terminal scales are unknown to tailwind-merge, so a caller's tracking-normal loses to the component's own class, and the linter cannot classify those classes either. That is a design decision about what the registry ships and is left for a separate change.

Ultracite now drives oxlint and oxfmt instead of Biome. The config keeps
the same file-level disagreements as before, ported to oxlint rule names,
and turns off the rules from Ultracite's stricter oxlint preset that the
repository has a stated reason to reject, each with that reason beside it.
Markdown keeps its hand wrapping and code fences stay unformatted, because
oxfmt was closing an open <html> snippet and adding semicolons to JSX.

@shadcn/lint is registered with all six rules on. The site and the
examples are held to the registry as a consumer would be; the registry
itself is exempt, since it is the system the rules protect. The contracts
name what a page may change on a part and why. Running the rules moved
three things into the payload rather than around it: CommandDialog owns
the cmdk spacing both callers were repeating, ComboboxTrigger drops the
button label treatment because it shows a value, and a shadow-glow-current
token replaces three hand-written swatch glows.

Co-Authored-By: Claude <[email protected]>
@vercel

vercel Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
afterglow Ready Ready Preview Sep 19, 2026 1:11pm UTC

Lint, typecheck, dead code and the registry build on pushes to main and
on pull requests. The Next build stays out, because Vercel already runs
it on every push.

Co-Authored-By: Claude <[email protected]>
@thebuilder
thebuilder merged commit fd9dc9c into main Sep 19, 2026
3 checks passed
@thebuilder
thebuilder deleted the feat/shadcn-lint-oxlint-migration-3cc3c3 branch September 19, 2026 13:12

This branch was successfully deployed

1 active deployment
Preview — 785c06ed Deployed Sep 19, 2026 by vercel[bot]
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