87 Commits

Author SHA1 Message Date
James Anderson 4fe92e9d79 fix: apps with both app + pages router returning 404s (#252)
* fix: apps with both app + pages router returning 404s

* apply same fix for api routes

* test for pages dir route

* always filter x-middleware headers, and preserve others on redirect
2026-03-04 20:46:27 -06:00
Liu Xiaopai 5f593983bd fix(app-router): avoid React 19 dev Flight decode crash in SSR (#118)
* fix(app-router): avoid React 19 dev Flight decode crash in SSR (#50)

* fix(app-router): avoid React 19 dev Flight decode crash in SSR (#50)

* test(ecosystem): harden fixture cleanup and isolate better-auth signup data

* chore: address review nits on React 19 dev Flight decode fix

- Convert standalone JSDoc block to plain // comment to avoid confusion
  with the adjacent createRscEmbedTransform JSDoc
- Add note that VinextFlightRoot returns a thenable (not ReactNode),
  relying on React 19's internal thenable-as-child behavior
- Add comment noting the 'Switched to client rendering' assertion
  depends on React internal message text
- Clarify error.tsx fixture exists for realistic route structure, not
  because the error boundary fires during SSR
- Merge main into branch

---------

Co-authored-by: root <root@localhost.localdomain>
Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-03-04 18:24:30 -06:00
milkboy2564 4ed5c89346 fix(rsc): emit actionable hint when non-plain object crosses server-client boundary (#250)
* fix(rsc): emit actionable hint when non-plain object crosses server-client boundary

  When a Server Component passes a class instance, ESM module namespace
  object, or null-prototype object as a prop to a Client Component,
  React's RSC serializer throws "Only plain objects, and a few built-ins,
  can be passed to Client Components from Server Components." In dev mode
  this error previously surfaced in the browser with no indication of
  which component or prop triggered it, making it hard to debug in large
  apps.

  Add a console.error in rscOnError that detects this specific error and
  prints an actionable guide explaining the four common causes, including
  the key ESM vs webpack difference: Vite produces true ESM module
  namespace objects (non-serializable), while Next.js webpack produces
  plain __esModule objects (serializable) — causing code to silently work
  in plain Next.js but correctly fail in vinext.

* test(rsc): add unit tests for rscOnError non-plain object dev hint

  Verify that the generated RSC entry code produced by generateRscEntry
  includes the dev-mode hint for issue #237. Tests cover:
  - Detection string for "Only plain objects" error is present
  - Hint is guarded by NODE_ENV !== "production"
  - Actionable guidance for module namespace objects is included
  - Actionable guidance for class instances is included
  - Existing digest return path for navigation errors is unaffected

* fix: escape newlines in rscOnError hint and add runtime tests

- Double-escape \n in the console.error hint strings so the generated
  JavaScript output contains valid string literals (the single-escaped
  \n was resolved to literal newlines by the template literal, producing
  unterminated strings that broke production builds).
- Add explicit 'return undefined' after the hint console.error block.
- Add runtime tests that extract rscOnError from the generated code via
  new Function() and verify: digest passthrough for navigation errors,
  console.error hint for RSC serialization errors, and undefined return
  for generic dev errors.

---------

Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-03-04 23:30:58 +00:00
Nathan Nguyen 706f40be0d fix: register MDX plugin at top level to fix transform ordering (#160)
* fix: register MDX plugin at top level to fix transform ordering

Plugins returned from the config() hook run after vite:import-analysis,
so @mdx-js/rollup transforms never fire. Move MDX to a top-level proxy
plugin with enforce:"pre" that delegates to the lazily-created MDX
instance once config() populates it.

Fixes #77

* address review: add config proxy, remove dead resolveId, fix comment separation

- Replace resolveId proxy with config proxy: @mdx-js/rollup has no
  resolveId hook, but its config() hook initializes format-aware
  processors with the Vite mode (development vs production). Without
  forwarding config(), MDX always compiles in production mode.
- Remove dead resolveId proxy (dead code, @mdx-js/rollup has no such hook)
- Separate MDX proxy comment from vinext:react-canary comment
- Update test to verify config and transform hooks on the proxy

* add E2E test for MDX import transform ordering

A regular page.tsx imports content.mdx as a React component and
renders it. This verifies the vinext:mdx proxy plugin transforms
.mdx files before vite:import-analysis runs (the core fix from #77).

---------

Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-03-04 22:47:28 +00:00
MD YUNUS b89afe0736 fix: wire up RouterContext.Provider in Pages Router (#239) (#249)
Add wrapWithRouterContext() function to the next/router shim that wraps
any React element in a <RouterContext.Provider>. This is the root-cause
fix: it builds the router value object directly from the SSR context
(server) or window.location + Router singleton (client) — no hooks, no
duplicate state, no extra component.

This provides a single source of truth for the router context value,
matching how Next.js provides RouterContext at the framework level.

Wrapping sites:
- dev-server.ts: main render, ISR re-render, error page render
- index.ts virtual module: production renderPage and ISR re-render
- Hydration script: client-side hydration entry
- navigateClient: client-side navigation re-renders

Tests:
- useRouter returns null without provider (default/App Router)
- useRouter returns full router when wrapWithRouterContext wraps tree
- useRouter reflects SSR context (pathname, query, asPath)
- wrapWithRouterContext is exported from next/router

Closes #239
2026-03-04 22:12:29 +00:00
Stephen Zhou a7fa81f190 fix: tree shaking for google fonts (#245) 2026-03-04 22:01:12 +00:00
Stephen Zhou 5b9d7756b2 fix: set correct NODE_ENV for build (#247)
* fix: set NODE_ENV

* address review feedback: readability, test coverage, and nextConfig.env guard

- Replace nested ternary with if/else for NODE_ENV resolution
- Add comment explaining why beforeEach/afterEach is file-scoped
- Add test cases: serve (development), build without mode, user override
- Skip NODE_ENV key in nextConfig.env loop to prevent silent override
- Extract setupTmpProject helper to reduce test boilerplate

---------

Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-03-04 21:59:40 +00:00
harris.dev df3b2c3e37 fix(shims): add missing URL setters to NextURL (#125)
* fix(shims): add missing URL setters to NextURL

The NextURL class only had setters for pathname, search, and hash, leaving
port, host, hostname, protocol, href, username, and password as getter-only.

Libraries like next-intl's getAlternateLinksHeaderValue clone nextUrl then set
.port and .host to normalise the URL for alternate-link headers. In Cloudflare
Workers, x-forwarded-host is always present, so this code path is always
triggered, producing:

  TypeError: Cannot set property port of [object Object] which has only a getter

Add setters for all writable URL properties so NextURL behaves identically to
the platform URL class.

Fixes runtime middleware errors when using next-intl in production on Workers.

* revert build artifact to main (remove local machine paths)

---------

Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-03-04 17:57:40 +00:00
gagipro 702367da9b fix: normalize null-prototype params from matchPattern() for RSC serialization (#243)
matchPattern() uses Object.create(null), producing objects without
Object.prototype. The RSC serializer rejects these during dynamic route
rendering, causing 500 errors.

The fix spreads params ({...obj}) to restore a normal prototype, then
assigns properties onto the Promise to preserve synchronous access
(params.id, params.slug) for pre-Next.js 15 code. This satisfies both
constraints:
1. RSC serializer accepts objects with Object.prototype
2. Existing components can access params synchronously without await

Fixes #241
2026-03-03 19:02:29 -06:00
MD YUNUS 567f0ddf1d fix: export ServerInsertedHTMLContext from next/navigation shim (#145) (#151)
Wire ServerInsertedHTMLContext into the SSR pipeline so libraries like
Apollo Client, styled-components, and emotion that use
useContext(ServerInsertedHTMLContext) get a working callback registration
function during SSR.

Changes:
- Add ServerInsertedHTMLContext export to navigation.ts (single shared
  context, RSC-safe with null fallback)
- Wrap SSR rendered tree with ServerInsertedHTMLContext.Provider in
  handleSsr() — the provider value is useServerInsertedHTML itself, so
  both direct callers and useContext() callers push to the same
  ALS-backed callback array
- Add TypeScript declaration in next-shims.d.ts
- Add 8 integration tests verifying the actual Apollo Client pattern
  (useContext returns a usable callback, callbacks are flushed correctly)

Addresses review feedback from #151:
- Single context (no dead getServerInsertedHTMLContext)
- Provider wired into SSR pipeline (not disconnected)
- Tests verify real integration behavior, not React internals
2026-03-03 18:56:35 -06:00
Stringer fe0a48dde4 fix: lowercase package name before sanitizing Worker name (#159)
* fix: lowercase package name before sanitizing Worker name

* test: assert exact sanitized project name value

Strengthen the sanitization test to assert the exact expected value
('my-app-v2') instead of just matching a regex pattern. The regex
assertion passed even when the bug was present.

---------

Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-03-03 17:35:54 +00:00
Justin Finnegan 8058c34761 fix: use detected package manager in install error/hint messages (#187)
* fix: use detected package manager in error/hint messages

Error messages and install hints in index.ts, check.ts, and cli.ts
were hardcoded to use `npm install`. This PR uses the existing
`detectPackageManager()` / `detectPackageManagerName()` utilities
(already used correctly in deploy.ts) so that Yarn, pnpm, and Bun
users see the right install command.

Before (on a Yarn project):
  Run: npm install -D @vitejs/plugin-rsc

After:
  Run: yarn add -D @vitejs/plugin-rsc

Fixes #186

* fix: use detectPackageManager() in cli.ts per review feedback

Replace detectPackageManagerName() + manual 'add -D' with
detectPackageManager() which already returns the correct install
command. This avoids producing 'npm add -D' (undocumented alias)
for npm users and stays consistent with check.ts and index.ts.

---------

Co-authored-by: Homiebot <homiebot@homielab.tech>
Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-03-03 17:13:41 +00:00
Liu Xiaopai d5e1f0029d fix(init): detect Bun and invoking package manager reliably (#139)
* fix(init): detect bun and invoking package manager reliably

* refactor: address review feedback on detectPackageManagerName

- Combine bun.lock/bun.lockb checks into block form for consistency
  with the npm lockfile check
- Accept optional env parameter (defaults to process.env) so the
  function can be unit-tested without mutating global state

---------

Co-authored-by: root <root@localhost.localdomain>
Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-03-03 17:01:05 +00:00
mounir eefee6d849 fix: resolve conditional React Hook calls in Link and Script shims (#223)
* fix: resolve conditional React Hook calls in Link and Script shims

Move early returns after all hook calls to satisfy the Rules of Hooks.

- link.tsx: the dangerous-scheme early return was before useState/useRef/
  useEffect/useCallback/useMemo — moved it to after all hooks
- script.tsx: the SSR early return was before useEffect — moved useEffect
  before the check since it never runs during SSR anyway

* fix: skip prefetch setup for dangerous href schemes

---------

Co-authored-by: Mounir Haddou <mhaddou@c1r2p4.1337.ma>
Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-03-03 16:40:17 +00:00
Robert Soriano 190c1c50e3 fix: Add next/compat/router shim (#224)
* feat: Add next/compat/router shim

* chore: update doc

* Apply suggestion from @ask-bonk[bot]

Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>

---------

Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>
2026-03-03 10:26:23 -06:00
Vikas Kapadiya 985e49980d feat: add /dist/ to .gitignore during vinext init (#220)
* feat: add /dist/ to .gitignore during vinext init

vinext build outputs to /dist/ which should not be committed.
The init command now ensures /dist/ is in .gitignore, creating
the file if needed and skipping if the entry already exists.

* fix: broaden .gitignore duplicate check to cover dist/ and dist variants

Address review feedback: detect dist/ (no leading slash) and bare dist
as existing coverage for the dist directory to avoid adding a redundant
/dist/ entry. Rename entry -> exactEntry for clarity.
2026-03-03 16:16:48 +00:00
Steve Faulkner 9b01dfec65 fix: load .env files into process.env before config evaluation (#234)
Closes #228. vinext was not loading .env files, so environment variables
defined in .env were unavailable to server-side code (getServerSideProps,
API routes, server components) and NEXT_PUBLIC_* vars were missing from
the client bundle defines.

Uses Vite's loadEnv() in the config hook with an empty prefix to load
all .env vars (not just VITE_-prefixed) into process.env before
loadNextConfig() runs, matching Next.js behavior.
2026-03-03 09:19:45 -06:00
Steve Faulkner 5ece95e9f8 fix: handle SVGs in image optimization to match Next.js behavior (#205) (#236)
SVGs are auto-skipped on the client side (set unoptimized=true) unless
dangerouslyAllowSVG is enabled in next.config. When dangerouslyAllowSVG
is true, SVGs that reach the server are passed through without
transformation, with security headers (CSP, Content-Disposition)
applied.

Config options added to NextConfig.images:
- dangerouslyAllowSVG (default: false)
- contentDispositionType (default: "inline")
- contentSecurityPolicy (default: "script-src 'none'; frame-src 'none'; sandbox;")

Co-authored-by: Dhruv Sharma <dhruvcoding67@gmail.com>
2026-03-03 08:53:36 -06:00
Steve Faulkner 1556dad856 fix: new routes fail on running dev server (#235)
* Skip deploy previews for fork PRs that lack Cloudflare secrets

Fork PRs don't have access to repository secrets (CLOUDFLARE_API_TOKEN,
CLOUDFLARE_ACCOUNT_ID), so deploy/smoke-test/comment steps always fail.
Add a fork detection condition to skip these steps gracefully. The build
steps still run, so example builds are still validated for fork PRs.

* fix: invalidate virtual RSC entry when app routes are added or removed

The App Router route table is baked into the virtual RSC entry module
at generation time. When a new route file is added (or removed) while
the dev server is running, invalidateAppRouteCache() cleared the route
scanner cache but the virtual module in Vite's module graph was never
invalidated. This meant the load() hook was never re-called and the
route table stayed stale, causing 404s for new routes until restart.

Now the file watcher also invalidates the virtual RSC entry in Vite's
module graph and sends a full-reload to the RSC environment, forcing
route re-generation.

Fixes #226
2026-03-03 08:14:10 -06:00
harris.dev 74f92ac910 fix(shims): copy request headers into mutable Headers in headersContextFromRequest (#128)
In Cloudflare Workers, Request.headers is immutable per the Fetch API spec.
headersContextFromRequest was storing the original request.headers reference
directly in HeadersContext, so when applyMiddlewareRequestHeaders later called
ctx.headers.set() (to propagate middleware-modified headers like x-request-url
into the server component context), Workers threw:

  TypeError: Can't modify immutable headers.

Fix by wrapping with new Headers(request.headers), which creates an independent
mutable copy while preserving all header values.
2026-03-01 19:44:37 -06:00
Jökull Sólberg Auðunsson f56b14d474 fix: matchConfigPattern incorrectly matches :param with literal suffix (#191)
Patterns like `/:slug.md` were falling through to the simple segment
matcher, which treated `slug.md` as the entire parameter name and
matched any single-segment path (including `/`). This caused rewrites
like `{ source: "/:slug.md", destination: "/api/markdown/:slug" }` to
rewrite every page request, resulting in 404s.

Add `/:[\w-]+\./.test(pattern)` to the condition that triggers the
regex-based matcher, which already tokenizes `:slug` + `.` + `md`
correctly.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 19:27:15 -06:00
Pooya Parsa e075adfba0 feat: support nitro plugin to deploy everywhere (#82)
* feat: support nitro plugin to deploy everywhere

* try vite 7

* cover more features in playground

---------

Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-03-01 18:54:59 +00:00
Steve Faulkner 512bf0dc71 fix: make RequestContext required on matchHeaders/matchRedirect/matchRewrite (#213)
Next.js always has request context when evaluating has/missing
conditions on headers, redirects, and rewrites. There is no code path
where these are evaluated without a request.

Making ctx required eliminates ambiguity about what happens when
context is missing, and surfaces any future call sites that forget
to pass it as compile-time errors instead of silent behavioral bugs.

Also fixes deploy.ts matchHeaders call that was missing reqCtx.

Co-authored-by: Eray <erayackgoz@gmail.com>
2026-03-01 12:03:02 -06:00
Steve Faulkner 1d3884981e fix: use function form of glob exclude for Node < 22.14 compat (#212)
The string array form of the exclude option on node:fs/promises glob
was only added in Node 22.14.0. Users on Node 22.0.0-22.13.x get
ERR_INVALID_ARG_TYPE at runtime. Switch to the function form which
works on all Node >= 22.

Co-authored-by: Eray <erayackgoz@gmail.com>
2026-03-01 12:02:32 -06:00
Steve Faulkner 199297c4f4 fix: throw on missing middleware/proxy exports instead of failing open (#203)
* fix: throw on missing middleware/proxy exports instead of failing open

Middleware and proxy files that don't export the expected function
(e.g. misspelled export, wrong named export) were silently skipped,
letting requests through unprotected. This matches Next.js behavior
which throws a ProxyMissingExportError in the same scenario.

Changes:
- proxy.ts files now require 'proxy' or 'default' export
- middleware.ts files now require 'middleware' or 'default' export
- All 3 code paths updated (Pages dev, Pages prod, App Router)
- Added resolveMiddlewareHandler() with file-type-aware validation
- Ported test cases from Next.js proxy-missing-export test suite
- Updated AGENTS.md: added required step to search Next.js tests

Ported from Next.js: test/e2e/app-dir/proxy-missing-export/
https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/proxy-missing-export/proxy-missing-export.test.ts

* fix: update export precedence test to match Next.js behavior (named > default)

* fix: use ?? instead of || in generated middleware export resolution for consistency

Matches the shared resolveMiddlewareHandler() which uses nullish
coalescing. No behavioral difference since typeof check catches
all cases, but keeps the three code paths consistent.
2026-02-28 23:18:38 +00:00
Paul 584c2e9266 Fix Pages prod middleware named proxy export resolution (#188)
* fix pages prod middleware named proxy export

* remove security remediation report from PR
2026-02-28 22:26:37 +00:00
tomasgferreira 3d8aed9868 fix(config): respect has/missing conditions in next.config headers (#48)
* fix(config): respect has/missing conditions in next.config headers

Adds has/missing condition support to matchHeaders, wires request context
through dev/prod paths, updates NextHeader types, and adds unit + e2e tests
for header/cookie/query conditions.

* chore: add backward-compat comment for optional ctx in matchHeaders

Addresses review feedback: clarify that when no request context is
passed, has/missing conditions are silently skipped for backward
compatibility with existing callers.

* fix: wire has/missing conditions through redirects and rewrites in Pages Router dev

Pages Router dev was the only path missing has/missing support for
redirects and rewrites. The reqCtx was already being built for headers,
so hoist it and pass it to applyRedirects and applyRewrites too.

Also refactors applyRedirects and applyRewrites to delegate to the
shared matchRedirect/matchRewrite functions, eliminating duplicated
pattern-matching logic (same dedup pattern used for applyHeaders).

---------

Co-authored-by: tomoconstrutor <tomoconstrutor@gmail.com>
Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-02-28 16:05:47 -06:00
Stringer bba9ebff92 fix: add React version check to vinext build (#194)
vinext deploy already checks if React needs upgrading for
react-server-dom-webpack compatibility, but vinext build did not.

Users who run `vinext build` + `wrangler deploy` manually (bypassing
vinext deploy) would get a cryptic runtime crash:
  TypeError: Cannot read properties of undefined (reading 'moduleMap')

This adds the same getReactUpgradeDeps() check to buildApp(), so
React <19.2.4 is automatically upgraded before App Router builds.

Closes: moduleMap runtime error on deployed Workers (react@19.2.0)
2026-02-28 15:40:38 -06:00
Stephen Zhou c0bc16c549 fix: export all google font (#57) 2026-02-28 21:35:54 +00:00
Zeb Piasecki bcd4bb14cf feat: add CJS support via vite-plugin-commonjs (#198)
* feat: add CJS support via vite-plugin-commonjs

Add vite-plugin-commonjs to handle require() and module.exports in
both App Router and Pages Router. This enables CJS interop in server
components and pages, including CJS require('server-only') boundary
enforcement by @vitejs/plugin-rsc.

- Add vite-plugin-commonjs as a dependency and register it in the plugin array
- Add App Router fixtures: CJS basic, CJS server-only, CJS server-only
  violation (client component importing server-only module)
- Add Pages Router fixture: CJS basic (require/module.exports)
- Add integration tests for both routers (cjs.test.ts)
- Add App Router e2e tests for CJS rendering and server-only boundary
  enforcement (tests/e2e/app-router/cjs.spec.ts)
- Add Pages Router e2e test for CJS rendering
  (tests/e2e/pages-router/cjs.spec.ts)

* fix: use buildViteConfig() for Pages Router production builds

The Pages Router build path hardcoded plugins: [vinext()] in inline
config, duplicating plugins when a vite.config.ts also registers
vinext(). This caused double-transformation errors during production
builds. Use buildViteConfig() instead, matching the App Router path
which already handles this correctly.

* fix: move server-only-violation fixture to isolated directory

The server-only-violation fixture causes a fatal build error during
production builds of app-basic (the RSC plugin rejects the intentional
violation). This breaks unrelated tests in app-router.test.ts.

Move the fixture to tests/fixtures/app-cjs-violation/ so it doesn't
poison the shared app-basic production build. Remove the E2E test case
since it requires the app-basic dev server; the server-only boundary
enforcement for CJS require() is verified by the RSC plugin's build
validation.

* fix: address review feedback from bonk

- Remove unused components/random.tsx (dead code, Pages Router CJS test
  uses pages/cjs/random.ts instead)
- Add trailing newlines to fixture files
- Move commonjs() plugin earlier in array (after tsconfigPaths, before
  vinext:config) so CJS->ESM transform runs before directive scanning
- Tighten test assertions: use regex for 'Random:.*4' to be specific
  while handling React SSR comment nodes between text and expressions

---------

Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-02-28 15:23:21 -06:00
Fred K. Schott 6914f5d567 fix: pre-bundle react/jsx-runtime to prevent CJS-in-browser errors (#155)
Add react/jsx-runtime and react/jsx-dev-runtime to the client
optimizeDeps.include list. These are CJS modules that Vite's
dependency scanner can miss when entries are virtual modules,
causing the raw CJS to be served to the browser and producing:

  SyntaxError: does not provide an export named 'jsx'

Explicitly pre-bundling them ensures Vite always converts them
to ESM for the browser.

Fixes #25
2026-02-28 15:08:24 -06:00
Dane e9fdc7b5b0 fix: bound fetch cache key body serialization (#176)
* fix: bound fetch cache key body serialization

* Safeguard cache key serialization from large bodies

Bump cache key version to v2 and add safeguards to serializeBody to avoid OOM when processing large request bodies. Track cumulative raw byte size of streamed chunks and throw BodyTooLargeForCacheKeyError when exceeding the 1 MiB limit, call reader.cancel on errors, and add a cheap string-length pre-check. Adds tests covering oversized Uint8Array, string, multi-chunk ReadableStream, and FormData/File cases to verify such requests bypass the cache and still perform fetches.

---------

Co-authored-by: Sunil Pai <spai@cloudflare.com>
2026-02-27 22:30:02 +00:00
Dane b70f4037fb fix: preserve x-middleware-request-* headers in generated runMiddleware (#175)
* fix: preserve middleware request override headers in runMiddleware

* Strip x-middleware-* headers, detect bun, update fixtures

Preserve and unpack x-middleware-request-* headers while removing any other x-middleware-* headers in the worker entry (defense-in-depth). Add bun package manager detection in project utils (detectPackageManager and detectPackageManagerName). Update tests to assert the new header-stripping behavior. Update pages-basic fixture entry: add explicit void for promise calls, inject a middleware header override route, add dangerous-scheme blocking for Link hrefs, include credentials on prefetch, update dynamic import asset names and file metadata, and rename several getServerSideProps helper identifiers to avoid collisions.

* Normalize absolute file paths in fixture

Update fileName annotations in tests/fixtures/pages-basic/dist/server/entry.js to replace local developer paths (/Users/sunilpai/...) with CI runner paths (/home/runner/work/...). This cleans up generated JSXDEV metadata in the compiled test fixture to avoid leaking local machine paths and make outputs reproducible.

---------

Co-authored-by: Sunil Pai <spai@cloudflare.com>
2026-02-27 22:29:46 +00:00
Fred K. Schott a553866800 fix: detect bun.lock (text format) in package manager detection (#152)
Bun 1.2+ uses a text-based bun.lock file instead of the binary
bun.lockb. Add detection for the new format in both
detectPackageManager and detectPackageManagerName so the CLI
correctly identifies Bun in newer projects.
2026-02-27 20:16:15 +00:00
Steve Faulkner c96f4b6b2a fix: preserve multiple Set-Cookie headers in middleware forwarding (#173)
* fix: preserve multiple Set-Cookie headers in middleware forwarding

Middleware header forwarding used Headers.set() which overwrites
previous values with the same key. When middleware sets multiple
Set-Cookie headers (session, CSRF, consent), only the last one
survived.

Changes:
- middleware.ts: .set() -> .append() in next and rewrite paths
- app-dev-server.ts: .set() -> .append() in collection and emission
- prod-server.ts: array accumulation for Set-Cookie, getSetCookie()
  for custom middleware responses
- index.ts codegen: .set() -> .append() in production middleware runner
- index.ts Pages dev: setHeader -> appendHeader for middleware headers

* fix: add type annotations for Headers.forEach callback in prod-server

---------

Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com>
2026-02-27 13:51:30 -06:00
Steve Faulkner 6abbb2a3be fix: block dangerous URI schemes in Link and Form components (#174)
Block javascript:, data:, and vbscript: URI schemes in the Link href
and Form action props. Handles evasion techniques including mixed case,
leading whitespace, and zero-width characters.

Link renders an inert <a> without href. Form renders without action
(submits to current page). Both warn in dev mode.

Shared isDangerousScheme() utility in url-safety.ts for consistency.

Based on the work by @kochrac in #142.
2026-02-27 13:48:41 -06:00
Steve Faulkner 75703d3b72 Align Pages Router worker entry with prod-server request handling (#171)
* Skip deploy previews for fork PRs that lack Cloudflare secrets

Fork PRs don't have access to repository secrets (CLOUDFLARE_API_TOKEN,
CLOUDFLARE_ACCOUNT_ID), so deploy/smoke-test/comment steps always fail.
Add a fork detection condition to skip these steps gracefully. The build
steps still run, so example builds are still validated for fork PRs.

* fix: align Pages Router worker entry with prod-server request handling

The generated Cloudflare Worker entry for Pages Router apps was missing
several request handling steps that the Node.js production server
(prod-server.ts) already handled. This brings the two in sync:

- Run middleware (runMiddleware) before routing
- Apply next.config.js redirects, rewrites (before/after/fallback), and headers
- Handle basePath stripping and trailing slash normalization
- Merge middleware response headers with correct precedence
- Guard renderPage with typeof check

Also updates the two Pages Router examples (pages-router-cloudflare,
realworld-api-rest) and exports vinext/config/config-matchers so the
worker entry can import the shared matching utilities.

* chore: sync pnpm-lock.yaml with upstream package.json changes
2026-02-27 13:01:34 -06:00
ubugeeei 2c650ec089 feat: type aware linting with tsgolint (#38) 2026-02-27 07:37:12 +00:00
situ2001 8dc679eccd fix: remove some of any type annotations in index.ts (#63)
* refactor: wrap Vite configuration with `defineConfig`

* refactor: remove type any

* refactor: remove as any

* refactor: remove as any

* refactor: use UserConfig type instead of defineConfig for internal config object

defineConfig is an identity function meant for user-land vite.config files.
Frameworks should use the UserConfig type annotation directly, which provides
the same type inference without the semantic mismatch.

---------

Co-authored-by: Fred K. Schott <622227+FredKSchott@users.noreply.github.com>
2026-02-27 07:29:41 +00:00
nianyi(likai) 35757845d1 fix: eliminate react-dom/server.edge dual-import Rollup warning in SSR entry (#41)
* fix: use static import for renderToStaticMarkup to eliminate dual-import warning

The generated SSR entry imported renderToReadableStream statically from
react-dom/server.edge at the top level, but later used a dynamic
import() for renderToStaticMarkup from the same module. Rollup detects
this mixed static+dynamic import pattern and emits a warning for every
build:

  "react-dom/server.edge" is imported as both a static and dynamic
  import in the same module

Fix: add renderToStaticMarkup to the existing static import, removing
the redundant dynamic import(). The module was already loaded — the
dynamic import added no lazy-loading benefit since it runs
unconditionally during SSR.

* Remove comment on renderToStaticMarkup import

Removed comment about static import of renderToStaticMarkup.

* Remove unneccesary test

---------

Co-authored-by: Fred K. Schott <622227+FredKSchott@users.noreply.github.com>
2026-02-27 07:13:10 +00:00
ToddLiao a82478404d fix: detect src/app App Router (#90) 2026-02-27 07:08:00 +00:00
mohammad 2efc089183 fix: log errors from failed error reporting calls (#133)
* fix: log errors from failed error reporting calls

Previously, error reporting calls (.catch(() => {})) silently swallowed
errors, making debugging impossible when error reporting itself failed.

This fix adds proper error logging:
- Server action error reporting failures are now logged
- Route handler error reporting failures are now logged
- Async component errors in loading boundaries are logged in dev mode

The third case is intentional (loading boundaries handle errors), but
now logs in development for visibility.

* revert: keep silent .catch for loading boundary pre-render test

The testResult.catch() suppresses expected rejections from async
components with loading boundaries — redirect/notFound errors are
handled by rscOnError during streaming. Logging here would be noisy
since this is a normal code path, not an error condition. This matches
Next.js behavior where prospective render rejections are silent by
default.

---------

Co-authored-by: Fred K. Schott <622227+FredKSchott@users.noreply.github.com>
2026-02-27 06:42:27 +00:00
milkboy2564 1b5ecf7625 fix(rsc): include cookies in RSC navigation and prefetch requests (#123)
RSC navigation and prefetch fetches were missing ,
causing browsers to omit cookies from these requests. This silently broke
cookie-based authentication (Better Auth, NextAuth, etc.) during client-side
navigation: a full-page load would succeed (cookies sent normally) but any
subsequent <Link> navigation to a protected route would fail authentication
because the RSC fetch arrived without the session cookie.

Three fetch call sites are fixed:
- server/app-dev-server.ts — __VINEXT_RSC_NAVIGATE__ navigation fetch
- shims/link.tsx            — <Link> prefetch on viewport intersection
- shims/navigation.ts      — router.prefetch() call
2026-02-27 06:40:03 +00:00
gembleman 083d765cec fix(navigation): cache getServerSnapshot fallback in useSearchParams (#79)
* fix(navigation): cache getServerSnapshot fallback in useSearchParams

useSyncExternalStore compares snapshots with Object.is. The previous
getServerSnapshot returned `new URLSearchParams()` on every call when no
server context was available, producing a new object reference each time
and triggering an infinite re-render loop in React.

Fix: introduce a module-level `_cachedServerSearchParams` that is
allocated once and reused, matching the same referential-stability
pattern already used by `getSearchParamsSnapshot` for the client
snapshot.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Refactor cached search params and pathname logic

Refactor cached variables for search params and pathname for better referential stability. Consolidate logic to avoid infinite re-renders.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Fred K. Schott <622227+FredKSchott@users.noreply.github.com>
2026-02-27 06:37:46 +00:00
ask-bonk[bot] aff5d24449 fix(routing): allow hyphens in dynamic segment param names (#78)
* fix(routing): allow hyphens in dynamic segment param names

Catch-all and dynamic route params with hyphens (e.g. [[...sign-in]],
[auth-method]) caused 404 because the regex used \w+ which doesn't
match hyphens. Next.js allows hyphens in param directory names — this
is commonly used by Clerk ([[...sign-in]], [[...sign-up]]).

Changed all \w+ patterns to [\w-]+ across:
- app-router.ts (fileToAppRoute, discoverSlotSubRoutes, computeInterceptTarget)
- pages-router.ts (fileToRoute, patternToNextFormat)
- app-dev-server.ts (middleware matcher regex, config pattern matching)
- middleware.ts (pattern matching)

Closes #71

* fix(routing): allow hyphens in config matching and middleware-codegen param patterns

Addresses review feedback on PR #78: the same \w+ bug that was fixed in
routing/middleware also existed in the config pattern matching functions
(redirects, rewrites, headers) and the middleware-codegen tokenizer.

Updated all remaining param-related regex patterns to [\w-]+ across:
- index.ts: matchConfigPattern guard, tokenizer, catchAllMatch, applyHeaders
- config-matchers.ts: matchConfigPattern guard, tokenizer, catchAllMatch,
  escapeHeaderSource tokenizer
- app-dev-server.ts: __matchConfigPattern template (4 .replace() chains,
  catchAllMatch, __applyConfigHeaders)
- middleware-codegen.ts: generated matchMiddlewarePattern tokenizer

Subsumes PR #127 which fixed middleware-codegen.ts independently.

Co-authored-by: SeolJaeHyeok <milkboy2564@naver.com>

---------

Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com>
Co-authored-by: Fred K. Schott <622227+FredKSchott@users.noreply.github.com>
Co-authored-by: SeolJaeHyeok <milkboy2564@naver.com>
2026-02-26 22:15:45 -08:00
Stephen Zhou 2061ff61c1 fix(image-imports): get cache (#59) 2026-02-27 05:54:37 +00:00
Gentrit Biba 7f33c2432b fix: use fileURLToPath for metadata-routes import in RSC entry (#130)
URL.pathname returns percent-encoded strings (e.g. %20 for spaces),
which Vite cannot resolve as filesystem paths. This causes all App
Router tests to fail when the project is in a directory with spaces
(common on macOS with iCloud, Google Drive, OneDrive).

Replace .pathname with fileURLToPath() from node:url, which properly
decodes the URL to a filesystem path. This pattern is already used
elsewhere in the codebase (cloudflare/tpr.ts).

Fixes #129
2026-02-27 05:42:27 +00:00
Steve Faulkner 0c66d98fc8 fix: use decoded pathname for config matcher evaluation (#150)
Config matchers (redirects, rewrites, headers) now receive the decoded,
normalized pathname so that percent-encoded request paths like /%61bout
correctly match config rules defined for /about.

Both dev and prod servers rebuild the working URL from the decoded
pathname + original query string, ensuring all downstream consumers
operate on canonical paths.

Also adds unit and integration tests for percent-encoded path handling
in config matchers across App Router and Pages Router (dev + prod).
2026-02-27 02:15:50 +00:00
Steve Faulkner 65f78a4db6 fix: strip internal routing headers from dev server responses (#149)
Strip all x-middleware-* prefixed headers from responses in both the
App Router and Pages Router dev servers before sending to clients.
These headers are internal routing signals (x-middleware-next,
x-middleware-rewrite, x-middleware-override-headers, etc.) and should
not be visible to end users.

The prod server already stripped these correctly. This brings the dev
servers into parity.
2026-02-26 20:05:30 -06:00
Steve Faulkner 6fb62b6017 fix: decode request pathname once at entry point for consistent routing (#148)
Ensures the pathname is decoded exactly once (via decodeURIComponent +
normalizePath) at the request entry point, and all downstream consumers
(middleware, config matchers, route matching) receive the same canonical
path. Previously, some code paths decoded the pathname a second time,
which could cause inconsistent matching between middleware and routing.

Also removes redundant decodeURIComponent calls from matchRoute and
matchConfigPattern catch-all branches that are no longer needed since
the caller now guarantees a pre-decoded pathname.
2026-02-26 20:04:40 -06:00