feat(migrate): upgrade existing Vite+ projects across versions#1891
feat(migrate): upgrade existing Vite+ projects across versions#1891fengmk2 wants to merge 124 commits into
Conversation
✅ Deploy Preview for viteplus-preview canceled.
|
|
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
E2E test projects
× typescript(TS2783): 'staged' is specified more than once, so this usage will be overwritten.
╭─[vite.config.ts:118:3]
117 │ export default defineConfig({
118 │ ╭─▶ staged: {
119 │ │ '*': 'vp check --fix',
120 │ ╰─▶ },
121 │ ...config, // shared lint/fmt/build from @janosh/vite-config (dotfiles)
╰────
✖ bash -c "tsc --noEmit":
error TS2688: Cannot find type definition file for 'vite-plus/client'.
The file is in the program because:
Entry point of type library 'vite-plus/client' specified in compilerOptions
tsconfig.json:12:33
12 "types": ["vitest/globals", "vite-plus/client"],
~~~~~~~~~~~~~~~~~~
File is entry point of type library specified here.
|
This comment was marked as outdated.
This comment was marked as outdated.
7a1d2de to
c68f763
Compare
1bba44c to
911881e
Compare
A stale pre-migrate lockfile can keep an optional-peer copy of vite-plus pinned to an older published version (e.g. a nested oxlint `vite-plus: '*'` peer pulled transitively by vite-plugin-checker/nuxt), which the `--no-frozen-lockfile` reinstall preserves rather than deduping, leaving `vp why` reporting two vite-plus versions. Remove the lockfiles and node_modules before migrate so the reinstall resolves that optional peer onto the in-tree managed version. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
- Node engines union floor gate: lift each disjunct of a multi-major pin independently instead of gating on the overall floor, so `^20.19.0 || ^22.0.0` lifts the below-floor 22 branch to `>=22.18.0 <23.0.0` (was left unchanged). - compat runner: resolve the rolldown-compat worker robustly across the source (sibling worker.js) and bundle (nested compat/worker.js) layouts. - yarn satisfaction: yarnrcSatisfiesVitePlus now requires the npmPreapprovedPackages vitest/@vitest exemptions, so an age-gated yarn project no longer takes the early "already using Vite+" path without them. - pnpm pending: detect missing minimumReleaseAgeExclude exemptions (shared PNPM_MINIMUM_RELEASE_AGE_EXCLUDES) in both detection and the bootstrap writer. - yarn hoisting: detect + apply the nmHoistingLimits opt-out on the bootstrap path, not only in rewriteMonorepoProject. - bun: gate the direct-vite injection to packages that need vitest/vite-plus/a browser provider instead of every workspace package. - typos: drop the `unparseable` whitelist; fix usages to `unparsable`. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
Replace the arity + element-wise comparison in supported_node_floor_range's union branch with a joined-string compare. It is equivalent (a dropped branch shortens the join, a lifted branch changes it) and reads clearer. 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: 595280825c
ℹ️ 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".
On the existing-Vite+ upgrade path, reconcileVitePlusBootstrapPackage only removed the @vitest/* subset of REMOVE_PACKAGES from package.json, while the catalog rewrite deletes the full set (oxlint, oxlint-tsgolint, oxfmt, tsdown, @vitest/browser, @vitest/browser-preview). An existing project that declared one of the non-@vitest tools via `catalog:` was then left with a dangling catalog reference whose catalog entry had just been removed, so `pnpm install` aborted with ERR_PNPM_CATALOG_ENTRY_NOT_FOUND_FOR_SPEC (delta-comic/oxlint-tsgolint, regle/tsdown). Iterate the full REMOVE_PACKAGES, matching the catalog removal and the fresh path (package-json.ts). Adds a unit test and a migration-upgrade-pnpm-bundled-catalog-dep snap proving the bundled tools are removed from both package.json and the catalog. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
- Node engines: bound the single-disjunct lift to its major, so `^20` / `20.18.0` become `>=20.19.0 <21.0.0` instead of an unbounded `>=20.19.0` that admitted unsupported Node 21/23. The single and union paths now share lift_disjunct; a genuinely open pin (`>=20`) stays open. - bun overrides: gate the bun catalog dependency resolver on supportCatalog in both the reconcile and the pending detection, so a standalone bun project's `overrides.vite: "catalog:"` is rewritten to the concrete core alias (and detection no longer reports it already settled) instead of leaving a catalog override that bun cannot resolve. - eslint versions import (false positive): the bundle resolves `../versions.js` correctly via the fix-versions-path tsdown plugin + a dist-root chunk; added a dist guard test that fails if any chunk's relative versions.js import stops resolving. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
Replace the hand-rolled recursive collectJsFiles walker with globSync, matching the in-package convention (utils/workspace.ts). Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
|
@codex review |
The .nvmrc to .node-version conversion only rewrote node-version-file
references in .github/workflows, leaving composite actions
(.github/actions/**/action.{yml,yaml}) pointing at the removed .nvmrc,
which breaks CI with "node version file ... does not exist".
Walk .github/actions recursively for action.{yml,yaml} alongside the
flat workflows scan and rewrite both.
Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
Replace the hand-rolled .github/workflows readdir and the .github/actions stack walk with a single globSync call, matching the in-package convention (utils/workspace.ts). nocase preserves the prior case-insensitive matching; the flat-workflows vs recursive-actions split is kept via two glob patterns. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fb2e405fcf
ℹ️ 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".
The native NAPI binding runs on any Node >=20.0.0 (its ABI floor), but the @voidzero-dev/vite-plus-* platform packages were inheriting the product support policy (^20.19.0 || ^22.18.0 || >=24.11.0). That policy's gaps (20.0-20.18, 22.0-22.17, 24.0-24.10) make engine-strict package managers skip the optional native dependency whenever a consumer's declared Node floor lands in a gap, surfacing as "Cannot find native binding". Rewrite each generated platform package to its true ABI floor so the native dep is never skipped; the vite-plus and core packages keep the product policy unchanged. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
With the native packages declaring their true >=20.0.0 ABI floor, a below-policy Node pin (engines.node "24.x", devEngines.runtime "^24", or a .node-version below the policy minimum) no longer causes package managers to skip the native binding, so migrate must leave it untouched. Remove the floor-raising: the Rust supported_node_floor_range / lift_disjunct / supported_node_requirement / resolved_if_supported helpers and the resolveSupportedNodeVersion / resolveSupportedNodeRange NAPI exports, plus the JS upgradeUnsupportedNodeVersions / planNodeVersionUpgrades / hasUnsupportedNodeVersionPin surface and SUPPORTED_NODE_RANGE. The .nvmrc/Volta -> .node-version conversion, the node-version-file workflow and composite-action rewrite, and the runtime node resolver (resolveProjectNodeVersion) are unchanged. Adds a reproducing snap fixture showing below-policy pins are preserved. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
This NAPI export (with ProjectNodeVersion / version_source_label) was added by the reverted Node auto-upgrade feature and never wired into the JS migrator, so it has zero callers. Removing it completes the revert and drops the binding crate's now-unused node-semver and vite_js_runtime dependencies (vite_path stays, used elsewhere in the crate). The binding is regenerated so the export is gone from the generated index files. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
Reword the native-addon engines comment and the migrate RFC to refer to the declared engines.node supported range instead of "product policy", and drop the now-stale "reuses the Rust runtime resolver" claim (that resolver was removed). Simplify the preserve-pins snap to `cat package.json` and add `cat pnpm-workspace.yaml` so it also covers the catalog update. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
collectInstalledPackageNames no longer counts peerDependencies as installed: a peer-only package is not in node_modules, so a JS plugin referencing it would fail to load at `vp lint` time. Limit it to dependencies, devDependencies, and optionalDependencies. The pnpm bootstrap pending check now scans every workspace package's vite-plus catalog reference, not just the root manifest, so a workspace package pinned to a stale named catalog (catalog:legacy) keeps the bootstrap pending. The same scan gates the pnpm-workspace.yaml rewrite so the bootstrap converges. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
The pre-migrate cleanup used a recursive `find -name node_modules -exec rm -rf`, which wiped every sub-workspace node_modules in a monorepo, not just the root. Removing the root lockfile and root node_modules is enough to force a clean re-resolution (the root lockfile pins the stale optional-peer vite-plus, and the reinstall recreates sub-package node_modules from the root store). Switch to `rm -rf "$project_dir/node_modules"` so sub-workspace node_modules are left untouched. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
The fixture package.json was not oxfmt-formatted, so the repo-wide `vp check` flagged it. Format it (key order only; the 24.x / ^24 Node pins are unchanged) and regenerate the snap so its post-migrate output matches. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
|
@codex review |
workspaceCatalogVitePlusDependencyPending and workspaceVitestEcosystemCatalogReferencesPending had byte-identical read/guard/iterate scaffold over bootstrapProjectPaths. Extract a shared someBootstrapProjectPackageJson(rootDir, packages, predicate) helper that both call, so each collapses to one line binding its resolver into the predicate. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
|
@codex review |
|
Codex Review: Didn't find any major issues. Delightful! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Bump the voidzero-dev/pkg-pr-registry-bridge action to its latest main and pass the new pr-url input (surfaced by the bridge's /-/refs). Point the registry at the canonical https://registry-bridge.viteplus.dev in the PR comment and the test-pkg-pr-new-migrate helper, per the bridge's ci-setup doc. Claude-Session: https://claude.ai/code/session_01DQhS6o1fyQd1yjiee6W8jR
RFC:
rfcs/migrate-existing-projects.mdProblem
vp migrateon an existing v0.1.x Vite+ project did not upgrade cleanly: it delegated to the stale local CLI, leftpnpm-workspace.yamloverrides and catalogs pinningvite/vitestto old versions, and skewed coverage providers. The v0.2.x release notes currently tell users not to runvp migrateyet.What it does
vite-plusis older than the globalvp, run migrate from the global CLI so the stale local CLI does not run.vite-plusand thevite->@voidzero-dev/vite-plus-corealias off 0.1.x, across dependencies, overrides/resolutions, and catalogs, for every workspace package rather than only the root manifest.@vitest/*(coverage-v8/-istanbul, ui, web-worker) to the bundled version, with the browser providers kept opt-in.package.json#pnpmsettings intopnpm-workspace.yaml, fix the empty"pnpm": {}misrouting that left stale overrides, inject the directviteedge where pnpm needs it, and approve required build scripts. The "pending" check scans every workspace package's catalog ref, so a package pinned to a stale named catalog (catalog:legacy) is not missed.vite/vitestimports invite-plugin-*and unplugin packages instead of rewriting them tovite-plus.Node.js version
The native binding runs on any Node
>=20.0.0, and the published@voidzero-dev/vite-plus-*platform packages now declare that ABI floor (thevite-plusand core packages keep their widerengines.nodesupport range). So a Node pin whose floor is below the support range but at or above20.0.0(engines.node: 24.x,>=22,.node-version24.3.0, ...) still installs the native binding and is left untouched by migration..nvmrcand Voltavolta.nodeare converted to.node-version, and anyactions/setup-nodenode-version-file: .nvmrcreference in workflows and composite actions (.github/actions/**/action.{yml,yaml}) is repointed to.node-versionso CI does not break.Manual pkg.pr.new testing
Install an isolated pkg.pr.new global CLI and run the PR's
vp migrateagainst a local project. Dependencies resolve through the registry bridge as ordinary0.0.0-commit.<sha>versions, persisted into the project's.npmrc(and.yarnrc.ymlfor Yarn Berry) so its own CI installs the same build:The first argument accepts a PR number or commit SHA. The helper keeps
~/.vite-plusuntouched, forces migration through the preview CLI even when the project has a same-version local CLI, clears only the workspace root lockfile andnode_modulesbefore migrating, and refuses dirty Git worktrees unlessALLOW_DIRTY=1. Confirm the result withvp why -r vite-plus vite vitest(each must resolve to a single version).