Skip to content

fix(core): resolve bundled Rolldown bindings via platform packages - #2313

Merged
fengmk2 merged 6 commits into
mainfrom
rfc/core-binding-resolution
Aug 4, 2026
Merged

fix(core): resolve bundled Rolldown bindings via platform packages#2313
fengmk2 merged 6 commits into
mainfrom
rfc/core-binding-resolution

Conversation

@fengmk2

@fengmk2 fengmk2 commented Aug 4, 2026

Copy link
Copy Markdown
Member

Closes #2054. Design: rfcs/core-binding-resolution.md. Solid edges are declared dependencies; dashed is an undeclared runtime require.

Before

flowchart TD
  app["user project"] --> vp["vite-plus"]
  vp -->|"dependency, exact pin"| core["@voidzero-dev/vite-plus-core<br>(bundled Rolldown)"]
  vp -->|"optionalDependencies x8,<br>injected at publish"| plat["@voidzero-dev/vite-plus-*<br>platform packages (merged .node)"]
  core -.->|"require('vite-plus/binding'),<br>undeclared"| vp
  linkStyle 3 stroke:#d33,stroke-width:2px
Loading

Release-built core reaches its Rolldown binding by requiring back into vite-plus, an undeclared cycle that only resolves through pnpm's hidden hoist. pnpm enable-global-virtual-store, Yarn PnP, and standalone core installs (the vite alias) fail with Cannot find module 'vite-plus/binding'.

After

flowchart TD
  app["user project"] --> vp["vite-plus"]
  vp -->|"dependency, exact pin"| core["@voidzero-dev/vite-plus-core<br>(bundled Rolldown)"]
  vp -->|"optionalDependencies x8,<br>injected at publish"| plat["@voidzero-dev/vite-plus-*<br>platform packages (merged .node)"]
  core -->|"optionalDependencies x8,<br>injected at publish"| plat
Loading

Core's bundled Rolldown requires the same platform packages vite-plus already uses, declared through optionalDependencies injected at publish time. No new packages, no cycle, core works standalone, and package managers dedupe the shared platform package.

@netlify

netlify Bot commented Aug 4, 2026

Copy link
Copy Markdown

Deploy Preview for viteplus-preview canceled.

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

@fengmk2 fengmk2 self-assigned this Aug 4, 2026
@fengmk2
fengmk2 force-pushed the rfc/core-binding-resolution branch from f6f8ce8 to fcca21b Compare August 4, 2026 02:02
@fengmk2 fengmk2 added test: e2e Auto run e2e tests test: install-e2e run vite install e2e test test: create-e2e Run `vp create` e2e tests preview-build Publish this PR's commits to the registry bridge as preview builds test: sfw labels Aug 4, 2026
@fengmk2

fengmk2 commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

@codex review

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Native binary sizes (db02d35)

Final release artifacts built by the canonical build-upstream and build-windows-cli actions.

Artifact Format Base PR Change
vp (Linux x64) Binary 10.52 MiB 10.52 MiB 0 B (0.00%)
vp (Linux x64) gzip -9 4.55 MiB 4.55 MiB 0 B (0.00%)
NAPI (Linux x64) Binary 33.61 MiB 33.61 MiB 0 B (0.00%)
NAPI (Linux x64) gzip -9 13.02 MiB 13.02 MiB 0 B (0.00%)
vp (macOS ARM64) Binary 7.84 MiB 7.84 MiB 0 B (0.00%)
vp (macOS ARM64) gzip -9 3.95 MiB 3.95 MiB 0 B (0.00%)
NAPI (macOS ARM64) Binary 40.92 MiB 40.92 MiB 0 B (0.00%)
NAPI (macOS ARM64) gzip -9 17.24 MiB 17.24 MiB -15 B (-0.00%)
vp (Windows x64) Binary 8.42 MiB 8.42 MiB 0 B (0.00%)
vp (Windows x64) gzip -9 3.67 MiB 3.67 MiB 0 B (0.00%)
NAPI (Windows x64) Binary 27.77 MiB 27.77 MiB 0 B (0.00%)
NAPI (Windows x64) gzip -9 10.86 MiB 10.86 MiB +2 B (+0.00%)
Trampoline (Windows x64) Binary 203.00 KiB 203.00 KiB 0 B (0.00%)
Trampoline (Windows x64) gzip -9 97.91 KiB 97.91 KiB 0 B (0.00%)
Installer (Windows x64) Binary 4.47 MiB 4.47 MiB 0 B (0.00%)
Installer (Windows x64) gzip -9 2.09 MiB 2.09 MiB -1 B (-0.00%)

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: fcca21bee9

