Skip to content

Adopt Ultracite's Oxlint and Oxfmt presets - #1141

Merged
smorimoto merged 4 commits into
tidy-platform-branchesfrom
adopt-ultracite
Oct 18, 2026
Merged

smorimoto merged 4 commits into
tidy-platform-branchesfrom
adopt-ultracite

Conversation

@smorimoto

@smorimoto smorimoto commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Why

Use a maintained lint and format preset instead of a growing set of project-specific rules.

What changed

  • vite.config.ts extends ultracite/oxlint/core and ultracite/oxlint/vitest, and formats with ultracite/oxfmt. vp check still runs both.
  • The code base follows the presets: 80-column formatting, arrow function expressions instead of function declarations, null for "nothing found", lookup tables for the supported architectures and platforms, and tests in describe blocks.

Scope

  • Four commits: the formatting, the automatic fixes, a codemod for the function style, and the remaining manual fixes together with the switch of the lint configuration.
  • Six disable comments, each with a reason, cover loops that must run in order and the asynchronous cache mock.
  • Does not run ultracite init, so there are no editor settings, agent rules, or separate Oxlint and Oxfmt config files.

Tradeoffs

The presets live in vite.config.ts rather than in oxlint.config.ts and oxfmt.config.ts. The project already runs lint and formatting through vite-plus, and Ultracite's documentation does not cover vite-plus, so a second config would have split the source of truth.

Blast radius

Almost every file changes, but only through formatting and lint fixes. The action manifests parse to the same YAML. yarn.lock grows by about 700 lines for Ultracite's dependencies.

Verification

  • Every commit passes yarn lint, yarn knip, yarn typecheck, CI=true yarn test, and a clean yarn build.
  • The fake-runner transcripts and the lint action bundles behave as before.

Replace the formatter defaults with the "ultracite/oxfmt" preset and
reformat every file it covers. The preset wraps code at 80 columns,
sorts imports and package.json keys, and never wraps prose, so the
folded descriptions in the action manifests now sit on one line.
Their parsed YAML is unchanged, as are the bundles.
Run "vp check --fix" with the "ultracite/oxlint/core" and
"ultracite/oxlint/vitest" presets and keep what it changed: sorted
object keys, explicit join separators, merged push calls, "utf-8"
encoding names, test() instead of it(), and resolves matchers. The
lint configuration itself switches in a later commit, once the rest
of the code base passes.

Two fixes needed help. The test() rename left the it imports behind,
and mocking modules through import() made the mocks follow the real
module types. The Actions cache mock now takes its parameter types
from @actions/cache, and the systeminformation mock is gone. The
action reads the real OS release, which the transcripts normalise
like the platform and architecture.
Ultracite's Oxlint preset sets func-style to "expression" with arrow
functions allowed, so turn each of the 80 function declarations into
a const bound to an arrow function. A codemod rewrote the head of each
declaration from the parsed source and left the bodies untouched.
No code calls a function before its new definition. The tests, the
action transcripts and the lint action bundles behave as before.
Extend "ultracite/oxlint/core" and "ultracite/oxlint/vitest" in the
lint configuration and fix what the automatic fixes left behind.

Functions that return a value on some paths now return null on the
others instead of falling off the end, so restoreCache, matchVersion
and the .ocamlformat reader say "nothing found" explicitly. Conditions
on optional strings compare against undefined and the empty string,
which keeps the empty string treated as absent, as before.

The supported architectures and platforms move from switch statements
into lookup tables, and the error messages list them from there. The
exhaustiveness check would otherwise want a case for every value that
Node.js defines.

Loops that must run in order, such as adding repositories by
priority, keep their await and say why in a disable comment. So does
the Actions cache mock, which mirrors an asynchronous API without
awaiting anything. Tests sit in describe blocks, regular expressions
use the Unicode flag and named groups, and node:path is a default
import.
@smorimoto
smorimoto added this pull request to stack #1142 October 18, 2026 15:39
@smorimoto smorimoto changed the title adopt ultracite Adopt Ultracite's Oxlint and Oxfmt presets Oct 18, 2026
@smorimoto smorimoto added the enhancement New feature or request label Oct 18, 2026
@smorimoto
smorimoto merged commit 72a1307 into master Oct 18, 2026
19 checks passed
@smorimoto
smorimoto deleted the adopt-ultracite branch October 18, 2026 16:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant