Commit Graph

1045 Commits

Author SHA1 Message Date
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
Jerry Zhao 2b7faf1ec1 test(font-local): normalize the shim path fixture so the skip guard matches on Windows (#2164) 2026-06-18 22:17:46 +01:00
Jerry Zhao ef78685beb test(next-config): build the cacheHandler file:// fixture with pathToFileURL (#2163) 2026-06-18 21:39:37 +01:00
Jilles Soeters 8354b98d64 fix(init): ignore vinext output directory (#2160) 2026-06-18 21:26:05 +01:00
James Anderson 12f04d6d63 fix(app-router): block dangerous RSC redirect URLs (#2154)
* fix(app-router): block dangerous RSC redirect URLs

* refactor(app-router): reuse blocked navigation reporter

* style(app-router): format redirect guard import
2026-06-18 17:51:07 +00:00
Alexander Lichter 2fc9845ed3 fix(fonts): preload only requested google font subsets (#2152)
Co-authored-by: James Anderson <james@eli.cx>
2026-06-18 17:20:05 +01:00
Nathan Nguyen 00f0a684e0 test(ppr): wait for fallback shell abort signal (#2072) 2026-06-18 15:07:56 +01:00
MaxtuneLee ef003c0ebe fix(server): q-value-aware Accept-Encoding negotiation (#2047)
* fix(server): implement q-value-aware Accept-Encoding negotiation and add tests

* docs(server): update comments for Accept-Encoding parsing to clarify q-value handling

* fix(server): enhance q-value handling in Accept-Encoding parsing and add tests

* fix(server): enhance q-value handling in Accept-Encoding parsing and add tests for isEncodingAccepted

* fix(tests): update negotiateEncoding tests to reflect zstd as highest preference in prod-server.ts

* fix(tests): update negotiateEncoding tests

* fix(tests): remove redundant test for explicit refusal overriding wildcard in negotiateEncoding

* fix(server): normalize encoding checks in isEncodingAccepted function

* refactor(server): extract accept-encoding helpers

* fix(tests): remove redundant zlib check and import HAS_ZSTD from accept-encoding module

* fix(server): honor Accept-Encoding quality weights

* fix(server): repair accept-encoding CI checks

* fix(server): harden content encoding negotiation

* fix(server): align encoding negotiation with Next.js

* refactor(server): remove unused encoding parse state

* fix(server): normalize empty vary headers

---------

Co-authored-by: James <james@eli.cx>
2026-06-18 14:07:05 +01:00
James Anderson 55d9a23860 fix(image): resolve aliased static imports (#2142) 2026-06-18 13:59:31 +01:00
James Anderson eeac5db0a3 fix(image): preserve unoptimized remote URLs (#2139)
* fix(image): preserve unoptimized remote URLs

* fix(image): honor overrideSrc when unoptimized
2026-06-18 10:49:42 +01:00
Jerry Zhao 0ca9513e01 test(optimize-imports): make barrel-resolution fixtures portable to Windows (#2132) 2026-06-18 10:20:40 +01:00
Jerry Zhao 9d31912773 test(type-of-window): test the cache-dir exclude filter with POSIX ids (#2129) 2026-06-18 00:33:17 +01:00
Jerry Zhao 33b24a27c9 refactor(instrumentation): resolve instrumentation hook files in forward-slash space (#2128) 2026-06-18 00:06:17 +01:00
Alexander Lichter 55b1417d22 perf(app-router): defer cache-only runtimes (#2126) 2026-06-18 00:05:31 +01:00
Alexander Lichter 01c3abbe38 perf(app-page-cache): reuse HTML ISR key during regeneration (#2127) 2026-06-18 00:04:30 +01:00
Jerry Zhao a85170c74a fix(transforms): emit sourcemaps from strip-server-exports and remove-console (#2124) 2026-06-17 23:45:59 +01:00
Alexander Lichter cc8d4d6dea perf(ssr): isolate open redirect detection (#2120)
* perf(ssr): isolate open redirect detection

* perf(ssr): isolate stream cleanup runtime

* Apply suggestions from code review

Co-authored-by: James Anderson <james@eli.cx>

* remove comments

---------

Co-authored-by: James Anderson <james@eli.cx>
2026-06-17 21:29:13 +00:00
Alexander Lichter d2edaf80b5 perf(build): rely on plugin hook filters (#2119) 2026-06-17 19:13:42 +01:00
Alexander Lichter 2eb394f0a4 perf(dev): skip typeof window folding for prebundles (#2118)
* perf(dev): skip typeof window folding for prebundles

* perf(dev): move prebundle skip into hook filter

* test: provide cacheDir in resolved config mock

* fix(dev): handle reused typeof-window plugins

* refactor(dev): share cache directory normalization

---------

Co-authored-by: James <james@eli.cx>
2026-06-17 18:13:31 +00:00
Jerry Zhao 891e920bbd test(tsconfig-paths): remove temp dir after restoring cwd to avoid EPERM on Windows (#2092) 2026-06-17 18:44:07 +01:00
Jerry Zhao e8153d28d1 test(helpers): link fixture node_modules with a dir symlink, not a junction (#2091) 2026-06-17 18:43:51 +01:00
Jerry Zhao 6590586636 test(fixture-dev-server): wait for child exit by exit/signal code, not killed (#2090) 2026-06-17 18:43:35 +01:00
Jerry Zhao bfc090e231 test(oxlint-prefer-shared-utils): remove dir-symlink fixtures on Windows (#2087) 2026-06-17 18:41:53 +01:00
Jerry Zhao 4980b666b6 test(dev-stack-sourcemap): make fixtures portable to Windows (#2086) 2026-06-17 18:41:42 +01:00
Alexander Lichter 59a2de53a3 perf(app-router): defer action and route-handler runtimes (#2079)
* perf(app-router): defer action and route-handler runtimes

* perf(app-router): defer middleware runtime

* perf(rsc): isolate server navigation runtime

* perf(ssr): isolate bfcache identity runtime

* perf(ssr): avoid browser navigation runtime

* refactor(navigation): share server-safe primitives

* fix(ssr): track error boundary pathname in fallback shells

---------

Co-authored-by: James <james@eli.cx>
2026-06-17 17:41:25 +00:00
James Anderson 284cbb443e fix(css): keep module hashes stable with url assets (#2114)
* fix(css): keep module hashes stable with url assets

* fix(css): restore url assets in server output
2026-06-17 18:21:59 +01:00
Alexander Lichter 4fdf617004 perf(cache): isolate handler and request state (#2107)
* perf(cache): isolate handler and request state

* perf(rsc): isolate page cache key marker
2026-06-17 18:17:41 +01:00
James Anderson 144c30d47e fix(middleware): normalize trailing slash before matching (#2112)
* fix(middleware): normalize trailing slash before matching

* test(middleware): preserve unmatched API fixture

* fix(middleware): normalize matcher source slashes

* fix(middleware): normalize direct matcher inputs

* fix(middleware): preserve explicit slash matchers
2026-06-17 13:19:49 +01:00
Alexander Lichter 56d7311994 perf(dev): externalize App Router request handler (#2108) 2026-06-17 11:55:48 +01:00
James Anderson 2f8b0ef78b fix(build): preserve lazy RSC framework chunks (#2074)
* fix(build): preserve lazy RSC framework chunks

* test(image): wait for completed watch output

* test(build): isolate RSC startup regression

* fix(build): preserve lazy framework chunks on Vite 7

* test(build): cover transitive framework reachability

* test(build): harden framework chunk regressions
2026-06-16 14:28:15 +01:00
James Anderson 06ffe4661c fix(middleware): fail closed for unsafe matchers (#2078) 2026-06-16 14:24:04 +01:00
Alexander Lichter 5495682f6a perf(rsc): prebundle static renderer entry (#2077) 2026-06-16 12:56:27 +00:00
Alexander Lichter f0e6be73f2 perf(dev): prune NODE_ENV during dependency optimization (#2075)
* perf(dev): fold NODE_ENV during dependency optimization

* fix(dev): preserve NODE_ENV optimizer defines

* test(dev): inspect optimized dependencies via metadata

* docs(dev): clarify dependency optimizer defines
2026-06-16 12:42:44 +00:00
James Anderson c845c0af05 fix(build): align Pages data export transforms (#2055)
* fix(build): align Pages data export transforms

* fix(build): address Pages export review feedback

* perf(build): narrow page export-all parsing

* perf(build): scan page export-all candidates linearly
2026-06-16 12:04:53 +01:00
Xplod13 b891b01d41 fix(fonts): detect trailing comma past comments when injecting font options (#2051)
The font plugins inject an extra option into the user's `next/font` options
object by string-splicing before the closing `}`. They chose the separator by
testing whether the text before `}` ends with `,`, but that test ran on the
raw source, so a trailing comment hid a real trailing comma:
`Inter({ weight: "400", /* x */ })` produced `{ weight: "400", /* x */, _vinext: … }`
— a double comma and a syntax error that broke the build.

Add a shared, string-aware `hasTrailingComma`/`lastSignificantChar` helper
(`utils/has-trailing-comma.ts`) that forward-scans tracking string and comment
state, and use it at both font injection sites and in dynamic-preload-metadata
(which previously kept a private comment-stripper). A stateful scan — rather
than stripping comments with a regex — is required so a `//` inside a string
literal (e.g. a URL) can never be mistaken for a comment and corrupt the
trailing-comma decision.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 23:42:16 +01:00
Divanshu Chauhan 067a7c6000 fix(csp): match script-src-elem/attr when extracting script nonce (#2049)
Select the CSP nonce directive via startsWith("script-src") /
startsWith("default-src"), matching Next.js get-script-nonce-from-header.
Previously the exact-match predicate skipped script-src-elem/script-src-attr
directives, so a CSP declaring the nonce only on script-src-elem fell through
to default-src and SSR scripts could be emitted without the expected nonce
and blocked by the browser CSP.

Closes #1989
2026-06-15 23:39:55 +01:00
Xplod13 33be48648d fix(script): register src beforeInteractive scripts and mark hoisted output (#2019)
## Summary
- Register `beforeInteractive` Scripts with `src` through
  BeforeInteractiveContext so they are hoisted into <head> ahead of
  interactive, exactly like the inline form (previously they bypassed the
  registry and rendered in source order, after React Float's head content).
- Suppress the client React render for both inline AND src beforeInteractive
  Scripts in the App Router so the hoisted tag is never duplicated.
- Tag every hoisted script with Next.js's `data-nscript="beforeInteractive"`
  marker.
- Map `crossOrigin` -> `crossorigin` and `referrerPolicy` -> `referrerpolicy`
  on hoisted scripts (parity with Next.js set-attributes-from-props).
- Extract `renderBeforeInteractiveInlineScripts` into a dedicated, pure module
  (before-interactive-head.ts) so the emit path is unit-testable.

## Root Cause
The beforeInteractive SSR branch computed `inlineContent = src ? null : ...`
and only registered when `inlineContent !== null`, so any Script with a `src`
was never captured by the registry and never spliced into <head>; it stayed
where the user wrote it, behind React Float's hoisted resource hints, defeating
the before-interactive guarantee. Hoisting src scripts then requires the client
to stop rendering them through React (the React-created <script> never executes
and would only add a duplicate DOM node), so the client dedupe condition is
aligned with the SSR registration condition. The hoisted output also lacked
Next.js's `data-nscript` marker, and REACT_TO_HTML_ATTR omitted
crossOrigin/referrerPolicy, so those attribute names did not match Next.js's
lowercased output.

## References
- Fixes #2016
- Next.js: .nextjs-ref/packages/next/src/client/script.tsx (data-nscript,
  src/inline registry parity), set-attributes-from-props.ts (lowercased attrs)
- Ported: test/e2e/app-dir/script-before-interactive/script-before-interactive.test.ts

## Verification
- CI=true pnpm test tests/script.test.ts tests/script-head-ordering.test.ts
  tests/nextjs-compat/script-nonce.test.ts tests/shims.test.ts -> 1139 passed
- CI=true npx vp check on all changed files -> pass

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 23:38:06 +01:00
Xplod13 59ddd0aded fix(i18n): match NEXT_LOCALE cookie case-insensitively (#2050)
## Summary
- Resolve the `NEXT_LOCALE` cookie case-insensitively and return the
  canonical configured locale (e.g. `NEXT_LOCALE=EN-US` -> `en-US`).
- Add unit tests covering uppercase/mixed-case cookies, exact-case, and
  non-configured values.

## Root Cause
`parseCookieLocaleFromHeader` matched the cookie value against
`i18nConfig.locales` with a case-sensitive `Array.includes`. A cookie like
`NEXT_LOCALE=EN-US` therefore failed to match a configured `en-US`, so the
sticky locale was silently dropped and locale detection fell through to the
default. Next.js's `getLocaleFromCookie` lowercases the cookie value and
finds the configured locale case-insensitively, returning the canonical
form; vinext now mirrors that.

## References
- Next.js: packages/next/src/shared/lib/i18n/get-locale-redirect.ts
  (`getLocaleFromCookie`).
- Next.js test: test/e2e/i18n-support/shared.ts (`addDefaultLocaleCookie`
  sets `NEXT_LOCALE=EN-US` "to ensure it's case-insensitive").

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 23:35:22 +01:00