feat(migrate): upgrade existing Vite+ projects across versions#1891
feat(migrate): upgrade existing Vite+ projects across versions#1891fengmk2 wants to merge 100 commits into
Conversation
✅ Deploy Preview for viteplus-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
feb8068 to
5090afc
Compare
vite-plus
@voidzero-dev/vite-plus-core
@voidzero-dev/vite-plus-prompts
@voidzero-dev/vite-plus-cli-darwin-arm64
@voidzero-dev/vite-plus-cli-darwin-x64
@voidzero-dev/vite-plus-cli-linux-arm64-gnu
@voidzero-dev/vite-plus-cli-linux-arm64-musl
@voidzero-dev/vite-plus-cli-linux-x64-gnu
@voidzero-dev/vite-plus-cli-linux-x64-musl
@voidzero-dev/vite-plus-cli-win32-arm64-msvc
@voidzero-dev/vite-plus-cli-win32-x64-msvc
@voidzero-dev/vite-plus-darwin-arm64
@voidzero-dev/vite-plus-darwin-x64
@voidzero-dev/vite-plus-linux-arm64-gnu
@voidzero-dev/vite-plus-linux-arm64-musl
@voidzero-dev/vite-plus-linux-x64-gnu
@voidzero-dev/vite-plus-linux-x64-musl
@voidzero-dev/vite-plus-win32-arm64-msvc
@voidzero-dev/vite-plus-win32-x64-msvc
commit: |
5090afc to
732edd6
Compare
This comment was marked as outdated.
This comment was marked as outdated.
7a1d2de to
c68f763
Compare
1bba44c to
911881e
Compare
The bridge moved from fengmk2/pkg-pr-registry-bridge (render.vip) to voidzero-dev/pkg-pr-registry-bridge (void.app). Per the new docs/ci-setup.md, switch the publish workflow to the root action voidzero-dev/pkg-pr-registry-bridge and the void.app bridge URL, and update the test-pkg-pr-new-migrate helper's bridge registry to void.app. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
Projects commonly gitignore .npmrc, so the bridge registry the helper writes was silently dropped from the commit and the project's CI resolved the commit build from the default registry (which has no 0.0.0-commit.<sha>), failing the supply-chain policy check with ERR_PNPM_TARBALL_URL_MISMATCH. Force-stage .npmrc (and .yarnrc.yml for Yarn Berry) so the bridge registry reaches the project's CI. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
The existing-Vite+ upgrade (bootstrap/re-pin) path did not add a direct `vite` devDep for pnpm, so an already-Vite+ pnpm project being re-pinned kept no direct vite and pnpm's autoInstallPeers fabricated a separate upstream vite to satisfy the vitest-ecosystem peer, splitting vite-plus/vite/vitest (`vp why -r vite` then shows two instances). reconcileVitePlusBootstrapPackage now calls ensureDirectViteForPnpm (the same helper the full-migration path uses in rewriteRootWorkspacePackageJson / rewriteMonorepoProject), so the root and every member that depends on vite-plus gain a direct `vite: catalog:`. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
pnpm evaluates a native optional dependency's engines.node against the FLOOR of the project's declared Node range (e.g. devEngines.runtime), not the running Node. A pin like ">=24" or bare "24" overlaps the supported range (^20.19.0 || ^22.18.0 || >=24.11.0) but its floor 24.0.0 is below the minimum, so pnpm skips the @voidzero-dev/vite-plus-* native packages and the toolchain fails with "Cannot find native binding". The Node-version upgrade now uses a floor-based check (was overlap via allows_any) and normalizes all three pins independently: .node-version to the concrete latest release of the major, devEngines.runtime and engines.node to an open >=<supported minimum> range (e.g. >=24 -> >=24.11.0). The supported minimum is derived from package.json engines.node (no hardcoded per-major floors). Docs updated in migrate-rules.md and the migrate RFC. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
In the existing-Vite+ upgrade path the baseUrl, Prettier, node-version-file, and below-range Node confirms were shown while an execution-phase progress spinner (e.g. "Configuring package manager", "Migrating ESLint") was still running, so the spinner animated beneath the prompt. Each confirm now clears the spinner first, and the upgrade-path upgradeUnsupportedNodeVersions call passes clearMigrationProgress like the fresh path; the next progress update restarts it. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
…eractive - Add "unparseable" to .typos.toml (a valid variant of "unparsable" used in the migrate Node-pin docs/comments) so the typos check passes. - The upgrade-path spinner-clear before the baseUrl / Prettier / node-version confirms now runs only in interactive mode. Running it unconditionally added a spinner line to `vp migrate --no-interactive` output, which diffed several migration-* snaps; non-interactive output is now byte-identical to before while the interactive spinner-over-prompt fix is preserved. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
The existing-Vite+ upgrade path now injects a direct `vite: catalog:` for pnpm projects that depend on vite-plus (reconcileVitePlusBootstrapPackage), so the migrated manifests gain a `"vite": "catalog:"` entry. Regenerate the affected migration-eslint-rerun*, migration-prettier-rerun, migration-upgrade-*, and migration-vite-plus-in-dependencies-pnpm snaps to match; this also clears the CLI E2E (musl) snapshot-diff failure, which has the same cause. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
…ojects The bun#8406 workaround (a direct `vite` devDep so bun's pre-override peer walk resolves vitest's `vite` peer) hardcoded the concrete core alias, leaving `vite: npm:@voidzero-dev/vite-plus-core@<v>` while `vite-plus`, the override, and the pnpm/Yarn paths use `catalog:`. #8406 only requires `vite` to be a DIRECT dependency; a `catalog:` edge satisfies it too because catalog references resolve during the dependency-graph build (unlike overrides) -- verified on bun 1.3.11. Route both bun sites (rewriteStandaloneProject, reconcileVitePlusBootstrapPackage) through getCatalogDependencySpec so catalog-capable bun gets `catalog:` and non-catalog bun keeps the concrete alias. Adds the migration-upgrade-bun-catalog snap fixture for coverage. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
…the standalone path Pass the hoisted `supportCatalog` (matching the parameter name and the reconcileVitePlusBootstrapPackage caller) instead of `usePnpmWorkspaceYaml`. They are equivalent because ensureDirectViteForPnpm no-ops for non-pnpm, and this removes the comment that existed only to justify the usePnpmWorkspaceYaml form. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
The bun and npm-opt-in direct-`vite` edges were appended (`{ ...devDeps, vite }`),
landing `vite` after `vite-plus`, which isn't `vp check`-clean since oxfmt sorts
package.json and migrate has no later format pass (only the pnpm path inserted in
sorted position). Extract `setDirectViteEdge` (spec via getCatalogDependencySpec
+ sorted insertion, mutating devDependencies IN PLACE so callers that captured
the reference keep their later edits) and route all six direct-vite injection
sites (pnpm, standalone/bootstrap/monorepo bun, npm-opt-in) through it. Each site
keeps its own gate; npm and standalone bun (supportCatalog=false) still get the
concrete core alias, now sorted. Regenerates migration-upgrade-bun-catalog.
Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
…rating .nvmrc
Converting `.nvmrc` to `.node-version` deletes `.nvmrc`, but GitHub Actions
workflows using actions/setup-node with `node-version-file: '.nvmrc'` then break
in CI ("The specified node version file ... does not exist"). After removing
`.nvmrc`, scan `.github/workflows/*.{yml,yaml}` and rewrite `node-version-file`
values pointing at `.nvmrc` to `.node-version`, preserving the original
quote/`./` style, best-effort, and scoped to `node-version-file:` lines only
(shell `cat .nvmrc` and comments are left untouched). Reports the updated files.
Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
Collapse the `.replace()` callback that only reassembled its own captured groups into the equivalent string-pattern replacement `$1$2$3.node-version$2`, and tighten the test's report assertion to name the actual updated workflow. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
The typos check flagged "look-alikes" ("alikes"). Reword to "similar values".
Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
Update the pinned commit from 3ee9882 to 5331470 (Update pnpm/action-setup action to v6, #15). Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b1f776c9ed
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…log) Bun resolves catalog: references only inside a workspace, so the upgrade path converting a standalone (non-workspace) bun project's deps to catalog: broke `bun install` with "vite@catalog: failed to resolve". Gate all three bun-catalog sites (the two supportCatalog computations and the rewriteBunCatalog sink) on a real bun workspace, mirroring the fresh path; standalone bun keeps concrete specs and writes no catalog field. Adds a unit test plus a runnable migration-standalone-bun-install snap that runs a real bun install. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
The fixture exercises the bun#8406 direct-vite-edge-as-catalog: behavior but was authored as a standalone bun project, where bun cannot resolve catalogs. After the standalone-bun fix, migrate correctly drops that catalog, breaking the snap. Declare `workspaces` so the catalog scenario stays valid (the standalone case is covered by migration-standalone-bun-install) and regenerate the global snap. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
- Node-pin reachability: an already-Vite+, otherwise up-to-date project whose only pending work was a below-floor Node pin (.node-version / devEngines.runtime / engines.node) hit the "already using Vite+" early return, so upgradeUnsupportedNodeVersions never ran. hasExistingVitePlusMigrationCandidates now detects a pending pin via the existing planNodeVersionUpgrades planner. - pnpm overrides: the ">vite" dependency-selector cleanup deleted entries while iterating the live items array, skipping an adjacent second selector and leaving a stale ">vite" override. Snapshot the keys first (mirrors the loop above); regenerate the affected snap. - Yarn hoisting: the nmHoistingLimits opt-out guard only checked devDependencies, so a workspace declaring vite-plus under dependencies was skipped. Use hasDirectVitePlusInstallEntry (checks both groups). Also format the migration-upgrade-bun-catalog fixture input. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
The "Comment bridge version" step creates/updates a sticky comment via github.rest.issues.createComment, which is gated on issues:write; the create path (a PR without a sticky comment yet) would 403 and be swallowed by continue-on-error. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 36a8160d49
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const supportCatalog = | ||
| !VITE_PLUS_VERSION.startsWith('file:') && | ||
| (usePnpmWorkspaceYaml || | ||
| workspaceInfo.packageManager === PackageManager.yarn || | ||
| isBunWorkspace); |
There was a problem hiding this comment.
Gate Yarn catalogs on supported Yarn versions
When an existing Vite+ project is pinned to Yarn 1/2/3, this unconditionally enables catalog: specs even though downloadSupportedPackageManager only upgrades Yarn >=4.0.0 <4.10.0. The bootstrap then rewrites vite-plus/vite/vitest to catalog references and writes catalog settings, but Yarn's own docs say the catalog plugin is included by default only starting from Yarn 4.10.0, so these older Yarn projects will fail the final install instead of upgrading cleanly; the fresh rewrite path has the same assumption in orchestrators.ts and should also be gated or upgraded.
Useful? React with 👍 / 👎.
| // yields an empty intersection → `None`, matching the resolve-then-verify | ||
| // rejection on the concrete path. | ||
| let supported_minimum = requirement_range.intersect(supported_range)?.min_version()?; | ||
| Some(format!(">={supported_minimum}")) |
There was a problem hiding this comment.
Preserve multi-major Node engine constraints
When an existing engines.node or devEngines.runtime range spans multiple majors and only its first floor is too low, e.g. ^20 || ^22, this returns just >=20.19.0. The migration then writes that back to package.json, dropping the explicit 22.x branch and widening the project to unsupported majors like 21 and 23 even though Vite+'s own supported range excludes them; the rewrite should preserve the original upper bounds/disjuncts while lifting the below-floor major.
Useful? React with 👍 / 👎.
| matches!( | ||
| item, | ||
| ast::CommandPrefixOrSuffixItem::Word(word) | ||
| if matches!(word.value.as_str(), "--" | "run" | "exec") | ||
| ) |
There was a problem hiding this comment.
Avoid rewriting bunx text after generic separators
When bunx appears as an argument after a generic --, run, or exec, this treats it as a command launcher and rewrites the rest of the argument list. That corrupts scripts that only pass or print those words, e.g. echo -- bunx --bun vite build becomes echo -- bunx --bun vp build; the prior AST rewrite left ordinary arguments alone, so this should be limited to known wrapper commands or actual command positions.
Useful? React with 👍 / 👎.
|
|
||
| ### Bun | ||
|
|
||
| - Preserve existing top-level or workspace catalog locations and named catalog |
There was a problem hiding this comment.
catalog only works on bun monorepo, single repo don't use catalog
RFC:
rfcs/migrate-existing-projects.mdProblem
Running
vp migrateon an existing v0.1.x Vite+ project did not upgrade cleanly: it delegated to the stale local CLI, leftpnpm-workspace.yamloverrides pinningvite/vitestto old versions, and skewed coverage providers. The v0.2.1 release notes currently tell users not to runvp migrateyet.What this does (verified)
vite-plusis older than the globalvp, run migrate from the global CLI.vite-plus/vite->core spec so the lockfile moves off 0.1.x."pnpm": {}misrouting that left stalepnpm-workspace.yamloverrides.vitestby usage: removed in the common case (vite-plus provides it transitively), kept + ecosystem-aligned when the project uses it directly or via a range-peer integration.@vitest/*ecosystem (coverage-v8/-istanbul, ui, web-worker) to the bundled version; exclude@vitest/eslint-plugin.Node.js version upgrade
A project pinning a Node version below the Vite+ supported range (
package.json#engines.node, currently^20.19.0 || ^22.18.0 || >=24.11.0) makes package managers skip the native binding's optional dependency ("Cannot find native binding"). Migration now reads the effective Node pin (.node-version->devEngines.runtime->engines.node, reusing the Rust runtime resolver, with.nvmrc/Volta converted to.node-versionfirst) and rewrites an exact ormajor.minorpin below the range to the concrete latest release of that major (24.3.0/24.2->24.18.0). A bare major or an open range that still resolves to a supported release is left unchanged. Interactive migration confirms the upgrade (default yes);--no-interactiveapplies it directly.vp migrateon a fresh project pinning.node-version= 24.3.0 (below the range).node-versionis rewritten24.3.0->24.18.0. The confirm prompt pauses the migration progress spinner so it does not animate beneath the prompt.Known gaps (draft, follow-ups)
apps/dashboard) is left without@vitest/browser-playwrightand a directvitest, so browser tests break. The fresh-migration path handles this; the upgrade path must too.vitestin the package that needs it instead of the shared root catalog.vitestpin into removal for official-@vitest/*-only projects.migration-*snap suite and do the docs /npm deprecaterollout, then drop the "do not runvp migrate" disclaimer.Manual pkg.pr.new migration testing
Use the repository helper to install an isolated pkg.pr.new global CLI and run the PR version of
vp migrateagainst any local project:The first argument accepts either a PR number or commit SHA. The helper keeps the normal
~/.vite-plusinstallation untouched, forces migration through the installed global preview CLI even when the project has a same-version local CLI, pinsvite-plusandvite/core to the matching pkg.pr.new URLs, refuses dirty Git worktrees by default, and forwards additional options such as--no-interactivetovp migrate.