ℹ️ 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".

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Registry bridge build (3a79c35)

This commit build is published to the registry bridge, which serves these as ordinary npm versions (every other package proxies to npmjs):

Package Version
vite-plus 0.0.0-commit.3a79c350a1c28c9f491c7c4642e42ed426dda78d
@voidzero-dev/vite-plus-core 0.0.0-commit.3a79c350a1c28c9f491c7c4642e42ed426dda78d

Install the Vite+ CLI built from this commit, then migrate a project:

# macOS / Linux
curl -fsSL https://vite.plus | VP_PR_VERSION=2313 bash
# Windows (PowerShell)
$env:VP_PR_VERSION="2313"; irm https://vite.plus/ps1 | iex

After installing, upgrade the current project's vite-plus to this test build with:

vp migrate

Or point your package manager at the bridge registry https://registry-bridge.viteplus.dev/:

Package manager Registry config
npm / pnpm / Bun .npmrc: registry=https://registry-bridge.viteplus.dev/
Yarn (v2+) .yarnrc.yml: npmRegistryServer: "https://registry-bridge.viteplus.dev/"

Then pin the build (vite aliases to vite-plus-core; pnpm can use a catalog, npm an overrides entry):

{
  "devDependencies": {
    "vite-plus": "0.0.0-commit.3a79c350a1c28c9f491c7c4642e42ed426dda78d",
    "vite": "npm:@voidzero-dev/vite-plus-core@0.0.0-commit.3a79c350a1c28c9f491c7c4642e42ed426dda78d"
  }
}

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🐳 Docker preview image

Built from this PR's registry bridge build:

Image Compressed size
ghcr.io/voidzero-dev/vite-plus:pr-2313 236MB
# remove any stale local copy from a previous run, then pull fresh
docker rmi ghcr.io/voidzero-dev/vite-plus:pr-2313 2>/dev/null; docker pull ghcr.io/voidzero-dev/vite-plus:pr-2313

Quick check:

docker run --rm ghcr.io/voidzero-dev/vite-plus:pr-2313 vp --version

See docs/guide/docker.md for usage.

@fengmk2

fengmk2 commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

