vinext 1.0.0-beta.7 → 1.0.0-beta.9
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 +43 -0
- package/dist/build/prerender-paths.d.ts +50 -1
- package/dist/build/prerender-paths.js +627 -88
- package/dist/build/prerender.d.ts +4 -2
- package/dist/build/prerender.js +40 -23
- package/dist/build/report.d.ts +16 -1
- package/dist/build/report.js +64 -3
- package/dist/build/static-export.d.ts +1 -1
- package/dist/cache/cache-adapters-virtual.d.ts +47 -1
- package/dist/cache/cache-adapters-virtual.js +10 -1
- package/dist/cli.js +7 -0
- package/dist/client/vinext-next-data.d.ts +2 -0
- package/dist/config/config-matchers.d.ts +3 -3
- package/dist/config/config-matchers.js +8 -3
- package/dist/config/prerender.d.ts +2 -2
- package/dist/config/prerender.js +2 -2
- package/dist/entries/app-browser-entry.d.ts +1 -1
- package/dist/entries/app-browser-entry.js +4 -1
- package/dist/entries/app-rsc-entry.d.ts +2 -0
- package/dist/entries/app-rsc-entry.js +28 -4
- package/dist/entries/app-rsc-manifest.js +1 -0
- package/dist/entries/pages-server-entry.d.ts +1 -1
- package/dist/entries/pages-server-entry.js +4 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +146 -28
- package/dist/init-cloudflare.d.ts +5 -3
- package/dist/init-cloudflare.js +58 -25
- package/dist/plugins/import-meta-url.js +7 -6
- package/dist/routing/app-router.d.ts +3 -1
- package/dist/routing/app-router.js +5 -1
- package/dist/server/app-browser-entry.js +41 -13
- package/dist/server/app-browser-navigation-controller.js +1 -1
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-middleware.d.ts +8 -0
- package/dist/server/app-middleware.js +28 -2
- package/dist/server/app-optimistic-routing.d.ts +6 -1
- package/dist/server/app-optimistic-routing.js +13 -1
- package/dist/server/app-page-boundary.js +2 -1
- package/dist/server/app-page-cache-finalizer.d.ts +16 -1
- package/dist/server/app-page-cache-finalizer.js +73 -4
- package/dist/server/app-page-cache.js +2 -2
- package/dist/server/app-page-dispatch.d.ts +2 -0
- package/dist/server/app-page-dispatch.js +12 -4
- package/dist/server/app-page-execution.js +1 -1
- package/dist/server/app-page-render.d.ts +1 -0
- package/dist/server/app-page-render.js +27 -9
- package/dist/server/app-page-response.js +2 -2
- package/dist/server/app-page-stream.js +2 -1
- package/dist/server/app-pages-bridge.js +2 -0
- package/dist/server/app-prerender-endpoints.js +23 -11
- package/dist/server/app-prerender-static-params.js +12 -5
- package/dist/server/app-route-handler-dispatch.js +13 -9
- package/dist/server/app-route-handler-execution.d.ts +1 -0
- package/dist/server/app-route-handler-execution.js +127 -14
- package/dist/server/app-route-handler-policy.d.ts +15 -3
- package/dist/server/app-route-handler-policy.js +19 -4
- package/dist/server/app-route-tree-prefetch.js +2 -2
- package/dist/server/app-router-entry.js +52 -8
- package/dist/server/app-rsc-cache-busting.d.ts +22 -4
- package/dist/server/app-rsc-cache-busting.js +68 -18
- package/dist/server/app-rsc-handler.d.ts +2 -0
- package/dist/server/app-rsc-handler.js +95 -30
- package/dist/server/app-rsc-response-finalizer.js +24 -7
- package/dist/server/app-rsc-route-matching.d.ts +1 -0
- package/dist/server/app-rsc-route-matching.js +1 -0
- package/dist/server/app-segment-config.d.ts +3 -1
- package/dist/server/app-segment-config.js +23 -11
- package/dist/server/app-server-action-execution.js +4 -4
- package/dist/server/app-ssr-entry.js +4 -4
- package/dist/server/cache-control.d.ts +8 -2
- package/dist/server/cache-control.js +35 -4
- package/dist/server/cacheability-limits.d.ts +9 -0
- package/dist/server/cacheability-limits.js +9 -0
- package/dist/server/cacheability-manifest.d.ts +42 -0
- package/dist/server/cacheability-manifest.js +191 -0
- package/dist/server/cacheability-request.d.ts +20 -0
- package/dist/server/cacheability-request.js +457 -0
- package/dist/server/config-headers.js +23 -3
- package/dist/server/headers.d.ts +16 -3
- package/dist/server/headers.js +29 -3
- package/dist/server/middleware-matcher.d.ts +9 -1
- package/dist/server/middleware-matcher.js +22 -1
- package/dist/server/middleware-runtime.d.ts +6 -0
- package/dist/server/middleware-runtime.js +18 -3
- package/dist/server/normalize-path.js +1 -1
- package/dist/server/pages-api-route.js +1 -1
- package/dist/server/pages-i18n.js +3 -2
- package/dist/server/pages-page-handler.js +22 -9
- package/dist/server/pages-request-pipeline.d.ts +2 -0
- package/dist/server/pages-request-pipeline.js +17 -6
- package/dist/server/pages-router-entry.js +56 -9
- package/dist/server/prerender-route-params.js +1 -0
- package/dist/server/prerender-work-unit-setup.d.ts +3 -2
- package/dist/server/prerender-work-unit-setup.js +34 -11
- package/dist/server/prod-server.js +5 -5
- package/dist/server/request-pipeline.d.ts +1 -5
- package/dist/server/request-pipeline.js +2 -9
- package/dist/server/revalidation-host.js +1 -0
- package/dist/server/server-globals.js +13 -0
- package/dist/server/static-file-signal.d.ts +13 -0
- package/dist/server/static-file-signal.js +33 -0
- package/dist/server/worker-prerender-discovery.d.ts +19 -0
- package/dist/server/worker-prerender-discovery.js +55 -0
- package/dist/server/worker-utils.js +2 -3
- package/dist/shims/cache-runtime.js +140 -102
- package/dist/shims/cache.js +11 -1
- package/dist/shims/cacheability-classification.d.ts +85 -0
- package/dist/shims/cacheability-classification.js +146 -0
- package/dist/shims/cdn-cache.d.ts +30 -1
- package/dist/shims/cdn-cache.js +25 -1
- package/dist/shims/error.js +1 -1
- package/dist/shims/fetch-cache.js +8 -3
- package/dist/shims/headers.d.ts +3 -1
- package/dist/shims/headers.js +9 -5
- package/dist/shims/internal/app-prefetch-rsc-request.d.ts +25 -0
- package/dist/shims/internal/app-prefetch-rsc-request.js +21 -0
- package/dist/shims/internal/app-route-prefetch-policy.d.ts +2 -0
- package/dist/shims/internal/app-route-prefetch-policy.js +2 -0
- package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +2 -2
- package/dist/shims/internal/hybrid-client-route-owner-direct.js +29 -11
- package/dist/shims/internal/hybrid-client-route-owner.js +2 -2
- package/dist/shims/internal/make-hanging-promise.d.ts +7 -1
- package/dist/shims/internal/make-hanging-promise.js +13 -2
- package/dist/shims/internal/work-unit-async-storage.d.ts +2 -0
- package/dist/shims/link.js +25 -14
- package/dist/shims/navigation.js +31 -11
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/request-context.d.ts +2 -0
- package/dist/shims/server.js +1 -1
- package/dist/shims/url-utils.d.ts +8 -1
- package/dist/shims/url-utils.js +10 -1
- package/dist/utils/prerender-output-paths.d.ts +7 -7
- package/dist/utils/prerender-output-paths.js +16 -9
- package/dist/utils/react-compiler-support.d.ts +20 -0
- package/dist/utils/react-compiler-support.js +29 -0
- package/package.json +10 -2
|
@@ -181,7 +181,9 @@ type StaticParamsMap = Record<string, ((opts: {
|
|
|
181
181
|
* Uses the `staticParamsMap` (pattern → generateStaticParams) exported from
|
|
182
182
|
* the production bundle.
|
|
183
183
|
*/
|
|
184
|
-
declare function resolveParentParams(childRoute: AppRoute, staticParamsMap: StaticParamsMap
|
|
184
|
+
declare function resolveParentParams(childRoute: AppRoute, staticParamsMap: StaticParamsMap, options?: {
|
|
185
|
+
includeLastDynamicSegment?: boolean;
|
|
186
|
+
}): Promise<Record<string, string | string[]>[]>;
|
|
185
187
|
/**
|
|
186
188
|
* Run the prerender phase for Pages Router.
|
|
187
189
|
*
|
|
@@ -202,7 +204,7 @@ declare function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, m
|
|
|
202
204
|
* Starts a local production server and fetches every static/ISR route via HTTP.
|
|
203
205
|
* Works for both plain Node and Cloudflare Workers builds — the CF Workers bundle
|
|
204
206
|
* (`dist/server/index.js`) is a standard Node-compatible server entry, so no
|
|
205
|
-
* wrangler/miniflare is needed. Writes HTML files,
|
|
207
|
+
* wrangler/miniflare is needed. Writes HTML files, Flight payloads, and
|
|
206
208
|
* `vinext-prerender.json` to `outDir`.
|
|
207
209
|
*
|
|
208
210
|
* If the bundle does not exist, an error is thrown directing the user to run
|
package/dist/build/prerender.js
CHANGED
|
@@ -181,16 +181,28 @@ function metadataOutputPath(servedUrl) {
|
|
|
181
181
|
if (segments.length === 0 || segments.some((segment) => segment === "." || segment === "..")) return null;
|
|
182
182
|
return segments.join("/");
|
|
183
183
|
}
|
|
184
|
-
function emitStaticMetadataFiles(metadataRoutes, outDir) {
|
|
184
|
+
function emitStaticMetadataFiles(metadataRoutes, outDir, basePath = "") {
|
|
185
185
|
const outputFiles = [];
|
|
186
|
+
const outputPrefix = basePath.replace(/^\//, "").replace(/\/$/, "");
|
|
186
187
|
for (const route of metadataRoutes) {
|
|
187
188
|
if (route.isDynamic) continue;
|
|
188
189
|
const outputPath = metadataOutputPath(route.servedUrl);
|
|
189
190
|
if (!outputPath) continue;
|
|
190
|
-
const
|
|
191
|
+
const namespacedOutputPath = outputPrefix ? `${outputPrefix}/${outputPath}` : outputPath;
|
|
192
|
+
const fullPath = path.join(outDir, ...namespacedOutputPath.split("/"));
|
|
191
193
|
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
192
194
|
fs.copyFileSync(route.filePath, fullPath);
|
|
193
|
-
outputFiles.push(
|
|
195
|
+
outputFiles.push(namespacedOutputPath);
|
|
196
|
+
}
|
|
197
|
+
return outputFiles;
|
|
198
|
+
}
|
|
199
|
+
function emitStatic404Files(outDir, html, trailingSlash) {
|
|
200
|
+
const outputFiles = ["404.html"];
|
|
201
|
+
if (trailingSlash) outputFiles.push("404/index.html");
|
|
202
|
+
for (const outputFile of outputFiles) {
|
|
203
|
+
const fullPath = path.join(outDir, ...outputFile.split("/"));
|
|
204
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
205
|
+
fs.writeFileSync(fullPath, html, "utf-8");
|
|
194
206
|
}
|
|
195
207
|
return outputFiles;
|
|
196
208
|
}
|
|
@@ -201,7 +213,7 @@ function emitStaticMetadataFiles(metadataRoutes, outDir) {
|
|
|
201
213
|
* Uses the `staticParamsMap` (pattern → generateStaticParams) exported from
|
|
202
214
|
* the production bundle.
|
|
203
215
|
*/
|
|
204
|
-
async function resolveParentParams(childRoute, staticParamsMap) {
|
|
216
|
+
async function resolveParentParams(childRoute, staticParamsMap, options = {}) {
|
|
205
217
|
const { patternParts } = childRoute;
|
|
206
218
|
let lastDynamicIdx = -1;
|
|
207
219
|
for (let i = patternParts.length - 1; i >= 0; i--) if (patternParts[i].startsWith(":")) {
|
|
@@ -210,7 +222,8 @@ async function resolveParentParams(childRoute, staticParamsMap) {
|
|
|
210
222
|
}
|
|
211
223
|
const parentSegments = [];
|
|
212
224
|
let prefixPattern = "";
|
|
213
|
-
|
|
225
|
+
const prefixEnd = options.includeLastDynamicSegment ? lastDynamicIdx + 1 : lastDynamicIdx;
|
|
226
|
+
for (let i = 0; i < prefixEnd; i++) {
|
|
214
227
|
const part = patternParts[i];
|
|
215
228
|
prefixPattern += "/" + part;
|
|
216
229
|
if (!part.startsWith(":")) continue;
|
|
@@ -292,7 +305,9 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
|
|
|
292
305
|
let renderRoundRobin = 0;
|
|
293
306
|
const renderPage = (urlPath) => {
|
|
294
307
|
const port = renderPorts[renderRoundRobin++ % renderPorts.length];
|
|
295
|
-
|
|
308
|
+
const routeRequestPath = config.trailingSlash && !urlPath.endsWith("/") ? `${urlPath}/` : urlPath;
|
|
309
|
+
const requestPath = config.basePath && routeRequestPath === "/" && !config.trailingSlash ? config.basePath : `${config.basePath ?? ""}${routeRequestPath}`;
|
|
310
|
+
return fetch(`http://127.0.0.1:${port}${requestPath}`, {
|
|
296
311
|
headers: secretHeaders,
|
|
297
312
|
redirect: "manual"
|
|
298
313
|
});
|
|
@@ -316,7 +331,7 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
|
|
|
316
331
|
fallback: false
|
|
317
332
|
};
|
|
318
333
|
}
|
|
319
|
-
if (text === "null") return {
|
|
334
|
+
if (res.status === 204 || text === "null") return {
|
|
320
335
|
paths: [],
|
|
321
336
|
fallback: false
|
|
322
337
|
};
|
|
@@ -430,7 +445,7 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
|
|
|
430
445
|
try {
|
|
431
446
|
const response = await renderPage(urlPath);
|
|
432
447
|
const outputFiles = [];
|
|
433
|
-
const htmlOutputPath = getOutputPath(urlPath, config.trailingSlash);
|
|
448
|
+
const htmlOutputPath = getOutputPath(urlPath, config.trailingSlash, mode === "export" ? config.basePath : "");
|
|
434
449
|
const htmlFullPath = path.join(outDir, htmlOutputPath);
|
|
435
450
|
if (response.status >= 300 && response.status < 400) {
|
|
436
451
|
const html = `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0;url=${(response.headers.get("location") ?? "/").replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">")}" /></head><body></body></html>`;
|
|
@@ -479,12 +494,10 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
|
|
|
479
494
|
const contentType = notFoundRes.headers.get("content-type") ?? "";
|
|
480
495
|
if (notFoundRes.status === 404 && contentType.includes("text/html")) {
|
|
481
496
|
const html404 = await notFoundRes.text();
|
|
482
|
-
const fullPath = path.join(outDir, "404.html");
|
|
483
|
-
fs.writeFileSync(fullPath, html404, "utf-8");
|
|
484
497
|
results.push({
|
|
485
498
|
route: "/404",
|
|
486
499
|
status: "rendered",
|
|
487
|
-
outputFiles:
|
|
500
|
+
outputFiles: emitStatic404Files(outDir, html404, config.trailingSlash),
|
|
488
501
|
revalidate: false,
|
|
489
502
|
router: "pages"
|
|
490
503
|
});
|
|
@@ -514,7 +527,7 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
|
|
|
514
527
|
* Starts a local production server and fetches every static/ISR route via HTTP.
|
|
515
528
|
* Works for both plain Node and Cloudflare Workers builds — the CF Workers bundle
|
|
516
529
|
* (`dist/server/index.js`) is a standard Node-compatible server entry, so no
|
|
517
|
-
* wrangler/miniflare is needed. Writes HTML files,
|
|
530
|
+
* wrangler/miniflare is needed. Writes HTML files, Flight payloads, and
|
|
518
531
|
* `vinext-prerender.json` to `outDir`.
|
|
519
532
|
*
|
|
520
533
|
* If the bundle does not exist, an error is thrown directing the user to run
|
|
@@ -589,7 +602,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
589
602
|
console.warn(`[vinext] Warning: /__vinext/prerender/static-params returned ${res.status} for ${pattern}. Static params will be skipped. This may indicate a stale or missing prerender secret.`);
|
|
590
603
|
return null;
|
|
591
604
|
}
|
|
592
|
-
if (text === "null") return null;
|
|
605
|
+
if (res.status === 204 || text === "null") return null;
|
|
593
606
|
return JSON.parse(text);
|
|
594
607
|
})();
|
|
595
608
|
request.catch(() => staticParamsCache.delete(cacheKey));
|
|
@@ -829,7 +842,9 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
829
842
|
const htmlHeaders = new Headers();
|
|
830
843
|
if (prerenderRouteParamsHeader !== null) htmlHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
|
|
831
844
|
if (isSpeculative) htmlHeaders.set(VINEXT_PRERENDER_SPECULATIVE_HEADER, "1");
|
|
832
|
-
const
|
|
845
|
+
const routeRequestPath = config.trailingSlash && !urlPath.endsWith("/") ? `${urlPath}/` : urlPath;
|
|
846
|
+
const requestPath = config.basePath && routeRequestPath === "/" && !config.trailingSlash ? config.basePath : `${config.basePath ?? ""}${routeRequestPath}`;
|
|
847
|
+
const htmlRequest = new Request(`http://localhost${requestPath}`, { headers: htmlHeaders });
|
|
833
848
|
const htmlRender = await runWithHeadersContext(headersContextFromRequest(htmlRequest), async () => {
|
|
834
849
|
const response = await rscHandler(htmlRequest);
|
|
835
850
|
const cacheControl = response.headers.get("cache-control") ?? "";
|
|
@@ -896,7 +911,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
896
911
|
});
|
|
897
912
|
if (prerenderRouteParamsHeader !== null) rscHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
|
|
898
913
|
if (isSpeculative) rscHeaders.set(VINEXT_PRERENDER_SPECULATIVE_HEADER, "1");
|
|
899
|
-
const rscRequest = new Request(`http://localhost${
|
|
914
|
+
const rscRequest = new Request(`http://localhost${requestPath}`, { headers: rscHeaders });
|
|
900
915
|
const rscRes = await runWithHeadersContext(headersContextFromRequest(rscRequest), () => rscHandler(rscRequest));
|
|
901
916
|
if (!rscRes.ok) {
|
|
902
917
|
await rscRes.body?.cancel();
|
|
@@ -905,12 +920,16 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
905
920
|
rscData = new Uint8Array(await rscRes.arrayBuffer());
|
|
906
921
|
}
|
|
907
922
|
const outputFiles = [];
|
|
908
|
-
const htmlOutputPath = getOutputPath(urlPath, config.trailingSlash);
|
|
923
|
+
const htmlOutputPath = getOutputPath(urlPath, config.trailingSlash, mode === "export" ? config.basePath : "");
|
|
909
924
|
const htmlFullPath = path.join(outDir, htmlOutputPath);
|
|
910
925
|
fs.mkdirSync(path.dirname(htmlFullPath), { recursive: true });
|
|
911
926
|
fs.writeFileSync(htmlFullPath, html, "utf-8");
|
|
912
927
|
outputFiles.push(htmlOutputPath);
|
|
913
|
-
const rscOutputPath = getRscOutputPath(urlPath
|
|
928
|
+
const rscOutputPath = getRscOutputPath(urlPath, {
|
|
929
|
+
mode,
|
|
930
|
+
trailingSlash: config.trailingSlash,
|
|
931
|
+
basePath: mode === "export" ? config.basePath : ""
|
|
932
|
+
});
|
|
914
933
|
const rscFullPath = path.join(outDir, rscOutputPath);
|
|
915
934
|
fs.mkdirSync(path.dirname(rscFullPath), { recursive: true });
|
|
916
935
|
fs.writeFileSync(rscFullPath, rscData);
|
|
@@ -967,19 +986,17 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
967
986
|
});
|
|
968
987
|
results.push(...appResults);
|
|
969
988
|
renderPool?.assertHealthy();
|
|
970
|
-
const outputFiles = mode === "export" && metadataRoutes.length > 0 ? emitStaticMetadataFiles(metadataRoutes, outDir) : [];
|
|
989
|
+
const outputFiles = mode === "export" && metadataRoutes.length > 0 ? emitStaticMetadataFiles(metadataRoutes, outDir, config.basePath) : [];
|
|
971
990
|
try {
|
|
972
|
-
const
|
|
991
|
+
const notFoundPath = config.trailingSlash && !NOT_FOUND_SENTINEL_PATH.endsWith("/") ? `${NOT_FOUND_SENTINEL_PATH}/` : NOT_FOUND_SENTINEL_PATH;
|
|
992
|
+
const notFoundRequest = new Request(`http://localhost${config.basePath ?? ""}${notFoundPath}`);
|
|
973
993
|
const notFoundRes = await runWithHeadersContext(headersContextFromRequest(notFoundRequest), () => rscHandler(notFoundRequest));
|
|
974
994
|
if (notFoundRes.status === 404) {
|
|
975
995
|
const html404 = await notFoundRes.text();
|
|
976
|
-
const fullPath = path.join(outDir, "404.html");
|
|
977
|
-
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
978
|
-
fs.writeFileSync(fullPath, html404, "utf-8");
|
|
979
996
|
results.push({
|
|
980
997
|
route: "/404",
|
|
981
998
|
status: "rendered",
|
|
982
|
-
outputFiles:
|
|
999
|
+
outputFiles: emitStatic404Files(outDir, html404, config.trailingSlash),
|
|
983
1000
|
revalidate: false,
|
|
984
1001
|
router: "app"
|
|
985
1002
|
});
|
package/dist/build/report.d.ts
CHANGED
|
@@ -92,6 +92,8 @@ declare function classifyLayoutSegmentConfig(code: string): LayoutBuildClassific
|
|
|
92
92
|
* API routes (files under pages/api/) are always `api`.
|
|
93
93
|
*/
|
|
94
94
|
declare function classifyPagesRoute(filePath: string): {
|
|
95
|
+
hasStaticProps?: boolean;
|
|
96
|
+
hasServerSideProps?: boolean;
|
|
95
97
|
type: RouteType;
|
|
96
98
|
revalidate?: number;
|
|
97
99
|
};
|
|
@@ -106,6 +108,19 @@ declare function classifyAppRoute(pagePath: string | null, routePath: string | n
|
|
|
106
108
|
type: RouteType;
|
|
107
109
|
revalidate?: number;
|
|
108
110
|
};
|
|
111
|
+
/**
|
|
112
|
+
* Return whether a Route Handler has a GET contract that Next.js considers
|
|
113
|
+
* eligible for static generation. This intentionally examines only the route
|
|
114
|
+
* module's direct segment config; request-time dynamic usage is decided by the
|
|
115
|
+
* staged Worker probe after the module has executed to completion.
|
|
116
|
+
*
|
|
117
|
+
* Ported from Next.js:
|
|
118
|
+
* packages/next/src/server/route-modules/app-route/helpers/is-static-gen-enabled.ts
|
|
119
|
+
*/
|
|
120
|
+
declare function classifyAppRouteHandler(filePath: string): {
|
|
121
|
+
hasGet: boolean;
|
|
122
|
+
staticGenerationEnabled: boolean;
|
|
123
|
+
};
|
|
109
124
|
/**
|
|
110
125
|
* Builds a sorted list of RouteRow objects from the discovered routes.
|
|
111
126
|
* Routes are sorted alphabetically by path, matching filesystem order.
|
|
@@ -144,4 +159,4 @@ declare function printBuildReport(options: {
|
|
|
144
159
|
prerenderResult?: PrerenderResult;
|
|
145
160
|
}): Promise<void>;
|
|
146
161
|
//#endregion
|
|
147
|
-
export { RouteRow, RouteType, StaticMiddlewareMatcher, buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, extractMiddlewareMatcherConfigValue, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
|
|
162
|
+
export { RouteRow, RouteType, StaticMiddlewareMatcher, buildReportRows, classifyAppRoute, classifyAppRouteHandler, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, extractMiddlewareMatcherConfigValue, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
|
package/dist/build/report.js
CHANGED
|
@@ -97,6 +97,20 @@ function hasNamedExportInProgram(program, name) {
|
|
|
97
97
|
}
|
|
98
98
|
return false;
|
|
99
99
|
}
|
|
100
|
+
function hasRuntimeExportedNameInProgram(program, name) {
|
|
101
|
+
for (const node of program.body) {
|
|
102
|
+
if (node.type !== "ExportNamedDeclaration" || node.exportKind === "type") continue;
|
|
103
|
+
if (declarationHasBindingName(node.declaration, name)) return true;
|
|
104
|
+
for (const specifier of node.specifiers) {
|
|
105
|
+
if (specifier.exportKind === "type") continue;
|
|
106
|
+
if (moduleExportNameValue(specifier.exported ?? specifier.local) === name) return true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
function hasRuntimeExportAllInProgram(program) {
|
|
112
|
+
return program.body.some((node) => node.type === "ExportAllDeclaration" && node.exportKind !== "type");
|
|
113
|
+
}
|
|
100
114
|
function unwrapStaticExpression(expression) {
|
|
101
115
|
let current = expression;
|
|
102
116
|
while (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression") current = current.expression;
|
|
@@ -434,12 +448,19 @@ function classifyPagesRoute(filePath) {
|
|
|
434
448
|
return { type: "unknown" };
|
|
435
449
|
}
|
|
436
450
|
const program = parseRouteModule(code);
|
|
437
|
-
if (program && hasNamedExportInProgram(program, "getServerSideProps")) return {
|
|
451
|
+
if (program && hasNamedExportInProgram(program, "getServerSideProps")) return {
|
|
452
|
+
hasServerSideProps: true,
|
|
453
|
+
type: "ssr"
|
|
454
|
+
};
|
|
438
455
|
if (program && hasNamedExportInProgram(program, "getStaticProps")) {
|
|
439
456
|
const revalidate = extractGetStaticPropsRevalidateFromProgram(program, code);
|
|
440
|
-
if (revalidate === null || revalidate === false || revalidate === Infinity) return {
|
|
457
|
+
if (revalidate === null || revalidate === false || revalidate === Infinity) return {
|
|
458
|
+
hasStaticProps: true,
|
|
459
|
+
type: "static"
|
|
460
|
+
};
|
|
441
461
|
if (revalidate === 0) return { type: "ssr" };
|
|
442
462
|
return {
|
|
463
|
+
hasStaticProps: true,
|
|
443
464
|
type: "isr",
|
|
444
465
|
revalidate
|
|
445
466
|
};
|
|
@@ -480,6 +501,46 @@ function classifyAppRoute(pagePath, routePath, isDynamic) {
|
|
|
480
501
|
return { type: "unknown" };
|
|
481
502
|
}
|
|
482
503
|
/**
|
|
504
|
+
* Return whether a Route Handler has a GET contract that Next.js considers
|
|
505
|
+
* eligible for static generation. This intentionally examines only the route
|
|
506
|
+
* module's direct segment config; request-time dynamic usage is decided by the
|
|
507
|
+
* staged Worker probe after the module has executed to completion.
|
|
508
|
+
*
|
|
509
|
+
* Ported from Next.js:
|
|
510
|
+
* packages/next/src/server/route-modules/app-route/helpers/is-static-gen-enabled.ts
|
|
511
|
+
*/
|
|
512
|
+
function classifyAppRouteHandler(filePath) {
|
|
513
|
+
let code;
|
|
514
|
+
try {
|
|
515
|
+
code = fs.readFileSync(filePath, "utf8");
|
|
516
|
+
} catch {
|
|
517
|
+
return {
|
|
518
|
+
hasGet: false,
|
|
519
|
+
staticGenerationEnabled: false
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
const program = parseRouteModule(code);
|
|
523
|
+
if (!program) return {
|
|
524
|
+
hasGet: false,
|
|
525
|
+
staticGenerationEnabled: false
|
|
526
|
+
};
|
|
527
|
+
const dynamicValue = extractExportConstStringFromProgram(program, "dynamic");
|
|
528
|
+
const revalidateValue = extractExportConstNumberFromProgram(program, "revalidate");
|
|
529
|
+
const hasGet = hasRuntimeExportedNameInProgram(program, "GET");
|
|
530
|
+
const hasNonStaticMethod = [
|
|
531
|
+
"POST",
|
|
532
|
+
"PUT",
|
|
533
|
+
"DELETE",
|
|
534
|
+
"PATCH",
|
|
535
|
+
"OPTIONS"
|
|
536
|
+
].some((method) => hasRuntimeExportedNameInProgram(program, method));
|
|
537
|
+
const hasUnknownRuntimeExports = hasRuntimeExportAllInProgram(program);
|
|
538
|
+
return {
|
|
539
|
+
hasGet,
|
|
540
|
+
staticGenerationEnabled: !hasNonStaticMethod && !hasUnknownRuntimeExports && (dynamicValue === "force-static" || dynamicValue === "error" || revalidateValue === Infinity || revalidateValue !== null && revalidateValue > 0 || hasRuntimeExportedNameInProgram(program, "generateStaticParams"))
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
483
544
|
* Builds a sorted list of RouteRow objects from the discovered routes.
|
|
484
545
|
* Routes are sorted alphabetically by path, matching filesystem order.
|
|
485
546
|
*
|
|
@@ -606,4 +667,4 @@ async function printBuildReport(options) {
|
|
|
606
667
|
}
|
|
607
668
|
}
|
|
608
669
|
//#endregion
|
|
609
|
-
export { buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, extractMiddlewareMatcherConfigValue, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
|
|
670
|
+
export { buildReportRows, classifyAppRoute, classifyAppRouteHandler, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, extractMiddlewareMatcherConfigValue, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
|
|
@@ -20,7 +20,7 @@ type StaticExportOptions = {
|
|
|
20
20
|
config: ResolvedNextConfig;
|
|
21
21
|
};
|
|
22
22
|
type StaticExportResult = {
|
|
23
|
-
/** Number of
|
|
23
|
+
/** Number of rendered route entries */
|
|
24
24
|
pageCount: number;
|
|
25
25
|
/** Generated file paths (relative to outDir) */
|
|
26
26
|
files: string[];
|
|
@@ -6,6 +6,45 @@
|
|
|
6
6
|
* by hand. `options` must be JSON-serializable: it is inlined into the generated
|
|
7
7
|
* registration module and forwarded to the adapter factory at runtime.
|
|
8
8
|
*/
|
|
9
|
+
type CdnCacheAdapterCapabilities = {
|
|
10
|
+
/**
|
|
11
|
+
* Page responses include the current application build identity in the
|
|
12
|
+
* framework-owned `X-Vinext-Build-Id` response header, including responses
|
|
13
|
+
* that are ultimately marked non-cacheable.
|
|
14
|
+
*
|
|
15
|
+
* Deploy adapters use this guarantee to distinguish the newly uploaded
|
|
16
|
+
* Worker from an older version while staged traffic is propagating.
|
|
17
|
+
*/
|
|
18
|
+
buildIdentity?: "response-header";
|
|
19
|
+
/**
|
|
20
|
+
* The shared cache selects response variants using every request header
|
|
21
|
+
* named by `Vary`, comparing the header values verbatim.
|
|
22
|
+
*
|
|
23
|
+
* Vinext only uses the canonical deploy-warmed RSC request shape when this
|
|
24
|
+
* guarantee is present. URL-only caches retain the contextual `_rsc` digest.
|
|
25
|
+
*/
|
|
26
|
+
responseVary?: "verbatim";
|
|
27
|
+
/**
|
|
28
|
+
* Cacheable App Page responses require a build-bound probe manifest before
|
|
29
|
+
* the adapter may emit public CDN cache policy. Cloudflare deploy tooling
|
|
30
|
+
* carries that manifest as a module in a second Worker version.
|
|
31
|
+
*/
|
|
32
|
+
routeCacheability?: "probe-manifest";
|
|
33
|
+
};
|
|
34
|
+
type CacheAdapterBuildOutput = {
|
|
35
|
+
/** Whether this adapter owns output for the resolved build platform. */
|
|
36
|
+
matchesBuild?: (build: {
|
|
37
|
+
plugins: readonly {
|
|
38
|
+
name?: string;
|
|
39
|
+
}[];
|
|
40
|
+
}) => boolean;
|
|
41
|
+
/** Finalize an emitted directory after other platform output hooks. */
|
|
42
|
+
finalizeBuildOutput?: (output: {
|
|
43
|
+
root: string;
|
|
44
|
+
outDir: string;
|
|
45
|
+
isPrimaryServerOutput: boolean;
|
|
46
|
+
}) => Promise<void> | void;
|
|
47
|
+
};
|
|
9
48
|
type CacheAdapterDescriptor<O extends Record<string, unknown> = Record<string, unknown>> = {
|
|
10
49
|
/**
|
|
11
50
|
* Module specifier (or absolute path, e.g. from `require.resolve(...)`) whose
|
|
@@ -14,7 +53,14 @@ type CacheAdapterDescriptor<O extends Record<string, unknown> = Record<string, u
|
|
|
14
53
|
adapter: string;
|
|
15
54
|
/** JSON-serializable options forwarded to the factory at runtime. */
|
|
16
55
|
options?: O;
|
|
56
|
+
/** Optional adapter-owned finalization for platform-generated build output. */
|
|
57
|
+
output?: CacheAdapterBuildOutput;
|
|
58
|
+
/** Build-time cache semantics used by shared request protocol code. */
|
|
59
|
+
capabilities?: CdnCacheAdapterCapabilities;
|
|
17
60
|
};
|
|
61
|
+
declare function hasVerbatimResponseVary(cache?: VinextCacheConfig | null): boolean;
|
|
62
|
+
declare function hasBuildIdentityResponseHeader(cache?: VinextCacheConfig | null): boolean;
|
|
63
|
+
declare function requiresRouteCacheabilityProbeManifest(cache?: VinextCacheConfig | null): boolean;
|
|
18
64
|
/**
|
|
19
65
|
* The `cache` option of the vinext() plugin: declaratively register cache
|
|
20
66
|
* handlers instead of calling `setDataCacheHandler()` / `setCdnCacheAdapter()`
|
|
@@ -40,4 +86,4 @@ declare function loadVinextCacheConfigFromViteConfig(vite: ViteConfigLoader, roo
|
|
|
40
86
|
*/
|
|
41
87
|
declare function generateCacheAdaptersModule(cache?: VinextCacheConfig): string;
|
|
42
88
|
//#endregion
|
|
43
|
-
export { VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY, VIRTUAL_CACHE_ADAPTERS, VinextCacheConfig, findVinextCacheConfigInPlugins, generateCacheAdaptersModule, loadVinextCacheConfigFromViteConfig };
|
|
89
|
+
export { CacheAdapterBuildOutput, CacheAdapterDescriptor, CdnCacheAdapterCapabilities, VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY, VIRTUAL_CACHE_ADAPTERS, VinextCacheConfig, findVinextCacheConfigInPlugins, generateCacheAdaptersModule, hasBuildIdentityResponseHeader, hasVerbatimResponseVary, loadVinextCacheConfigFromViteConfig, requiresRouteCacheabilityProbeManifest };
|
|
@@ -16,6 +16,15 @@ import { flattenPluginOptions } from "../utils/plugin-options.js";
|
|
|
16
16
|
* never touches the Workers runtime — instantiation is deferred to the first
|
|
17
17
|
* request.
|
|
18
18
|
*/
|
|
19
|
+
function hasVerbatimResponseVary(cache) {
|
|
20
|
+
return cache?.cdn?.capabilities?.responseVary === "verbatim";
|
|
21
|
+
}
|
|
22
|
+
function hasBuildIdentityResponseHeader(cache) {
|
|
23
|
+
return cache?.cdn?.capabilities?.buildIdentity === "response-header";
|
|
24
|
+
}
|
|
25
|
+
function requiresRouteCacheabilityProbeManifest(cache) {
|
|
26
|
+
return cache?.cdn?.capabilities?.routeCacheability === "probe-manifest";
|
|
27
|
+
}
|
|
19
28
|
/** Public virtual module id imported by the server entries. */
|
|
20
29
|
const VIRTUAL_CACHE_ADAPTERS = "virtual:vinext-cache-adapters";
|
|
21
30
|
const VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY = "__vinextCacheConfig";
|
|
@@ -75,4 +84,4 @@ function generateCacheAdaptersModule(cache) {
|
|
|
75
84
|
return lines.join("\n");
|
|
76
85
|
}
|
|
77
86
|
//#endregion
|
|
78
|
-
export { VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY, VIRTUAL_CACHE_ADAPTERS, findVinextCacheConfigInPlugins, generateCacheAdaptersModule, loadVinextCacheConfigFromViteConfig };
|
|
87
|
+
export { VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY, VIRTUAL_CACHE_ADAPTERS, findVinextCacheConfigInPlugins, generateCacheAdaptersModule, hasBuildIdentityResponseHeader, hasVerbatimResponseVary, loadVinextCacheConfigFromViteConfig, requiresRouteCacheabilityProbeManifest };
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import { detectPackageManager, ensureViteConfigCompatibility, hasAppDir, hasVite
|
|
|
4
4
|
import { formatReport, runCheck } from "./check.js";
|
|
5
5
|
import { parseArgs } from "./cli-args.js";
|
|
6
6
|
import { createDevServerConfigPlugin, normalizeDevServerHostname } from "./cli-dev-config.js";
|
|
7
|
+
import { findVinextCacheConfigInPlugins, hasBuildIdentityResponseHeader, hasVerbatimResponseVary } from "./cache/cache-adapters-virtual.js";
|
|
7
8
|
import { PHASE_PRODUCTION_BUILD } from "./shims/constants.js";
|
|
8
9
|
import { createRscCompatibilityId, findVinextNextConfigInPlugins, loadNextConfig, resolveNextConfig, resolveNextConfigInput } from "./config/next-config.js";
|
|
9
10
|
import { loadDotenv } from "./config/dotenv.js";
|
|
@@ -127,6 +128,7 @@ function hasPagesDir() {
|
|
|
127
128
|
}
|
|
128
129
|
async function loadBuildViteConfigMetadata(vite, root, mode) {
|
|
129
130
|
if (!hasViteConfig(root)) return {
|
|
131
|
+
cacheConfig: null,
|
|
130
132
|
nextConfig: null,
|
|
131
133
|
prerenderConfig: null,
|
|
132
134
|
routeRootConfig: null
|
|
@@ -137,6 +139,7 @@ async function loadBuildViteConfigMetadata(vite, root, mode) {
|
|
|
137
139
|
}, void 0, root);
|
|
138
140
|
const emptyOutDir = loaded?.config.build?.emptyOutDir;
|
|
139
141
|
return {
|
|
142
|
+
cacheConfig: await findVinextCacheConfigInPlugins(loaded?.config.plugins),
|
|
140
143
|
emptyOutDir: typeof emptyOutDir === "boolean" ? emptyOutDir : void 0,
|
|
141
144
|
nextConfig: await findVinextNextConfigInPlugins(loaded?.config.plugins),
|
|
142
145
|
prerenderConfig: await findVinextPrerenderConfigInPlugins(loaded?.config.plugins),
|
|
@@ -293,7 +296,9 @@ async function buildApp() {
|
|
|
293
296
|
const resolvedNextConfig = await resolveNextConfig(buildConfigMetadata.nextConfig ? await resolveNextConfigInput(buildConfigMetadata.nextConfig, PHASE_PRODUCTION_BUILD) : await loadNextConfig(root, PHASE_PRODUCTION_BUILD), root);
|
|
294
297
|
process.env.__VINEXT_SHARED_BUILD_ID = resolvedNextConfig.buildId;
|
|
295
298
|
process.env.__VINEXT_SHARED_RSC_COMPATIBILITY_ID = createRscCompatibilityId(resolvedNextConfig);
|
|
299
|
+
process.env.__VINEXT_SHARED_RSC_BUILD_IDENTITY = randomBytes(16).toString("hex");
|
|
296
300
|
if (!process.env.__VINEXT_SHARED_REVALIDATE_SECRET) process.env.__VINEXT_SHARED_REVALIDATE_SECRET = randomBytes(32).toString("hex");
|
|
301
|
+
if (!process.env.__VINEXT_SHARED_PRERENDER_SECRET) process.env.__VINEXT_SHARED_PRERENDER_SECRET = randomBytes(32).toString("hex");
|
|
297
302
|
const outputMode = resolvedNextConfig.output;
|
|
298
303
|
const distDir = path.resolve(root, "dist");
|
|
299
304
|
if (outputMode === "standalone") {
|
|
@@ -397,6 +402,8 @@ async function buildApp() {
|
|
|
397
402
|
await emitPrerenderPathManifest({
|
|
398
403
|
root,
|
|
399
404
|
nextConfig: resolvedNextConfig,
|
|
405
|
+
buildIdentity: hasBuildIdentityResponseHeader(buildConfigMetadata.cacheConfig) ? "response-header" : void 0,
|
|
406
|
+
responseVary: hasVerbatimResponseVary(buildConfigMetadata.cacheConfig) ? "verbatim" : void 0,
|
|
400
407
|
routeRootConfig: buildConfigMetadata.routeRootConfig
|
|
401
408
|
});
|
|
402
409
|
}
|
|
@@ -2,6 +2,8 @@ import { NEXT_DATA } from "../shims/internal/utils.js";
|
|
|
2
2
|
//#region src/client/vinext-next-data.d.ts
|
|
3
3
|
type VinextLinkPrefetchRoute = {
|
|
4
4
|
canPrefetchLoadingShell: boolean;
|
|
5
|
+
/** The loading shell is an ordinary main-tree variant shared with deploy warmup. */
|
|
6
|
+
canUseCanonicalLoadingShell?: true;
|
|
5
7
|
documentOnly?: boolean;
|
|
6
8
|
isDynamic: boolean;
|
|
7
9
|
patternParts: string[];
|
|
@@ -110,7 +110,7 @@ declare function matchConfigPattern(pathname: string, pattern: string): Record<s
|
|
|
110
110
|
* an original index < N are checked via matchConfigPattern first — they are
|
|
111
111
|
* few in practice (typically zero) so this is not a hot-path concern.
|
|
112
112
|
*/
|
|
113
|
-
declare function matchRedirect(pathname: string, redirects: NextRedirect[], ctx: RequestContext, basePathState?: BasePathMatchState): {
|
|
113
|
+
declare function matchRedirect(pathname: string, redirects: NextRedirect[], ctx: RequestContext, basePathState?: BasePathMatchState, onRuleSourceMatch?: (rule: NextRedirect) => void): {
|
|
114
114
|
destination: string;
|
|
115
115
|
permanent: boolean;
|
|
116
116
|
} | null;
|
|
@@ -122,7 +122,7 @@ declare function matchRedirect(pathname: string, redirects: NextRedirect[], ctx:
|
|
|
122
122
|
* to evaluate has/missing conditions. Next.js always has request context
|
|
123
123
|
* when evaluating rewrites, so this parameter is required.
|
|
124
124
|
*/
|
|
125
|
-
declare function matchRewrite(pathname: string, rewrites: NextRewrite[], ctx: RequestContext, basePathState?: BasePathMatchState, paramsPathname?: string): string | null;
|
|
125
|
+
declare function matchRewrite(pathname: string, rewrites: NextRewrite[], ctx: RequestContext, basePathState?: BasePathMatchState, paramsPathname?: string, onRuleSourceMatch?: (rule: NextRewrite) => void): string | null;
|
|
126
126
|
/**
|
|
127
127
|
* Check whether a rewrite source can match a pathname without evaluating its
|
|
128
128
|
* request-dependent `has` / `missing` conditions.
|
|
@@ -187,7 +187,7 @@ declare function proxyExternalRequest(request: Request, externalUrl: string): Pr
|
|
|
187
187
|
* to evaluate has/missing conditions. Next.js always has request context
|
|
188
188
|
* when evaluating headers, so this parameter is required.
|
|
189
189
|
*/
|
|
190
|
-
declare function matchHeaders(pathname: string, headers: NextHeader[], ctx: RequestContext, basePathState?: BasePathMatchState): Array<{
|
|
190
|
+
declare function matchHeaders(pathname: string, headers: NextHeader[], ctx: RequestContext, basePathState?: BasePathMatchState, onRuleSourceMatch?: (rule: NextHeader) => void): Array<{
|
|
191
191
|
key: string;
|
|
192
192
|
value: string;
|
|
193
193
|
}>;
|
|
@@ -546,7 +546,7 @@ function matchConfigPattern(pathname, pattern) {
|
|
|
546
546
|
* an original index < N are checked via matchConfigPattern first — they are
|
|
547
547
|
* few in practice (typically zero) so this is not a hot-path concern.
|
|
548
548
|
*/
|
|
549
|
-
function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAULT) {
|
|
549
|
+
function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAULT, onRuleSourceMatch) {
|
|
550
550
|
if (redirects.length === 0) return null;
|
|
551
551
|
const normalizedPathname = stripTrailingSlashForConfigMatch(pathname);
|
|
552
552
|
const index = _getRedirectIndex(redirects);
|
|
@@ -559,6 +559,7 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
|
|
|
559
559
|
if (entry.originalIndex >= localeMatchIndex) continue;
|
|
560
560
|
const redirect = entry.redirect;
|
|
561
561
|
if (!shouldEvaluateRule(redirect.basePath, basePathState)) continue;
|
|
562
|
+
onRuleSourceMatch?.(redirect);
|
|
562
563
|
const conditionParams = redirect.has || redirect.missing ? collectConditionParams(redirect.has, redirect.missing, ctx) : _emptyParams();
|
|
563
564
|
if (!conditionParams) continue;
|
|
564
565
|
localeMatch = {
|
|
@@ -581,6 +582,7 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
|
|
|
581
582
|
if (!entry.altRe.test(localePart)) continue;
|
|
582
583
|
const redirect = entry.redirect;
|
|
583
584
|
if (!shouldEvaluateRule(redirect.basePath, basePathState)) continue;
|
|
585
|
+
onRuleSourceMatch?.(redirect);
|
|
584
586
|
const conditionParams = redirect.has || redirect.missing ? collectConditionParams(redirect.has, redirect.missing, ctx) : _emptyParams();
|
|
585
587
|
if (!conditionParams) continue;
|
|
586
588
|
localeMatch = {
|
|
@@ -600,6 +602,7 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
|
|
|
600
602
|
if (!shouldEvaluateRule(redirect.basePath, basePathState)) continue;
|
|
601
603
|
const params = matchConfigPattern(pathname, redirect.source);
|
|
602
604
|
if (params) {
|
|
605
|
+
onRuleSourceMatch?.(redirect);
|
|
603
606
|
const conditionParams = redirect.has || redirect.missing ? collectConditionParams(redirect.has, redirect.missing, ctx) : _emptyParams();
|
|
604
607
|
if (!conditionParams) continue;
|
|
605
608
|
return {
|
|
@@ -621,11 +624,12 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
|
|
|
621
624
|
* to evaluate has/missing conditions. Next.js always has request context
|
|
622
625
|
* when evaluating rewrites, so this parameter is required.
|
|
623
626
|
*/
|
|
624
|
-
function matchRewrite(pathname, rewrites, ctx, basePathState = _BASEPATH_DEFAULT, paramsPathname = pathname) {
|
|
627
|
+
function matchRewrite(pathname, rewrites, ctx, basePathState = _BASEPATH_DEFAULT, paramsPathname = pathname, onRuleSourceMatch) {
|
|
625
628
|
for (const rewrite of rewrites) {
|
|
626
629
|
if (!shouldEvaluateRule(rewrite.basePath, basePathState)) continue;
|
|
627
630
|
const matchedParams = matchConfigPattern(pathname, rewrite.source);
|
|
628
631
|
if (matchedParams) {
|
|
632
|
+
onRuleSourceMatch?.(rewrite);
|
|
629
633
|
const params = paramsPathname === pathname ? matchedParams : matchConfigPattern(paramsPathname, rewrite.source) ?? matchedParams;
|
|
630
634
|
const conditionParams = rewrite.has || rewrite.missing ? collectConditionParams(rewrite.has, rewrite.missing, ctx) : _emptyParams();
|
|
631
635
|
if (!conditionParams) continue;
|
|
@@ -891,7 +895,7 @@ async function proxyExternalRequest(request, externalUrl) {
|
|
|
891
895
|
* to evaluate has/missing conditions. Next.js always has request context
|
|
892
896
|
* when evaluating headers, so this parameter is required.
|
|
893
897
|
*/
|
|
894
|
-
function matchHeaders(pathname, headers, ctx, basePathState = _BASEPATH_DEFAULT) {
|
|
898
|
+
function matchHeaders(pathname, headers, ctx, basePathState = _BASEPATH_DEFAULT, onRuleSourceMatch) {
|
|
895
899
|
const pathnameHadTrailingSlash = pathname.length > 1 && pathname.endsWith("/");
|
|
896
900
|
pathname = stripTrailingSlashForConfigMatch(pathname);
|
|
897
901
|
const result = [];
|
|
@@ -900,6 +904,7 @@ function matchHeaders(pathname, headers, ctx, basePathState = _BASEPATH_DEFAULT)
|
|
|
900
904
|
const source = pathnameHadTrailingSlash ? stripTrailingSlashForConfigMatch(rule.source) : rule.source;
|
|
901
905
|
const sourceRegex = getCachedRegex(_compiledHeaderSourceCache, source, () => safeRegExp("^" + escapeHeaderSource(source) + "$", "i"));
|
|
902
906
|
if (sourceRegex && sourceRegex.test(pathname)) {
|
|
907
|
+
onRuleSourceMatch?.(rule);
|
|
903
908
|
if (rule.has || rule.missing) {
|
|
904
909
|
if (!checkHasConditions(rule.has, rule.missing, ctx)) continue;
|
|
905
910
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY, VinextCacheConfig, findVinextCacheConfigInPlugins, loadVinextCacheConfigFromViteConfig } from "../cache/cache-adapters-virtual.js";
|
|
1
|
+
import { VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY, VinextCacheConfig, findVinextCacheConfigInPlugins, hasBuildIdentityResponseHeader, hasVerbatimResponseVary, loadVinextCacheConfigFromViteConfig, requiresRouteCacheabilityProbeManifest } from "../cache/cache-adapters-virtual.js";
|
|
2
2
|
import { PluginOption } from "vite";
|
|
3
3
|
//#region src/config/prerender.d.ts
|
|
4
4
|
type VinextPrerenderConfig = true | {
|
|
@@ -40,4 +40,4 @@ declare function resolveVinextPrerenderDecision(options: {
|
|
|
40
40
|
}): VinextPrerenderDecision | null;
|
|
41
41
|
declare function formatVinextPrerenderLabel(decision: VinextPrerenderDecision): string;
|
|
42
42
|
//#endregion
|
|
43
|
-
export { ResolvedVinextPrerenderConfig, VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY, VINEXT_PRERENDER_CONFIG_PLUGIN_PROPERTY, VINEXT_ROUTE_ROOT_CONFIG_PLUGIN_PROPERTY, type VinextCacheConfig, VinextPrerenderConfig, VinextPrerenderDecision, VinextPrerenderDecisionReason, VinextRouteRootConfig, findVinextCacheConfigInPlugins, findVinextPrerenderConfigInPlugins, findVinextRouteRootConfigInPlugins, formatVinextPrerenderLabel, loadVinextCacheConfigFromViteConfig, loadVinextPrerenderConfigFromViteConfig, loadVinextRouteRootConfigFromViteConfig, normalizeVinextPrerenderConfig, resolveVinextPrerenderDecision };
|
|
43
|
+
export { ResolvedVinextPrerenderConfig, VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY, VINEXT_PRERENDER_CONFIG_PLUGIN_PROPERTY, VINEXT_ROUTE_ROOT_CONFIG_PLUGIN_PROPERTY, type VinextCacheConfig, VinextPrerenderConfig, VinextPrerenderDecision, VinextPrerenderDecisionReason, VinextRouteRootConfig, findVinextCacheConfigInPlugins, findVinextPrerenderConfigInPlugins, findVinextRouteRootConfigInPlugins, formatVinextPrerenderLabel, hasBuildIdentityResponseHeader, hasVerbatimResponseVary, loadVinextCacheConfigFromViteConfig, loadVinextPrerenderConfigFromViteConfig, loadVinextRouteRootConfigFromViteConfig, normalizeVinextPrerenderConfig, requiresRouteCacheabilityProbeManifest, resolveVinextPrerenderDecision };
|
package/dist/config/prerender.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isUnknownRecord } from "../utils/record.js";
|
|
2
2
|
import { flattenPluginOptions } from "../utils/plugin-options.js";
|
|
3
|
-
import { VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY, findVinextCacheConfigInPlugins, loadVinextCacheConfigFromViteConfig } from "../cache/cache-adapters-virtual.js";
|
|
3
|
+
import { VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY, findVinextCacheConfigInPlugins, hasBuildIdentityResponseHeader, hasVerbatimResponseVary, loadVinextCacheConfigFromViteConfig, requiresRouteCacheabilityProbeManifest } from "../cache/cache-adapters-virtual.js";
|
|
4
4
|
//#region src/config/prerender.ts
|
|
5
5
|
const VINEXT_PRERENDER_CONFIG_PLUGIN_PROPERTY = "__vinextPrerenderConfig";
|
|
6
6
|
const VINEXT_ROUTE_ROOT_CONFIG_PLUGIN_PROPERTY = "__vinextRouteRootConfig";
|
|
@@ -62,4 +62,4 @@ function formatVinextPrerenderLabel(decision) {
|
|
|
62
62
|
return "Pre-rendering all routes...";
|
|
63
63
|
}
|
|
64
64
|
//#endregion
|
|
65
|
-
export { VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY, VINEXT_PRERENDER_CONFIG_PLUGIN_PROPERTY, VINEXT_ROUTE_ROOT_CONFIG_PLUGIN_PROPERTY, findVinextCacheConfigInPlugins, findVinextPrerenderConfigInPlugins, findVinextRouteRootConfigInPlugins, formatVinextPrerenderLabel, loadVinextCacheConfigFromViteConfig, loadVinextPrerenderConfigFromViteConfig, loadVinextRouteRootConfigFromViteConfig, normalizeVinextPrerenderConfig, resolveVinextPrerenderDecision };
|
|
65
|
+
export { VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY, VINEXT_PRERENDER_CONFIG_PLUGIN_PROPERTY, VINEXT_ROUTE_ROOT_CONFIG_PLUGIN_PROPERTY, findVinextCacheConfigInPlugins, findVinextPrerenderConfigInPlugins, findVinextRouteRootConfigInPlugins, formatVinextPrerenderLabel, hasBuildIdentityResponseHeader, hasVerbatimResponseVary, loadVinextCacheConfigFromViteConfig, loadVinextPrerenderConfigFromViteConfig, loadVinextRouteRootConfigFromViteConfig, normalizeVinextPrerenderConfig, requiresRouteCacheabilityProbeManifest, resolveVinextPrerenderDecision };
|
|
@@ -13,7 +13,7 @@ declare function generateBrowserEntry(routes?: readonly AppRoute[], routeManifes
|
|
|
13
13
|
afterFiles: NextRewrite[];
|
|
14
14
|
beforeFiles: NextRewrite[];
|
|
15
15
|
fallback: NextRewrite[];
|
|
16
|
-
}): string;
|
|
16
|
+
}, locales?: readonly string[]): string;
|
|
17
17
|
/** Project an `AppRoute` down to the public `VinextLinkPrefetchRoute` shape. */
|
|
18
18
|
declare function toLinkPrefetchRoute(route: AppRoute, hasSiblingInterceptLoading?: boolean): VinextLinkPrefetchRoute;
|
|
19
19
|
/** Project App routes together so sibling-intercept loading is applied to its target route. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolveClientRuntimeModule, resolveRuntimeEntryModule } from "./runtime-entry-module.js";
|
|
2
2
|
import { toClientRewrites } from "../client/client-rewrites.js";
|
|
3
3
|
import { patternsStructurallyEquivalent } from "../routing/app-route-graph.js";
|
|
4
|
+
import { appRouteHasMainTreeLoadingBoundary } from "../routing/app-router.js";
|
|
4
5
|
//#region src/entries/app-browser-entry.ts
|
|
5
6
|
/**
|
|
6
7
|
* Generate the virtual browser entry module.
|
|
@@ -13,7 +14,7 @@ function generateBrowserEntry(routes = [], routeManifest = null, pagesPrefetchRo
|
|
|
13
14
|
afterFiles: [],
|
|
14
15
|
beforeFiles: [],
|
|
15
16
|
fallback: []
|
|
16
|
-
}) {
|
|
17
|
+
}, locales = []) {
|
|
17
18
|
const entryPath = resolveRuntimeEntryModule("app-browser-entry");
|
|
18
19
|
const reactInstanceBootstrapPath = resolveClientRuntimeModule("react-instance-bootstrap");
|
|
19
20
|
const navigationRuntimePath = resolveClientRuntimeModule("navigation-runtime");
|
|
@@ -29,6 +30,7 @@ window.__VINEXT_LINK_PREFETCH_ROUTES__ = ${JSON.stringify(prefetchRoutes)};
|
|
|
29
30
|
// the same — whichever entry runs first emits both globals).
|
|
30
31
|
window.__VINEXT_PAGES_LINK_PREFETCH_ROUTES__ = ${JSON.stringify(pagesPrefetchRoutes)};
|
|
31
32
|
window.__VINEXT_CLIENT_REWRITES__ = ${JSON.stringify(clientRewrites)};
|
|
33
|
+
window.__VINEXT_LOCALES__ = ${JSON.stringify(locales)};
|
|
32
34
|
registerNavigationRuntimeBootstrap({
|
|
33
35
|
routeManifest: ${buildRouteManifestExpression(routeManifest)}
|
|
34
36
|
});
|
|
@@ -68,6 +70,7 @@ function hasLoadingBoundary(route, hasSiblingInterceptLoading) {
|
|
|
68
70
|
function toLinkPrefetchRoute(route, hasSiblingInterceptLoading = route.siblingIntercepts.some((intercept) => interceptTargetsRoute(intercept.targetPattern, route) && (intercept.loadingPaths?.length ?? 0) > 0)) {
|
|
69
71
|
return {
|
|
70
72
|
canPrefetchLoadingShell: hasLoadingBoundary(route, hasSiblingInterceptLoading),
|
|
73
|
+
...appRouteHasMainTreeLoadingBoundary(route) ? { canUseCanonicalLoadingShell: true } : {},
|
|
71
74
|
patternParts: [...route.patternParts],
|
|
72
75
|
isDynamic: route.isDynamic,
|
|
73
76
|
...requiresDynamicNavigationRequest(route) ? { requiresDynamicNavigationRequest: true } : {},
|