Commit Graph

1082 Commits

Author SHA1 Message Date
James Anderson 2cbd2a0356 fix(build): apply default server externals (#2257)
* fix(build): externalize Next.js default server packages

* test(node): cover externalized shiki in standalone output
2026-06-23 10:20:55 +00:00
ponharu 66a7b4e6e8 fix(app-router): exclude dev overlay from production client bundle (#2284)
* fix(app-router): exclude dev overlay from production client bundle

* fix(app-router): reuse loaded dev overlay during HMR

---------

Co-authored-by: James <james@eli.cx>
2026-06-23 11:09:56 +01:00
ponharu 68547961ee test(deploy): stabilize Wrangler fallback resolution test (#2272) 2026-06-23 00:02:32 +01:00
James Anderson a34c083e88 fix(app-router): match interception route precedence (#2231)
* fix(app-router): prioritize specific interception routes

* fix(app-router): match interception route precedence

* Reviewed PR #2231: LGTM, minor notes

Co-authored-by: james-elicx <james-elicx@users.noreply.github.com>

* fix(app-router): align interception route tiebreaks

---------

Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com>
Co-authored-by: james-elicx <james-elicx@users.noreply.github.com>
2026-06-22 23:37:34 +01:00
James Anderson d65dd4c601 fix(router): ignore stale initial popstate with i18n (#2230)
* fix(router): ignore stale initial popstate with i18n

* Review done: no issues found

Co-authored-by: james-elicx <james-elicx@users.noreply.github.com>

* test(router): run i18n popstate coverage in existing project

---------

Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com>
Co-authored-by: james-elicx <james-elicx@users.noreply.github.com>
2026-06-22 22:36:52 +00:00
James Anderson 6e998ec114 fix(middleware): run on missing build assets (#2242)
* fix(middleware): run on missing build assets

* fix(middleware): preserve missing asset rewrites on workers
2026-06-22 23:36:49 +01:00
James Anderson 7b21c429c0 fix(app-router): preserve global error details (#2249)
* fix(app-router): preserve global error details

* fix(app-router): preserve default global error semantics
2026-06-22 23:29:40 +01:00
ponharu 7c932f39cd fix(headers): strip internal dev request ID headers (#2260) 2026-06-22 23:25:08 +01:00
Jerry Zhao 14549e323f test(shims): expect forward-slash shim ids in the next/error and subpath resolution fixtures (#2265) 2026-06-22 23:19:51 +01:00
Jerry Zhao ecfc68874f test(pages-router): wrap the client-navigation page module URL as a Vite /@fs/ id on Windows (#2266) 2026-06-22 23:19:37 +01:00
Jerry Zhao d89fcb5070 test(og-inline): use a forward-slash scoped package name in the linked-workspace fixture (#2267) 2026-06-22 23:19:17 +01:00
James Anderson fbf68d588e fix(app-router): restore cache and parallel route parity (#2261)
* fix(app-router): preserve searchParams cache variants

* test(app-router): cover RSC metadata cache variants

* fix(app-router): isolate metadata searchParams observation

* fix(app-router): preserve static ISR cache proofs

* fix(app-router): limit queryless cache probes

* fix(app-router): skip redundant cache proof probes

* fix(app-router): harden incomplete cache proofs

* fix(app-router): observe loading page execution

* fix(app-router): preserve exotic page exports

* fix(app-router): preserve client page ISR

* fix(app-router): avoid duplicate queryless page execution

* fix(app-router): observe viewport search params

* fix(app-router): clear force-static search params

* fix(app-router): isolate intercept static context

* fix(app-router): preserve intercept request context

* fix(app-router): isolate action static context

* fix(app-router): match static request API semantics

* fix(app-router): preserve dynamic segment constraints

* fix(app-router): apply slot-only segment config

* fix(app-router): preserve draft mode reads in static errors

* fix(app-router): include nested slot constraints

* fix(app-router): refresh slot layout constraints

* fix(app-router): render nested slot layouts

* fix(app-router): preserve nested slot params

* fix(app-router): preserve intercepted slot head

* fix(app-router): scope parallel layout params

* fix(app-router): preserve intercept layout parity

* fix(app-router): preserve intercept ancestor params

* fix(app-router): scope intercept source params

* fix(app-router): preserve intercept source route params

* test(app-router): stabilize parity validation
2026-06-22 23:17:41 +01:00
Steve Faulkner 826ae4cc7c perf(app-router): skip speculative page probes for HTML renders (#2226) 2026-06-22 08:53:17 -05:00
James Anderson 65f9531d00 fix(app-router): canonicalize initial history URL (#2250) 2026-06-22 14:50:06 +01:00
James Anderson 1908494d4d fix(pages-router): allow server-only in API routes (#2234) 2026-06-22 14:46:49 +01:00
Stephen Zhou 6b55874ee0 fix(config): make vite-tsconfig-paths an optional peer (#2258) 2026-06-22 14:39:36 +01:00
Alexander Lichter d6afa2e874 perf(link): omit Pages runtime in app-only builds (#2225)
* perf(link): omit Pages runtime in app-only builds

* fix(link): use document fallback in app-only builds
2026-06-22 14:28:10 +01:00
Alexander Lichter 573997382e perf(client): omit unused server action client (#2222)
* perf(client): omit unused server action client

* fix(client): capture server action state before lazy load

---------

Co-authored-by: James <james@eli.cx>
2026-06-22 14:27:38 +01:00
Alexander Lichter 95b320f79f perf(app): omit unused PPR runtime (#2223)
Co-authored-by: James <james@eli.cx>
2026-06-22 13:06:27 +00:00
Alexander Lichter eb18704318 perf(app): omit unused file metadata runtime (#2224) 2026-06-22 13:54:45 +01:00
Alexander Lichter bd7562ac74 feat(web): compare benchmarks with recent median (#2221) 2026-06-22 13:46:25 +01:00
James Anderson 82d5f7691c fix(perf): resolve Vite from benchmark target (#2212) 2026-06-20 14:05:33 +00:00
Divanshu Chauhan e7a4e9b08b fix(image): emit hoisted var for local image import binding to avoid TDZ (#2067)
The vinext:image-imports transform rewrote `import X from './a.png'` into
two imports plus `const X = {...}`. The original import is a module-scoped
binding initialized before module-body execution, but the `const` is
block-scoped and TDZ-bound until its textual line, so any reference to `X`
that runs earlier — a hoisted function called above the import, or a
circular-import re-entry — flipped from working to a runtime
`Cannot access 'X' before initialization`.

Emit `var X = {...}` instead: it hoists like the import it replaces, so the
forward reference no longer throws (it reads `undefined` before the line,
strictly better than crashing). Matches Next.js, which leaves the consumer's
`import X` untouched and rewrites the image module itself.

Closes #1975
2026-06-20 14:49:17 +01:00
Divanshu Chauhan ad2fcfb16f fix(dynamic): render ssr:false loading with pastDelay:true to match client (#1967) (#2064)
next/dynamic's ssr:false server branch rendered the loading component with
pastDelay:false, while the client's first (pre-mount) render and Suspense
fallback default pastDelay to true. For a loading component that branches on
pastDelay (the documented `if (!pastDelay) return null` pattern), the server
emitted nothing but the client rendered the spinner, causing a React hydration
mismatch and a visible content jump.

Drop the hardcoded pastDelay:false override on the server so it defaults to
true, matching both the client render and Next.js App Router, which always
renders the loading fallback with pastDelay=true on server and client. Loading
components that ignore pastDelay are unaffected.

Update the three tests that asserted the buggy server-side pastDelay:false, and
add a server-equals-client-first-render parity assertion.

Fixes #1967
2026-06-20 14:47:02 +01:00
Divanshu Chauhan 05038707e2 fix(actions): dedupe Set-Cookie on no-JS non-redirect action responses (#1976) (#2063)
The progressive (form-state) non-redirect server-action path returned the
raw `getAndClearPendingCookies()` array, so calling `cookies().set(name, ...)`
twice for the same name emitted two `Set-Cookie` headers for that name on
no-JS form submissions. Next.js stores mutable cookies in a name-keyed
`ResponseCookies` map (last value wins) and collapses to one header, as do the
redirect and RSC action paths in vinext via `dedupePendingCookies()`.

Wrap the non-redirect branch's cookie collection in `dedupePendingCookies()`
to match the sibling paths, so same-name sets collapse to a single Set-Cookie
(last value wins). The single binding feeds both the action-failed and success
returns, covering both non-redirect sub-cases.

Follow-up to the #1481 dedup fix, which landed on the redirect and RSC paths
but missed the progressive non-redirect branch.

Adds a regression test mirroring the existing redirect-dedup test for the
non-redirect form-state path.
2026-06-20 14:45:36 +01:00
Alexander Lichter e7225b92cc fix(benchmarks): measure eager RSC entry closure (#2208) 2026-06-20 14:43:49 +01:00
Divanshu Chauhan 32973e9ddf fix(rsc): only short-circuit well-known signal digests so real errors reach onRequestError (#2066)
createRscOnErrorHandler treated ANY error carrying a digest field as a
control-flow signal: it returned String(error.digest) and skipped
reportRequestError, so a genuine server-render error that merely carried a
digest (e.g. a hashed digest from sanitizeErrorForClient, or one transported
from a nested boundary) was silently swallowed with no instrumentation and
its digest shipped to the client as if it were a signal.

Mirror Next.js getDigestForWellKnownError: add a helper that recognizes only
genuine signals (redirect / notFound+http-fallback / bailout-to-CSR /
dynamic-server) and gate the short-circuit on it. Non-signal digest errors now
fall through to reportRequestError and keep their existing digest as-is.

Fix the same conflation in createAppPageRscErrorTracker.onRenderError, which
routed any digest-bearing error to the special 307/404-swap slot; gate it on
isNavigationSignalError so real errors reach the error boundary and a later
real signal can still win the slot.

Closes #1978
2026-06-20 14:43:17 +01:00
Nathan Nguyen 3e83ab24b4 perf(server): reuse App SSR bfcache metadata (#2182)
* perf(server): reuse App SSR bfcache metadata

* refactor(server): clarify bfcache segment parsing
2026-06-20 14:17:07 +01:00
Alexander Lichter 7c5f4c592a perf(app): omit unused server action runtime (#2206) 2026-06-20 13:51:01 +01:00
Alexander Lichter ef2e4a95dc perf(client): preserve additional route-owned shims (#2205) 2026-06-20 13:38:18 +01:00
Alexander Lichter 4fa904e10e test(e2e): stabilize app router history navigation (#2207)
* test(e2e): stabilize app router history navigation

* test(e2e): isolate temporary Vite caches
2026-06-20 13:37:21 +01:00
Alexander Lichter 94eb44ef66 feat(benchmarks): track eager client entry size (#2190) 2026-06-20 01:37:30 +01:00
Alexander Lichter 85d49ec814 perf(dev): externalize SSR React runtime (#2191)
* perf(dev): externalize SSR React runtime

* fix(test): pass Vite config environment
2026-06-20 01:33:33 +01:00
Alexander Lichter b9653e8a3a perf(app): omit unused metadata route runtime (#2196)
* perf(app): omit unused metadata route runtime

* fix(app): keep metadata route helper type private

---------

Co-authored-by: James <jamesanderson@cloudflare.com>
2026-06-20 01:26:28 +01:00
Alexander Lichter 6e35d4e206 perf(client): preserve App Router route chunk boundaries (#2189) 2026-06-20 01:25:30 +01:00
Alexander Lichter 9970430c29 perf(app): omit unused middleware runtime (#2194) 2026-06-20 01:15:33 +01:00
Jerry Zhao 680cbafabc perf(mdx): gate the MDX transform handler behind a native id filter (#2200) 2026-06-20 01:05:02 +01:00
James Anderson 656a381de9 fix(og): constrain inlined assets to trusted roots (#2172)
* fix(og): constrain inlined assets to trusted roots

* fix(og): canonicalize asset package boundaries

* test(og): cover canonical boundary intersections

* fix(og): support file-symlinked dependency modules

* docs(og): clarify logical asset boundary resolution

* fix(og): pin symlinked modules to matching packages

* fix(og): keep linked package boundaries narrow

* fix(og): reject workspace-root asset boundaries

* fix(og): name-pin external package boundaries

* fix(og): validate linked package ownership

* fix(og): track linked package ownership

* fix(og): harden linked package provenance

* fix(og): confine captured alias packages

* fix(og): pin captured directory aliases

* fix(og): require alias package ownership

* fix(og): support single-segment aliases

* perf(og): preserve alias lookup precedence

* refactor(og): extract asset ownership policy

* test(og): cover linked package fonts in e2e

* fix(og): recognize resolved package aliases

* fix(og): support regex package aliases

* test(og): isolate linked font fixture

* docs(test): clarify linked font coverage
2026-06-19 16:08:46 +01:00
James Anderson a3f137802b fix(perf): avoid dispatch publisher main race (#2180) 2026-06-19 15:22:49 +01:00
Nathan Nguyen 301418a3f1 perf(app-router): defer layout, template and boundary modules to first request (#2145)
* perf(app-router): defer layout, template and boundary modules to first request

The generated RSC entry statically imported every route's layout, template,
loading/error/not-found/forbidden/unauthorized boundary and parallel-slot
module at the top level. On a cold isolate — and on the dev server's first
request — that eagerly evaluated the whole app's segment tree, including
routes never visited in that lifetime. Page and route-handler modules were
already lazy; the surrounding segment modules were not.

Emit those modules as lazy `() => import()` loaders paired with `null`
placeholders in the manifest, and hydrate them onto the route's synchronous
fields in `ensureAppRouteModulesLoaded` before any read (extended from
page/handler to layouts, templates, boundaries and parallel-slot modules).
Intercept layouts hydrate via the dedicated `loadAppInterceptLayouts` because
they live on the match object, not the route. Every reader already runs behind
`ensureAppRouteModulesLoaded` (handler dispatch and `buildPageElements`) or the
intercept resolvers, so no synchronous module field is read before hydration.

The shared intercept `interceptLayouts` arrays keep their `readonly` public
types; the loader is their sole writer and fills slots through one
boundary-local cast, so route metadata stays immutable to callers.

Dev cold start is unchanged on page-heavy apps — pages were already lazy — but
on a boundary-heavy app (80 independent sections, each with
layout+template+error+loading+not-found) first-request module compile drops
704ms -> 534ms (~24%) and time-to-first-200 1917ms -> 1665ms. The saving scales
with the number of unrelated segments deferred.

* test(app-router): localize react max header font fixture

* fix(app-router): dedupe lazy intercept module loads

* fix(app-router): share intercept load state across matches

* docs(app-router): explain root layout loader reuse

---------

Co-authored-by: James <james@eli.cx>
2026-06-19 14:49:29 +01:00
Alexander Lichter 388b5c3efb feat(benchmarks): track server bundle sizes (#2173) 2026-06-19 13:23:11 +01:00
James Anderson 53e33babe2 fix(perf): ensure full profiles exist (#2175) 2026-06-19 12:35:08 +01:00
James Anderson 51b2c34958 fix(perf): allow publisher PR comments (#2174) 2026-06-19 11:43:38 +01:00
James Anderson 715ed317ed fix(perf): preserve permissioned cache roots (#2171)
* fix(perf): preserve permissioned cache roots

* fix(perf): run trusted same-repo harness

* fix(perf): ignore reaped benchmark zombies

* fix(perf): profile the build process tree

* fix(perf): wait for benchmark process cleanup

* fix(perf): profile the real build command

* fix(perf): profile the Vite Plus build entry

* fix(perf): avoid nested profiling sudo

* fix(perf): validate traces across profiles
2026-06-19 11:26:38 +01:00
James Anderson a2823aef7c fix(perf): allow isolated Vite task cache (#2170) 2026-06-19 09:26:28 +00:00
Alexander Lichter 44b0c9dcff chore(perf): pair pull request benchmark baselines (#2159)
* fix(perf): pair pull request benchmark baselines

* fix(perf): bootstrap benchmark fingerprint rollout

* fix(perf): harden paired benchmark publishing

* fix(perf): allow isolated build output creation

* fix(perf): transfer isolated build outputs

* fix(perf): verify isolated output permissions

* fix(perf): validate diagnostic trace categories

* fix(perf): retain trusted benchmark manifests

* fix(perf): close benchmark validation gaps

---------

Co-authored-by: James <james@eli.cx>
2026-06-19 10:10:22 +01:00
MaxtuneLee 42f8700f15 fix(router): enhance navigation with as mask for URL handling (#2076)
* fix(router): enhance navigation with `as` mask for URL handling

* fix(router): implement bracket-pattern interpolation for dynamic routes in Pages Router

* fix(router): preserve masked state in beforePopState

* fix(router): cover basepath navigation in ci

* docs(router): clarify navigation state invariants

* refactor(router): require complete history state

* refactor(router): share dynamic href interpolation

* test(router): cover localized masked navigation

* fix(router): keep interpolation internals private

---------

Co-authored-by: James <james@eli.cx>
2026-06-19 00:07:03 +01:00
James Anderson b2ea860284 fix(proxy): match Next.js file conventions (#2155)
* fix(proxy): match Next.js file conventions

* test(proxy): keep convention fixture out of workspace

* test(proxy): provide middleware path in handler fixture
2026-06-18 23:54:39 +01:00
Divanshu Chauhan 7487886b38 fix(prod-server): send headers-only for HEAD in sendCompressed (#1980) (#2058)
sendCompressed wrote the full body for every request. HEAD requests routed
through it (bot/crawler-buffered Pages HTML and Pages API routes) still
compressed and emitted a payload that Node then discards at the socket level.

Short-circuit with res.end() after writeHead when req.method is HEAD,
mirroring sendWebResponse. This skips spinning up a compressor for a body
that would be thrown away and keeps the buffered sender consistent with the
streamed and static-file senders, which already handle HEAD.

Co-authored-by: James <james@eli.cx>
2026-06-18 22:13:07 +00:00
Jerry Zhao b352d671fa fix(rsc): resolve the app-rsc-handler shim to a forward-slash id on Windows (#2165) 2026-06-18 22:18:03 +01:00