vinext 1.0.0-beta.5 → 1.0.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -3
- package/dist/build/action-owner-manifest.d.ts +42 -0
- package/dist/build/action-owner-manifest.js +110 -0
- package/dist/build/clean-output.js +1 -4
- package/dist/build/client-build-config.js +10 -11
- package/dist/build/prerender-paths.js +3 -4
- package/dist/build/prerender-phase.d.ts +15 -0
- package/dist/build/prerender-phase.js +37 -0
- package/dist/build/prerender-server-entry.js +3 -3
- package/dist/build/prerender-server-pool.js +2 -0
- package/dist/build/prerender.d.ts +7 -3
- package/dist/build/prerender.js +103 -21
- package/dist/build/report.d.ts +1 -1
- package/dist/build/route-classification-manifest.d.ts +1 -1
- package/dist/build/run-prerender.js +9 -6
- package/dist/build/static-export.d.ts +1 -1
- package/dist/client/app-nav-failure-handler.js +1 -0
- package/dist/client/navigation-runtime.d.ts +5 -3
- package/dist/client/navigation-runtime.js +4 -8
- package/dist/client/vinext-next-data.d.ts +2 -0
- package/dist/config/next-config.js +24 -7
- package/dist/deps/.pnpm/image-size@2.0.2/deps/image-size/dist/index.js +808 -0
- package/dist/entries/app-browser-entry.d.ts +1 -1
- package/dist/entries/app-browser-entry.js +2 -1
- package/dist/entries/app-rsc-entry.d.ts +2 -1
- package/dist/entries/app-rsc-entry.js +136 -8
- package/dist/entries/pages-client-entry.d.ts +1 -1
- package/dist/entries/pages-client-entry.js +8 -3
- package/dist/entries/pages-server-entry.d.ts +1 -1
- package/dist/index.js +190 -181
- package/dist/plugins/action-owner-manifest.d.ts +14 -0
- package/dist/plugins/action-owner-manifest.js +84 -0
- package/dist/plugins/ast-utils.d.ts +30 -1
- package/dist/plugins/ast-utils.js +76 -1
- package/dist/plugins/css-module-imports.d.ts +2 -2
- package/dist/plugins/css-module-imports.js +5 -13
- package/dist/plugins/dynamic-preload-metadata.js +13 -34
- package/dist/plugins/environment.d.ts +11 -0
- package/dist/plugins/environment.js +7 -0
- package/dist/plugins/extensionless-dynamic-import.js +11 -35
- package/dist/plugins/fonts.js +8 -12
- package/dist/plugins/ignore-dynamic-requests.d.ts +2 -4
- package/dist/plugins/ignore-dynamic-requests.js +29 -59
- package/dist/plugins/import-meta-url.d.ts +15 -9
- package/dist/plugins/import-meta-url.js +309 -85
- package/dist/plugins/instrumentation-client.js +4 -5
- package/dist/plugins/middleware-export-validation.js +5 -17
- package/dist/plugins/middleware-server-only.js +2 -2
- package/dist/plugins/og-asset-ownership.js +18 -26
- package/dist/plugins/og-assets.js +2 -4
- package/dist/plugins/optimize-imports.js +2 -4
- package/dist/plugins/pages-node-externals.d.ts +23 -0
- package/dist/plugins/pages-node-externals.js +179 -0
- package/dist/plugins/remove-console.js +2 -4
- package/dist/plugins/require-condition-resolution.d.ts +16 -0
- package/dist/plugins/require-condition-resolution.js +167 -0
- package/dist/plugins/require-context.js +11 -46
- package/dist/plugins/sass.js +1 -1
- package/dist/plugins/server-externals-manifest.js +1 -23
- package/dist/plugins/strip-server-exports.js +13 -19
- package/dist/plugins/styled-jsx.js +17 -19
- package/dist/plugins/transform-result.d.ts +10 -0
- package/dist/plugins/transform-result.js +10 -0
- package/dist/plugins/transitive-externals.d.ts +24 -0
- package/dist/plugins/transitive-externals.js +107 -0
- package/dist/plugins/typeof-window.d.ts +10 -5
- package/dist/plugins/typeof-window.js +142 -24
- package/dist/plugins/use-cache-callable.d.ts +12 -0
- package/dist/plugins/use-cache-callable.js +199 -0
- package/dist/plugins/wasm-module-import.js +1 -1
- package/dist/plugins/worker-image-imports.d.ts +12 -0
- package/dist/plugins/worker-image-imports.js +136 -0
- package/dist/routing/app-route-graph.d.ts +3 -2
- package/dist/routing/app-route-graph.js +1 -1
- package/dist/routing/route-pattern.d.ts +8 -1
- package/dist/routing/route-pattern.js +28 -1
- package/dist/routing/route-validation.js +7 -1
- package/dist/server/accept-encoding.d.ts +1 -2
- package/dist/server/accept-encoding.js +1 -4
- package/dist/server/api-handler.js +1 -1
- package/dist/server/app-action-forwarding.d.ts +18 -0
- package/dist/server/app-action-forwarding.js +140 -0
- package/dist/server/app-browser-entry.js +236 -51
- package/dist/server/app-browser-error.d.ts +1 -2
- package/dist/server/app-browser-error.js +1 -5
- package/dist/server/app-browser-history-controller.d.ts +16 -1
- package/dist/server/app-browser-history-controller.js +135 -18
- package/dist/server/app-browser-navigation-abort.d.ts +16 -0
- package/dist/server/app-browser-navigation-abort.js +29 -0
- package/dist/server/app-browser-navigation-controller.d.ts +4 -0
- package/dist/server/app-browser-navigation-controller.js +22 -6
- package/dist/server/app-browser-navigation-response.d.ts +11 -0
- package/dist/server/app-browser-navigation-response.js +8 -0
- package/dist/server/app-browser-popstate.d.ts +6 -1
- package/dist/server/app-browser-popstate.js +6 -2
- package/dist/server/app-browser-server-action-client.js +1 -1
- package/dist/server/app-browser-state.d.ts +2 -2
- package/dist/server/app-browser-state.js +3 -3
- package/dist/server/app-browser-stream.d.ts +0 -2
- package/dist/server/app-browser-supplemental-refresh.d.ts +49 -0
- package/dist/server/app-browser-supplemental-refresh.js +190 -0
- package/dist/server/app-browser-visible-commit.js +23 -12
- package/dist/server/app-elements-wire.d.ts +2 -0
- package/dist/server/app-elements-wire.js +7 -0
- package/dist/server/app-history-state.d.ts +22 -1
- package/dist/server/app-history-state.js +93 -9
- package/dist/server/app-optimistic-routing.d.ts +22 -1
- package/dist/server/app-optimistic-routing.js +137 -12
- package/dist/server/app-page-boundary-render.js +4 -7
- package/dist/server/app-page-cache-finalizer.d.ts +4 -1
- package/dist/server/app-page-cache-finalizer.js +11 -15
- package/dist/server/app-page-cache-render.d.ts +1 -0
- package/dist/server/app-page-cache-render.js +3 -1
- package/dist/server/app-page-cache.d.ts +4 -2
- package/dist/server/app-page-cache.js +13 -6
- package/dist/server/app-page-dispatch.d.ts +4 -1
- package/dist/server/app-page-dispatch.js +13 -3
- package/dist/server/app-page-element-builder.d.ts +1 -0
- package/dist/server/app-page-element-builder.js +1 -0
- package/dist/server/app-page-execution.js +19 -6
- package/dist/server/app-page-ppr-runtime.js +3 -2
- package/dist/server/app-page-render.d.ts +2 -1
- package/dist/server/app-page-render.js +10 -4
- package/dist/server/app-page-request.d.ts +2 -33
- package/dist/server/app-page-request.js +1 -27
- package/dist/server/app-page-response.js +7 -14
- package/dist/server/app-page-route-wiring.d.ts +1 -0
- package/dist/server/app-page-route-wiring.js +4 -0
- package/dist/server/app-page-segment-state.d.ts +2 -2
- package/dist/server/app-page-segment-state.js +14 -8
- package/dist/server/app-page-stream.d.ts +12 -0
- package/dist/server/app-page-stream.js +10 -3
- package/dist/server/app-ppr-fallback-shell.d.ts +2 -2
- package/dist/server/app-ppr-fallback-shell.js +3 -6
- package/dist/server/app-prerender-endpoints.d.ts +1 -0
- package/dist/server/app-prerender-endpoints.js +12 -0
- package/dist/server/app-response-header-provenance.d.ts +13 -0
- package/dist/server/app-response-header-provenance.js +26 -0
- package/dist/server/app-route-handler-cache.d.ts +1 -0
- package/dist/server/app-route-handler-cache.js +2 -2
- package/dist/server/app-route-handler-dispatch.js +8 -3
- package/dist/server/app-route-handler-execution.js +11 -7
- package/dist/server/app-route-handler-policy.d.ts +1 -0
- package/dist/server/app-route-handler-response.d.ts +3 -1
- package/dist/server/app-route-handler-response.js +15 -5
- package/dist/server/app-route-handler-runtime.d.ts +1 -1
- package/dist/server/app-route-handler-runtime.js +218 -84
- package/dist/server/app-route-request-built-ins.d.ts +16 -0
- package/dist/server/app-route-request-built-ins.js +41 -0
- package/dist/server/app-router-entry.js +2 -2
- package/dist/server/app-rsc-cache-busting.d.ts +4 -1
- package/dist/server/app-rsc-cache-busting.js +51 -17
- package/dist/server/app-rsc-handler.d.ts +6 -2
- package/dist/server/app-rsc-handler.js +68 -18
- package/dist/server/app-rsc-request-normalization.d.ts +2 -0
- package/dist/server/app-rsc-request-normalization.js +5 -1
- package/dist/server/app-rsc-response-finalizer.d.ts +2 -0
- package/dist/server/app-rsc-response-finalizer.js +4 -1
- package/dist/server/app-rsc-route-matching.d.ts +4 -3
- package/dist/server/app-rsc-route-matching.js +14 -42
- package/dist/server/app-rsc-state-fingerprint.d.ts +14 -0
- package/dist/server/app-rsc-state-fingerprint.js +13 -0
- package/dist/server/app-server-action-execution.d.ts +3 -0
- package/dist/server/app-server-action-execution.js +79 -4
- package/dist/server/app-ssr-entry.d.ts +6 -0
- package/dist/server/app-ssr-entry.js +21 -9
- package/dist/server/app-ssr-stream.d.ts +7 -2
- package/dist/server/app-ssr-stream.js +30 -6
- package/dist/server/app-visited-response-cache.d.ts +6 -1
- package/dist/server/app-visited-response-cache.js +18 -2
- package/dist/server/cache-control.d.ts +11 -7
- package/dist/server/cache-control.js +23 -12
- package/dist/server/config-headers.d.ts +4 -0
- package/dist/server/config-headers.js +19 -3
- package/dist/server/dev-route-files.js +2 -5
- package/dist/server/dev-server.d.ts +5 -13
- package/dist/server/dev-server.js +49 -32
- package/dist/server/dev-stack-sourcemap.d.ts +1 -2
- package/dist/server/dev-stack-sourcemap.js +1 -4
- package/dist/server/headers.d.ts +11 -1
- package/dist/server/headers.js +13 -2
- package/dist/server/http-error-responses.d.ts +1 -1
- package/dist/server/http-error-responses.js +7 -2
- package/dist/server/isr-cache.d.ts +6 -14
- package/dist/server/isr-cache.js +6 -27
- package/dist/server/metadata-route-build-data.js +1 -1
- package/dist/server/metadata-route-cache-policy.d.ts +6 -0
- package/dist/server/metadata-route-cache-policy.js +6 -0
- package/dist/server/metadata-route-response.d.ts +26 -1
- package/dist/server/metadata-route-response.js +219 -6
- package/dist/server/metadata-routes.js +1 -1
- package/dist/server/middleware-response-headers.js +4 -2
- package/dist/server/middleware.d.ts +3 -16
- package/dist/server/middleware.js +2 -19
- package/dist/server/navigation-planner.d.ts +1 -0
- package/dist/server/navigation-planner.js +57 -9
- package/dist/server/navigation-trace.d.ts +1 -0
- package/dist/server/navigation-trace.js +1 -0
- package/dist/server/pages-data-route.d.ts +1 -10
- package/dist/server/pages-data-route.js +1 -12
- package/dist/server/pages-dev-hydration.js +11 -4
- package/dist/server/pages-node-compat.d.ts +1 -4
- package/dist/server/pages-node-compat.js +1 -7
- package/dist/server/pages-page-handler.d.ts +3 -1
- package/dist/server/pages-page-handler.js +4 -15
- package/dist/server/pages-page-response.js +10 -5
- package/dist/server/pages-readiness.js +4 -4
- package/dist/server/prerender-cache-life-header.d.ts +11 -0
- package/dist/server/prerender-cache-life-header.js +18 -0
- package/dist/server/prerender-manifest.d.ts +5 -2
- package/dist/server/prerender-manifest.js +4 -1
- package/dist/server/prerender-route-params.d.ts +1 -2
- package/dist/server/prerender-route-params.js +1 -4
- package/dist/server/prod-server.js +9 -11
- package/dist/server/request-pipeline.js +2 -0
- package/dist/server/seed-cache.d.ts +4 -1
- package/dist/server/seed-cache.js +28 -4
- package/dist/server/worker-utils.js +1 -1
- package/dist/shims/cache-callable-runtime.d.ts +11 -0
- package/dist/shims/cache-callable-runtime.js +30 -0
- package/dist/shims/cache-request-state.d.ts +4 -1
- package/dist/shims/cache-request-state.js +6 -1
- package/dist/shims/cache-runtime.d.ts +6 -1
- package/dist/shims/cache-runtime.js +137 -30
- package/dist/shims/cache.d.ts +2 -2
- package/dist/shims/cache.js +2 -2
- package/dist/shims/cdn-cache.d.ts +18 -6
- package/dist/shims/cdn-cache.js +4 -2
- package/dist/shims/dynamic.d.ts +3 -3
- package/dist/shims/dynamic.js +4 -6
- package/dist/shims/error.js +1 -1
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +27 -7
- package/dist/shims/font-local.js +3 -3
- package/dist/shims/head.d.ts +16 -1
- package/dist/shims/head.js +70 -8
- package/dist/shims/internal/app-route-prefetch-policy.d.ts +8 -7
- package/dist/shims/internal/app-route-prefetch-policy.js +13 -6
- package/dist/shims/internal/pages-data-target.d.ts +1 -1
- package/dist/shims/internal/pages-data-target.js +18 -5
- package/dist/shims/link.js +50 -55
- package/dist/shims/navigation-context-state.d.ts +4 -0
- package/dist/shims/navigation.d.ts +39 -3
- package/dist/shims/navigation.js +139 -18
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/request-state-types.d.ts +1 -1
- package/dist/shims/root-params.d.ts +3 -1
- package/dist/shims/root-params.js +7 -1
- package/dist/shims/router.js +252 -80
- package/dist/shims/server.js +2 -1
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/utils/built-asset-url.d.ts +1 -1
- package/dist/utils/built-asset-url.js +4 -2
- package/dist/utils/encode-cache-tag.d.ts +1 -1
- package/dist/utils/encode-cache-tag.js +1 -1
- package/dist/utils/package-name.d.ts +11 -0
- package/dist/utils/package-name.js +18 -0
- package/dist/utils/path.d.ts +8 -1
- package/dist/utils/path.js +21 -1
- package/dist/utils/prerender-output-paths.d.ts +3 -1
- package/dist/utils/prerender-output-paths.js +6 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Run Next.js applications on Vite, with Cloudflare Workers as the primary deployment target.
|
|
4
4
|
|
|
5
|
+
**Website:** [vinext.dev](https://vinext.dev)
|
|
6
|
+
|
|
5
7
|
> **Read the announcement:** [How we rebuilt Next.js with AI in one week](https://blog.cloudflare.com/vinext/)
|
|
6
8
|
|
|
7
9
|
> **Under active development.** vinext supports substantial Next.js applications today, but it is not yet a drop-in replacement for every application or production workload. Expect compatibility gaps, especially in newer App Router features, and evaluate it against your own application before adopting it.
|
|
@@ -516,12 +518,12 @@ These are deployed to Cloudflare Workers and updated on every push to `main`:
|
|
|
516
518
|
| App Router (minimal) | Minimal App Router on Workers | [app-router-cloudflare.vinext.workers.dev](https://app-router-cloudflare.vinext.workers.dev) |
|
|
517
519
|
| Pages Router (minimal) | Minimal Pages Router on Workers | [pages-router-cloudflare.vinext.workers.dev](https://pages-router-cloudflare.vinext.workers.dev) |
|
|
518
520
|
| RealWorld API | REST API routes example | [realworld-api-rest.vinext.workers.dev](https://realworld-api-rest.vinext.workers.dev) |
|
|
519
|
-
| Benchmarks Dashboard | Build performance tracking over time (D1-backed) | [vinext
|
|
521
|
+
| Benchmarks Dashboard | Build performance tracking over time (D1-backed) | [vinext.dev/benchmarks](https://vinext.dev/benchmarks) |
|
|
520
522
|
| App Router + Nitro | App Router deployed via Nitro (multi-platform) | [examples/app-router-nitro](examples/app-router-nitro) |
|
|
521
523
|
|
|
522
524
|
## API coverage
|
|
523
525
|
|
|
524
|
-
~94% of the Next.js 16 API surface has full or partial support. The remaining gaps are intentional stubs for deprecated features
|
|
526
|
+
~94% of the Next.js 16 API surface has full or partial support. The remaining gaps are intentional stubs for deprecated features, plus Partial Prerendering and Cache Components. Next.js 16 reworked PPR into `"use cache"`; vinext implements that directive for file-level and function-level caching, but full `cacheComponents` behavior is still incomplete — see [Known gaps we're working on](#known-gaps-were-working-on).
|
|
525
527
|
|
|
526
528
|
> ✅ = full implementation | 🟡 = partial (runtime behavior correct, some build-time optimizations missing) | ⬜ = intentional stub/no-op
|
|
527
529
|
|
|
@@ -643,16 +645,21 @@ The cache is pluggable. The default `MemoryCacheHandler` works out of the box. S
|
|
|
643
645
|
Instead of wiring up cache handlers imperatively from a worker entry, you can declare them in the `vinext()` plugin config. The `@vinext/cloudflare` package ships Cloudflare adapters for this:
|
|
644
646
|
|
|
645
647
|
- **`kvDataAdapter()`** (`@vinext/cloudflare/cache/kv-data-adapter`) — backs the `"use cache"` data cache with a Workers KV namespace.
|
|
648
|
+
- **`cdnAdapter()`** (`@vinext/cloudflare/cache/cdn-adapter`) — serves page-level ISR from the Cloudflare Workers Cache (`ctx.cache`) instead of from the origin.
|
|
649
|
+
|
|
650
|
+
The two fill different slots and can be used together:
|
|
646
651
|
|
|
647
652
|
```ts
|
|
648
653
|
import { defineConfig } from "vite";
|
|
649
654
|
import vinext from "vinext";
|
|
655
|
+
import { cdnAdapter } from "@vinext/cloudflare/cache/cdn-adapter";
|
|
650
656
|
import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";
|
|
651
657
|
|
|
652
658
|
export default defineConfig({
|
|
653
659
|
plugins: [
|
|
654
660
|
vinext({
|
|
655
661
|
cache: {
|
|
662
|
+
cdn: cdnAdapter(),
|
|
656
663
|
data: kvDataAdapter(),
|
|
657
664
|
},
|
|
658
665
|
}),
|
|
@@ -670,6 +677,16 @@ The KV data adapter reads `env[binding]` at runtime, so add the matching KV name
|
|
|
670
677
|
|
|
671
678
|
`binding` defaults to `VINEXT_KV_CACHE`, so `kvDataAdapter()` with no options works as long as that's your binding name. Other options: `appPrefix` (namespace cache keys to isolate multiple apps in one KV namespace), `ttlSeconds` (default KV `expirationTtl`, default 30 days), and `tagCacheTtlMs` (in-memory tag-invalidation cache TTL, default 5s).
|
|
672
679
|
|
|
680
|
+
`cdnAdapter()` takes no options, but the Workers Cache only exposes `ctx.cache` when `cache.enabled` is set in `wrangler.jsonc`:
|
|
681
|
+
|
|
682
|
+
```jsonc
|
|
683
|
+
{
|
|
684
|
+
"cache": { "enabled": true },
|
|
685
|
+
}
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
While the data adapter can store entries and serve HIT/STALE itself, the CDN adapter delegates serving to Cloudflare's edge: the origin renders fresh responses and tags them with `Cache-Tag`, and `revalidateTag()` / `revalidatePath()` purge the edge through `ctx.cache.purge({ tags })`. See [examples/workers-cache](examples/workers-cache) for both adapters wired up together.
|
|
689
|
+
|
|
673
690
|
Each builder returns a plain, serializable `{ adapter, options }` descriptor — **it never touches the Workers runtime**, so nothing throws at build or dev time when bindings aren't available. The actual adapter (and its `env` binding lookup) is instantiated lazily on the first request.
|
|
674
691
|
|
|
675
692
|
Registration is wired into **every router and runtime** — App Router and Pages Router, on Cloudflare Workers as well as the Node.js server (`vinext start`) and dev. It self-guards (instantiated once per isolate) and is resilient: if an adapter can't initialize on a given runtime (e.g. a KV binding doesn't exist on the Node server), vinext logs a warning and falls back to the default handler instead of failing requests.
|
|
@@ -713,7 +730,7 @@ We measure three things:
|
|
|
713
730
|
- **Client bundle size** — gzipped output of each build.
|
|
714
731
|
- **Dev server cold start** — 10 runs, randomized execution order. Vite's dependency optimizer cache is cleared before each run.
|
|
715
732
|
|
|
716
|
-
Benchmarks run on GitHub CI runners (2-core Ubuntu) on every merge to `main`. See the launch numbers in the [announcement blog post](https://blog.cloudflare.com/vinext/) and the latest results at **[vinext
|
|
733
|
+
Benchmarks run on GitHub CI runners (2-core Ubuntu) on every merge to `main`. See the launch numbers in the [announcement blog post](https://blog.cloudflare.com/vinext/) and the latest results at **[vinext.dev/benchmarks](https://vinext.dev/benchmarks)**.
|
|
717
734
|
|
|
718
735
|
<details>
|
|
719
736
|
<summary>Why the bundle size difference?</summary>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { AppRoute } from "../routing/app-route-graph.js";
|
|
2
|
+
import { Rollup } from "vite";
|
|
3
|
+
import { RscPluginManager } from "@vitejs/plugin-rsc";
|
|
4
|
+
//#region src/build/action-owner-manifest.d.ts
|
|
5
|
+
type ActionOwnerRoute = Pick<AppRoute, "errorPath" | "errorPaths" | "forbiddenPath" | "forbiddenPaths" | "layoutErrorPaths" | "layouts" | "loadingPath" | "loadingPaths" | "notFoundPath" | "notFoundPaths" | "pagePath" | "parallelSlots" | "pattern" | "siblingIntercepts" | "templates" | "unauthorizedPath" | "unauthorizedPaths">;
|
|
6
|
+
type ActionOwnerRouteReachability = Map<string, {
|
|
7
|
+
clientReferenceImportIds: Set<string>;
|
|
8
|
+
serverReferenceIds: Set<string>;
|
|
9
|
+
}>;
|
|
10
|
+
type ActionOwnerModuleInfo = Pick<Rollup.ModuleInfo, "dynamicallyImportedIds" | "importedIds">;
|
|
11
|
+
type ActionOwnerReferenceMaps = {
|
|
12
|
+
clientReferenceMetaMap: RscPluginManager["clientReferenceMetaMap"];
|
|
13
|
+
serverReferenceMetaMap: RscPluginManager["serverReferences"]["metaMap"];
|
|
14
|
+
};
|
|
15
|
+
declare function actionOwnerRouteEntryIds(route: ActionOwnerRoute): string[];
|
|
16
|
+
declare function collectReachableActionReferences(options: {
|
|
17
|
+
canonicalizeModuleId?: (id: string) => string;
|
|
18
|
+
getModuleInfo: (id: string) => ActionOwnerModuleInfo | null;
|
|
19
|
+
roots: readonly string[];
|
|
20
|
+
} & ActionOwnerReferenceMaps): {
|
|
21
|
+
clientReferenceImportIds: Set<string>;
|
|
22
|
+
serverReferenceIds: Set<string>;
|
|
23
|
+
};
|
|
24
|
+
declare function collectRscActionReachability(options: {
|
|
25
|
+
canonicalizeModuleId?: (id: string) => string;
|
|
26
|
+
getModuleInfo: (id: string) => ActionOwnerModuleInfo | null;
|
|
27
|
+
routes: readonly ActionOwnerRoute[];
|
|
28
|
+
sharedRoots?: readonly string[];
|
|
29
|
+
} & ActionOwnerReferenceMaps): ActionOwnerRouteReachability;
|
|
30
|
+
declare function resolveClientReferenceImportIds(options: {
|
|
31
|
+
canonicalizeModuleId?: (id: string) => string;
|
|
32
|
+
resolveId: (id: string) => Promise<string | null>;
|
|
33
|
+
routeReachability: ActionOwnerRouteReachability;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
declare function addClientActionReachability(options: {
|
|
36
|
+
canonicalizeModuleId?: (id: string) => string;
|
|
37
|
+
getModuleInfo: (id: string) => ActionOwnerModuleInfo | null;
|
|
38
|
+
routeReachability: ActionOwnerRouteReachability;
|
|
39
|
+
} & ActionOwnerReferenceMaps): void;
|
|
40
|
+
declare function buildActionOwnerManifest(routeReachability: ActionOwnerRouteReachability): Record<string, string[]>;
|
|
41
|
+
//#endregion
|
|
42
|
+
export { ActionOwnerRouteReachability, actionOwnerRouteEntryIds, addClientActionReachability, buildActionOwnerManifest, collectReachableActionReferences, collectRscActionReachability, resolveClientReferenceImportIds };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
//#region src/build/action-owner-manifest.ts
|
|
2
|
+
function actionOwnerRouteEntryIds(route) {
|
|
3
|
+
return [
|
|
4
|
+
route.pagePath,
|
|
5
|
+
...route.layouts,
|
|
6
|
+
...route.templates,
|
|
7
|
+
route.loadingPath,
|
|
8
|
+
...route.loadingPaths ?? [],
|
|
9
|
+
route.errorPath,
|
|
10
|
+
...route.layoutErrorPaths ?? [],
|
|
11
|
+
...route.errorPaths ?? [],
|
|
12
|
+
route.notFoundPath,
|
|
13
|
+
...route.notFoundPaths ?? [],
|
|
14
|
+
route.forbiddenPath,
|
|
15
|
+
...route.forbiddenPaths ?? [],
|
|
16
|
+
route.unauthorizedPath,
|
|
17
|
+
...route.unauthorizedPaths ?? [],
|
|
18
|
+
...route.parallelSlots.flatMap((slot) => [
|
|
19
|
+
slot.pagePath,
|
|
20
|
+
slot.defaultPath,
|
|
21
|
+
slot.layoutPath,
|
|
22
|
+
...slot.configLayoutPaths ?? [],
|
|
23
|
+
slot.loadingPath,
|
|
24
|
+
...slot.loadingPaths ?? [],
|
|
25
|
+
slot.errorPath,
|
|
26
|
+
slot.notFoundPath,
|
|
27
|
+
...slot.interceptingRoutes.flatMap((intercept) => [
|
|
28
|
+
intercept.pagePath,
|
|
29
|
+
...intercept.layoutPaths,
|
|
30
|
+
...intercept.loadingPaths ?? [],
|
|
31
|
+
intercept.notFoundPath
|
|
32
|
+
])
|
|
33
|
+
]),
|
|
34
|
+
...route.siblingIntercepts.flatMap((intercept) => [
|
|
35
|
+
intercept.pagePath,
|
|
36
|
+
...intercept.layoutPaths,
|
|
37
|
+
...intercept.loadingPaths ?? [],
|
|
38
|
+
intercept.notFoundPath
|
|
39
|
+
])
|
|
40
|
+
].filter((value) => typeof value === "string");
|
|
41
|
+
}
|
|
42
|
+
function collectReachableActionReferences(options) {
|
|
43
|
+
const canonicalizeModuleId = options.canonicalizeModuleId ?? ((id) => id);
|
|
44
|
+
const clientReferenceImportIds = /* @__PURE__ */ new Set();
|
|
45
|
+
const serverReferenceIds = /* @__PURE__ */ new Set();
|
|
46
|
+
const visited = /* @__PURE__ */ new Set();
|
|
47
|
+
const queue = options.roots.map(canonicalizeModuleId);
|
|
48
|
+
for (let index = 0; index < queue.length; index++) {
|
|
49
|
+
const id = queue[index];
|
|
50
|
+
if (visited.has(id)) continue;
|
|
51
|
+
visited.add(id);
|
|
52
|
+
const clientReference = options.clientReferenceMetaMap[id];
|
|
53
|
+
if (clientReference) clientReferenceImportIds.add(clientReference.importId);
|
|
54
|
+
const serverReference = options.serverReferenceMetaMap.get(id);
|
|
55
|
+
if (serverReference) for (const exportName of serverReference.exportNames) serverReferenceIds.add(`${serverReference.referenceKey}#${exportName}`);
|
|
56
|
+
const info = options.getModuleInfo(id);
|
|
57
|
+
for (const importedId of [...info?.importedIds ?? [], ...info?.dynamicallyImportedIds ?? []]) if (!visited.has(importedId)) queue.push(importedId);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
clientReferenceImportIds,
|
|
61
|
+
serverReferenceIds
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function collectRscActionReachability(options) {
|
|
65
|
+
const routeReachability = new Map(options.routes.map((route) => [route.pattern, collectReachableActionReferences({
|
|
66
|
+
canonicalizeModuleId: options.canonicalizeModuleId,
|
|
67
|
+
clientReferenceMetaMap: options.clientReferenceMetaMap,
|
|
68
|
+
getModuleInfo: options.getModuleInfo,
|
|
69
|
+
roots: actionOwnerRouteEntryIds(route),
|
|
70
|
+
serverReferenceMetaMap: options.serverReferenceMetaMap
|
|
71
|
+
})]));
|
|
72
|
+
if (options.sharedRoots?.length) routeReachability.set("*", collectReachableActionReferences({
|
|
73
|
+
canonicalizeModuleId: options.canonicalizeModuleId,
|
|
74
|
+
clientReferenceMetaMap: options.clientReferenceMetaMap,
|
|
75
|
+
getModuleInfo: options.getModuleInfo,
|
|
76
|
+
roots: options.sharedRoots,
|
|
77
|
+
serverReferenceMetaMap: options.serverReferenceMetaMap
|
|
78
|
+
}));
|
|
79
|
+
return routeReachability;
|
|
80
|
+
}
|
|
81
|
+
async function resolveClientReferenceImportIds(options) {
|
|
82
|
+
const canonicalizeModuleId = options.canonicalizeModuleId ?? ((id) => id);
|
|
83
|
+
for (const reachability of options.routeReachability.values()) {
|
|
84
|
+
const resolvedImportIds = /* @__PURE__ */ new Set();
|
|
85
|
+
for (const importId of reachability.clientReferenceImportIds) {
|
|
86
|
+
const resolvedId = await options.resolveId(importId) ?? importId;
|
|
87
|
+
resolvedImportIds.add(canonicalizeModuleId(resolvedId));
|
|
88
|
+
}
|
|
89
|
+
reachability.clientReferenceImportIds = resolvedImportIds;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function addClientActionReachability(options) {
|
|
93
|
+
for (const reachability of options.routeReachability.values()) {
|
|
94
|
+
const clientReachability = collectReachableActionReferences({
|
|
95
|
+
canonicalizeModuleId: options.canonicalizeModuleId,
|
|
96
|
+
clientReferenceMetaMap: options.clientReferenceMetaMap,
|
|
97
|
+
getModuleInfo: options.getModuleInfo,
|
|
98
|
+
roots: [...reachability.clientReferenceImportIds],
|
|
99
|
+
serverReferenceMetaMap: options.serverReferenceMetaMap
|
|
100
|
+
});
|
|
101
|
+
for (const actionId of clientReachability.serverReferenceIds) reachability.serverReferenceIds.add(actionId);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function buildActionOwnerManifest(routeReachability) {
|
|
105
|
+
const manifest = {};
|
|
106
|
+
for (const [pattern, reachability] of routeReachability) for (const actionId of reachability.serverReferenceIds) (manifest[actionId] ??= []).push(pattern);
|
|
107
|
+
return manifest;
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
export { actionOwnerRouteEntryIds, addClientActionReachability, buildActionOwnerManifest, collectReachableActionReferences, collectRscActionReachability, resolveClientReferenceImportIds };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
|
+
import { isPathInsideOrEqual } from "../utils/path.js";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
//#region src/build/clean-output.ts
|
|
4
5
|
function resolveOutDir(root, outDir) {
|
|
@@ -6,10 +7,6 @@ function resolveOutDir(root, outDir) {
|
|
|
6
7
|
if (!outDir) return path.join(resolvedRoot, "dist");
|
|
7
8
|
return path.isAbsolute(outDir) ? outDir : path.resolve(resolvedRoot, outDir);
|
|
8
9
|
}
|
|
9
|
-
function isPathInsideOrEqual(parent, child) {
|
|
10
|
-
const relative = path.relative(parent, child);
|
|
11
|
-
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
12
|
-
}
|
|
13
10
|
function shouldCleanBuildOutput(options) {
|
|
14
11
|
if (options.emptyOutDir === false) return false;
|
|
15
12
|
if (options.emptyOutDir === true) return true;
|
|
@@ -86,11 +86,20 @@ function getPackageName(id) {
|
|
|
86
86
|
*/
|
|
87
87
|
function createClientManualChunks(shimsDir, preserveRouteBoundaries = false) {
|
|
88
88
|
return function clientManualChunks(id) {
|
|
89
|
+
const slashedId = toSlash(id);
|
|
90
|
+
if (slashedId.startsWith(shimsDir)) {
|
|
91
|
+
if (preserveRouteBoundaries) {
|
|
92
|
+
const relativeId = slashedId.slice(shimsDir.length).split("?", 1)[0] ?? "";
|
|
93
|
+
const extensionIndex = relativeId.lastIndexOf(".");
|
|
94
|
+
const shimName = extensionIndex === -1 ? relativeId : relativeId.slice(0, extensionIndex);
|
|
95
|
+
if (ROUTE_OWNED_CLIENT_SHIMS.has(shimName)) return void 0;
|
|
96
|
+
}
|
|
97
|
+
return "vinext";
|
|
98
|
+
}
|
|
89
99
|
if (id.includes("node_modules")) {
|
|
90
100
|
const pkg = getPackageName(id);
|
|
91
101
|
if (!pkg) return void 0;
|
|
92
102
|
if (pkg === "react-dom") {
|
|
93
|
-
const slashedId = toSlash(id);
|
|
94
103
|
const sub = slashedId.slice(slashedId.lastIndexOf("react-dom/") + 10);
|
|
95
104
|
if (sub.startsWith("server.") || sub.startsWith("static.") || sub.startsWith("cjs/react-dom-server")) return "react-dom-server";
|
|
96
105
|
return "framework";
|
|
@@ -98,16 +107,6 @@ function createClientManualChunks(shimsDir, preserveRouteBoundaries = false) {
|
|
|
98
107
|
if (pkg === "react" || pkg === "scheduler") return "framework";
|
|
99
108
|
return;
|
|
100
109
|
}
|
|
101
|
-
const slashedId = toSlash(id);
|
|
102
|
-
if (slashedId.startsWith(shimsDir)) {
|
|
103
|
-
if (preserveRouteBoundaries) {
|
|
104
|
-
const relativeId = slashedId.slice(shimsDir.length).split("?", 1)[0] ?? "";
|
|
105
|
-
const extensionIndex = relativeId.lastIndexOf(".");
|
|
106
|
-
const shimName = extensionIndex === -1 ? relativeId : relativeId.slice(0, extensionIndex);
|
|
107
|
-
if (ROUTE_OWNED_CLIENT_SHIMS.has(shimName)) return void 0;
|
|
108
|
-
}
|
|
109
|
-
return "vinext";
|
|
110
|
-
}
|
|
111
110
|
};
|
|
112
111
|
}
|
|
113
112
|
function createClientFileNameConfig(assetsDir) {
|
|
@@ -9,6 +9,7 @@ import { BLOCKED_PAGES, PHASE_PRODUCTION_BUILD } from "../shims/constants.js";
|
|
|
9
9
|
import { loadNextConfig, resolveNextConfig } from "../config/next-config.js";
|
|
10
10
|
import { readPrerenderSecret } from "./server-manifest.js";
|
|
11
11
|
import { startProdServer } from "../server/prod-server.js";
|
|
12
|
+
import { enterPrerenderPhase } from "./prerender-phase.js";
|
|
12
13
|
import { buildUrlFromParams, resolveParentParams } from "./prerender.js";
|
|
13
14
|
import fs from "node:fs";
|
|
14
15
|
//#region src/build/prerender-paths.ts
|
|
@@ -96,15 +97,13 @@ async function shouldStartPathDiscoveryServer(options) {
|
|
|
96
97
|
return false;
|
|
97
98
|
}
|
|
98
99
|
async function withPrerenderEndpoints(fn) {
|
|
99
|
-
const
|
|
100
|
+
const restorePrerenderPhase = enterPrerenderPhase();
|
|
100
101
|
const previousPathDiscoveryFlag = process.env[PRERENDER_PATH_DISCOVERY_ENV];
|
|
101
|
-
process.env.VINEXT_PRERENDER = "1";
|
|
102
102
|
process.env[PRERENDER_PATH_DISCOVERY_ENV] = "1";
|
|
103
103
|
try {
|
|
104
104
|
return await fn();
|
|
105
105
|
} finally {
|
|
106
|
-
|
|
107
|
-
else process.env.VINEXT_PRERENDER = previousPrerenderFlag;
|
|
106
|
+
restorePrerenderPhase();
|
|
108
107
|
if (previousPathDiscoveryFlag === void 0) delete process.env[PRERENDER_PATH_DISCOVERY_ENV];
|
|
109
108
|
else process.env[PRERENDER_PATH_DISCOVERY_ENV] = previousPathDiscoveryFlag;
|
|
110
109
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/build/prerender-phase.d.ts
|
|
2
|
+
type PrerenderPhaseEnv = {
|
|
3
|
+
NEXT_PHASE?: string;
|
|
4
|
+
NODE_ENV?: string;
|
|
5
|
+
VINEXT_PRERENDER?: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Enter the same process environment phase that Next.js exposes while its
|
|
9
|
+
* static workers execute application code. The returned cleanup is nest-safe:
|
|
10
|
+
* direct prerender helpers can run inside the CLI's broader build scope without
|
|
11
|
+
* overwriting the caller's prior phase when they finish.
|
|
12
|
+
*/
|
|
13
|
+
declare function enterPrerenderPhase(env?: PrerenderPhaseEnv): () => void;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { enterPrerenderPhase };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { PHASE_PRODUCTION_BUILD } from "../shims/constants.js";
|
|
2
|
+
//#region src/build/prerender-phase.ts
|
|
3
|
+
const activePrerenderPhases = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
/**
|
|
5
|
+
* Enter the same process environment phase that Next.js exposes while its
|
|
6
|
+
* static workers execute application code. The returned cleanup is nest-safe:
|
|
7
|
+
* direct prerender helpers can run inside the CLI's broader build scope without
|
|
8
|
+
* overwriting the caller's prior phase when they finish.
|
|
9
|
+
*/
|
|
10
|
+
function enterPrerenderPhase(env = process.env) {
|
|
11
|
+
let state = activePrerenderPhases.get(env);
|
|
12
|
+
if (state) state.depth += 1;
|
|
13
|
+
else {
|
|
14
|
+
state = {
|
|
15
|
+
depth: 1,
|
|
16
|
+
previousPrerenderFlag: env.VINEXT_PRERENDER,
|
|
17
|
+
previousNextPhase: env.NEXT_PHASE
|
|
18
|
+
};
|
|
19
|
+
activePrerenderPhases.set(env, state);
|
|
20
|
+
}
|
|
21
|
+
let restored = false;
|
|
22
|
+
env.VINEXT_PRERENDER = "1";
|
|
23
|
+
env.NEXT_PHASE = PHASE_PRODUCTION_BUILD;
|
|
24
|
+
return () => {
|
|
25
|
+
if (restored) return;
|
|
26
|
+
restored = true;
|
|
27
|
+
state.depth -= 1;
|
|
28
|
+
if (state.depth > 0) return;
|
|
29
|
+
activePrerenderPhases.delete(env);
|
|
30
|
+
if (state.previousPrerenderFlag === void 0) delete env.VINEXT_PRERENDER;
|
|
31
|
+
else env.VINEXT_PRERENDER = state.previousPrerenderFlag;
|
|
32
|
+
if (state.previousNextPhase === void 0) delete env.NEXT_PHASE;
|
|
33
|
+
else env.NEXT_PHASE = state.previousNextPhase;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { enterPrerenderPhase };
|
|
@@ -11,9 +11,9 @@ import { startProdServer } from "../server/prod-server.js";
|
|
|
11
11
|
* vinext production server on an ephemeral port and reports it to the parent
|
|
12
12
|
* over IPC. The parent then load-balances per-route fetches across the pool.
|
|
13
13
|
*
|
|
14
|
-
* `VINEXT_PRERENDER` / `VINEXT_PRERENDER_OUTDIR` are passed via
|
|
15
|
-
* they are set before any module loads (some server
|
|
16
|
-
* import time).
|
|
14
|
+
* `VINEXT_PRERENDER` / `NEXT_PHASE` / `VINEXT_PRERENDER_OUTDIR` are passed via
|
|
15
|
+
* the fork env so they are set before any module loads (some server and user
|
|
16
|
+
* modules read the phase at import time).
|
|
17
17
|
*/
|
|
18
18
|
async function main() {
|
|
19
19
|
const outDir = process.env.VINEXT_PRERENDER_OUTDIR;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
|
+
import { PHASE_PRODUCTION_BUILD } from "../shims/constants.js";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
4
5
|
import os from "node:os";
|
|
@@ -105,6 +106,7 @@ async function startPrerenderServerPool(outDir, size, entry = WORKER_ENTRY) {
|
|
|
105
106
|
env: {
|
|
106
107
|
...process.env,
|
|
107
108
|
VINEXT_PRERENDER: "1",
|
|
109
|
+
NEXT_PHASE: PHASE_PRODUCTION_BUILD,
|
|
108
110
|
VINEXT_PRERENDER_OUTDIR: outDir
|
|
109
111
|
},
|
|
110
112
|
stdio: [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { AppRoute } from "../routing/app-route-graph.js";
|
|
1
2
|
import { ResolvedNextConfig } from "../config/next-config.js";
|
|
2
3
|
import { Route } from "../routing/pages-router.js";
|
|
3
|
-
import { AppRoute } from "../routing/app-route-graph.js";
|
|
4
4
|
import { MetadataFileRoute } from "../server/metadata-routes.js";
|
|
5
5
|
import { readPrerenderSecret } from "./server-manifest.js";
|
|
6
6
|
import { Server } from "node:http";
|
|
@@ -27,11 +27,15 @@ type PrerenderRouteResult = {
|
|
|
27
27
|
*/
|
|
28
28
|
path?: string;
|
|
29
29
|
/** Which router produced this route. Used by cache seeding. */
|
|
30
|
-
router: "app" | "pages";
|
|
30
|
+
router: "app" | "pages" | "metadata";
|
|
31
31
|
/** Response headers that must be replayed with the prerendered artifact. */
|
|
32
|
-
headers?: Record<string, string>;
|
|
32
|
+
headers?: Record<string, string | string[]>;
|
|
33
|
+
/** Original HTTP status for prerendered App Route-style responses. */
|
|
34
|
+
responseStatus?: number;
|
|
33
35
|
/** Cache tags collected while rendering this route. */
|
|
34
36
|
tags?: string[];
|
|
37
|
+
/** Raw app-tree segments used to derive App Route implicit tags. */
|
|
38
|
+
routeSegments?: string[];
|
|
35
39
|
/** Set to true when this is a PPR fallback shell. */
|
|
36
40
|
fallback?: boolean;
|
|
37
41
|
} | {
|
package/dist/build/prerender.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
2
|
import { createValidFileMatcher, findFileWithExtensions } from "../routing/file-matcher.js";
|
|
3
3
|
import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
|
|
4
|
-
import { NEXT_CACHE_TAGS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER } from "../server/headers.js";
|
|
4
|
+
import { NEXT_CACHE_TAGS_HEADER, VINEXT_METADATA_ROUTE_CACHE_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_METADATA_ROUTES_PATH, VINEXT_PRERENDER_RENDER_ERROR_HEADER } from "../server/headers.js";
|
|
5
5
|
import { classifyAppRoute, classifyPagesRoute, getAppRouteRenderEntryPath } from "./report.js";
|
|
6
6
|
import { resolveClientStaleTimeSeconds } from "../utils/cache-control-metadata.js";
|
|
7
7
|
import { NoOpCacheHandler, getCacheHandler, setCacheHandler } from "../shims/cache-handler.js";
|
|
@@ -15,9 +15,12 @@ import { navigationRuntimeRscBootstrapExpression } from "../server/app-ssr-strea
|
|
|
15
15
|
import { encodePrerenderRouteParams, serializePrerenderRouteParamsHeader } from "../server/prerender-route-params.js";
|
|
16
16
|
import { readPrerenderSecret } from "./server-manifest.js";
|
|
17
17
|
import { createAppPprFallbackShells, markAppPprDynamicFallbackShellHtml } from "../server/app-ppr-fallback-shell.js";
|
|
18
|
-
import { getOutputPath, getRscOutputPath } from "../utils/prerender-output-paths.js";
|
|
18
|
+
import { getAppRouteOutputPath, getOutputPath, getRscOutputPath } from "../utils/prerender-output-paths.js";
|
|
19
19
|
import { startProdServer } from "../server/prod-server.js";
|
|
20
20
|
import { prerenderPoolAvailable, resolvePrerenderPoolSize, startPrerenderServerPool } from "./prerender-server-pool.js";
|
|
21
|
+
import { enterPrerenderPhase } from "./prerender-phase.js";
|
|
22
|
+
import { buildAppRouteCacheValue } from "../server/app-route-handler-response.js";
|
|
23
|
+
import { isMetadataResponseCacheable } from "../server/metadata-route-cache-policy.js";
|
|
21
24
|
import fs from "node:fs";
|
|
22
25
|
import os from "node:os";
|
|
23
26
|
//#region src/build/prerender.ts
|
|
@@ -265,8 +268,7 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
|
|
|
265
268
|
});
|
|
266
269
|
const previousHandler = getCacheHandler();
|
|
267
270
|
setCacheHandler(new NoOpCacheHandler());
|
|
268
|
-
const
|
|
269
|
-
process.env.VINEXT_PRERENDER = "1";
|
|
271
|
+
const restorePrerenderPhase = enterPrerenderPhase();
|
|
270
272
|
let ownedProdServerHandle = null;
|
|
271
273
|
let renderPool = null;
|
|
272
274
|
try {
|
|
@@ -497,11 +499,13 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
|
|
|
497
499
|
});
|
|
498
500
|
return { routes: results };
|
|
499
501
|
} finally {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
502
|
+
try {
|
|
503
|
+
if (renderPool) await renderPool.close();
|
|
504
|
+
setCacheHandler(previousHandler);
|
|
505
|
+
if (ownedProdServerHandle) await new Promise((resolve) => ownedProdServerHandle.server.close(() => resolve()));
|
|
506
|
+
} finally {
|
|
507
|
+
restorePrerenderPhase();
|
|
508
|
+
}
|
|
505
509
|
}
|
|
506
510
|
}
|
|
507
511
|
/**
|
|
@@ -530,8 +534,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
530
534
|
fs.mkdirSync(outDir, { recursive: true });
|
|
531
535
|
const previousHandler = getCacheHandler();
|
|
532
536
|
setCacheHandler(new NoOpCacheHandler());
|
|
533
|
-
const
|
|
534
|
-
process.env.VINEXT_PRERENDER = "1";
|
|
537
|
+
const restorePrerenderPhase = enterPrerenderPhase();
|
|
535
538
|
const serverDir = path.dirname(rscBundlePath);
|
|
536
539
|
let rscHandler;
|
|
537
540
|
let staticParamsMap = {};
|
|
@@ -743,14 +746,85 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
743
746
|
isSpeculative: false
|
|
744
747
|
});
|
|
745
748
|
}
|
|
749
|
+
if (metadataRoutes.length > 0) {
|
|
750
|
+
const response = await fetch(`${baseUrl}${VINEXT_PRERENDER_METADATA_ROUTES_PATH}`, { headers: secretHeaders });
|
|
751
|
+
const text = await response.text();
|
|
752
|
+
if (!response.ok) throw new Error(`[vinext] Failed to enumerate prerenderable metadata routes: ${parsePrerenderEndpointError(text)}`);
|
|
753
|
+
const metadataEntries = JSON.parse(text);
|
|
754
|
+
if (!Array.isArray(metadataEntries)) throw new Error("[vinext] Invalid prerender metadata route response");
|
|
755
|
+
const queuedMetadataPaths = /* @__PURE__ */ new Set();
|
|
756
|
+
for (const metadataEntry of metadataEntries) {
|
|
757
|
+
if (typeof metadataEntry !== "object" || metadataEntry === null || typeof Reflect.get(metadataEntry, "path") !== "string" || typeof Reflect.get(metadataEntry, "routePattern") !== "string" || !Array.isArray(Reflect.get(metadataEntry, "routeSegments"))) continue;
|
|
758
|
+
const { path: metadataPath, routePattern, routeSegments } = metadataEntry;
|
|
759
|
+
if (!metadataPath.startsWith("/") || !routePattern.startsWith("/") || routeSegments.some((segment) => typeof segment !== "string") || queuedMetadataPaths.has(metadataPath)) continue;
|
|
760
|
+
queuedMetadataPaths.add(metadataPath);
|
|
761
|
+
urlsToRender.push({
|
|
762
|
+
kind: "metadata",
|
|
763
|
+
metadataRouteSegments: routeSegments,
|
|
764
|
+
urlPath: metadataPath,
|
|
765
|
+
routePattern,
|
|
766
|
+
prerenderRouteParams: null,
|
|
767
|
+
revalidate: false,
|
|
768
|
+
isSpeculative: false
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
}
|
|
746
772
|
/**
|
|
747
773
|
* Render a single URL and return its result.
|
|
748
774
|
* `onProgress` is intentionally not called here; the outer loop calls it
|
|
749
775
|
* exactly once per URL after this function returns, keeping the callback
|
|
750
776
|
* at a single, predictable call site.
|
|
751
777
|
*/
|
|
752
|
-
async function renderUrl({ urlPath, routePattern, prerenderRouteParams, revalidate, isSpeculative, isFallback }) {
|
|
778
|
+
async function renderUrl({ kind, metadataRouteSegments, urlPath, routePattern, prerenderRouteParams, revalidate, isSpeculative, isFallback }) {
|
|
753
779
|
try {
|
|
780
|
+
if (kind === "metadata") {
|
|
781
|
+
const request = new Request(`http://localhost${config.basePath ?? ""}${urlPath}`);
|
|
782
|
+
const response = await runWithHeadersContext(headersContextFromRequest(request), () => rscHandler(request));
|
|
783
|
+
if (!response.ok) {
|
|
784
|
+
const fatal = response.headers.get(VINEXT_PRERENDER_RENDER_ERROR_HEADER) === "1";
|
|
785
|
+
await response.body?.cancel();
|
|
786
|
+
return {
|
|
787
|
+
route: routePattern,
|
|
788
|
+
status: "error",
|
|
789
|
+
error: `Metadata route returned ${response.status}`,
|
|
790
|
+
...fatal ? { fatal: true } : {}
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
const cacheControl = response.headers.get("cache-control") ?? "";
|
|
794
|
+
if (!isMetadataResponseCacheable(response)) {
|
|
795
|
+
await response.body?.cancel();
|
|
796
|
+
return {
|
|
797
|
+
route: routePattern,
|
|
798
|
+
status: "skipped",
|
|
799
|
+
reason: "dynamic"
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
const requestCacheLife = readPrerenderCacheLifeHeader(response.headers);
|
|
803
|
+
const collectedTags = readPrerenderCacheTagsHeader(response.headers);
|
|
804
|
+
const cacheValue = await buildAppRouteCacheValue(response);
|
|
805
|
+
cacheValue.headers[VINEXT_METADATA_ROUTE_CACHE_HEADER] = "1";
|
|
806
|
+
const outputPath = getAppRouteOutputPath(urlPath);
|
|
807
|
+
const fullPath = path.join(outDir, outputPath);
|
|
808
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
809
|
+
fs.writeFileSync(fullPath, new Uint8Array(cacheValue.body));
|
|
810
|
+
const renderedCacheControl = resolveRenderedCacheControl(requestCacheLife ?? {}, cacheControl, config.expireTime);
|
|
811
|
+
const renderedRevalidate = renderedCacheControl.revalidate ?? false;
|
|
812
|
+
const renderedStale = resolveClientStaleTimeSeconds(requestCacheLife ?? void 0);
|
|
813
|
+
return {
|
|
814
|
+
route: routePattern,
|
|
815
|
+
...urlPath === routePattern ? {} : { path: urlPath },
|
|
816
|
+
status: "rendered",
|
|
817
|
+
outputFiles: [outputPath],
|
|
818
|
+
revalidate: renderedRevalidate,
|
|
819
|
+
...typeof renderedRevalidate === "number" ? { expire: renderedCacheControl.expire } : {},
|
|
820
|
+
...renderedStale === void 0 ? {} : { stale: renderedStale },
|
|
821
|
+
router: "metadata",
|
|
822
|
+
headers: cacheValue.headers,
|
|
823
|
+
responseStatus: cacheValue.status,
|
|
824
|
+
routeSegments: metadataRouteSegments ?? [],
|
|
825
|
+
tags: collectedTags
|
|
826
|
+
};
|
|
827
|
+
}
|
|
754
828
|
const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(prerenderRouteParams);
|
|
755
829
|
const htmlHeaders = new Headers();
|
|
756
830
|
if (prerenderRouteParamsHeader !== null) htmlHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
|
|
@@ -762,6 +836,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
762
836
|
const linkHeader = response.headers.get("link");
|
|
763
837
|
const responseCacheLife = readPrerenderCacheLifeHeader(response.headers);
|
|
764
838
|
const cacheTags = readPrerenderCacheTagsHeader(response.headers);
|
|
839
|
+
const fatal = response.headers.get(VINEXT_PRERENDER_RENDER_ERROR_HEADER) === "1";
|
|
765
840
|
if (!response.ok || cacheControl.includes("no-store")) {
|
|
766
841
|
await response.body?.cancel();
|
|
767
842
|
return {
|
|
@@ -771,7 +846,8 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
771
846
|
ok: response.ok,
|
|
772
847
|
requestCacheLife: null,
|
|
773
848
|
tags: [],
|
|
774
|
-
status: response.status
|
|
849
|
+
status: response.status,
|
|
850
|
+
fatal
|
|
775
851
|
};
|
|
776
852
|
}
|
|
777
853
|
const html = await response.text();
|
|
@@ -783,12 +859,13 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
783
859
|
ok: true,
|
|
784
860
|
requestCacheLife: responseCacheLife ?? processCacheLife,
|
|
785
861
|
status: response.status,
|
|
786
|
-
tags: cacheTags
|
|
862
|
+
tags: cacheTags,
|
|
863
|
+
fatal: false
|
|
787
864
|
};
|
|
788
865
|
});
|
|
789
866
|
const htmlCacheControl = htmlRender.cacheControl;
|
|
790
867
|
if (!htmlRender.ok) {
|
|
791
|
-
if (isSpeculative) return {
|
|
868
|
+
if (isSpeculative && !htmlRender.fatal) return {
|
|
792
869
|
route: routePattern,
|
|
793
870
|
status: "skipped",
|
|
794
871
|
reason: "dynamic"
|
|
@@ -796,7 +873,8 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
796
873
|
return {
|
|
797
874
|
route: routePattern,
|
|
798
875
|
status: "error",
|
|
799
|
-
error: `RSC handler returned ${htmlRender.status}
|
|
876
|
+
error: `RSC handler returned ${htmlRender.status}`,
|
|
877
|
+
...htmlRender.fatal ? { fatal: true } : {}
|
|
800
878
|
};
|
|
801
879
|
}
|
|
802
880
|
if (htmlCacheControl.includes("no-store")) return {
|
|
@@ -919,11 +997,13 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
919
997
|
...outputFiles.length > 0 ? { outputFiles } : {}
|
|
920
998
|
};
|
|
921
999
|
} finally {
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1000
|
+
try {
|
|
1001
|
+
if (renderPool) await renderPool.close();
|
|
1002
|
+
setCacheHandler(previousHandler);
|
|
1003
|
+
if (ownedProdServerHandle) await new Promise((resolve) => ownedProdServerHandle.server.close(() => resolve()));
|
|
1004
|
+
} finally {
|
|
1005
|
+
restorePrerenderPhase();
|
|
1006
|
+
}
|
|
927
1007
|
}
|
|
928
1008
|
}
|
|
929
1009
|
function resolveRenderedCacheControl(requestCacheLife, cacheControl, fallbackExpireSeconds) {
|
|
@@ -991,7 +1071,9 @@ function writePrerenderIndex(routes, outDir, options) {
|
|
|
991
1071
|
...typeof r.stale === "number" ? { stale: r.stale } : {},
|
|
992
1072
|
router: r.router,
|
|
993
1073
|
...r.tags && r.tags.length > 0 ? { tags: r.tags } : {},
|
|
1074
|
+
...r.routeSegments ? { routeSegments: r.routeSegments } : {},
|
|
994
1075
|
...r.headers ? { headers: r.headers } : {},
|
|
1076
|
+
...typeof r.responseStatus === "number" ? { responseStatus: r.responseStatus } : {},
|
|
995
1077
|
...r.path ? { path: r.path } : {},
|
|
996
1078
|
...r.fallback ? { fallback: true } : {}
|
|
997
1079
|
};
|
package/dist/build/report.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { AppRoute } from "../routing/app-route-graph.js";
|
|
1
2
|
import { LayoutBuildClassification } from "./layout-classification-types.js";
|
|
2
3
|
import { Route } from "../routing/pages-router.js";
|
|
3
|
-
import { AppRoute } from "../routing/app-route-graph.js";
|
|
4
4
|
import { PrerenderResult } from "./prerender.js";
|
|
5
5
|
//#region src/build/report.d.ts
|
|
6
6
|
type RouteType = "static" | "isr" | "ssr" | "unknown" | "api";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ClassificationReason, ModuleGraphStaticReason } from "./layout-classification-types.js";
|
|
2
1
|
import { AppRoute } from "../routing/app-route-graph.js";
|
|
2
|
+
import { ClassificationReason, ModuleGraphStaticReason } from "./layout-classification-types.js";
|
|
3
3
|
//#region src/build/route-classification-manifest.d.ts
|
|
4
4
|
type Layer1Class = "static" | "dynamic";
|
|
5
5
|
type RouteManifestEntry = {
|