Commit Graph

1315 Commits

Author SHA1 Message Date
github-actions[bot] 39ce2ba170 chore: version packages (#2113)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
vinext@0.1.5
2026-06-18 09:54:39 +00: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 d077d67b79 refactor(instrumentation): drop redundant normalizePath on instrumentation-client path (#2130) 2026-06-18 00:33:30 +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 d98adce4bc fix(image-imports): normalize watchChange id so image cache invalidates on Windows (#2125) 2026-06-17 23:50:19 +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
Jerry Zhao 29248465c6 perf(app-page-cache): reuse the ISR cache key on the stale-regeneration path (#2123) 2026-06-17 23:39:11 +01:00
Jerry Zhao 19d8f04cbb perf(image-imports): hoist image-extension regex to module scope (#2122) 2026-06-17 21:51:49 +00: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 c3ff8a7028 perf(dev): skip JSX transform for compiled runtime (#2121) 2026-06-17 22:13:21 +01: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 0ef6e10538 fix(middleware): match middleware taint ids by forward-slash on Windows (#2088) 2026-06-17 19:02:38 +01: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 54cb9e3a2f fix(build): normalize canonicalized module ids to forward slashes on Windows (#2089) 2026-06-17 18:43:20 +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 73d01f085e perf(build): filter compatibility transform hooks (#2103) 2026-06-17 12:32:42 +01:00
Alexander Lichter 56d7311994 perf(dev): externalize App Router request handler (#2108) 2026-06-17 11:55:48 +01:00
github-actions[bot] 58154448cf chore: version packages (#2046)
* chore: version packages

* Apply suggestion from @james-elicx

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: James Anderson <james@eli.cx>
vinext@0.1.4
2026-06-16 14:45:52 +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
Xplod13 50f435ce13 fix(metadata): gate twitter app and player tags on card type (#2053)
The Twitter metadata renderer emitted `twitter:app:*` tags whenever a
`twitter.app` object was present and `twitter:player:*` tags whenever
`twitter.players` was present, regardless of the resolved card type. It also
emitted `twitter:app:id`/`twitter:app:url` for falsy values (`0`, `""`).

Next.js gates these blocks on the resolved card: `if (card === 'app')` and
`if (card === 'player')`, and uses truthy checks for app id/url. Mirror that:
gate the app block on `card === 'app'`, the player block on `card === 'player'`,
and skip falsy app ids/urls. The card is already defaulted in
postProcessMetadata (summary_large_image / summary), matching Next.js.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 23:34:53 +01:00
Xplod13 157cf7ab70 fix(config): support tsconfig extends array form (#2054)
## Summary
- Handle the `extends` array form (TypeScript 5.0+) when resolving
  tsconfig/jsconfig path aliases and `baseUrl` for `next.config.ts` loading.
- Normalize `extends` (string | string[] | undefined) into an ordered list at
  both loader sites; iterate parents so later entries override earlier ones.
- Add array-extends regression tests (single parent, multi-parent precedence,
  child override).

## Root Cause
Both tsconfig loaders only handled the string form of `extends`
(`typeof parsed.extends === "string"`). When `extends` was an array
(`["./a.json", "./b.json"]`), it was silently dropped, so inherited
`compilerOptions.paths`/`baseUrl` were lost and path-aliased imports in
`next.config.ts` failed to resolve. Next.js normalizes via
`Array.isArray(config.extends) ? config.extends : [config.extends]` and merges
parents in order; this change mirrors that behavior at both vinext sites
(packages/vinext/src/config/tsconfig-paths.ts and packages/vinext/src/index.ts).

## References
- Fixes #1974
- Next.js parity: packages/next/src/build/next-config-ts/transpile-config.ts (loadTsConfigFile)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 23:29:27 +01:00
Nathan Nguyen 4de8347f31 chore(ci): refresh integration timing manifest (#2048) 2026-06-15 23:25:56 +01:00
Nathan Nguyen 5b3ea11153 fix(navigation): promote OperationToken to the eligibility authority (#2009)
The App Router client carried an OperationToken on every navigation
decision but only read its `lane` field. The authority the token was
meant to own lived inline elsewhere: the commit-staleness gate was a raw
boolean (startedNavigationId !== activeNavigationId or a visible-commit
version mismatch) evaluated before the token was even built, and cache
reuse was gated separately. The token also lacked the lifecycle
navigation id, so it could not answer "does this result belong to the
active navigation?" at all — its operationId is a per-render counter.

Promote OperationToken to the single proof-of-eligibility object. A new
operation-token.ts module owns the token type and verifyOperationToken,
a pure verifier over four dimensions (active-navigation, visible-commit,
graph-version, cache-variant) that returns a branded VerifiedOperationToken
on success. The token verifies; ApprovedVisibleCommit still mutates — the
two stay separate.

- Add navigationId to the token (sourced from startedNavigationId) so it
  can carry the active-navigation authority.
- Route the commit-staleness gate through verifyOperationTokenForCommit
  and delete the inline boolean. The staleOperation skip + trace are
  byte-identical; planPendingRootBoundaryFlightResponse now requires the
  VerifiedOperationToken brand, making "verify before plan" a compile-time
  guarantee.
- Share the authority with cache reuse: planFlightResponseArrived gates an
  accepted cache-entry reuse decision through verifyOperationTokenForCacheReuse,
  hard-navigating (cacheReuseTokenRejected) when the proof's graph version no
  longer matches the installed route graph. Behavior-preserving today (the
  token's graphVersion is minted from the same manifest); a real guard once
  cross-document and segment reuse can diverge (PR 6/7).

Absence is not permission: a checked dimension tolerates an absent
authority fact, but a *required* one fails closed, and require implies
evaluation — the contract segment-cache/BFCache writes need in PR 6/7.

Tests: operation-token.test.ts covers every verdict dimension, the brand,
the absent/mismatch split, and require⇒evaluate; navigation-planner and
app-browser-entry suites stay green, proving the gate refactor preserved
behavior; two new planner tests cover the cache-reuse graph-version seam.
2026-06-15 23:21:38 +01:00
James Anderson bacee9ef52 fix(pages): emit canonical __NEXT_DATA__ JSON (#2043)
* fix(pages): emit canonical next data script

* test(pages): expect canonical next data markup

* fix(pages): initialize canonical next data before routing

* test(pages): parse canonical next data markup

* fix(pages): preserve router bootstrap ordering

* test(pages): expect readiness bootstrap import

* fix(pages): guard readiness bootstrap without next data
2026-06-15 23:16:23 +01:00
James Anderson 51d0208d3d fix(pages): re-enter filesystem routes after rewrites (#2041)
* fix(dev): defer rewrite conditions until after middleware

* docs(dev): clarify rewritten static handling
2026-06-15 23:15:25 +01:00
James Anderson 78bb36a656 fix(app-router): preserve dynamic interception source routes (#2042) 2026-06-15 23:06:50 +01:00
James Anderson 76de660c20 fix(image): emit static imports as managed assets (#2040)
* fix(image): emit static imports as managed assets

* fix(image): handle static asset rebuilds and caching

* fix(image): scope managed media hash etags

* test(image): escape all URL ampersands
2026-06-15 23:04:40 +01:00
James Anderson b58adb3cfa fix(middleware): classify Pages data requests by URL (#2039) 2026-06-15 23:00:09 +01:00
James Anderson 48f4df28cb fix(pages-router): cancel deduped GSSP data waits (#2038) 2026-06-15 22:58:02 +01:00
James Anderson 0fb9bd32bf fix(pages): apply Document renderPage enhancers (#2034)
* fix(pages): apply document renderPage enhancers

* fix(pages): propagate document render errors

* fix(pages): delegate document initial props

* fix(pages): render dev errors through document

* fix(pages): preserve document-provided html

* fix(pages): keep render page enhancer type internal

* fix(pages): buffer custom dev error documents
2026-06-15 22:53:40 +01:00