[GH-ISSUE #710] Pages Router deploy broken from v0.0.26+: wrangler cannot resolve virtual:vinext-server-entry #153

Closed
opened 2026-05-06 12:37:40 +02:00 by BreizhHardware · 3 comments

Originally created by @aashishvanand on GitHub (Mar 29, 2026).
Original GitHub issue: https://github.com/cloudflare/vinext/issues/710

Description

Starting from vinext v0.0.26, Pages Router projects with custom worker/index.ts entries fail to deploy to Cloudflare Workers. The vinext deploy command (and vinext build && vinext deploy --skip-build) fails because wrangler's esbuild bundler cannot resolve the virtual:vinext-server-entry virtual module that vinext now generates in worker/index.ts.

Environment

  • vinext: v0.0.26 through v0.0.37 (v0.0.25 works)
  • vite: 8.0.3
  • wrangler: 4.78.0
  • Node: 22.22.2
  • Deploy target: Cloudflare Workers (built via Cloudflare Pages CI)
  • Router: Pages Router

Steps to Reproduce

  1. Have a Pages Router project with a custom worker/index.ts (auto-generated by vinext deploy --dry-run)
  2. Upgrade vinext from 0.0.25 to 0.0.26+
  3. Run vinext build && vinext deploy --skip-build

Error 1: Virtual module not resolved

When using vinext build && vinext deploy --skip-build, wrangler's esbuild bundler cannot resolve the virtual module:

> vinext build && vinext deploy --skip-build

  vinext build  (Vite 8.0.3)

  Building client...
  ✓ 1922 modules transformed.
  ✓ built in 1.97s
  Building server...
  ✓ 52 modules transformed.
  ✓ built in 212ms

  Build complete. Run `vinext start` to start the production server.

  vinext deploy  (Vite 8.0.3)

  Deploying to production...
Error: Command failed: /opt/buildhome/repo/node_modules/.bin/wrangler deploy
✘ [ERROR] Build failed with 1 error:

  ✘ [ERROR] Could not resolve "virtual:vinext-server-entry"

      worker/index.ts:19:72:
        19 │ ... runMiddleware, vinextConfig } from "virtual:vinext-server-entry";
           ╵                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    To fix this, you can add an entry to "alias" in your Wrangler configuration.

    at runWranglerDeploy (file:///opt/buildhome/repo/node_modules/vinext/dist/deploy.js:914:17)
    at deploy (file:///opt/buildhome/repo/node_modules/vinext/dist/deploy.js:995:14)
    at async deployCommand (file:///opt/buildhome/repo/node_modules/vinext/dist/cli.js:352:2)

Error 2: Adding wrangler alias — file not found

Adding "alias": { "virtual:vinext-server-entry": "./dist/server/entry.js" } to wrangler.jsonc resolves the virtual module, but when using vinext deploy (without --skip-build), dist/server/entry.js doesn't exist because the server build is skipped entirely — only the client is built:

> vinext deploy

  vinext deploy  (Vite 8.0.3)

  Building for Cloudflare Workers...

  ✓ 1922 modules transformed.
  ✓ built in 2.48s

  Deploying to production...
Error: Command failed: /opt/buildhome/repo/node_modules/.bin/wrangler deploy
✘ [ERROR] Build failed with 1 error:

  ✘ [ERROR] Cannot find module './dist/server/entry.js'
  Require stack:
  - /opt/buildhome/repo/node_modules/wrangler/wrangler-dist/cli.js [plugin alias]

      worker/index.ts:19:72:
        19 │ ... runMiddleware, vinextConfig } from "virtual:vinext-server-entry";
           ╵                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    This error came from the "onResolve" callback registered here:

      node_modules/wrangler/wrangler-dist/cli.js:131750:13:
        131750 │       build5.onResolve({ filter: filter2 }, (args) => {
               ╵              ~~~~~~~~~

Error 3: Missing client entry globals (UI stuck on loading)

Even when deploy succeeds (using the alias + vinext build && vinext deploy --skip-build), the UI is stuck on "Loading API Documentation..." because vinext's post-build hook doesn't inject __VINEXT_CLIENT_ENTRY__ / __VINEXT_SSR_MANIFEST__ globals for Pages Router. The hook looks for a wrangler.json file in a dist subdirectory (line ~2274 of dist/index.js) which doesn't exist, so it returns early without injecting the client entry script tag into the HTML.

Root Cause

Four issues combine:

  1. v0.0.26+ requires @cloudflare/vite-plugin in vite.config.ts — builds fail without it (Error: [vinext] Missing @cloudflare/vite-plugin)
  2. The auto-generated worker/index.ts uses virtual:vinext-server-entry — this is a Vite virtual module that wrangler's esbuild bundler cannot resolve
  3. vinext deploy calls wrangler deploy directly (via execFileSync at deploy.js:914) which re-bundles the worker with esbuild, bypassing Vite's plugin pipeline where virtual modules are resolved
  4. For Pages Router, the post-build hook doesn't inject __VINEXT_CLIENT_ENTRY__ / __VINEXT_SSR_MANIFEST__ globals — it looks for a directory with wrangler.json in dist (index.js:2274) which doesn't exist with the cloudflare() plugin, causing the client entry script to never be injected into the HTML

Workaround

Pin vinext to v0.0.25 and use the old worker pattern with direct imports:

// worker/index.ts — use direct imports instead of virtual module
import { renderPage, handleApiRoute, runMiddleware, vinextConfig } from "../dist/server/entry.js";
import ssrManifest from "../dist/client/.vite/ssr-manifest.json";
import buildManifest from "../dist/client/.vite/manifest.json";

// Manually set globals that the server entry reads
(globalThis as any).__VINEXT_SSR_MANIFEST__ = ssrManifest;
(globalThis as any).__VINEXT_CLIENT_ENTRY__ = Object.values(buildManifest).find(
  (entry: any) => entry.isEntry
)?.file;

With vite.config.ts (no cloudflare plugin needed on v0.0.25):

import { defineConfig } from "vite";
import vinext from "vinext";
export default defineConfig({ plugins: [vinext()] });

Deploy script: vinext build && vinext deploy --skip-build

Expected Behavior

vinext deploy should work for Pages Router projects with custom worker entries, either by:

  • Pre-bundling the worker through Vite before passing to wrangler
  • Generating worker code that uses direct file imports instead of virtual modules
  • Auto-injecting the wrangler alias for virtual:vinext-server-entry
  • Properly injecting __VINEXT_CLIENT_ENTRY__ and __VINEXT_SSR_MANIFEST__ globals in the post-build step for Pages Router
Originally created by @aashishvanand on GitHub (Mar 29, 2026). Original GitHub issue: https://github.com/cloudflare/vinext/issues/710 ## Description Starting from vinext v0.0.26, Pages Router projects with custom `worker/index.ts` entries fail to deploy to Cloudflare Workers. The `vinext deploy` command (and `vinext build && vinext deploy --skip-build`) fails because wrangler's esbuild bundler cannot resolve the `virtual:vinext-server-entry` virtual module that vinext now generates in `worker/index.ts`. ## Environment - **vinext**: v0.0.26 through v0.0.37 (v0.0.25 works) - **vite**: 8.0.3 - **wrangler**: 4.78.0 - **Node**: 22.22.2 - **Deploy target**: Cloudflare Workers (built via Cloudflare Pages CI) - **Router**: Pages Router ## Steps to Reproduce 1. Have a Pages Router project with a custom `worker/index.ts` (auto-generated by `vinext deploy --dry-run`) 2. Upgrade vinext from 0.0.25 to 0.0.26+ 3. Run `vinext build && vinext deploy --skip-build` ## Error 1: Virtual module not resolved When using `vinext build && vinext deploy --skip-build`, wrangler's esbuild bundler cannot resolve the virtual module: ``` > vinext build && vinext deploy --skip-build vinext build (Vite 8.0.3) Building client... ✓ 1922 modules transformed. ✓ built in 1.97s Building server... ✓ 52 modules transformed. ✓ built in 212ms Build complete. Run `vinext start` to start the production server. vinext deploy (Vite 8.0.3) Deploying to production... Error: Command failed: /opt/buildhome/repo/node_modules/.bin/wrangler deploy ✘ [ERROR] Build failed with 1 error: ✘ [ERROR] Could not resolve "virtual:vinext-server-entry" worker/index.ts:19:72: 19 │ ... runMiddleware, vinextConfig } from "virtual:vinext-server-entry"; ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To fix this, you can add an entry to "alias" in your Wrangler configuration. at runWranglerDeploy (file:///opt/buildhome/repo/node_modules/vinext/dist/deploy.js:914:17) at deploy (file:///opt/buildhome/repo/node_modules/vinext/dist/deploy.js:995:14) at async deployCommand (file:///opt/buildhome/repo/node_modules/vinext/dist/cli.js:352:2) ``` ## Error 2: Adding wrangler alias — file not found Adding `"alias": { "virtual:vinext-server-entry": "./dist/server/entry.js" }` to `wrangler.jsonc` resolves the virtual module, but when using `vinext deploy` (without `--skip-build`), `dist/server/entry.js` doesn't exist because the server build is skipped entirely — only the client is built: ``` > vinext deploy vinext deploy (Vite 8.0.3) Building for Cloudflare Workers... ✓ 1922 modules transformed. ✓ built in 2.48s Deploying to production... Error: Command failed: /opt/buildhome/repo/node_modules/.bin/wrangler deploy ✘ [ERROR] Build failed with 1 error: ✘ [ERROR] Cannot find module './dist/server/entry.js' Require stack: - /opt/buildhome/repo/node_modules/wrangler/wrangler-dist/cli.js [plugin alias] worker/index.ts:19:72: 19 │ ... runMiddleware, vinextConfig } from "virtual:vinext-server-entry"; ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This error came from the "onResolve" callback registered here: node_modules/wrangler/wrangler-dist/cli.js:131750:13: 131750 │ build5.onResolve({ filter: filter2 }, (args) => { ╵ ~~~~~~~~~ ``` ## Error 3: Missing client entry globals (UI stuck on loading) Even when deploy succeeds (using the alias + `vinext build && vinext deploy --skip-build`), the UI is stuck on "Loading API Documentation..." because vinext's post-build hook doesn't inject `__VINEXT_CLIENT_ENTRY__` / `__VINEXT_SSR_MANIFEST__` globals for Pages Router. The hook looks for a `wrangler.json` file in a dist subdirectory (line ~2274 of `dist/index.js`) which doesn't exist, so it returns early without injecting the client entry script tag into the HTML. ## Root Cause Four issues combine: 1. **v0.0.26+ requires `@cloudflare/vite-plugin`** in `vite.config.ts` — builds fail without it (`Error: [vinext] Missing @cloudflare/vite-plugin`) 2. **The auto-generated `worker/index.ts` uses `virtual:vinext-server-entry`** — this is a Vite virtual module that wrangler's esbuild bundler cannot resolve 3. **`vinext deploy` calls `wrangler deploy` directly** (via `execFileSync` at `deploy.js:914`) which re-bundles the worker with esbuild, bypassing Vite's plugin pipeline where virtual modules are resolved 4. **For Pages Router, the post-build hook doesn't inject `__VINEXT_CLIENT_ENTRY__` / `__VINEXT_SSR_MANIFEST__` globals** — it looks for a directory with `wrangler.json` in dist (index.js:2274) which doesn't exist with the cloudflare() plugin, causing the client entry script to never be injected into the HTML ## Workaround Pin vinext to v0.0.25 and use the old worker pattern with direct imports: ```ts // worker/index.ts — use direct imports instead of virtual module import { renderPage, handleApiRoute, runMiddleware, vinextConfig } from "../dist/server/entry.js"; import ssrManifest from "../dist/client/.vite/ssr-manifest.json"; import buildManifest from "../dist/client/.vite/manifest.json"; // Manually set globals that the server entry reads (globalThis as any).__VINEXT_SSR_MANIFEST__ = ssrManifest; (globalThis as any).__VINEXT_CLIENT_ENTRY__ = Object.values(buildManifest).find( (entry: any) => entry.isEntry )?.file; ``` With `vite.config.ts` (no cloudflare plugin needed on v0.0.25): ```ts import { defineConfig } from "vite"; import vinext from "vinext"; export default defineConfig({ plugins: [vinext()] }); ``` Deploy script: `vinext build && vinext deploy --skip-build` ## Expected Behavior `vinext deploy` should work for Pages Router projects with custom worker entries, either by: - Pre-bundling the worker through Vite before passing to wrangler - Generating worker code that uses direct file imports instead of virtual modules - Auto-injecting the wrangler alias for `virtual:vinext-server-entry` - Properly injecting `__VINEXT_CLIENT_ENTRY__` and `__VINEXT_SSR_MANIFEST__` globals in the post-build step for Pages Router
Author
Owner

@james-elicx commented on GitHub (Mar 29, 2026):

Are you able to share a minimal reproduction repository?

<!-- gh-comment-id:4149854317 --> @james-elicx commented on GitHub (Mar 29, 2026): Are you able to share a minimal reproduction repository?
Author
Owner

@aashishvanand commented on GitHub (Mar 29, 2026):

Here's a minimal reproduction repository:

https://github.com/aashishvanand/vinext-pages-router-repro

To reproduce

git clone https://github.com/aashishvanand/vinext-pages-router-repro
cd vinext-pages-router-repro
npm install

Works on v0.0.25 (current pinned version)

npm run build          # builds client + server successfully
npx wrangler deploy --dry-run  # wrangler bundles successfully

Breaks on v0.0.26+

npm install vinext@0.0.26
npx vinext build

Fails immediately:

Error: [vinext] Missing @cloudflare/vite-plugin in vite.config.ts.

After adding cloudflare() to vite.config.ts and running vinext deploy --dry-run, the worker gets regenerated with virtual:vinext-server-entry, and wrangler deploy fails:

✘ [ERROR] Could not resolve "virtual:vinext-server-entry"

What the repro shows

  • Minimal Pages Router project (src/pages/index.tsx with a dynamic import)
  • Custom worker/index.ts using direct imports (the workaround that works on v0.0.25)
  • wrangler.jsonc with Workers deployment config
  • The worker/index.ts manually sets __VINEXT_SSR_MANIFEST__ and __VINEXT_CLIENT_ENTRY__ globals which vinext's post-build hook fails to inject for Pages Router from v0.0.26+
<!-- gh-comment-id:4149903525 --> @aashishvanand commented on GitHub (Mar 29, 2026): Here's a minimal reproduction repository: **https://github.com/aashishvanand/vinext-pages-router-repro** ## To reproduce ```bash git clone https://github.com/aashishvanand/vinext-pages-router-repro cd vinext-pages-router-repro npm install ``` ### ✅ Works on v0.0.25 (current pinned version) ```bash npm run build # builds client + server successfully npx wrangler deploy --dry-run # wrangler bundles successfully ``` ### ❌ Breaks on v0.0.26+ ```bash npm install vinext@0.0.26 npx vinext build ``` Fails immediately: ``` Error: [vinext] Missing @cloudflare/vite-plugin in vite.config.ts. ``` After adding `cloudflare()` to vite.config.ts and running `vinext deploy --dry-run`, the worker gets regenerated with `virtual:vinext-server-entry`, and `wrangler deploy` fails: ``` ✘ [ERROR] Could not resolve "virtual:vinext-server-entry" ``` ## What the repro shows - Minimal Pages Router project (`src/pages/index.tsx` with a dynamic import) - Custom `worker/index.ts` using **direct imports** (the workaround that works on v0.0.25) - `wrangler.jsonc` with Workers deployment config - The `worker/index.ts` manually sets `__VINEXT_SSR_MANIFEST__` and `__VINEXT_CLIENT_ENTRY__` globals which vinext's post-build hook fails to inject for Pages Router from v0.0.26+
Author
Owner

@james-elicx commented on GitHub (Mar 29, 2026):

Looks like we weren't building with the Vite config properly if you ran vinext build in a pager router-only project. Should be fixed in the next release.

<!-- gh-comment-id:4150088084 --> @james-elicx commented on GitHub (Mar 29, 2026): Looks like we weren't building with the Vite config properly if you ran `vinext build` in a pager router-only project. Should be fixed in the next release.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#153
No description provided.