Verified the bridge build (fcca21b) against the issue repro (https://github.com/jong-kyung/repro-vite-plus-2054, pnpm enableGlobalVirtualStore: true).

Baseline (repro as published, vite-plus@0.2.2):

vite realpath: ~/Library/pnpm/store/v11/links/@voidzero-dev/vite-plus-core/0.2.2/.../package.json
Error: Cannot find module 'vite-plus/binding'

This build (catalog pinned to 0.0.0-commit.fcca21bee9fa82419c9d0d85541393bf7dd2cace, fresh lockfile, bridge registry):

vite realpath: ~/Library/pnpm/store/v11/links/@voidzero-dev/vite-plus-core/0.0.0-commit.fcca21be.../package.json
rolldown loaded: function

Same layout, core still resolving from the global store realpath, now working. Mechanism checks on the installed artifacts:

  • Installed core declares all 8 @voidzero-dev/vite-plus-<platform> optionalDependencies at the commit version (injected at publish; the committed package.json stays pin-free), and the darwin-arm64 package is a resolvable sibling of core's store realpath.
  • The loader chunk carries 8 version guards at the commit version (the new core stamp in reusable-release-build.yml) and 19 untouched upstream guards at Rolldown 1.2.2 for platforms Vite+ does not ship.
  • NAPI_RS_ENFORCE_VERSION_CHECK=1 passes: the guard is live and matching again, after being dead code in previously published releases.
  • import('vite/rolldown') reports VERSION === "1.2.2", the bundled Rolldown version, no longer rewritten to the vite-plus version.

@fengmk2
fengmk2 marked this pull request as ready for review August 4, 2026 03:27
@fengmk2
fengmk2 force-pushed the rfc/core-binding-resolution branch from 8fc4ef5 to 3a79c35 Compare August 4, 2026 03:33
@fengmk2 fengmk2 added preview-build Publish this PR's commits to the registry bridge as preview builds and removed preview-build Publish this PR's commits to the registry bridge as preview builds labels Aug 4, 2026
fengmk2 added 5 commits August 4, 2026 13:53
Release-built core resolves bundled Rolldown's native binding through
vite-plus/binding, an export of a package core never declares, which
breaks pnpm enable-global-virtual-store, Yarn PnP, and standalone core
installs. The RFC keeps the per-platform rewrite direction from PR #2067
but moves the platform optionalDependencies pins out of the committed
package.json and into the publish pipeline, mirroring how napi-rs
injects the same packages into vite-plus.
Release builds now rewrite each supported @rolldown/binding-<platform>
require to @voidzero-dev/vite-plus-<platform> (suffixes derived from the
CLI's napi.targets via parseTriple) instead of collapsing all of them to
vite-plus/binding, and rewrite each branch's version guard to core's
version in the same specifier-anchored pattern. Branches for platforms
Vite+ does not ship, the WASI fallback, and Rolldown's public VERSION
export stay untouched, and the build fails if the rewrite counts diverge
from the napi-rs loader shape.

publish-native-addons.ts mirrors the napi-injected platform pins from
packages/cli/package.json into core's optionalDependencies in both npm
and pkg-pr-new modes, so core declares what its loader requires while
the committed package.json stays pin-free and release version bumps,
the dirty-tree check, and preview builds keep working.
reusable-release-build.yml stamps packages/core/package.json so preview
builds bake guard versions that match the bridge-served platform
packages.

Closes #2054

See rfcs/core-binding-resolution.md for the design.
binding-resolution-layout.spec.ts rebuilds pnpm's enable-global-virtual-store
shape with stub packages and requires through it in a child process: the old
collapsed rewrite fails with the exact #2054 error, the transform output
resolves through the declared platform package, and the re-armed version
guard rejects a mismatched platform package under
NAPI_RS_ENFORCE_VERSION_CHECK. A PTY snapshot case cannot cover this because
snapshot installs use dev-built core, which embeds the .node in dist and
never takes the rewritten path.

Writing the spec surfaced a transform gap: the guard rewrite only matched
the bundled-chunk __require( form. It now also matches plain require( for
raw CJS loader output; the build.ts count assertion would have caught the
miss, but the transform should handle both.
The export existed only for the old collapsed @rolldown/binding-* rewrite.
Nothing imports the specifier: the CLI loads its binding through a relative
import, and no repo, dist, snapshot, or ecosystem reference exists. Old
published cores that require it always pair with an old vite-plus through
the exact version pin, so removal cannot strand them. The binding/ files
still ship for the CLI's own relative import.
The rewrite transform now reports which platform suffixes it redirected,
and the release build asserts set equality with the published platforms
instead of a specifier/guard count ratio, so a dropped or renamed loader
branch fails the build by name instead of shipping a partial rewrite.
The platform-package prefix derives from the CLI's napi.packageName in
both the transform and the publish script, removing three hard-coded
copies of the scope string.

Also: lazy-load @napi-rs/cli in build.ts (release-only, ~120ms saved on
every dev core build), share one fresh package.json read in
publish-native-addons.ts via readJsonFile, single-source the workflow's
stamp/verify file list through GITHUB_ENV, simplify the guard regex and
both specs, and shrink core BUNDLING.md's derived mapping table to
examples plus a pointer at the napi.targets table.
@fengmk2
fengmk2 force-pushed the rfc/core-binding-resolution branch from 3a79c35 to e872dae Compare August 4, 2026 05:53
@fengmk2
fengmk2 merged commit 0a5e76e into main Aug 4, 2026
105 checks passed
@fengmk2
fengmk2 deleted the rfc/core-binding-resolution branch August 4, 2026 06:40
fengmk2 added a commit that referenced this pull request Aug 5, 2026
…t variable renames, and install fixes (#2325)

Release vite-plus v0.2.8: monorepo target resolution, breaking `VP_*`
environment variable renames, and install fixes.

Bare `vp dev`/`build`/`preview`/`pack` at a monorepo root now resolve a
target package instead of silently running against the root, and three
Vite+-specific environment variables move to the `VP_*` prefix without
compatibility aliases. Two failures that broke Vite+ before it could run
are also fixed: the crash on container images that ship no CA
certificates, and the missing Rolldown binding under pnpm's global
virtual store.

### Breaking Changes

- Rename three Vite+-specific environment variables to the `VP_*`
prefix, with no compatibility aliases, so the old names stop working
([#2312](#2312)), by
@jong-kyung:

  | Old | New |
  | --- | --- |
  | `VITE_LOG` | `VP_LOG` |
  | `VITE_GLOBAL_CLI_JS_SCRIPTS_DIR` | `VP_GLOBAL_CLI_JS_SCRIPTS_DIR` |
  | `VITE_UPDATE_TASK_TYPES` | `VP_UPDATE_TASK_TYPES` |

Update any shell profile, CI job, or Dockerfile that sets the old names.

### Highlights

- Resolve a target package for `vp dev`, `build`, `preview`, and `pack`
at a monorepo root: interactive shells get a fuzzy package picker,
non-interactive runs list the candidates and exit 1 instead of building
the root, and a new global `-C <dir>` flag or a `defaultPackage` setting
(a single directory, or an object mapping each of the four commands to
its own directory) skips the prompt
([#2031](#2031),
[#2305](#2305)), by
@fengmk2
- Stop aborting with exit 134 on container images that ship no CA
certificates (Debian slim, distroless): the shared HTTP client now
retries once with the bundled Mozilla root list, like Node's own bundled
roots, and reports a real error instead of panicking when it still
cannot be built
([#2273](#2273),
[#2295](#2295)), by
@jbmusso and @fengmk2
- Resolve the bundled Rolldown binding through platform packages instead
of an undeclared require back into `vite-plus`, fixing `Cannot find
module 'vite-plus/binding'` under pnpm `enable-global-virtual-store` and
in standalone `@voidzero-dev/vite-plus-core` installs
([#2313](#2313)), by
@fengmk2
- Add `vp pm ci` for reproducible frozen-lockfile installs, and `vp pm
patch` / `vp pm patch-commit` for editing dependencies in place on pnpm,
bun, and Yarn Berry (npm and Yarn Classic warn and exit successfully)
([#2082](#2082),
[#2308](#2308)), by
@forehalo and @jong-kyung

### Features

- Upgrade the bundled toolchain: vite `8.1.5` -> `8.2.0`, rolldown
`1.2.0` -> `1.2.2`, oxlint `1.75.0` -> `1.76.0`, oxfmt `0.60.0` ->
`0.61.0`, and Vite DevTools `0.4.5` -> `0.4.10`
([#2302](#2302),
[#2311](#2311)), by
@voidzero-guard[bot]. The new oxfmt and oxlint can flag code that passed
before, so run `vp fmt` after upgrading if your CI runs `vp check`.
- Read the Node.js version from `.nvmrc` when no other version source is
present ([#2244](#2244)),
by @BlankParticle
- Support pnpm v12, which ships as a native binary: Vite+ now downloads
the platform-specific `@pnpm/exe.*` package and generates native shims,
so `pnpm` and `pnpx` work instead of failing to exec
([#2289](#2289)), by
@jong-kyung
- Verify the downloaded bun platform tarball against the registry
`dist.integrity` hash
([#2310](#2310)), by
@jong-kyung

### Fixes & Enhancements

- Let `vp config` install the Git hook dispatcher without creating or
modifying project hook scripts or staged-file configuration, so a custom
`.vite-hooks/pre-commit` survives
([#2280](#2280)), by
@TheAlexLichter
- Nest immutable global package installs under
`packages/<package>/<uuid>` instead of using `#` in the path, which Node
treated as a URL fragment and which broke dynamic imports inside
installed packages
([#2222](#2222)), by
@liangmiQwQ
- Keep the recorded version spec on global installs, so `vp update -g`
follows a dist tag or range instead of silently resolving back to
`latest`, `vp outdated -g` reports Wanted versus Latest, and `vp update
-g --latest` explicitly moves packages back to `latest`
([#2249](#2249)), by
@TheAlexLichter
- Stop deleting a managed Node.js runtime that another process is
concurrently installing
([#2248](#2248)), by
@shulaoda
- Preserve the real exit code when a spawned process is terminated by a
signal on Unix
([#2154](#2154)), by
@liangmiQwQ
- Honor an explicit `vp create --package-manager` outside monorepos
instead of inheriting the manager from a non-monorepo ancestor directory
([#2226](#2226)), by
@jong-kyung
- Scaffold the `vite:library` template into a directory that contains
only `.git`, while still refusing to overwrite existing user files
([#2287](#2287)), by
@RSS1102
- Render help for delegated commands from the local CLI, so `vp
<command> --help` matches the installed toolchain instead of drifting
([#2184](#2184)), by
@liangmiQwQ
- Resolve `typeAware` and `typeCheck` options inherited through Oxlint
`extends`, so `vp check --no-lint` runs and classifies type checking
correctly
([#2228](#2228)), by
@jong-kyung
- Report `(no version)` instead of `unknown` when globally installing a
local package that has no `version` field
([#2232](#2232)), by
@liangmiQwQ

### Refactor

- Rename the Git hooks environment variable to `VP_GIT_HOOKS`, keeping
`VITE_GIT_HOOKS` working as a deprecated alias
([#2195](#2195)), by
@dennybiasiolli
- Consolidate the package manager infrastructure so typed command
arguments are the source of truth for per-manager compatibility
([#2140](#2140)), by
@forehalo
- Generate the Zed language settings from a language list instead of 17
near-identical blocks
([#2294](#2294)), by
@jong-kyung
- Share the agent-file detect and write traversal helpers so both passes
apply identical rules
([#2296](#2296)), by
@jong-kyung
- Drop redundant clippy allow attributes in the global CLI
([#2235](#2235)), by
@shulaoda

### Docs

- Avoid a duplicate `vp` installation step in the onboarding prompt
([#2291](#2291)), by
@Arcadi4
- Recommend stacked pull requests for submitting changes
([#2281](#2281)), by
@fengmk2
- Correct stale delegation comments in the global CLI
([#2236](#2236)), by
@shulaoda
- Improve the release draft review guidance in the release-manager skill
([#2285](#2285)), by
@wan9chi

### Chore

- Stop emitting unmet peer warnings for the `vite-plus` peer of oxfmt
and oxlint on every install
([#2321](#2321)), by
@fengmk2
- Remove duplicate direct dependency declarations so each build
dependency is owned by one workspace
([#2318](#2318)), by
@jong-kyung
- Declare `@emnapi` peers where `@napi-rs/cli` is used
([#2319](#2319)), by
@jong-kyung
- Exclude `rollup-tests` from the workspace and update `basic-ftp`
([#2322](#2322)), by
@fengmk2
- Remove unused `EnvConfig` fields
([#2320](#2320)), by
@jong-kyung
- Remove the obsolete peer dependency merger tool
([#2303](#2303)), by
@jong-kyung
- Stop the staging deploy from triggering on external pull requests
([#2293](#2293)), by
@BlankParticle
- Kill the real `vp` process, and kill it before its children, in the
`env_install_interrupt` snapshot test
([#2299](#2299),
[#2316](#2316)), by
@fengmk2
- Suppress racy optimizer logs in the `vitest_browser_mode` snapshot
test ([#2297](#2297)), by
@fengmk2
- Remove the obsolete auto-install environment from the snapshot tests
([#2163](#2163)), by
@liangmiQwQ

### Bundled Versions

| Tool | Version | Source |
| --------------- | ---------- |
-------------------------------------------------------------------------------------------------
|
| vite | `8.2.0` |
[`fa79f9a`](vitejs/vite@fa79f9a)
|
| rolldown | `1.2.2` |
[`872b98a`](rolldown/rolldown@872b98a)
|
| tsdown | `0.22.14` | [npm](https://npmx.dev/package/tsdown/v/0.22.14)
|
| vitest | `4.1.10` | [npm](https://npmx.dev/package/vitest/v/4.1.10) |
| oxlint | `1.76.0` | [npm](https://npmx.dev/package/oxlint/v/1.76.0) |
| oxlint-tsgolint | `7.0.2001` |
[npm](https://npmx.dev/package/oxlint-tsgolint/v/7.0.2001) |
| oxfmt | `0.61.0` | [npm](https://npmx.dev/package/oxfmt/v/0.61.0) |

### Upgrade

```bash
vp upgrade
```

### New Contributors

@jbmusso, @Arcadi4, @dennybiasiolli, @RSS1102

**Full Changelog**:
v0.2.7...v0.2.8

---

Merging this PR will trigger the release workflow.

---------

Co-authored-by: voidzero-guard[bot] <278573678+voidzero-guard[bot]@users.noreply.github.com>
Co-authored-by: MK <fengmk2@gmail.com>
fengmk2 added a commit that referenced this pull request Aug 7, 2026
Apply STE-style simplification to the report, scripts, and POC comment.
Link every referenced nub/vp issue. Record the verified #667 status:
PR #2313 fixes the core binding lookup, nub 0.7.2 materializes
packages with undeclared imports per-project, and aube 1.37's slot
layout resolves the old require. vp build passes on current versions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview-build Publish this PR's commits to the registry bridge as preview builds test: create-e2e Run `vp create` e2e tests test: e2e Auto run e2e tests test: install-e2e run vite install e2e test test: sfw

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Avoid circular binding resolution between vite-plus-core and vite-plus

2 participants