Skip to content

fix(cli): skip merging standalone oxfmt/oxlint config when key already in vite.config.ts#1601

Merged
fengmk2 merged 6 commits into
mainfrom
fix-cli-duplicate-fmt-merge
May 18, 2026
Merged

fix(cli): skip merging standalone oxfmt/oxlint config when key already in vite.config.ts#1601
fengmk2 merged 6 commits into
mainfrom
fix-cli-duplicate-fmt-merge

Conversation

@fengmk2
Copy link
Copy Markdown
Member

@fengmk2 fengmk2 commented May 16, 2026

Summary

  • vp create fate produced a vite.config.ts with two fmt: blocks because mergeAndRemoveJsonConfig (packages/cli/src/migration/migrator.ts) was the only mergeJsonConfig call site missing the "skip if key already present" guard the other call sites use. The Rust merge step always prepends, so when fate's template ships both an inline fmt: block and a standalone .oxfmtrc.jsonc, both ended up in the output.
  • Now reads vite.config.ts first; if the key is already declared, fs.unlinkSyncs the redundant standalone file and emits an info log instead of merging. Mirrors the existing skip-existing behavior in applyToolInitConfigToViteConfig. Also covers the analogous lint: + .oxlintrc.json case.

Test plan

  • New unit tests in packages/cli/src/migration/__tests__/migrator.spec.ts (fmt + lint), confirmed red before the fix and green after — vp test --run packages/cli/src/migration/__tests__/migrator.spec.ts (75/75 passing).
  • New global snap test packages/cli/snap-tests-global/migration-preserves-existing-fmt-and-lint/ modeled on fate's real-world fmt/lint config (experimental options, ignore patterns, overrides); standalone files carry conflicting values so any regression would be obvious in the snapshot.
  • pnpm -F vite-plus snap-test-global migration — all existing migration snaps still pass with no diffs.
  • pnpm -F vite-plus snap-test-global new-create-vite — all create snaps still pass with no diffs.

Closes the duplicate-fmt regression reported when running vp create fate.


Note

Medium Risk
Migration logic now conditionally deletes standalone .oxfmtrc/.oxlintrc files based on a new AST-based vite config scan exposed via a native binding; mistakes in the detector could cause config merges to be skipped or applied unexpectedly.

Overview
Fixes a migration regression where projects with an existing inline fmt/lint block in vite.config.* could end up with duplicated blocks after merging standalone .oxfmtrc/.oxlintrc files.

This adds a Rust AST-based has_config_key check (exported through the NAPI binding as hasConfigKey) and uses it in the CLI migration paths to skip merging when the key already exists, instead deleting the redundant standalone config and optionally logging an info message. New unit and snapshot tests cover the create-fate-style template shape and ensure only a single fmt/lint block remains.

Reviewed by Cursor Bugbot for commit e0c99be. Configure here.

…y in vite.config.ts

`mergeAndRemoveJsonConfig` was the only `mergeJsonConfig` call site missing
the "skip if key already present" guard the other call sites use. Since the
Rust merge step always prepends, templates that ship a populated
`vite.config.ts` AND a standalone `.oxfmtrc.jsonc` (e.g. create-fate) ended
up with two `fmt:` blocks after `vp create` / `vp migrate`.

Now read `vite.config.ts` first; if the key is already declared, unlink the
redundant standalone file (matching the existing skip-existing behavior in
`applyToolInitConfigToViteConfig`) and log info instead of merging.

Adds reproducing unit tests (fmt + lint) and a global snap test mirroring
fate's real-world fmt/lint configuration.
@fengmk2 fengmk2 self-assigned this May 16, 2026
@netlify
Copy link
Copy Markdown

netlify Bot commented May 16, 2026

Deploy Preview for viteplus-preview canceled.

Name Link
🔨 Latest commit bd4eb4e
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/6a0aac8e341a890008aedf8d

fengmk2 added 3 commits May 16, 2026 16:37
…nfig_key

`mergeAndRemoveJsonConfig`'s regex gate (`\b${configKey}\s*:`) had known
false positives (comments, string literals, nested keys like
`plugins: [{ fmt: ... }]`) and false negatives (e.g. spread). Move the
check into Rust as a precise AST walker that mirrors the six object-literal
shapes the merger already understands (`defineConfig({...})`, arrow
callback, `return {...}` inside callback, plain `export default`,
`satisfies` export, arrow-wrapped defineConfig). Bare identifier keys and
quoted string keys both match; computed keys, comments, strings, and
nested-object keys are ignored.

The `return $VAR` variant cannot be inspected statically — that path
conservatively reports `false`, which is safe because the merger uses
object spread (`{ key: ..., ...$VAR }`) so duplicate keys resolve to the
later spread at runtime.

- crates/vite_migration: new `has_config_key`, 13 unit tests covering the
  shapes plus comment/string/nesting traps and the fate template shape.
- packages/cli/binding: NAPI export `hasConfigKey(viteConfigPath, configKey)`.
- packages/cli: `mergeAndRemoveJsonConfig` calls `hasConfigKey` instead of
  the regex.

Snap output is byte-identical to the regex version on the regression case;
all 75 migrator unit tests and the migration snap suite still pass.
Address review feedback from /simplify:

- Drop the regex-based hasConfigKey twin in init-config.ts; route
  inspectInitCommand / applyToolInitConfigToViteConfig through the new
  AST-based NAPI. Same swap for the inline regex in injectConfigDefaults.
  Removes the bug-prone duplicate that motivated this PR.
- Simplify Rust helpers: .map().unwrap_or(false) → .is_some_and(),
  is_define_config_arrow_body to a filter-chain, drop narrating comments
  that restate kind names already in the match arms.
- Drop the redundant lint unit test in migrator.spec.ts; the Rust suite
  exhaustively covers AST shapes (13 cases), and the fmt test alone proves
  the TS wiring through NAPI + mergeAndRemoveJsonConfig.

All 180 Rust + 81 TS tests pass; init-config and migration snap tests
unchanged.
@fengmk2 fengmk2 marked this pull request as ready for review May 18, 2026 00:48
@fengmk2 fengmk2 requested a review from cpojer May 18, 2026 00:48
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented May 18, 2026

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit e0c99be. Configure here.

@fengmk2 fengmk2 added test: e2e Auto run e2e tests test: create-e2e Run `vp create` e2e tests labels May 18, 2026
Copy link
Copy Markdown
Member

@cpojer cpojer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, I didn't realize the fate templates ship with duplicate configs. Woops. Thank you for the fix in Vite+!

@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented May 18, 2026

@cpojer fate templates help me realize how fragile the previous temporary solutions were 😢

@fengmk2 fengmk2 merged commit 9e44db1 into main May 18, 2026
91 checks passed
@fengmk2 fengmk2 deleted the fix-cli-duplicate-fmt-merge branch May 18, 2026 06:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test: create-e2e Run `vp create` e2e tests test: e2e Auto run e2e tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants