Skip to content

fix(cli): run lint fixes before the formatter in ultracite fix - #791

Open
keyurgovrani wants to merge 1 commit into
haydenbleasel:mainfrom
keyurgovrani:fix/lint-before-format
Open

fix(cli): run lint fixes before the formatter in ultracite fix#791
keyurgovrani wants to merge 1 commit into
haydenbleasel:mainfrom
keyurgovrani:fix/lint-before-format

Conversation

@keyurgovrani

@keyurgovrani keyurgovrani commented Sep 2, 2026

Copy link
Copy Markdown

Summary

ultracite fix runs the formatter before the linter's fixes: oxfmt --write before oxlint --fix, and prettier --write before eslint --fix. A fixer can insert code that is not in the formatter's shape, for example the braces curly adds:

// input
if (a) return 1;
// after `ultracite fix` on 7.10.7
if (a) {return 1;}
// after a second `ultracite fix`
if (a) {
  return 1;
}

The same happens with the imports import/consistent-type-specifier-style splits and with switch-case-braces. So one fix is not idempotent, and a lint-staged setup commits half-formatted code. This PR swaps the order so the linter runs first and the formatter second. Stylelint stays last on the ESLint path: it only touches style files, which neither ESLint nor Prettier rewrite.

Changes

  • packages/cli/src/commands/fix.ts: runSteps([oxlint, oxfmt]) and runSteps([eslint, prettier, stylelint]).
  • packages/cli/__tests__/fix.test.ts: order assertions and the spawn-failure tests follow the new order.
  • .changeset/lint-before-format.md: patch.

How I found it

Adopting ultracite in a ~35k-file monorepo, pnpm fix left every curly fix on one line until the next run. Verified against the shipped bundle (ye([vs, Cs]) in dist/index.js, where vs is oxfmt --write). We are running a trailing oxfmt --write in lint-staged as the workaround.

Test plan

  • bun test in packages/cli: 536 pass, 0 fail
  • bun ./packages/cli/src/index.ts check on the repo
  • turbo build + turbo types
  • maintainers: is there a reason the formatter was first that I am missing (e.g. an ESLint rule whose fix depends on Prettier's output)? I could not find one in the history.

Summary by CodeRabbit

  • Bug Fixes
    • Updated the fix command to run linters before formatters, ensuring all automatic fixes are formatted in the same run.
    • The fix command is now idempotent for changes such as added braces and adjusted type-specifier imports.
    • Improved error reporting to accurately identify failures from the first tool in the sequence.

`fix` ran the formatter first (oxfmt before oxlint --fix, Prettier before
ESLint --fix). A fixer can insert unformatted code, such as the braces
`curly` adds or the imports `consistent-type-specifier-style` splits, so
a single run left that code unformatted until the next run. Running the
linter first and the formatter second makes one `fix` idempotent.

Stylelint still runs last on the ESLint path; it only touches style
files, which neither ESLint nor Prettier rewrite.
@vercel

vercel Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated
ultracite Ready Ready Preview Sep 2, 2026 9:57pm UTC

Request Review

@changeset-bot

changeset-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 13afb57

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
ultracite Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: c9e7255c-2f95-45f5-b077-1d7252a85e17

📥 Commits

Reviewing files that changed from the base of the PR and between 963292a and 13afb57.

📒 Files selected for processing (3)
  • .changeset/lint-before-format.md
  • packages/cli/__tests__/fix.test.ts
  • packages/cli/src/commands/fix.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The fix command now runs ESLint before Prettier and Oxlint before Oxfmt. Tests verify the new order and related failure behavior. A patch changeset documents the update.

Changes

Fix command ordering

Layer / File(s) Summary
Update fix command ordering
packages/cli/src/commands/fix.ts, .changeset/lint-before-format.md
The command runs linter fixes before formatting. The changeset declares a patch release.
Validate reordered workflows
packages/cli/__tests__/fix.test.ts
Tests verify invocation order, flags, failures, continuation, and agent-mode behavior.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 13afb

The command now applies lint fixes before formatting, improving one-run consistency and idempotence; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers: haydenbleasel

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: lint fixes run before formatting in ultracite fix.
Description check ✅ Passed The description is detailed and relevant. It explains the problem, the implementation, the affected tests, the changeset, and the test results. It does not use the template headings exactly and does n…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description is detailed and relevant. It explains the problem, the implementation, the affected tests, the changeset, and the test results. It does not use the template headings exactly and does not provide a related issue number, but these omissions do not prevent review.

Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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