vinext 1.0.1 → 1.2.0
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 +93 -10
- package/dist/build/app-page-static-eligibility.d.ts +15 -0
- package/dist/build/app-page-static-eligibility.js +82 -0
- package/dist/build/client-build-config.d.ts +10 -0
- package/dist/build/client-build-config.js +18 -2
- package/dist/build/pages-client-assets-module.d.ts +8 -0
- package/dist/build/pages-client-assets-module.js +21 -1
- package/dist/build/prerender-paths.js +8 -81
- package/dist/build/prerender.js +33 -8
- package/dist/build/run-prerender.js +1 -1
- package/dist/build/server-manifest.d.ts +7 -0
- package/dist/build/server-manifest.js +11 -1
- package/dist/build/ssr-manifest.d.ts +2 -0
- package/dist/build/ssr-manifest.js +6 -2
- package/dist/cache/cache-adapters-virtual.d.ts +7 -1
- package/dist/cache/cache-adapters-virtual.js +3 -3
- package/dist/check.js +7 -6
- package/dist/cli.js +2 -1
- package/dist/client/client-redirect-matcher.d.ts +14 -0
- package/dist/client/client-redirect-matcher.js +37 -0
- package/dist/client/client-redirects.d.ts +27 -0
- package/dist/client/client-redirects.js +28 -0
- package/dist/client/client-rewrites.d.ts +2 -1
- package/dist/client/client-rewrites.js +1 -1
- package/dist/client/navigation-runtime.d.ts +11 -0
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/config/config-matchers.d.ts +26 -11
- package/dist/config/config-matchers.js +81 -82
- package/dist/config/destination-params.d.ts +47 -0
- package/dist/config/destination-params.js +116 -0
- package/dist/config/next-config.d.ts +14 -0
- package/dist/config/next-config.js +21 -3
- package/dist/entries/app-rsc-entry.d.ts +6 -0
- package/dist/entries/app-rsc-entry.js +84 -49
- package/dist/entries/pages-client-entry.js +3 -1
- package/dist/entries/pages-server-entry.js +13 -37
- package/dist/index.d.ts +7 -2
- package/dist/index.js +193 -73
- package/dist/init-cloudflare.d.ts +9 -0
- package/dist/init-cloudflare.js +107 -31
- package/dist/init-platform.d.ts +4 -0
- package/dist/init-platform.js +27 -2
- package/dist/init.d.ts +1 -0
- package/dist/init.js +67 -3
- package/dist/plugins/ast-scope.d.ts +7 -0
- package/dist/plugins/ast-scope.js +59 -3
- package/dist/plugins/cache-flight-codec.d.ts +19 -0
- package/dist/plugins/cache-flight-codec.js +68 -0
- package/dist/plugins/commonjs-esm-facade.d.ts +35 -0
- package/dist/plugins/commonjs-esm-facade.js +80 -0
- package/dist/plugins/dynamic-preload-metadata.d.ts +1 -1
- package/dist/plugins/dynamic-preload-metadata.js +9 -8
- package/dist/plugins/fonts.d.ts +8 -11
- package/dist/plugins/fonts.js +71 -34
- package/dist/plugins/ignore-dynamic-requests.d.ts +1 -1
- package/dist/plugins/ignore-dynamic-requests.js +3 -4
- package/dist/plugins/middleware-server-only.d.ts +5 -4
- package/dist/plugins/middleware-server-only.js +8 -5
- package/dist/plugins/pages-node-externals.js +1 -1
- package/dist/plugins/rsc-scan-glob-imports.d.ts +19 -0
- package/dist/plugins/rsc-scan-glob-imports.js +50 -0
- package/dist/plugins/strip-server-exports.js +262 -98
- package/dist/plugins/transform-result.d.ts +9 -5
- package/dist/plugins/transitive-externals.js +1 -1
- package/dist/plugins/typeof-window.d.ts +2 -2
- package/dist/plugins/use-cache-callable.js +8 -2
- package/dist/plugins/worker-server-only-imports.d.ts +20 -0
- package/dist/plugins/worker-server-only-imports.js +31 -0
- package/dist/plugins/worker-use-server.d.ts +25 -0
- package/dist/plugins/worker-use-server.js +129 -0
- package/dist/routing/file-matcher.d.ts +1 -0
- package/dist/routing/file-matcher.js +1 -1
- package/dist/server/api-handler.d.ts +1 -0
- package/dist/server/api-handler.js +19 -8
- package/dist/server/app-browser-action-result.d.ts +1 -1
- package/dist/server/app-browser-action-result.js +9 -2
- package/dist/server/app-browser-entry.js +11 -7
- package/dist/server/app-browser-history-controller.d.ts +2 -0
- package/dist/server/app-browser-history-controller.js +8 -0
- package/dist/server/app-browser-interception-context.js +26 -2
- package/dist/server/app-browser-navigation-controller.d.ts +7 -1
- package/dist/server/app-browser-navigation-controller.js +94 -11
- package/dist/server/app-browser-server-action-client.js +1 -1
- package/dist/server/app-fallback-renderer.d.ts +2 -0
- package/dist/server/app-fallback-renderer.js +1 -0
- package/dist/server/app-history-state.d.ts +3 -0
- package/dist/server/app-history-state.js +7 -0
- package/dist/server/app-middleware.d.ts +11 -0
- package/dist/server/app-middleware.js +19 -3
- package/dist/server/app-optimistic-routing.js +1 -1
- package/dist/server/app-page-boundary-render.d.ts +5 -0
- package/dist/server/app-page-boundary-render.js +6 -4
- package/dist/server/app-page-cache-finalizer.d.ts +11 -0
- package/dist/server/app-page-cache-finalizer.js +41 -7
- package/dist/server/app-page-cache-render.d.ts +18 -0
- package/dist/server/app-page-cache-render.js +63 -43
- package/dist/server/app-page-cache.d.ts +6 -1
- package/dist/server/app-page-cache.js +28 -8
- package/dist/server/app-page-dispatch.d.ts +12 -2
- package/dist/server/app-page-dispatch.js +51 -33
- package/dist/server/app-page-element-builder.d.ts +7 -0
- package/dist/server/app-page-element-builder.js +27 -42
- package/dist/server/app-page-execution.d.ts +15 -12
- package/dist/server/app-page-execution.js +30 -40
- package/dist/server/app-page-head.d.ts +5 -0
- package/dist/server/app-page-head.js +9 -3
- package/dist/server/app-page-ppr-runtime.js +1 -1
- package/dist/server/app-page-probe.d.ts +0 -109
- package/dist/server/app-page-probe.js +3 -136
- package/dist/server/app-page-render-identity.d.ts +0 -1
- package/dist/server/app-page-render-identity.js +1 -1
- package/dist/server/app-page-render-observation.js +3 -3
- package/dist/server/app-page-render.d.ts +13 -5
- package/dist/server/app-page-render.js +219 -119
- package/dist/server/app-page-request.d.ts +0 -1
- package/dist/server/app-page-request.js +54 -17
- package/dist/server/app-page-response.d.ts +4 -3
- package/dist/server/app-page-response.js +4 -3
- package/dist/server/app-page-route-wiring.d.ts +17 -5
- package/dist/server/app-page-route-wiring.js +57 -35
- package/dist/server/app-page-rsc-render-status.d.ts +48 -0
- package/dist/server/app-page-rsc-render-status.js +54 -0
- package/dist/server/app-page-stream.d.ts +15 -8
- package/dist/server/app-page-stream.js +6 -0
- package/dist/server/app-pages-bridge.d.ts +1 -1
- package/dist/server/app-pages-bridge.js +2 -3
- package/dist/server/app-prerender-endpoints.js +9 -6
- package/dist/server/app-request-stage-dispatch.js +7 -3
- package/dist/server/app-request-stage-independent-entry.js +29 -9
- package/dist/server/app-response-header-provenance.d.ts +2 -0
- package/dist/server/app-response-header-provenance.js +10 -4
- package/dist/server/app-response-stage-entry.js +10 -2
- package/dist/server/app-route-handler-cache.d.ts +1 -0
- package/dist/server/app-route-handler-cache.js +1 -0
- package/dist/server/app-route-handler-dispatch.d.ts +1 -0
- package/dist/server/app-route-handler-dispatch.js +6 -3
- package/dist/server/app-route-handler-execution.d.ts +1 -0
- package/dist/server/app-route-handler-execution.js +1 -0
- package/dist/server/app-route-handler-runtime.d.ts +4 -1
- package/dist/server/app-route-handler-runtime.js +45 -24
- package/dist/server/app-router-entry.js +27 -8
- package/dist/server/app-rsc-cache-busting.js +1 -1
- package/dist/server/app-rsc-errors.js +20 -16
- package/dist/server/app-rsc-handler.d.ts +11 -0
- package/dist/server/app-rsc-handler.js +291 -125
- package/dist/server/app-rsc-request-normalization.d.ts +3 -1
- package/dist/server/app-rsc-request-normalization.js +4 -2
- package/dist/server/app-rsc-response-stage.d.ts +1 -1
- package/dist/server/app-rsc-response-stage.js +10 -6
- package/dist/server/app-rsc-route-matching.d.ts +9 -1
- package/dist/server/app-rsc-route-matching.js +36 -19
- package/dist/server/app-segment-config.d.ts +2 -1
- package/dist/server/app-server-action-execution.d.ts +2 -1
- package/dist/server/app-server-action-execution.js +62 -23
- package/dist/server/app-ssr-entry.d.ts +3 -2
- package/dist/server/app-ssr-entry.js +8 -5
- package/dist/server/app-ssr-stream.d.ts +6 -1
- package/dist/server/app-ssr-stream.js +34 -15
- package/dist/server/app-worker-stages.d.ts +7 -2
- package/dist/server/app-worker-stages.js +4 -4
- package/dist/server/cacheability-request.js +11 -7
- package/dist/server/client-build-files.d.ts +16 -0
- package/dist/server/client-build-files.js +36 -0
- package/dist/server/client-reuse-manifest.js +1 -1
- package/dist/server/config-headers.js +2 -0
- package/dist/server/dev-server.js +50 -29
- package/dist/server/dev-stack-sourcemap.js +18 -23
- package/dist/server/document-initial-head.d.ts +0 -11
- package/dist/server/document-initial-head.js +1 -1
- package/dist/server/forwarded-middleware-context.d.ts +5 -0
- package/dist/server/forwarded-middleware-context.js +22 -0
- package/dist/server/headers.d.ts +18 -1
- package/dist/server/headers.js +19 -2
- package/dist/server/http-conditional.d.ts +11 -0
- package/dist/server/http-conditional.js +10 -1
- package/dist/server/http-error-responses.d.ts +1 -1
- package/dist/server/image-optimization.d.ts +54 -0
- package/dist/server/image-optimization.js +118 -9
- package/dist/server/isr-cache.d.ts +10 -0
- package/dist/server/isr-cache.js +43 -8
- package/dist/server/metadata-route-response.js +9 -8
- package/dist/server/middleware-matcher-pattern.js +8 -4
- package/dist/server/middleware-matcher.d.ts +2 -0
- package/dist/server/middleware-matcher.js +23 -2
- package/dist/server/middleware-response-headers.js +2 -0
- package/dist/server/middleware-runtime.d.ts +13 -0
- package/dist/server/middleware-runtime.js +33 -20
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +3 -1
- package/dist/server/multi-stage.d.ts +19 -0
- package/dist/server/navigation-planner.js +1 -1
- package/dist/server/node-response-signal.d.ts +16 -0
- package/dist/server/node-response-signal.js +26 -0
- package/dist/server/normalize-path.d.ts +0 -13
- package/dist/server/normalize-path.js +1 -34
- package/dist/server/open-redirect.d.ts +47 -0
- package/dist/server/open-redirect.js +74 -1
- package/dist/server/pages-api-route.d.ts +3 -2
- package/dist/server/pages-api-route.js +4 -3
- package/dist/server/pages-cache-tag.d.ts +12 -0
- package/dist/server/pages-cache-tag.js +19 -0
- package/dist/server/pages-client-assets.d.ts +2 -0
- package/dist/server/pages-data-route.d.ts +10 -0
- package/dist/server/pages-data-route.js +19 -2
- package/dist/server/pages-document-asset-props.d.ts +5 -1
- package/dist/server/pages-document-asset-props.js +13 -5
- package/dist/server/pages-document-splice.d.ts +19 -0
- package/dist/server/pages-document-splice.js +55 -0
- package/dist/server/pages-i18n.d.ts +12 -1
- package/dist/server/pages-i18n.js +18 -4
- package/dist/server/pages-node-compat.d.ts +16 -5
- package/dist/server/pages-node-compat.js +20 -12
- package/dist/server/pages-page-data.d.ts +32 -24
- package/dist/server/pages-page-data.js +50 -120
- package/dist/server/pages-page-handler.d.ts +0 -6
- package/dist/server/pages-page-handler.js +209 -130
- package/dist/server/pages-page-response.d.ts +12 -6
- package/dist/server/pages-page-response.js +41 -34
- package/dist/server/pages-request-pipeline.d.ts +10 -2
- package/dist/server/pages-request-pipeline.js +37 -18
- package/dist/server/pages-request-stage-entry.d.ts +6 -0
- package/dist/server/pages-request-stage-entry.js +79 -16
- package/dist/server/pages-response-stage-entry.js +4 -0
- package/dist/server/pages-revalidate.js +1 -1
- package/dist/server/pages-router-entry.js +4 -3
- package/dist/server/prerender-manifest.d.ts +3 -3
- package/dist/server/prerender-manifest.js +4 -3
- package/dist/server/prerender-work-unit-setup.js +4 -0
- package/dist/server/prod-server.d.ts +20 -7
- package/dist/server/prod-server.js +343 -129
- package/dist/server/proper-error.d.ts +4 -0
- package/dist/server/proper-error.js +31 -0
- package/dist/server/request-pipeline.d.ts +24 -9
- package/dist/server/request-pipeline.js +24 -9
- package/dist/server/response-compression.d.ts +43 -0
- package/dist/server/response-compression.js +111 -0
- package/dist/server/revalidation-request.d.ts +1 -0
- package/dist/server/revalidation-request.js +1 -1
- package/dist/server/rsc-stream-hints.js +31 -2
- package/dist/server/seed-cache.d.ts +1 -0
- package/dist/server/seed-cache.js +15 -11
- package/dist/server/server-reference-loader.d.ts +4 -0
- package/dist/server/server-reference-loader.js +29 -0
- package/dist/server/socket-error-backstop.d.ts +23 -1
- package/dist/server/socket-error-backstop.js +69 -17
- package/dist/server/static-file-signal.d.ts +15 -4
- package/dist/server/static-file-signal.js +46 -12
- package/dist/server/worker-image-optimization.d.ts +34 -0
- package/dist/server/worker-image-optimization.js +86 -0
- package/dist/server/worker-stages.d.ts +1 -1
- package/dist/server/worker-stages.js +2 -2
- package/dist/server/worker-utils.d.ts +8 -3
- package/dist/server/worker-utils.js +12 -7
- package/dist/shims/app-router-scroll.js +4 -1
- package/dist/shims/app-router-tree-context.d.ts +5 -0
- package/dist/shims/app-router-tree-context.js +27 -0
- package/dist/shims/cache-adapter-instantiate.d.ts +50 -0
- package/dist/shims/cache-adapter-instantiate.js +118 -0
- package/dist/shims/cache-callable-runtime.d.ts +6 -42
- package/dist/shims/cache-callable-runtime.js +65 -174
- package/dist/shims/cache-flight-arguments.d.ts +22 -0
- package/dist/shims/cache-flight-arguments.js +56 -0
- package/dist/shims/cache-handler.js +1 -9
- package/dist/shims/cache-request-state.d.ts +15 -0
- package/dist/shims/cache-request-state.js +31 -8
- package/dist/shims/cache-runtime.d.ts +12 -20
- package/dist/shims/cache-runtime.js +166 -225
- package/dist/shims/cache.d.ts +4 -4
- package/dist/shims/cache.js +44 -16
- package/dist/shims/cacheability-classification.d.ts +7 -0
- package/dist/shims/cdn-cache.d.ts +8 -0
- package/dist/shims/cdn-cache.js +17 -1
- package/dist/shims/disable-smooth-scroll.d.ts +3 -0
- package/dist/shims/disable-smooth-scroll.js +36 -0
- package/dist/shims/document-placeholders.d.ts +19 -0
- package/dist/shims/document-placeholders.js +45 -0
- package/dist/shims/document.js +21 -5
- package/dist/shims/dynamic-preload-chunks.d.ts +1 -0
- package/dist/shims/dynamic-preload-chunks.js +13 -9
- package/dist/shims/dynamic.d.ts +1 -1
- package/dist/shims/dynamic.js +60 -8
- package/dist/shims/error-boundary-navigation.d.ts +7 -0
- package/dist/shims/error-boundary-navigation.js +19 -1
- package/dist/shims/error-boundary.d.ts +10 -10
- package/dist/shims/error-boundary.js +26 -7
- package/dist/shims/fetch-cache-state.d.ts +66 -0
- package/dist/shims/fetch-cache-state.js +92 -0
- package/dist/shims/fetch-cache.d.ts +3 -61
- package/dist/shims/fetch-cache.js +161 -108
- package/dist/shims/form.js +1 -1
- package/dist/shims/head.d.ts +10 -6
- package/dist/shims/head.js +15 -13
- package/dist/shims/headers.d.ts +13 -0
- package/dist/shims/headers.js +47 -4
- package/dist/shims/image.js +2 -1
- package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -2
- package/dist/shims/internal/app-page-props-cache-key.js +24 -15
- package/dist/shims/internal/headers-state.d.ts +0 -1
- package/dist/shims/internal/work-store-scope.d.ts +5 -0
- package/dist/shims/internal/work-store-scope.js +24 -0
- package/dist/shims/metadata.js +3 -3
- package/dist/shims/navigation-errors.d.ts +6 -0
- package/dist/shims/navigation-errors.js +11 -1
- package/dist/shims/navigation.d.ts +7 -0
- package/dist/shims/navigation.js +45 -9
- package/dist/shims/request-context.d.ts +11 -0
- package/dist/shims/request-state-types.d.ts +2 -1
- package/dist/shims/router.js +12 -20
- package/dist/shims/server.d.ts +16 -0
- package/dist/shims/server.js +24 -4
- package/dist/shims/thenable-params.d.ts +4 -0
- package/dist/shims/thenable-params.js +25 -3
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/shims/url-utils.d.ts +2 -0
- package/dist/shims/url-utils.js +3 -1
- package/dist/utils/client-runtime-metadata.js +1 -1
- package/dist/utils/hash.d.ts +7 -0
- package/dist/utils/hash.js +11 -1
- package/dist/utils/mdx-scan.d.ts +5 -0
- package/dist/utils/mdx-scan.js +8 -1
- package/dist/utils/package-name.d.ts +6 -0
- package/dist/utils/package-name.js +12 -1
- package/dist/utils/plugin-rsc-version.d.ts +4 -0
- package/dist/utils/plugin-rsc-version.js +15 -0
- package/dist/utils/prerender-output-paths.js +15 -4
- package/dist/utils/public-file-path.d.ts +10 -2
- package/dist/utils/public-file-path.js +13 -9
- package/dist/utils/public-routes.js +2 -2
- package/dist/utils/react-version.d.ts +3 -0
- package/dist/utils/react-version.js +1 -1
- package/dist/utils/regex-safety.d.ts +10 -0
- package/dist/utils/regex-safety.js +70 -18
- package/package.json +9 -5
package/README.md
CHANGED
|
@@ -147,6 +147,9 @@ Other options: `--port <port>` (default: 3001), `--skip-check`, `--force`.
|
|
|
147
147
|
Cloudflare init uses `cf` and `cloudflare.config.ts` by default. Both `vinext init`
|
|
148
148
|
and `create-vinext-app` accept `--legacy-wrangler-cloudflare-init` to retain the
|
|
149
149
|
legacy Wrangler setup. Existing Wrangler configs are not automatically migrated.
|
|
150
|
+
`vinext init --name <name>` sets the Worker name in the generated config instead of
|
|
151
|
+
deriving it from `package.json`. An existing config keeps its own Worker name, and init
|
|
152
|
+
prints a warning.
|
|
150
153
|
|
|
151
154
|
If your `next.config.*` sets `output: "standalone"`, `vite build` emits a self-hosting bundle at `dist/standalone/`. Start it with:
|
|
152
155
|
|
|
@@ -158,6 +161,42 @@ Environment variables: `PORT` (default `3000`), `HOST` (default `0.0.0.0`).
|
|
|
158
161
|
|
|
159
162
|
> **Note:** Next.js standalone uses `HOSTNAME` for the bind address, but vinext uses `HOST` to avoid collision with the system-set `HOSTNAME` variable on Linux. Update your deployment config accordingly.
|
|
160
163
|
|
|
164
|
+
#### Custom standalone entries
|
|
165
|
+
|
|
166
|
+
The generated `server.js` is a small wrapper around `startProdServer` from `vinext/server/prod-server`. To control where the server loads the build output from, write your own entry and call `startProdServer` directly. All options are optional:
|
|
167
|
+
|
|
168
|
+
| Option | Default | Description |
|
|
169
|
+
| ----------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
170
|
+
| `port` | `PORT` env var, otherwise `3000` | Port to listen on. |
|
|
171
|
+
| `host` | `"0.0.0.0"` | Address to bind to. Unlike the generated `server.js`, `startProdServer` doesn't read `HOST`, so pass `process.env.HOST` yourself if you need it. |
|
|
172
|
+
| `outDir` | `dist`, resolved against the current directory | Build output directory containing `client/` and `server/`. Relative paths resolve against the current directory. In standalone output, this is `dist/standalone/dist`. |
|
|
173
|
+
| `serverDir` | `<outDir>/server` | Directory containing the server bundles, manifests, and prerendered output. |
|
|
174
|
+
| `rscEntryPath` | `<serverDir>/index.js` | App Router server entry. If this file exists, the server runs in App Router mode. |
|
|
175
|
+
| `serverEntryPath` | `<serverDir>/entry.js` | Pages Router server entry. Used when there's no App Router entry. |
|
|
176
|
+
| `noCompression` | `false` | Disable response compression. |
|
|
177
|
+
|
|
178
|
+
`ProdServerOptions` also has `purpose` and `silent`, which vinext uses for its own build-time servers. Leave them unset. The returned promise resolves to `{ server, port }` once the server is listening.
|
|
179
|
+
|
|
180
|
+
**Compiled single-file binaries.** A `bun build --compile` binary can't use the generated `server.js`. Bun fixes `import.meta.dirname` when it compiles the binary, so `join(import.meta.dirname, "dist")` doesn't point at the directory the binary runs from. Depending on the Bun version, it points at the build machine's path or at Bun's embedded `/$bunfs/root`. Resolve `outDir` from the executable's location instead:
|
|
181
|
+
|
|
182
|
+
```js
|
|
183
|
+
// server-bin.mjs, in your project root
|
|
184
|
+
import path from "node:path";
|
|
185
|
+
import { startProdServer } from "vinext/server/prod-server";
|
|
186
|
+
|
|
187
|
+
await startProdServer({
|
|
188
|
+
port: Number.parseInt(process.env.PORT ?? "3000", 10),
|
|
189
|
+
host: process.env.HOST ?? "0.0.0.0",
|
|
190
|
+
outDir: path.join(path.dirname(process.execPath), "dist"),
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
bun build --compile --compile-autoload-package-json server-bin.mjs --outfile dist/standalone/server
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Keep the entry outside `dist/standalone`: each build deletes and recreates that directory. Compile after `vite build`, then ship the whole `dist/standalone` directory, because the `server` binary needs the `dist/` and `node_modules/` directories next to it. The server bundles in `dist/server` still import their dependencies from `node_modules` at runtime. `--compile-autoload-package-json` lets the binary read package `exports` and `main` fields when it resolves them. Under plain Node, `process.execPath` is the Node binary, so use this entry only for compiled builds.
|
|
199
|
+
|
|
161
200
|
### Starting a new vinext project
|
|
162
201
|
|
|
163
202
|
Use `create-vinext-app` for new projects. It creates a TypeScript App Router project
|
|
@@ -246,7 +285,7 @@ You can, with caution. vinext has known compatibility gaps and has not yet been
|
|
|
246
285
|
Yes. Next.js supports [self-hosting](https://nextjs.org/docs/app/building-your-application/deploying#self-hosting) on Node.js servers, Docker containers, and static exports. If you're happy with the Next.js toolchain and just want to run it somewhere other than Vercel, self-hosting is the simplest path.
|
|
247
286
|
|
|
248
287
|
**How are you verifying this works?**
|
|
249
|
-
The test suite has over 1,700 Vitest tests and 380 Playwright E2E tests. This includes tests ported directly from the [Next.js test suite](https://github.com/vercel/next.js/tree/canary/test) and [OpenNext's Cloudflare conformance suite](https://github.com/opennextjs/opennextjs-cloudflare), covering routing, SSR, RSC, server actions, caching, metadata, middleware, streaming, and more. Vercel's [App Router Playground](https://github.com/vercel/next-app-router-playground) also runs on vinext as an integration test. See the [Tests](#tests) section
|
|
288
|
+
The test suite has over 1,700 Vitest tests and 380 Playwright E2E tests. This includes tests ported directly from the [Next.js test suite](https://github.com/vercel/next.js/tree/canary/test) and [OpenNext's Cloudflare conformance suite](https://github.com/opennextjs/opennextjs-cloudflare), covering routing, SSR, RSC, server actions, caching, metadata, middleware, streaming, and more. Vercel's [App Router Playground](https://github.com/vercel/next-app-router-playground) also runs on vinext as an integration test. See the [Tests](#tests) section for details.
|
|
250
289
|
|
|
251
290
|
**Who is reviewing this code?**
|
|
252
291
|
A mix of humans and AI agents. Humans review PRs before they merge, focused on behavior, structure, and long-term direction. We lean heavily on agent-driven code review to catch issues at PR time and across the codebase. The test suite is the primary quality gate. Outside contributions and deeper human code review are very welcome.
|
|
@@ -401,10 +440,14 @@ export default defineConfig({
|
|
|
401
440
|
|
|
402
441
|
#### Module Federation (client-side)
|
|
403
442
|
|
|
404
|
-
|
|
443
|
+
Use [`@module-federation/vinext`](https://github.com/module-federation/vinext), a thin wrapper around `@module-federation/vite` with vinext defaults. Register `federation()` before `vinext()`:
|
|
444
|
+
|
|
445
|
+
```bash
|
|
446
|
+
npm install @module-federation/vinext
|
|
447
|
+
```
|
|
405
448
|
|
|
406
449
|
```ts
|
|
407
|
-
import { federation } from "@module-federation/
|
|
450
|
+
import { federation } from "@module-federation/vinext";
|
|
408
451
|
import { defineConfig } from "vite";
|
|
409
452
|
import vinext from "vinext";
|
|
410
453
|
|
|
@@ -412,11 +455,14 @@ export default defineConfig({
|
|
|
412
455
|
plugins: [
|
|
413
456
|
federation({
|
|
414
457
|
name: "host",
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
458
|
+
remotes: {
|
|
459
|
+
catalog: {
|
|
460
|
+
type: "module",
|
|
461
|
+
name: "catalog",
|
|
462
|
+
entry: "https://catalog.example.com/remoteEntry.js",
|
|
463
|
+
entryGlobalName: "catalog",
|
|
464
|
+
shareScope: "default",
|
|
465
|
+
},
|
|
420
466
|
},
|
|
421
467
|
}),
|
|
422
468
|
vinext(),
|
|
@@ -424,6 +470,8 @@ export default defineConfig({
|
|
|
424
470
|
});
|
|
425
471
|
```
|
|
426
472
|
|
|
473
|
+
The wrapper shares `react` and `react-dom` as singletons, defaults `filename` to `remoteEntry.js`, and injects the host's federation startup into the entry, because vinext has no conventional HTML entry. Explicit options override these defaults, and every `@module-federation/vite` option remains available. Passing your own `shared` replaces the default map, so keep `react`, `react/`, `react-dom`, and `react-dom/` as singletons in it, in both the host and remotes.
|
|
474
|
+
|
|
427
475
|
In a remote client component, use `getVinextReact()` before reading React hooks. vinext registers the host's browser React instance before application modules execute, and the first registration remains stable across remote evaluation and HMR:
|
|
428
476
|
|
|
429
477
|
```tsx
|
|
@@ -442,7 +490,7 @@ export function RemoteCounter() {
|
|
|
442
490
|
|
|
443
491
|
This bridge is browser-only. It does not provide App Router Module Federation SSR or transparently replace React imports inside third-party packages; compatible React versions remain the responsibility of the Module Federation `shared` configuration.
|
|
444
492
|
|
|
445
|
-
See the [
|
|
493
|
+
See the [Module Federation guide](https://vinext.dev/docs/guides/module-federation) for loading remote components in the host.
|
|
446
494
|
|
|
447
495
|
### Other platforms (via Nitro)
|
|
448
496
|
|
|
@@ -792,7 +840,7 @@ their `env` binding lookups) are instantiated lazily on the first request.
|
|
|
792
840
|
|
|
793
841
|
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.
|
|
794
842
|
|
|
795
|
-
To write your own adapter, point a slot at any module by path
|
|
843
|
+
To write your own adapter, point a slot at any module by path. Its default export receives one `{ env, options }` argument at runtime and produces a data-cache `CacheHandler` (or a CDN adapter):
|
|
796
844
|
|
|
797
845
|
```ts
|
|
798
846
|
vinext({
|
|
@@ -805,6 +853,41 @@ vinext({
|
|
|
805
853
|
});
|
|
806
854
|
```
|
|
807
855
|
|
|
856
|
+
The default export can be a factory or a class:
|
|
857
|
+
|
|
858
|
+
```ts
|
|
859
|
+
// my-adapter.js: a factory
|
|
860
|
+
export default ({ env, options }) => ({
|
|
861
|
+
async get(key) {
|
|
862
|
+
/* … */
|
|
863
|
+
},
|
|
864
|
+
async set(key, data) {
|
|
865
|
+
/* … */
|
|
866
|
+
},
|
|
867
|
+
async revalidateTag(tags) {
|
|
868
|
+
/* … */
|
|
869
|
+
},
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
// my-adapter.js: or a class
|
|
873
|
+
export default class MyAdapter {
|
|
874
|
+
constructor({ env, options }) {
|
|
875
|
+
/* … */
|
|
876
|
+
}
|
|
877
|
+
async get(key) {
|
|
878
|
+
/* … */
|
|
879
|
+
}
|
|
880
|
+
async set(key, data) {
|
|
881
|
+
/* … */
|
|
882
|
+
}
|
|
883
|
+
async revalidateTag(tags) {
|
|
884
|
+
/* … */
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
vinext invokes class syntax with `new` and calls any other function, so existing factories keep plain-call semantics. A constructor that isn't class syntax (an ES5-compiled, bound, or Proxy-wrapped class) can't be told apart from a factory, so export a factory that constructs it: `export default (args) => new MyAdapter(args)`. The result must be the adapter itself, not a Promise. A data adapter implements `get`, `set`, and `revalidateTag`; a CDN adapter also implements `buildResponseHeaders` and sets `ownsBackgroundRevalidation` to a boolean. If the export isn't a function or the result is missing a member, vinext logs a warning naming the slot and what's wrong, then keeps the default handler.
|
|
890
|
+
|
|
808
891
|
## What's NOT supported (and won't be)
|
|
809
892
|
|
|
810
893
|
These are intentional exclusions. For things that are missing today but on the roadmap, see [Known gaps we're working on](#known-gaps-were-working-on) above.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AppRoute } from "../routing/app-route-graph.js";
|
|
2
|
+
//#region src/build/app-page-static-eligibility.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Whether Next.js classifies an App page route as static or SSG, read from its
|
|
5
|
+
* layout, page and parallel-slot sources with the helpers dispatch applies to
|
|
6
|
+
* the loaded modules. Only such a route has listed paths.
|
|
7
|
+
*
|
|
8
|
+
* An MDX source read without the MDX parser has unknown exports, so its route
|
|
9
|
+
* is "unreadable". Its paths are still listed: the built runtime reads the real
|
|
10
|
+
* exports and never stores a route that isn't static, while an unlisted path's
|
|
11
|
+
* render failure would be dropped instead of failing. But no probe renders an
|
|
12
|
+
* unreadable route's fallback, so it never certifies one.
|
|
13
|
+
*/
|
|
14
|
+
export declare function classifyAppPageRouteStaticEligibility(route: AppRoute, readMdxEsm: ((source: string) => string) | null): "eligible" | "ineligible" | "unreadable";
|
|
15
|
+
//#endregion
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { extractExportConstNumber, extractExportConstString, hasRuntimeExportedName } from "./report.js";
|
|
2
|
+
import { collectAppPageStaticGenerationRuntimes, hasAppPageGenerateStaticParamsAtLastDynamicSegment, isAppPageStaticEligible, isEdgeRuntime, resolveAppPageSegmentConfig, resolveAppPageStaticGenerationRuntime } from "../server/app-segment-config.js";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
//#region src/build/app-page-static-eligibility.ts
|
|
5
|
+
/**
|
|
6
|
+
* Whether Next.js classifies an App page route as static or SSG, read from its
|
|
7
|
+
* layout, page and parallel-slot sources with the helpers dispatch applies to
|
|
8
|
+
* the loaded modules. Only such a route has listed paths.
|
|
9
|
+
*
|
|
10
|
+
* An MDX source read without the MDX parser has unknown exports, so its route
|
|
11
|
+
* is "unreadable". Its paths are still listed: the built runtime reads the real
|
|
12
|
+
* exports and never stores a route that isn't static, while an unlisted path's
|
|
13
|
+
* render failure would be dropped instead of failing. But no probe renders an
|
|
14
|
+
* unreadable route's fallback, so it never certifies one.
|
|
15
|
+
*/
|
|
16
|
+
function classifyAppPageRouteStaticEligibility(route, readMdxEsm) {
|
|
17
|
+
let unreadable = false;
|
|
18
|
+
const readSegmentConfig = (filePath) => {
|
|
19
|
+
if (!filePath) return null;
|
|
20
|
+
let code = fs.readFileSync(filePath, "utf8");
|
|
21
|
+
if (filePath.toLowerCase().endsWith(".mdx")) {
|
|
22
|
+
let esm = null;
|
|
23
|
+
try {
|
|
24
|
+
esm = readMdxEsm?.(code) ?? null;
|
|
25
|
+
} catch {}
|
|
26
|
+
unreadable ||= esm === null;
|
|
27
|
+
code = esm ?? "";
|
|
28
|
+
}
|
|
29
|
+
const dynamic = extractExportConstString(code, "dynamic");
|
|
30
|
+
const revalidate = extractExportConstNumber(code, "revalidate");
|
|
31
|
+
const runtime = extractExportConstString(code, "runtime");
|
|
32
|
+
return {
|
|
33
|
+
...dynamic === null ? {} : { dynamic },
|
|
34
|
+
...hasRuntimeExportedName(code, "generateStaticParams") ? { generateStaticParams() {} } : {},
|
|
35
|
+
...revalidate === null ? {} : { revalidate },
|
|
36
|
+
...runtime === null ? {} : { runtime }
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const layouts = route.layouts.map(readSegmentConfig);
|
|
40
|
+
const page = readSegmentConfig(route.pagePath);
|
|
41
|
+
const parallelBranches = route.parallelSlots.map((slot) => ({
|
|
42
|
+
configLayouts: (slot.configLayoutPaths ?? []).map(readSegmentConfig),
|
|
43
|
+
configLayoutTreePositions: slot.configLayoutTreePositions ?? [],
|
|
44
|
+
isDefault: !slot.pagePath,
|
|
45
|
+
layout: readSegmentConfig(slot.layoutPath),
|
|
46
|
+
name: slot.name,
|
|
47
|
+
ownerTreePosition: slot.ownerTreePosition ?? null,
|
|
48
|
+
page: readSegmentConfig(slot.pagePath ?? slot.defaultPath),
|
|
49
|
+
routeSegments: slot.routeSegments
|
|
50
|
+
}));
|
|
51
|
+
if (unreadable) return "unreadable";
|
|
52
|
+
const segmentConfig = resolveAppPageSegmentConfig({
|
|
53
|
+
layouts,
|
|
54
|
+
layoutTreePositions: route.layoutTreePositions,
|
|
55
|
+
page,
|
|
56
|
+
parallelBranches,
|
|
57
|
+
routeSegments: route.routeSegments
|
|
58
|
+
});
|
|
59
|
+
return isAppPageStaticEligible({
|
|
60
|
+
dynamicConfig: segmentConfig.dynamicConfig,
|
|
61
|
+
hasGenerateStaticParams: hasAppPageGenerateStaticParamsAtLastDynamicSegment({
|
|
62
|
+
childrenSlot: route.childrenSlot ?? null,
|
|
63
|
+
layouts,
|
|
64
|
+
layoutTreePositions: route.layoutTreePositions,
|
|
65
|
+
page,
|
|
66
|
+
parallelBranches,
|
|
67
|
+
routeSegments: route.routeSegments
|
|
68
|
+
}),
|
|
69
|
+
isDynamicRoute: route.isDynamic,
|
|
70
|
+
isStaticGenerationEdgeRuntime: isEdgeRuntime(resolveAppPageStaticGenerationRuntime(collectAppPageStaticGenerationRuntimes({
|
|
71
|
+
childrenSlot: route.childrenSlot ?? null,
|
|
72
|
+
layouts,
|
|
73
|
+
layoutTreePositions: route.layoutTreePositions,
|
|
74
|
+
page,
|
|
75
|
+
parallelBranches,
|
|
76
|
+
routeSegments: route.routeSegments
|
|
77
|
+
}))),
|
|
78
|
+
revalidateSeconds: segmentConfig.revalidateSeconds
|
|
79
|
+
}) ? "eligible" : "ineligible";
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
export { classifyAppPageRouteStaticEligibility };
|
|
@@ -116,4 +116,14 @@ export declare function createMultiStageCodeSplittingConfig(existing: VinextBuil
|
|
|
116
116
|
* the host's existing output pattern (or vinext's server-assets default).
|
|
117
117
|
*/
|
|
118
118
|
export declare function createMultiStageChunkFileNames(assetsDir: string, existing: VinextBuildOutput["chunkFileNames"]): ChunkFileNameFunction;
|
|
119
|
+
/**
|
|
120
|
+
* The client environment builds after RSC and SSR, and Vite empties its
|
|
121
|
+
* output directory first. A client output directory that is, or contains, a
|
|
122
|
+
* server output directory would therefore delete the server build.
|
|
123
|
+
*/
|
|
124
|
+
export declare function assertClientOutDirIsSeparate(root: string, dirs: {
|
|
125
|
+
clientOutDir?: string;
|
|
126
|
+
rscOutDir?: string;
|
|
127
|
+
ssrOutDir?: string;
|
|
128
|
+
}): void;
|
|
119
129
|
//#endregion
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
1
|
+
import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
2
|
//#region src/build/client-build-config.ts
|
|
3
3
|
const ROUTE_OWNED_CLIENT_SHIMS = /* @__PURE__ */ new Set([
|
|
4
|
+
"app-router-tree-context",
|
|
4
5
|
"compat-router",
|
|
5
6
|
"dynamic",
|
|
6
7
|
"dynamic-preload-chunks",
|
|
@@ -266,5 +267,20 @@ function createMultiStageChunkFileNames(assetsDir, existing) {
|
|
|
266
267
|
return (existing ?? joinAssetFileNamePattern(assetsDir, "[name]-[hash].js")).replaceAll("[name]", name);
|
|
267
268
|
};
|
|
268
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* The client environment builds after RSC and SSR, and Vite empties its
|
|
272
|
+
* output directory first. A client output directory that is, or contains, a
|
|
273
|
+
* server output directory would therefore delete the server build.
|
|
274
|
+
*/
|
|
275
|
+
function assertClientOutDirIsSeparate(root, dirs) {
|
|
276
|
+
if (!dirs.clientOutDir) return;
|
|
277
|
+
const clientOutDir = path.resolve(root, dirs.clientOutDir);
|
|
278
|
+
const serverOutDirs = [["rscOutDir", dirs.rscOutDir ?? "dist/server"], ["ssrOutDir", dirs.ssrOutDir ?? "dist/server/ssr"]];
|
|
279
|
+
for (const [name, dir] of serverOutDirs) {
|
|
280
|
+
const relative = path.relative(clientOutDir, path.resolve(root, dir));
|
|
281
|
+
if (relative === ".." || relative.startsWith("../") || path.isAbsolute(relative)) continue;
|
|
282
|
+
throw new Error(`[vinext] clientOutDir "${dirs.clientOutDir}" contains ${name} "${dir}". The client build runs last and empties its output directory, which would delete the server build. Use separate output directories.`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
269
285
|
//#endregion
|
|
270
|
-
export { RSC_FRAMEWORK_CHUNK_TEST, createClientAssetFileNames, createClientCodeSplittingConfig, createClientFileNameConfig, createClientManualChunks, createMultiStageChunkFileNames, createMultiStageCodeSplittingConfig, createRscFrameworkChunkOutputConfig, getBuildBundlerOptions, getClientTreeshakeConfig, isRscFrameworkModule, sanitizeRscChunkFileName, withBuildBundlerOptions };
|
|
286
|
+
export { RSC_FRAMEWORK_CHUNK_TEST, assertClientOutDirIsSeparate, createClientAssetFileNames, createClientCodeSplittingConfig, createClientFileNameConfig, createClientManualChunks, createMultiStageChunkFileNames, createMultiStageCodeSplittingConfig, createRscFrameworkChunkOutputConfig, getBuildBundlerOptions, getClientTreeshakeConfig, isRscFrameworkModule, sanitizeRscChunkFileName, withBuildBundlerOptions };
|
|
@@ -2,5 +2,13 @@ import { PagesClientAssets } from "../server/pages-client-assets.js";
|
|
|
2
2
|
//#region src/build/pages-client-assets-module.d.ts
|
|
3
3
|
export declare const PAGES_CLIENT_ASSETS_MODULE = "vinext-client-assets.js";
|
|
4
4
|
export declare function buildPagesClientAssetsModule(assets: PagesClientAssets): string;
|
|
5
|
+
/**
|
|
6
|
+
* Select the client bundle files that live outside `_next/static`, as URL
|
|
7
|
+
* pathnames. Plugins emit these at the output root, for example a Module
|
|
8
|
+
* Federation host's `remoteEntry.js`. Public directory files are copied
|
|
9
|
+
* outside the bundle, and build metadata (vinext's and Cloudflare's
|
|
10
|
+
* `.assetsignore`) is excluded.
|
|
11
|
+
*/
|
|
12
|
+
export declare function collectClientBuildFiles(fileNames: Iterable<string>): string[];
|
|
5
13
|
export declare function writePagesClientAssetsModuleIfMissing(outputDir: string, moduleSource: string): void;
|
|
6
14
|
//#endregion
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
|
+
import "../utils/asset-prefix.js";
|
|
2
3
|
import { getSharedChunkFiles } from "../server/pages-asset-tags.js";
|
|
4
|
+
import "../utils/client-entry-manifest.js";
|
|
3
5
|
import fs from "node:fs";
|
|
4
6
|
//#region src/build/pages-client-assets-module.ts
|
|
5
7
|
const PAGES_CLIENT_ASSETS_MODULE = "vinext-client-assets.js";
|
|
@@ -10,6 +12,24 @@ function buildPagesClientAssetsModule(assets) {
|
|
|
10
12
|
} : assets;
|
|
11
13
|
return `export default ${JSON.stringify(prepared)};\n`;
|
|
12
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Select the client bundle files that live outside `_next/static`, as URL
|
|
17
|
+
* pathnames. Plugins emit these at the output root, for example a Module
|
|
18
|
+
* Federation host's `remoteEntry.js`. Public directory files are copied
|
|
19
|
+
* outside the bundle, and build metadata (vinext's and Cloudflare's
|
|
20
|
+
* `.assetsignore`) is excluded.
|
|
21
|
+
*/
|
|
22
|
+
function collectClientBuildFiles(fileNames) {
|
|
23
|
+
const files = [];
|
|
24
|
+
for (const fileName of fileNames) {
|
|
25
|
+
const pathname = `/${fileName}`;
|
|
26
|
+
if (pathname.includes(`/_next/static/`)) continue;
|
|
27
|
+
if (pathname.startsWith("/.vite/") || fileName === "vinext-client-entry-manifest.json") continue;
|
|
28
|
+
if (fileName === ".assetsignore") continue;
|
|
29
|
+
files.push(pathname);
|
|
30
|
+
}
|
|
31
|
+
return files.sort();
|
|
32
|
+
}
|
|
13
33
|
function writePagesClientAssetsModuleIfMissing(outputDir, moduleSource) {
|
|
14
34
|
const outputPath = path.join(outputDir, PAGES_CLIENT_ASSETS_MODULE);
|
|
15
35
|
if (fs.existsSync(outputPath)) return;
|
|
@@ -17,4 +37,4 @@ function writePagesClientAssetsModuleIfMissing(outputDir, moduleSource) {
|
|
|
17
37
|
fs.writeFileSync(outputPath, moduleSource);
|
|
18
38
|
}
|
|
19
39
|
//#endregion
|
|
20
|
-
export { PAGES_CLIENT_ASSETS_MODULE, buildPagesClientAssetsModule, writePagesClientAssetsModuleIfMissing };
|
|
40
|
+
export { PAGES_CLIENT_ASSETS_MODULE, buildPagesClientAssetsModule, collectClientBuildFiles, writePagesClientAssetsModuleIfMissing };
|
|
@@ -10,17 +10,18 @@ import { apiRouter, matchRoute, pagesRouter } from "../routing/pages-router.js";
|
|
|
10
10
|
import "../server/headers.js";
|
|
11
11
|
import { matchesMiddlewarePathname } from "../server/middleware-matcher.js";
|
|
12
12
|
import { extractLocaleFromUrl, normalizeDefaultLocalePathname } from "../server/pages-i18n.js";
|
|
13
|
-
import { classifyAppRoute, classifyAppRouteHandler, classifyPagesRoute,
|
|
13
|
+
import { classifyAppRoute, classifyAppRouteHandler, classifyPagesRoute, extractExportConstString, extractMiddlewareMatcherConfig, getAppRouteRenderEntryPath } from "./report.js";
|
|
14
14
|
import { normalizeStaticPathname, normalizeStaticPathsEntry } from "../routing/route-pattern.js";
|
|
15
15
|
import { buildPagesDataHref } from "../shims/internal/pages-data-url.js";
|
|
16
16
|
import { normalizePathTrailingSlash } from "../shims/url-utils.js";
|
|
17
|
+
import { resolveAppPageDynamicConfig } from "../server/app-segment-config.js";
|
|
18
|
+
import { classifyAppPageRouteStaticEligibility } from "./app-page-static-eligibility.js";
|
|
17
19
|
import { pagesRouteHasPriorityOverAppRoute } from "../server/hybrid-route-priority.js";
|
|
18
20
|
import { readPrerenderSecret } from "./server-manifest.js";
|
|
19
21
|
import { loadMdxEsmReader } from "../utils/mdx-scan.js";
|
|
20
22
|
import { startProdServer } from "../server/prod-server.js";
|
|
21
23
|
import { enterPrerenderPhase } from "./prerender-phase.js";
|
|
22
24
|
import { buildUrlFromParams, layoutOnlyParamSets, localizePagesPath, resolveParentParams, routeStaticParamSets, validateDiscoveredParams } from "./prerender.js";
|
|
23
|
-
import { collectAppPageStaticGenerationRuntimes, hasAppPageGenerateStaticParamsAtLastDynamicSegment, isAppPageStaticEligible, isEdgeRuntime, resolveAppPageDynamicConfig, resolveAppPageSegmentConfig, resolveAppPageStaticGenerationRuntime } from "../server/app-segment-config.js";
|
|
24
25
|
import { resolveBuiltPagesEntryPath, resolveBuiltRscEntryPath } from "./server-entry.js";
|
|
25
26
|
import fs from "node:fs";
|
|
26
27
|
//#region src/build/prerender-paths.ts
|
|
@@ -133,6 +134,7 @@ async function fetchDiscoveryEndpoint(url, headers, retryOptions = {}) {
|
|
|
133
134
|
const request = (async () => {
|
|
134
135
|
const res = await fetch(url, {
|
|
135
136
|
headers,
|
|
137
|
+
redirect: "manual",
|
|
136
138
|
signal: controller.signal
|
|
137
139
|
});
|
|
138
140
|
return {
|
|
@@ -151,10 +153,11 @@ async function fetchDiscoveryEndpoint(url, headers, retryOptions = {}) {
|
|
|
151
153
|
if (res.status === 204) return null;
|
|
152
154
|
return text;
|
|
153
155
|
}
|
|
154
|
-
const
|
|
156
|
+
const redirected = res.status >= 300 && res.status < 400;
|
|
157
|
+
const userFailure = redirected ? null : readDiscoveryUserFailure(res, text);
|
|
155
158
|
const detail = userFailure ?? (/cloudflare:|ERR_UNSUPPORTED_ESM_URL_SCHEME/i.test(text) ? text.trim() : "");
|
|
156
|
-
lastError = /* @__PURE__ */ new Error(`path discovery returned HTTP ${res.status}${detail ? `: ${detail}` : ""}`);
|
|
157
|
-
if (!(res.status === 404 || res.status >= 500 && res.status <= 599 && userFailure === null)) {
|
|
159
|
+
lastError = redirected ? /* @__PURE__ */ new Error(`path discovery returned a redirect (HTTP ${res.status}); refusing to follow it with the prerender secret`) : /* @__PURE__ */ new Error(`path discovery returned HTTP ${res.status}${detail ? `: ${detail}` : ""}`);
|
|
160
|
+
if (!(redirected || res.status === 404 || res.status >= 500 && res.status <= 599 && userFailure === null)) {
|
|
158
161
|
shouldRetry = false;
|
|
159
162
|
throw lastError;
|
|
160
163
|
}
|
|
@@ -351,82 +354,6 @@ function extractPagesStaticPathLocale(url, i18n) {
|
|
|
351
354
|
url: `${rest || "/"}${query}`
|
|
352
355
|
};
|
|
353
356
|
}
|
|
354
|
-
/**
|
|
355
|
-
* Whether Next.js classifies an App page route as static or SSG, read from its
|
|
356
|
-
* layout, page and parallel-slot sources with the helpers dispatch applies to
|
|
357
|
-
* the loaded modules. Only such a route has listed paths.
|
|
358
|
-
*
|
|
359
|
-
* An MDX source read without the MDX parser has unknown exports, so its route
|
|
360
|
-
* is "unreadable". Its paths are still listed: the built runtime reads the real
|
|
361
|
-
* exports and never stores a route that isn't static, while an unlisted path's
|
|
362
|
-
* render failure would be dropped instead of failing. But no probe renders an
|
|
363
|
-
* unreadable route's fallback, so it never certifies one.
|
|
364
|
-
*/
|
|
365
|
-
function classifyAppPageRouteStaticEligibility(route, readMdxEsm) {
|
|
366
|
-
let unreadable = false;
|
|
367
|
-
const readSegmentConfig = (filePath) => {
|
|
368
|
-
if (!filePath) return null;
|
|
369
|
-
let code = fs.readFileSync(filePath, "utf8");
|
|
370
|
-
if (filePath.toLowerCase().endsWith(".mdx")) {
|
|
371
|
-
let esm = null;
|
|
372
|
-
try {
|
|
373
|
-
esm = readMdxEsm?.(code) ?? null;
|
|
374
|
-
} catch {}
|
|
375
|
-
unreadable ||= esm === null;
|
|
376
|
-
code = esm ?? "";
|
|
377
|
-
}
|
|
378
|
-
const dynamic = extractExportConstString(code, "dynamic");
|
|
379
|
-
const revalidate = extractExportConstNumber(code, "revalidate");
|
|
380
|
-
const runtime = extractExportConstString(code, "runtime");
|
|
381
|
-
return {
|
|
382
|
-
...dynamic === null ? {} : { dynamic },
|
|
383
|
-
...hasRuntimeExportedName(code, "generateStaticParams") ? { generateStaticParams() {} } : {},
|
|
384
|
-
...revalidate === null ? {} : { revalidate },
|
|
385
|
-
...runtime === null ? {} : { runtime }
|
|
386
|
-
};
|
|
387
|
-
};
|
|
388
|
-
const layouts = route.layouts.map(readSegmentConfig);
|
|
389
|
-
const page = readSegmentConfig(route.pagePath);
|
|
390
|
-
const parallelBranches = route.parallelSlots.map((slot) => ({
|
|
391
|
-
configLayouts: (slot.configLayoutPaths ?? []).map(readSegmentConfig),
|
|
392
|
-
configLayoutTreePositions: slot.configLayoutTreePositions ?? [],
|
|
393
|
-
isDefault: !slot.pagePath,
|
|
394
|
-
layout: readSegmentConfig(slot.layoutPath),
|
|
395
|
-
name: slot.name,
|
|
396
|
-
ownerTreePosition: slot.ownerTreePosition ?? null,
|
|
397
|
-
page: readSegmentConfig(slot.pagePath ?? slot.defaultPath),
|
|
398
|
-
routeSegments: slot.routeSegments
|
|
399
|
-
}));
|
|
400
|
-
if (unreadable) return "unreadable";
|
|
401
|
-
const segmentConfig = resolveAppPageSegmentConfig({
|
|
402
|
-
layouts,
|
|
403
|
-
layoutTreePositions: route.layoutTreePositions,
|
|
404
|
-
page,
|
|
405
|
-
parallelBranches,
|
|
406
|
-
routeSegments: route.routeSegments
|
|
407
|
-
});
|
|
408
|
-
return isAppPageStaticEligible({
|
|
409
|
-
dynamicConfig: segmentConfig.dynamicConfig,
|
|
410
|
-
hasGenerateStaticParams: hasAppPageGenerateStaticParamsAtLastDynamicSegment({
|
|
411
|
-
childrenSlot: route.childrenSlot ?? null,
|
|
412
|
-
layouts,
|
|
413
|
-
layoutTreePositions: route.layoutTreePositions,
|
|
414
|
-
page,
|
|
415
|
-
parallelBranches,
|
|
416
|
-
routeSegments: route.routeSegments
|
|
417
|
-
}),
|
|
418
|
-
isDynamicRoute: route.isDynamic,
|
|
419
|
-
isStaticGenerationEdgeRuntime: isEdgeRuntime(resolveAppPageStaticGenerationRuntime(collectAppPageStaticGenerationRuntimes({
|
|
420
|
-
childrenSlot: route.childrenSlot ?? null,
|
|
421
|
-
layouts,
|
|
422
|
-
layoutTreePositions: route.layoutTreePositions,
|
|
423
|
-
page,
|
|
424
|
-
parallelBranches,
|
|
425
|
-
routeSegments: route.routeSegments
|
|
426
|
-
}))),
|
|
427
|
-
revalidateSeconds: segmentConfig.revalidateSeconds
|
|
428
|
-
}) ? "eligible" : "ineligible";
|
|
429
|
-
}
|
|
430
357
|
/** Keys a path an App page route lists by the route and the pathname together. */
|
|
431
358
|
function appPageListingKey(routePattern, pathname) {
|
|
432
359
|
return `${routePattern}\0${pathname}`;
|
package/dist/build/prerender.js
CHANGED
|
@@ -4,7 +4,7 @@ import { isUnknownRecord } from "../utils/record.js";
|
|
|
4
4
|
import { BLOCKED_PAGES } from "../shims/constants.js";
|
|
5
5
|
import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
|
|
6
6
|
import { appRouteLayoutStaticParamsGroups } from "../routing/app-router.js";
|
|
7
|
-
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";
|
|
7
|
+
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, VINEXT_PRERENDER_SPECIAL_ERROR_HEADER } from "../server/headers.js";
|
|
8
8
|
import { extractLocaleFromUrl } from "../server/pages-i18n.js";
|
|
9
9
|
import { classifyAppRoute, classifyPagesRoute, getAppRouteRenderEntryPath } from "./report.js";
|
|
10
10
|
import { resolveClientStaleTimeSeconds } from "../utils/cache-control-metadata.js";
|
|
@@ -12,6 +12,7 @@ import { NoOpCacheHandler, getCacheHandler, setCacheHandler } from "../shims/cac
|
|
|
12
12
|
import { headersContextFromRequest, runWithHeadersContext } from "../shims/headers.js";
|
|
13
13
|
import { _consumeRequestScopedCacheLife } from "../shims/cache-request-state.js";
|
|
14
14
|
import { normalizeStaticPathsEntry } from "../routing/route-pattern.js";
|
|
15
|
+
import { createRscRequestUrl } from "../server/app-rsc-cache-busting.js";
|
|
15
16
|
import { readPrerenderSecret } from "./server-manifest.js";
|
|
16
17
|
import { buildPregeneratedConcretePathTable } from "../server/prerender-manifest.js";
|
|
17
18
|
import { concatUint8Arrays, decodeRscEmbeddedChunk } from "../server/app-rsc-embedded-chunks.js";
|
|
@@ -1041,17 +1042,22 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
1041
1042
|
const responseCacheLife = readPrerenderCacheLifeHeader(response.headers);
|
|
1042
1043
|
const cacheTags = readPrerenderCacheTagsHeader(response.headers);
|
|
1043
1044
|
const fatal = response.headers.get(VINEXT_PRERENDER_RENDER_ERROR_HEADER) === "1";
|
|
1044
|
-
|
|
1045
|
+
const specialError = mode === "export" ? null : readPrerenderSpecialErrorHeader(response.headers);
|
|
1046
|
+
const isSpecialError = specialError !== null;
|
|
1047
|
+
const ok = response.ok || isSpecialError;
|
|
1048
|
+
if (!ok || cacheControl.includes("no-store")) {
|
|
1045
1049
|
await response.body?.cancel();
|
|
1046
1050
|
return {
|
|
1047
1051
|
cacheControl,
|
|
1048
1052
|
linkHeader,
|
|
1049
1053
|
html: null,
|
|
1050
|
-
ok
|
|
1054
|
+
ok,
|
|
1051
1055
|
requestCacheLife: null,
|
|
1052
1056
|
tags: [],
|
|
1053
1057
|
status: response.status,
|
|
1054
|
-
fatal
|
|
1058
|
+
fatal,
|
|
1059
|
+
specialErrorLocation: null,
|
|
1060
|
+
isSpecialError
|
|
1055
1061
|
};
|
|
1056
1062
|
}
|
|
1057
1063
|
const html = await response.text();
|
|
@@ -1064,7 +1070,9 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
1064
1070
|
requestCacheLife: responseCacheLife ?? processCacheLife,
|
|
1065
1071
|
status: response.status,
|
|
1066
1072
|
tags: cacheTags,
|
|
1067
|
-
fatal: false
|
|
1073
|
+
fatal: false,
|
|
1074
|
+
specialErrorLocation: specialError?.location ?? null,
|
|
1075
|
+
isSpecialError
|
|
1068
1076
|
};
|
|
1069
1077
|
});
|
|
1070
1078
|
const htmlCacheControl = htmlRender.cacheControl;
|
|
@@ -1092,7 +1100,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
1092
1100
|
error: "RSC handler returned no prerender HTML"
|
|
1093
1101
|
};
|
|
1094
1102
|
const html = isFallback ? markAppPprDynamicFallbackShellHtml(htmlRender.html) : htmlRender.html;
|
|
1095
|
-
let rscData = extractRscPayloadFromPrerenderedHtml(html);
|
|
1103
|
+
let rscData = htmlRender.isSpecialError ? null : extractRscPayloadFromPrerenderedHtml(html);
|
|
1096
1104
|
if (rscData === null) {
|
|
1097
1105
|
const rscHeaders = new Headers({
|
|
1098
1106
|
Accept: "text/x-component",
|
|
@@ -1100,7 +1108,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
1100
1108
|
});
|
|
1101
1109
|
if (prerenderRouteParamsHeader !== null) rscHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
|
|
1102
1110
|
if (isSpeculative) rscHeaders.set(VINEXT_PRERENDER_SPECULATIVE_HEADER, "1");
|
|
1103
|
-
const rscRequest = new Request(`http://localhost${requestPath}`, { headers: rscHeaders });
|
|
1111
|
+
const rscRequest = new Request(`http://localhost${await createRscRequestUrl(requestPath, rscHeaders)}`, { headers: rscHeaders });
|
|
1104
1112
|
const rscRes = await runWithHeadersContext(headersContextFromRequest(rscRequest), () => rscHandler(rscRequest));
|
|
1105
1113
|
if (!rscRes.ok) {
|
|
1106
1114
|
await rscRes.body?.cancel();
|
|
@@ -1135,7 +1143,11 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
1135
1143
|
...renderedStale === void 0 ? {} : { stale: renderedStale },
|
|
1136
1144
|
router: "app",
|
|
1137
1145
|
...htmlRender.tags.length > 0 ? { tags: htmlRender.tags } : {},
|
|
1138
|
-
...htmlRender.linkHeader
|
|
1146
|
+
...htmlRender.linkHeader || htmlRender.specialErrorLocation !== null ? { headers: {
|
|
1147
|
+
...htmlRender.linkHeader ? { link: htmlRender.linkHeader } : {},
|
|
1148
|
+
...htmlRender.specialErrorLocation !== null ? { location: htmlRender.specialErrorLocation } : {}
|
|
1149
|
+
} } : {},
|
|
1150
|
+
...htmlRender.isSpecialError ? { responseStatus: htmlRender.status } : {},
|
|
1139
1151
|
...urlPath !== routePattern ? { path: urlPath } : {},
|
|
1140
1152
|
...isFallback ? { fallback: true } : {}
|
|
1141
1153
|
};
|
|
@@ -1239,6 +1251,19 @@ function readPrerenderCacheLifeHeader(headers) {
|
|
|
1239
1251
|
return null;
|
|
1240
1252
|
}
|
|
1241
1253
|
}
|
|
1254
|
+
/** Reads the special-error marker: the page's stored headers, or null. */
|
|
1255
|
+
function readPrerenderSpecialErrorHeader(headers) {
|
|
1256
|
+
const value = headers.get(VINEXT_PRERENDER_SPECIAL_ERROR_HEADER);
|
|
1257
|
+
if (!value) return null;
|
|
1258
|
+
try {
|
|
1259
|
+
const parsed = JSON.parse(value);
|
|
1260
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
1261
|
+
const location = parsed.location;
|
|
1262
|
+
return { location: typeof location === "string" ? location : null };
|
|
1263
|
+
} catch {
|
|
1264
|
+
return null;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1242
1267
|
function readPrerenderCacheTagsHeader(headers) {
|
|
1243
1268
|
const value = headers.get(NEXT_CACHE_TAGS_HEADER);
|
|
1244
1269
|
if (!value) return [];
|
|
@@ -124,7 +124,7 @@ async function runPrerender(options) {
|
|
|
124
124
|
let totalUrls = 0;
|
|
125
125
|
let completedUrls = 0;
|
|
126
126
|
const progress = new PrerenderProgress();
|
|
127
|
-
const outDir = mode === "export" ? path.
|
|
127
|
+
const outDir = mode === "export" ? path.resolve(root, options.routeRootConfig?.clientOutDir ?? path.join("dist", "client")) : path.join(manifestDir, "prerendered-routes");
|
|
128
128
|
let sharedProdServer = null;
|
|
129
129
|
let sharedPrerenderSecret;
|
|
130
130
|
const restorePrerenderPhase = enterPrerenderPhase();
|
|
@@ -14,6 +14,13 @@
|
|
|
14
14
|
* warn when this returns `undefined`.
|
|
15
15
|
*/
|
|
16
16
|
export declare function readPrerenderSecret(serverDir: string): string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Read next.config `compress` from `vinext-server.json` in `serverDir`.
|
|
19
|
+
*
|
|
20
|
+
* Like `next start`, compression stays on unless the build recorded an
|
|
21
|
+
* explicit `compress: false`, so builds without the field keep compressing.
|
|
22
|
+
*/
|
|
23
|
+
export declare function readServerCompress(serverDir: string): boolean;
|
|
17
24
|
/**
|
|
18
25
|
* Read every server output root that contains a deployable response-stage
|
|
19
26
|
* graph. Paths are stored relative to the project root so build artifacts stay
|
|
@@ -20,6 +20,16 @@ function readPrerenderSecret(serverDir) {
|
|
|
20
20
|
return readJsonFile(manifestPath)?.prerenderSecret;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
+
* Read next.config `compress` from `vinext-server.json` in `serverDir`.
|
|
24
|
+
*
|
|
25
|
+
* Like `next start`, compression stays on unless the build recorded an
|
|
26
|
+
* explicit `compress: false`, so builds without the field keep compressing.
|
|
27
|
+
*/
|
|
28
|
+
function readServerCompress(serverDir) {
|
|
29
|
+
const manifestPath = path.join(serverDir, "vinext-server.json");
|
|
30
|
+
return readJsonFile(manifestPath)?.compress !== false;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
23
33
|
* Read every server output root that contains a deployable response-stage
|
|
24
34
|
* graph. Paths are stored relative to the project root so build artifacts stay
|
|
25
35
|
* relocatable, then resolved for post-build sidecar updates.
|
|
@@ -31,4 +41,4 @@ function readServerRuntimeOutputDirs(serverDir, root) {
|
|
|
31
41
|
return manifest.runtimeOutputDirs.filter((outputDir) => typeof outputDir === "string").map((outputDir) => path.resolve(root, outputDir));
|
|
32
42
|
}
|
|
33
43
|
//#endregion
|
|
34
|
-
export { readPrerenderSecret, readServerRuntimeOutputDirs };
|
|
44
|
+
export { readPrerenderSecret, readServerCompress, readServerRuntimeOutputDirs };
|