Commit Graph

1609 Commits

Author SHA1 Message Date
github-actions[bot] 8d0a18d440 chore: version packages (beta) (#2549)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@vinext/cloudflare@1.0.0-beta.1 vinext@1.0.0-beta.1
2026-07-10 08:44:13 +00:00
James Anderson 4c8399d504 fix(release): publish betas as latest (#2535)
* fix(release): publish betas as latest

* fix(release): promote beta github releases
2026-07-10 09:39:51 +01:00
Lubomir Georgiev (Любомир Георгиев) bbec971d8f fix(app-router): preserve streamed metadata placement parity (#2572)
* fix(app-router): hoist streamed metadata into <head> instead of body

Async generateMetadata was serialized via dangerouslySetInnerHTML into a
hidden <div> in the body, which React cannot hoist. For JS-capable clients
(browsers, Googlebot) the tags stayed in <body>, where Google ignores
rel=canonical, hreflang and robots.

Render the resolved metadata as real <title>/<meta>/<link> elements through
MetadataHead so React 19 hoists them into <head>, matching Next.js. Remove
the now-unused renderMetadataToHtml string serializer.

* Fix tests

* fix(app-router): harden metadata hoisting coverage

* fix(app-router): restore streamed metadata placement

* fix(app-router): preserve static metadata placement

---------

Co-authored-by: James <james@eli.cx>
2026-07-10 09:17:32 +01:00
James Anderson 132d351ed7 chore(deps): upgrade TypeScript to 7.0 (#2577)
* chore(deps): upgrade TypeScript to 7.0

* fix(build): resolve native TypeScript executable
2026-07-09 22:57:23 +01:00
Nathan Nguyen 2c3c0b7d57 fix(app-router): delay dynamic SSR stream pulls (#2575)
Dynamic App SSR currently pipes React's edge stream as soon as the shell resolves. That first pull can flush a Suspense fallback even when the boundary resolves in the next React render task, producing fallback HTML that Next avoids.

The SSR entry now marks successful dynamic streams and awaits one React render task before wiring them through the HTML transform. waitForAllReady, PPR fallback-shell, and recovered shell-error paths keep their existing behavior.

Add a stream-boundary regression that proves immediate consumption leaks the fallback marker while delayed consumption emits only the resolved content.
2026-07-09 22:41:00 +01:00
James Anderson 04850ddb8d ci: restrict compatibility ingest to scheduled Next.js version (#2576) 2026-07-09 18:09:04 +01:00
James Anderson 6734371a59 fix(pages): preserve missing page props on errors (#2568)
* fix(pages): preserve missing page props on errors

* fix(pages): complete default error props contract

* fix(pages): match default error component contract

* fix(pages): complete next error parity

* fix(pages): make next error subclassable

* fix(pages): align next error context types

* fix(pages): isolate error context types

* test(pages): cover default error production hydration

* test(pages): cover default error hydration in dev

* fix(pages): hydrate dev error responses

* fix(pages): set dev error router context

* fix(pages): isolate dev error router state

* fix(pages): align dev error router hydration

* fix(pages): keep error router ready during hydration

* fix(pages): clean up error fallback handling

* fix(pages): load custom error page on navigation

* refactor(pages): share dev hydration entry
2026-07-09 16:23:18 +01:00
James Anderson 555481714c fix(pages): align preview mode behavior (#2561)
* fix(pages): align preview mode behavior

* fix(pages): secure preview mode boundaries

* fix(pages): isolate preview credentials

* fix(pages): unify draft mode credentials

* fix(build): isolate preview credentials per build

* fix(api): align pages draft mode helpers

* fix(pages): address preview review findings

* fix(pages): align preview request types

* fix(pages): export preview data type

* fix(types): align pages API declarations

* fix(types): preserve NextApiRequest augmentation

* fix(pages): align API redirect responses

* fix(types): align API request env runtime

* fix(types): preserve API request env parity

* fix(pages): align dev preview fallback behavior

* fix(pages): scope preview state to data pages

* fix(pages): preserve cookies when clearing preview

* fix(pages): scope preview clear cookie dedupe
2026-07-09 14:41:49 +00:00
James Anderson 7dc85d87ec fix(pages): mark auto exports in next data (#2569)
* fix(pages): mark auto exports in next data

* fix(pages): preserve inherited app optimization
2026-07-09 14:52:11 +01:00
James Anderson 2afe4b6ee6 fix(pages): normalize i18n router URLs (#2565)
* fix(pages): normalize i18n router URLs

* fix(pages): preserve i18n hash link context

* fix(pages): resolve string hash links from asPath

* fix(pages): retain query-only link interpolation

* fix(config): validate i18n default locale

* fix(config): validate i18n before callbacks

* test(config): cover i18n domain validation
2026-07-09 14:50:28 +01:00
James Anderson c265852222 fix(build): gate native typeof window folding (#2574)
* fix(build): gate native typeof window folding

* test(build): share Vite folding version predicate

* fix(build): detect bundled Rolldown folding support

* test(build): prefer bundled Rolldown capability
2026-07-09 11:05:00 +01:00
James Anderson 617bffa95e test(deploy): skip suites that stall deploy shards (#2573)
* test(deploy): skip segment cache memory pressure suite

* test(deploy): skip instant navigation suite
2026-07-09 08:20:55 +00:00
James Anderson b6571ce5f3 perf(build): use native typeof window folding (#2564)
* perf(build): use native typeof window folding

* test(build): tighten typeof window scan plugin
2026-07-08 19:05:23 +01:00
Nathan Nguyen aa1b6663b0 fix(app-router): handle redirects in route-miss fallbacks (#2553)
* fix(app-router): handle redirects in route-miss fallbacks

* test(app-router): fix frozen-lockfile break in not-found-redirect fixture

The route-miss redirect fixture shipped a package.json declaring six
"latest" dependencies (@vitejs/plugin-react, typescript, @types/react*,
vinext, vite) with no matching pnpm-lock.yaml importer. CI runs
frozen-lockfile installs, so every job died at the shared setup step with
ERR_PNPM_OUTDATED_LOCKFILE, turning the whole suite red before a single
test ran.

The fixture never needs those dependencies: dev tests load it via
createServer({ configFile: false }) with the plugin built in code, and
the production test copies it through createIsolatedFixture, which
symlinks the workspace root node_modules. The only file the build
actually reads is package.json's "type": "module" (for ESM output), so
the deps, vite.config.ts, tsconfig.json, and next-env.d.ts were all dead
scaffolding.

Trim package.json to name/private/type, add the empty lockfile importer
so frozen install is satisfied, and delete the unused config files. Also
drop app/result/page.tsx, which .gitignore's bare "result" rule silently
excluded from the commit anyway; the tests only assert the redirect
Location header, never render /result.

* fix(app-router): encode async route-miss redirects in the RSC flight

A redirect() thrown by an async root layout (one that `await headers()`
before redirecting) during a route-miss not-found render was lost on RSC
navigations. renderAppPageBoundaryResponse returns the RSC stream without
consuming it, so the async redirect only surfaced through React's onError
once the stream was pulled — after the synchronous captured-special-error
check had already run and found nothing. The request fell through to a raw
404 flight instead of the 200 flight-encoded redirect the client router
expects. The document path was unaffected because createHtmlResponse
consumes the stream, surfacing the redirect before the same check.

Drain a tee'd copy of the RSC boundary stream to force the render to
settle before the capture check, then hand the buffered copy back as the
body. Boundary responses are small terminal documents, so buffering them
is an acceptable cost for correct redirect handling; the document path is
untouched. Mirrors app-page-render.ts's pre-flush special-error capture.

Also:
- Extract the duplicated redirect-flight encoding (digest format, throwing
  error, stream builder) from app-page-dispatch and app-page-boundary-render
  into app-rsc-redirect-flight.ts as a single owner, with focused unit tests.
- Replace the `new Error(...) as Error & { digest }` cast with a typed
  RscRedirectFlightError subclass, and read the metadata-error marker via
  Reflect.get instead of an `as Record<symbol, unknown>` cast.
- Document that renderBoundarySpecialErrorResponse deliberately omits
  renderFallbackPage: redirects are fully handled, but a notFound()/
  forbidden()/unauthorized() re-thrown inside boundary rendering has no
  parent boundary and terminates with a plain status-text response. Pin
  that terminal behavior with a test.
- Add route-miss RSC redirect coverage (dev + production) asserting the
  200 flight response, text/x-component, and X-Vinext-Rsc-Redirect header.

* test(app-router): prove RSC redirect drain covers matched-route fallbacks

The RSC special-error drain in the HTTP-access fallback path was described
as route-miss-only, but it runs for every http-access fallback: a matched
route's notFound()/forbidden()/unauthorized() renders its boundary through
the same path, where a layout can also async-redirect. Narrowing the drain
to route misses would leave that matched-route case with the original
lazy-stream bug, so the broad behavior is intentional.

Add a matched-route fixture page (`/gated` calls notFound()) and dev +
production coverage: with the redirect trigger the matched-route not-found's
async layout redirect is encoded as a 200 flight; without it the response is
a normal 404 flight, proving the pre-response buffering neither drops nor
corrupts the matched-route payload. Broaden the drain comment to state the
behavior spans route-miss and matched-route fallbacks and why.

* fix(app-router): honor html-limited bots for fallback metadata redirects

A redirect() thrown from generateMetadata() while rendering an HTTP-access
fallback boundary always rode as a 200 streaming response (HTML meta-refresh
/ RSC flight), even for html-limited bots that Next.js serves a blocking 307.
renderBoundarySpecialErrorResponse never passed serveStreamingMetadata, so
buildAppPageSpecialErrorResponse defaulted `serveStreamingMetadata !== false`
to streaming. Matched-page dispatch already threads
shouldServeStreamingMetadata(userAgent, htmlLimitedBots); the fallback path
did not.

Thread htmlLimitedBots into the fallback renderer, compute the same
per-request decision there, and pass serveStreamingMetadata through the
boundary options into the special-error responder. Hoist the entry's
__htmlLimitedBots declaration above __createAppFallbackRenderer (it runs at
module init) to avoid a temporal-dead-zone reference. Cover both branches:
streaming document → 200 meta-refresh, Bingbot → 307 + Location.

Also fix the matched-route drain test so it actually exercises the new path.
The prior test redirected from the root layout, which fires during the
matched-route layout probe and is caught by the layout special-error path
before the fallback renders — so it passed without touching the boundary
drain. Redirect from the route's own not-found boundary instead (on its own
header, so the root layout renders normally and the fallback is reached),
which surfaces through the RSC drain in renderAppPageBoundaryElementResponse.

* fix(app-router): prefix basePath on global-not-found fallback redirects

The global-not-found branch of renderHttpAccessFallback did not pass
basePath/trailingSlash into renderAppPageHttpAccessFallback, while the normal
fallback branch did. renderBoundarySpecialErrorResponse forwards
options.basePath into buildAppPageSpecialErrorResponse, which prefixes
app-internal redirect Locations with the configured base path. So a redirect()
thrown from app/global-not-found.tsx or its generateMetadata() produced an
unprefixed Location under basePath, unlike every other fallback boundary.

Pass basePath and trailingSlash in the global-not-found branch too, matching
the sibling branch. Also refresh the stale renderBoundarySpecialErrorResponse
comment: it described a route-miss-only path and a "root not-found or error
boundary", but the responder now covers matched-route HTTP-access fallbacks
and no longer claims error-boundary handling.

* fix(app-router): preserve fallback redirect semantics

* fix(app-router): preserve push redirect history

---------

Co-authored-by: James <james@eli.cx>
2026-07-08 12:07:22 +01:00
James Anderson 75b9d1a6d8 perf(pages): reuse dev stylesheet dependency analysis (#2550)
* perf(pages): reuse dev stylesheet dependency graph

* perf(build): filter dynamic request transforms natively

* test(pages): harden dependency analysis caching

* refactor(pages): keep dependency cache internal
2026-07-08 11:05:07 +01:00
Nathan Nguyen 7e721b3e53 fix(app-router): preserve semicolons in redirect digests (#2487)
* fix(app-router): preserve raw redirect digest URLs

Redirect digests can carry raw URLs, and raw URLs can contain semicolons. Treating semicolon-delimited URL content as digest structure can truncate or misclassify the redirect target.

Keep redirect digest parsing strict about the final status segment while preserving semicolons in the URL. The server parser stays self-contained so App Router startup does not import the navigation shim.

* fix: simplify malformed redirect digest fallback

* fix: simplify malformed redirect digest fallback

* test: cover malformed redirect digest tails consistently

* fix: restore unstable rethrow cause handling

* fix: guard decodeURIComponent throw and comment status allowlist

- Wrap decodeURIComponent in try/catch for malformed lone % in raw URLs
- Add clarifying comment that only canonical 303/307/308 are extracted
- Both address ask-bonk review feedback from PR #2487

* fix(app-router): preserve raw redirect digest URLs

* fix(app-router): support empty redirect destinations

* docs(app-router): explain redirect digest formats

* docs(app-router): clarify redirect digest parsing

* test(app-router): cover redirect digest re-emission

* docs(app-router): document redirect parser invariants

---------

Co-authored-by: James <james@eli.cx>
2026-07-08 10:55:33 +01:00
Ching Wei Kang 2efac2c02b fix: pass server externals to Nitro traceDeps (#2521)
* fix: pass server externals to Nitro traceDeps

* fix: propagate Nitro externals in dev

---------

Co-authored-by: James <james@eli.cx>
2026-07-07 00:10:41 +01:00
Jerry Zhao 1860632d87 refactor: migrate path handling from node:path to pathslash (#2502)
* refactor: migrate the CLI cluster path handling to pathslash

* refactor(routing): migrate the routing cluster path handling to pathslash

* refactor(plugins): migrate the plugins cluster path handling to pathslash

* refactor(server): migrate the server cluster path handling to pathslash

* refactor(build): migrate the build cluster path handling to pathslash

* refactor(core): migrate entries, config, and shared utils to pathslash

* refactor(plugin): migrate index.ts path handling to pathslash

* refactor: finish the pathslash migration — test sweep, stragglers, lint guard

* refactor: address final-review findings on the pathslash migration

* docs(agents): document the pathslash path-handling convention

* fix(build): emit inlined dependencies outside dist/node_modules

* fix: normalize wire- and bundler-shaped ids unconditionally

* docs(agents): document the unconditional-normalization exception

* fix: gate Windows-shaped path fixtures to Windows

* docs(agents): gate Windows-shaped fixtures with runIf

* refactor: migrate upstream prerender path discovery to pathslash

* fix(create-vinext-app): bundle pathslash into the scaffolder dist

---------

Co-authored-by: James <james@eli.cx>
2026-07-06 23:49:04 +01:00
James Anderson 60ad0b1650 fix(pages): preserve fast refresh state (#2544)
* fix(pages): preserve fast refresh state

* fix(pages): preload transformed dev styles
2026-07-06 23:29:47 +01:00
James Anderson 753c229ef8 fix(build): honor inline next config for static export (#2543)
* fix(build): honor inline next config for static export

* fix(build): reuse resolved config during prerender

* fix(config): resolve promised plugin metadata

* refactor(config): share plugin option flattening

* test(deploy): codify eager config loading

* refactor(deploy): use resolved cache metadata

* refactor(config): remove unused inline loader

* test(config): preserve internal metadata loaders

* refactor(build): keep hybrid plugin extraction unchanged

* refactor(prerender): use resolved next config
2026-07-06 23:15:29 +01:00
Ching Wei Kang d7635d34a6 fix(routing): discover dot-directory routes (#2531) 2026-07-06 23:13:16 +01:00
dependabot[bot] 0862df37ec chore(deps): bump voidzero-dev/setup-vp from 1.13.0 to 1.15.0 (#2548)
Bumps [voidzero-dev/setup-vp](https://github.com/voidzero-dev/setup-vp) from 1.13.0 to 1.15.0.
- [Release notes](https://github.com/voidzero-dev/setup-vp/releases)
- [Commits](https://github.com/voidzero-dev/setup-vp/compare/35171c92dd08b67d5a9d3f2a4327800e58396f2a...250f29ce396baf5e8f24498e17c0dfdebabc26eb)

---
updated-dependencies:
- dependency-name: voidzero-dev/setup-vp
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 19:53:02 +00:00
MK (fengmk2) 2bf68e906b chore(deps): bump vite-plus to v0.2.2 (#2524)
Vite+ is now in beta.
https://voidzero.dev/posts/announcing-vite-plus-beta
2026-07-06 20:49:17 +01:00
Angus Hawkings 7e1ff083da docs(cloudflare): update agent skill deploy commands (#2545) 2026-07-06 14:00:18 +01:00
Alexander Lichter 0f999d1047 feat(benchmarks): add rolling median trendlines (#2285)
* feat(benchmarks): add rolling median trendlines

* fix(benchmarks): clarify individual run toggle

* feat(benchmarks): persist trend chart view
2026-07-06 11:17:35 +01:00
James Anderson fd1cc3d3dd docs: recommend vinext setup commands (#2536) 2026-07-04 17:21:22 +00:00
James Anderson 6d98d53627 fix(ci): use experimental CDN warmup flag (#2534) 2026-07-04 18:07:35 +01:00
github-actions[bot] 4fad98a20e chore: version packages (beta) (#2499)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@vinext/cloudflare@1.0.0-beta.0 create-vinext-app@1.0.0-beta.0 vinext@1.0.0-beta.0
2026-07-04 16:55:33 +00:00
James Anderson ed0e343d05 feat(init): mark CDN warmup flag experimental (#2533)
* feat(init): mark CDN warmup flag experimental

* chore(init): tweak experimental prewarm prompt

* chore(init): default experimental prewarm to no
2026-07-04 16:50:59 +00:00
James Anderson 201f423677 fix(init): use built Wrangler config for deploy script (#2532) 2026-07-04 17:40:33 +01:00
James Anderson f114c07d59 chore(release): reclassify prerender KV population as minor (#2530)
* chore(release): reclassify prerender KV population

* chore: format changeset override
2026-07-04 17:11:20 +01:00
James Anderson eba70463d4 feat(cloudflare): warm prerendered paths before deploy (#2481)
* feat(cloudflare): warm prerendered paths before deploy

* fix(cloudflare): warm production CDN for uploaded versions

* fix(cloudflare): resolve Worker names for CDN warmup

* fix(cloudflare): harden CDN warmup diagnostics

* fix(cloudflare): harden CDN warmup deploy flow

* fix(cloudflare): clarify CDN warmup trigger failures

* feat(cloudflare): warm cdn from build path manifest

* fix(cloudflare): emit warm paths for skip-build deploys

* fix(cloudflare): avoid stale skip-build path manifests

* test(cloudflare): lock skip-build prerender manifest gate

* fix(cloudflare): warm fallback deploy URL from triggers

* fix(cloudflare): clarify CDN warmup deploy logs

* feat(init): offer CDN warmup deploy script

* update wrangler configs

* fix(init): default CDN warmup to yes

* fix(cloudflare): explain first warm deploy failure

* fix(cloudflare): keep deploy errors concise

* fix(init): make generated home page cacheable
2026-07-04 16:09:55 +00:00
James Anderson 454dfbdf6b fix(cloudflare): populate kv cache from prerendered routes (#2509)
* fix(cloudflare): populate kv cache from prerendered routes

* fix(cloudflare): harden prerender kv population

* fix(cloudflare): match runtime app cache keys

* fix(cloudflare): clean prerender kv checks

* fix(cloudflare): align zero revalidate kv entries

* fix(cloudflare): tighten prerender kv deploy seeding

* fix(cloudflare): align toml env deploy parsing

* fix(cloudflare): cover toml deploy domain parsing

* fix(cloudflare): accept indented toml deploy fields

* fix(cloudflare): align json env domain precedence

* fix(cloudflare): upload prerender cache through wrangler

* fix(cloudflare): scope prerender kv upload to deploy
2026-07-04 15:52:01 +01:00
James Anderson 078073d75c fix(cloudflare): stream deploy logs (#2528) 2026-07-04 15:39:42 +01:00
James Anderson f22c962af8 refactor(cloudflare)!: remove vinext package coupling (#2527)
* refactor(cloudflare)!: remove vinext package coupling

* Review: PR #2527 breaks a test

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

* test(cache): remove CDN auto-detection expectations

---------

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-07-04 15:29:45 +01:00
James Anderson 282698a77f fix(app-router): stop varying RSC responses by Accept (#2526) 2026-07-04 14:14:10 +00:00
Cameron f7507ce238 perf(build): filter virtual module hooks (#2519)
* perf(build): filter virtual module hooks

Move several virtual-module resolveId/load hooks to Vite's object hook form with native id filters.

This lets Vite/Rolldown skip invoking vinext JavaScript handlers for unrelated module ids during dev and build module graph walks, reducing repeated string comparisons on the hot plugin hook path. The filtered hooks now only run for vinext virtual modules, instrumentation client injection, and the React canary shim ids they can actually handle.

* perf(build): anchor react canary load filter

Tighten the React canary virtual module load filter to match only the resolved null-byte-prefixed id.

I also checked exact string filters for other resolved virtual load ids, but Vite's native string filter skipped the instrumentation-client virtual module in the focused test path. Keep those null-byte virtual ids on regex filters for now.
2026-07-04 14:53:58 +01:00
James Anderson c47c74a919 docs: update vinext project status (#2525) 2026-07-04 13:46:10 +00:00
James Anderson bfeaaa7dc5 docs(web): highlight Workers Cache for ISR (#2522)
* docs(web): highlight Workers Cache for ISR

* docs(web): refresh homepage metrics

* docs(web): refine client bundle metric
2026-07-04 13:57:49 +01:00
James Anderson 5825c6807f fix(app-router): preserve client state during action revalidation (#2517)
* fix(app-router): preserve client state during action revalidation

* test(app-router): pin loading boundary to slot entry

* test(app-router): deploy action revalidation repro
2026-07-04 11:08:54 +01:00
Steve Faulkner 54497d42e7 fix: dev-server polish batch — HTML charset, client global polyfill, trailingSlash image endpoint (#2512)
* fix(pages): send charset=utf-8 on HTML Content-Type

Next.js serves every HTML response (SSR and prerendered) with
`Content-Type: text/html; charset=utf-8`, but vinext's Pages Router
paths sent a bare `text/html`. Without the header charset — and without
an early <meta charset> in the page — Chromium falls back to
windows-1252, so non-ASCII content renders as mojibake (e.g. nbsp as
'Â ') and the resulting DOM diverges from the hydrated tree, triggering
the full-screen hydration error overlay in dev. Reproduced against
nextjs-notion-starter-kit; baseline `next dev`/`next start` both send
the charset.

Append `; charset=utf-8` on every Pages Router HTML producer (the App
Router paths already send it):

- pages-page-response.ts — page render response headers + gSSP header
  merge
- dev-server.ts — streaming SSR, ISR HIT/STALE, and static-HTML dev
  responses
- pages-page-data.ts — ISR cache HIT/STALE responses in prod
- pages-request-pipeline.ts — the `defaultContentType` a buffering
  adapter applies when a render response carries no Content-Type
- static-file-cache.ts — `.html` static files (prerendered pages),
  matching Next.js static serving

Compression negotiation is unaffected: COMPRESSIBLE_TYPES matching
splits the media type on ";" before lookup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(client): polyfill `global` in browser bundles

Next.js exposes the Node-style `global` alias to client code: webpack
via its `node.global` runtime shim, Turbopack by compile-time rewriting
the free `global` identifier to its globalThis shortcut and folding
`typeof global` to "object" (turbopack-ecmascript references). vinext
provided nothing, so any client dependency that reads `global` (e.g.
use-dark-mode via nextjs-notion-starter-kit) threw
`ReferenceError: global is not defined` after hydration.

Add a `vinext:client-global-define` plugin that scopes
`define: { global: "globalThis" }` to the client environment:

- builds statically rewrite free `global` references (Turbopack-style)
- dev injects `"global": globalThis` into the client runtime defines
  (/@vite/env), assigning `globalThis.global` before user code runs
  (webpack-style)
- the same define is layered into the client dep optimizer
  (rolldownOptions.transform.define / esbuildOptions.define) because
  pre-bundled deps bypass the plugin transform pipeline

`typeof global` evaluates to "object" in the browser either way,
matching Next.js. Server environments are untouched — `global` remains
the real Node global — and a user-configured `compiler.define.global`
takes precedence, mirroring Turbopack's or_insert free-var semantics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(images): accept /_next/image/ with trailingSlash: true

With `trailingSlash: true`, the App Router dev handler 308-redirects
`/_next/image?url=...` to `/_next/image/?url=...` (the trailing-slash
normalizer runs before the image-endpoint check), but the image endpoint
only matched the exact `/_next/image` pathname — so the redirected
request 404'd and every dev-mode next/image request broke. Reproduced
on tailwind-nextjs-starter-blog, which ships trailingSlash: true.

Baseline Next 16.2.10 behaves the same way up to the redirect (dev with
trailingSlash: true also 308s `/_next/image` to `/_next/image/`) but
then SERVES the slashed form: its route matching strips a trailing
slash before matching internal paths (getItem in
packages/next/src/server/lib/router-utils/filesystem.ts), so image
requests never fail.

Match that: isImageOptimizationPath() now strips a single trailing
slash before comparing, which covers every caller — App Router
dev/prod (app-rsc-handler), Pages Router dev middleware, the Node prod
server, and the Cloudflare worker entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: align existing assertions with charset and trailing-slash fixes

Three assertions added on main after these fixes were authored still
asserted the old behavior: bare text/html Content-Type in
static-file-cache and the pages pipeline defaultContentType, and
isImageOptimizationPath rejecting the trailing-slash form.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 00:46:29 +01:00
James Anderson 73156b36e7 fix(release): support beta prereleases (#2511) 2026-07-03 23:40:40 +01:00
James Anderson e4aa03ac5c feat(create): add create-vinext-app (#2483)
* feat(create): add create-vinext-app

* fix(ci): support create package checks

* fix(create): address creator review findings

* fix(create): document version flag

* fix(ci): keep create-next-app cloudflare coverage

* fix(create): update init option test for cdn cache

* fix(create): bundle creator helper deps

* fix(ci): allow local vinext dlx deploy test

* fix(ci): run create-vinext-app deploy test from local tarballs

* fix(create): keep creator package self-contained

* fix(ci): preview release create-vinext-app

* fix(init): install runtime deps as app dependencies

* fix(create-vinext-app): scope workers copy to cloudflare

* fix(create-vinext-app): use pnpm in template examples

* fix(init): start cloudflare apps with wrangler
2026-07-03 17:51:49 +01:00
James Anderson c84f5d476b fix(check): mark cache components partially supported (#2507)
* fix(check): mark cache components unsupported

* fix(check): classify cache components as partial
2026-07-03 15:27:02 +00:00
Steve Faulkner 160645c6d3 fix(config): tsconfig paths — longest-prefix matching and stylesheet-scoped aliases (#2504)
* fix(config): match overlapping tsconfig paths by longest prefix

TypeScript (and Next.js) match compilerOptions.paths patterns by longest
matched prefix regardless of declaration order, but vinext materialized
them into Vite resolve.alias entries in declaration order, where the
alias plugin picks the first match. With overlapping patterns like
`"@/*": ["./src/*"]` + `"@/public/*": ["./public/*"]` (the
ixartz/Next-js-Boilerplate shape), `@/public/...` imports resolved into
src/public/ and every page 500'd.

Sort materialized aliases longest-prefix-first in both the plugin's
resolve.alias materialization and the next.config.ts loader's
runnerImport aliases.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(config): keep tsconfig path aliases out of stylesheet resolution

TypeScript compilerOptions.paths never apply to CSS in Next.js —
@import specifiers in stylesheets use standard bundler resolution,
including package.json exports maps. vinext's materialized
resolve.alias entries also ran inside Vite's internal CSS resolver
(which only consults the alias plugin plus Vite's own resolver), so a
monorepo alias like `"@scope/ui/*": ["../../packages/ui/src/*"]`
rewrote `@import "@scope/ui/globals.css"` away from its
exports-mapped target and every route failed with a postcss ENOENT in
dev, and builds failed while analyzing client references
(create-better-t-stack scaffolds).

Emit the merged alias map as alias entries and attach a customResolver
to tsconfig-derived ones that bails out for stylesheet importers
(stylesheet file paths and the synthetic <basedir>/* importer that
postcss-import/less use), letting Vite resolve the original specifier
normally. JS/TS importers keep full alias behavior — including
`import "@/styles/globals.css"` from a layout and alias-based
import.meta.glob / dynamic-import patterns, which require blind prefix
replacement. Vite 8 reports alias customResolver as deprecated during
config resolution; the replacement it suggests (a resolveId plugin)
never runs inside the CSS resolver container, so the warning is
filtered while this remains the only importer-aware hook available.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:25:51 +01:00
James Anderson b5897d0595 feat(build): require Vite 8 (#2486)
* feat(build)!: require Vite 8

* fix(build): remove unused Vite version export

* test(link): harden prefetch timing assertions
2026-07-03 15:20:33 +01:00
James Anderson 17216d605b chore(release): reclassify 9eed252 as feat (#2500) 2026-07-03 10:20:53 +00:00
James Anderson 9eed252701 fix(init): default to Workers Cache on Cloudflare (#2482)
* fix(init): default to Workers Cache on Cloudflare

* fix(init): prompt for Cloudflare CDN cache

* fix(init): prompt for missing CDN cache choice

* fix(init): add Cloudflare deploy script
2026-07-03 11:15:52 +01:00
github-actions[bot] 5471b41eec chore: version packages (#2428)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@vinext/cloudflare@0.2.1 vinext@0.2.1
2026-07-03 09:40:50 +00:00
Steve Faulkner b7455c3fa6 fix: fully elide inline type-only import specifiers (#2498)
Vite 8's OXC transform (and Vite 7's esbuild transform) honour
`"verbatimModuleSyntax": true` from the app's tsconfig, so
`import { type Metadata } from "next"` leaves a side-effect
`import "next"` behind when every specifier is type-only. Next.js
(SWC) — and esbuild/tsc without verbatimModuleSyntax — elide the whole
statement. create-t3-app and other scaffolds emit both the tsconfig
option and the inline-type import form stock, so on vinext the real
Next.js server runtime was pulled into the RSC graph (dev 500
"require is not defined", build failures via styled-jsx/client-only)
and server-only modules were pulled into the browser bundle when a
"use client" file imported only types from them (t3's tRPC router
shipped to clients and crashed hydration).

Force `typescript.onlyRemoveTypeImports: false` in the oxc transform
options (and `verbatimModuleSyntax: false` in the Vite 7 esbuild
tsconfigRaw) so type-import elision matches Next.js regardless of the
app's tsconfig.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 10:35:59 +01:00