vinext 0.1.1 → 0.1.2
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 +2 -5
- package/dist/build/client-build-config.d.ts +7 -1
- package/dist/build/client-build-config.js +9 -1
- package/dist/check.js +4 -3
- package/dist/client/navigation-runtime.d.ts +3 -2
- package/dist/client/window-next.d.ts +6 -4
- package/dist/config/config-matchers.d.ts +11 -4
- package/dist/config/config-matchers.js +15 -2
- package/dist/config/next-config.d.ts +13 -0
- package/dist/config/next-config.js +2 -0
- package/dist/deploy.js +9 -2
- package/dist/entries/app-rsc-entry.js +7 -1
- package/dist/entries/pages-client-entry.js +1 -1
- package/dist/entries/pages-server-entry.js +7 -6
- package/dist/index.d.ts +0 -2
- package/dist/index.js +86 -78
- package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
- package/dist/plugins/dynamic-preload-metadata.js +415 -0
- package/dist/plugins/og-assets.js +2 -2
- package/dist/plugins/optimize-imports.d.ts +8 -4
- package/dist/plugins/optimize-imports.js +16 -12
- package/dist/plugins/sass.d.ts +53 -24
- package/dist/plugins/sass.js +249 -1
- package/dist/plugins/wasm-module-import.d.ts +15 -0
- package/dist/plugins/wasm-module-import.js +50 -0
- package/dist/routing/app-route-graph.d.ts +23 -1
- package/dist/routing/app-route-graph.js +47 -8
- package/dist/routing/file-matcher.js +1 -1
- package/dist/server/app-browser-entry.js +108 -213
- package/dist/server/app-browser-error.d.ts +4 -1
- package/dist/server/app-browser-error.js +7 -1
- package/dist/server/app-browser-history-controller.d.ts +104 -0
- package/dist/server/app-browser-history-controller.js +210 -0
- package/dist/server/app-browser-navigation-controller.d.ts +3 -2
- package/dist/server/app-browser-navigation-controller.js +10 -7
- package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
- package/dist/server/app-browser-rsc-redirect.js +30 -8
- package/dist/server/app-browser-state.js +4 -7
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-fallback-renderer.d.ts +2 -1
- package/dist/server/app-fallback-renderer.js +3 -1
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.js +22 -1
- package/dist/server/app-page-boundary-render.d.ts +2 -1
- package/dist/server/app-page-boundary-render.js +4 -2
- package/dist/server/app-page-cache.js +9 -7
- package/dist/server/app-page-dispatch.d.ts +8 -0
- package/dist/server/app-page-dispatch.js +18 -5
- package/dist/server/app-page-element-builder.d.ts +22 -2
- package/dist/server/app-page-element-builder.js +37 -8
- package/dist/server/app-page-execution.d.ts +1 -1
- package/dist/server/app-page-execution.js +32 -17
- package/dist/server/app-page-render.d.ts +1 -1
- package/dist/server/app-page-render.js +7 -14
- package/dist/server/app-page-request.d.ts +1 -0
- package/dist/server/app-page-request.js +3 -2
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.d.ts +3 -1
- package/dist/server/app-page-route-wiring.js +8 -7
- package/dist/server/app-page-stream.d.ts +1 -6
- package/dist/server/app-page-stream.js +1 -4
- package/dist/server/app-route-handler-response.js +11 -10
- package/dist/server/app-route-handler-runtime.js +12 -1
- package/dist/server/app-rsc-handler.js +1 -1
- package/dist/server/app-rsc-response-finalizer.js +1 -1
- package/dist/server/app-server-action-execution.d.ts +11 -0
- package/dist/server/app-server-action-execution.js +5 -2
- package/dist/server/app-ssr-entry.js +2 -2
- package/dist/server/app-ssr-stream.js +9 -1
- package/dist/server/dev-lockfile.js +2 -1
- package/dist/server/dev-server.js +43 -12
- package/dist/server/headers.d.ts +8 -1
- package/dist/server/headers.js +8 -1
- package/dist/server/instrumentation-runtime.d.ts +6 -0
- package/dist/server/instrumentation-runtime.js +8 -0
- package/dist/server/isr-decision.d.ts +79 -0
- package/dist/server/isr-decision.js +70 -0
- package/dist/server/metadata-route-response.js +5 -3
- package/dist/server/middleware-runtime.d.ts +13 -0
- package/dist/server/middleware-runtime.js +11 -7
- package/dist/server/middleware.js +1 -0
- package/dist/server/navigation-planner.d.ts +62 -1
- package/dist/server/navigation-planner.js +188 -0
- package/dist/server/navigation-trace.d.ts +11 -1
- package/dist/server/navigation-trace.js +11 -1
- package/dist/server/normalize-path.d.ts +0 -8
- package/dist/server/normalize-path.js +3 -1
- package/dist/server/otel-tracer-extension.d.ts +45 -0
- package/dist/server/otel-tracer-extension.js +89 -0
- package/dist/server/pages-api-route.d.ts +14 -3
- package/dist/server/pages-api-route.js +6 -1
- package/dist/server/pages-asset-tags.d.ts +15 -4
- package/dist/server/pages-asset-tags.js +18 -12
- package/dist/server/pages-data-route.js +5 -1
- package/dist/server/pages-node-compat.d.ts +3 -11
- package/dist/server/pages-node-compat.js +174 -121
- package/dist/server/pages-page-data.d.ts +28 -0
- package/dist/server/pages-page-data.js +61 -17
- package/dist/server/pages-page-handler.d.ts +1 -0
- package/dist/server/pages-page-handler.js +22 -6
- package/dist/server/pages-page-response.d.ts +45 -1
- package/dist/server/pages-page-response.js +66 -5
- package/dist/server/pages-readiness.d.ts +1 -1
- package/dist/server/pages-request-pipeline.d.ts +15 -1
- package/dist/server/pages-request-pipeline.js +23 -2
- package/dist/server/prod-server.d.ts +39 -1
- package/dist/server/prod-server.js +98 -34
- package/dist/shims/cache-runtime.js +9 -2
- package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
- package/dist/shims/dynamic-preload-chunks.js +77 -0
- package/dist/shims/dynamic.d.ts +4 -0
- package/dist/shims/dynamic.js +4 -2
- package/dist/shims/error-boundary.d.ts +4 -4
- package/dist/shims/error.js +37 -11
- package/dist/shims/fetch-cache.d.ts +9 -1
- package/dist/shims/fetch-cache.js +11 -1
- package/dist/shims/head.js +6 -1
- package/dist/shims/headers.d.ts +16 -2
- package/dist/shims/headers.js +37 -1
- package/dist/shims/image-config.js +7 -1
- package/dist/shims/internal/app-route-detection.d.ts +6 -3
- package/dist/shims/internal/app-route-detection.js +10 -6
- package/dist/shims/internal/app-router-context.d.ts +5 -0
- package/dist/shims/metadata.d.ts +6 -2
- package/dist/shims/metadata.js +32 -14
- package/dist/shims/navigation.d.ts +7 -16
- package/dist/shims/navigation.js +33 -16
- package/dist/shims/router.js +28 -1
- package/dist/shims/script-nonce-context.d.ts +1 -1
- package/dist/shims/script-nonce-context.js +11 -3
- package/dist/shims/server.d.ts +17 -1
- package/dist/shims/server.js +31 -6
- package/dist/shims/slot.js +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -0
- package/dist/utils/client-build-manifest.js +15 -5
- package/dist/utils/client-runtime-metadata.d.ts +45 -0
- package/dist/utils/client-runtime-metadata.js +63 -0
- package/dist/utils/hash.d.ts +17 -1
- package/dist/utils/hash.js +36 -1
- package/dist/utils/lazy-chunks.d.ts +27 -1
- package/dist/utils/lazy-chunks.js +65 -1
- package/dist/utils/manifest-paths.d.ts +20 -2
- package/dist/utils/manifest-paths.js +38 -3
- package/dist/utils/path.d.ts +2 -1
- package/dist/utils/path.js +5 -1
- package/package.json +2 -2
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
|
|
2
2
|
import { patternToNextFormat } from "../routing/route-validation.js";
|
|
3
3
|
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
4
|
+
import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
|
|
4
5
|
import { reportRequestError } from "./instrumentation.js";
|
|
6
|
+
import { NEVER_CACHE_CONTROL } from "./cache-control.js";
|
|
7
|
+
import "./isr-decision.js";
|
|
5
8
|
import { PRERENDER_REVALIDATE_HEADER, isOnDemandRevalidateRequest, isrCacheKey, isrGet, isrSet, triggerBackgroundRegeneration } from "./isr-cache.js";
|
|
6
9
|
import { ensureFetchPatch } from "../shims/fetch-cache.js";
|
|
7
10
|
import { mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
|
|
@@ -140,8 +143,8 @@ function createPagesPageHandler(opts) {
|
|
|
140
143
|
});
|
|
141
144
|
if (methodResponse) return methodResponse;
|
|
142
145
|
}
|
|
143
|
-
const pageModuleUrl = resolveClientModuleUrl(manifest, route.filePath);
|
|
144
|
-
const appModuleUrl = resolveClientModuleUrl(manifest, appAssetPath);
|
|
146
|
+
const pageModuleUrl = resolveClientModuleUrl(manifest, route.filePath, vinextConfig.basePath, vinextConfig.assetPrefix);
|
|
147
|
+
const appModuleUrl = resolveClientModuleUrl(manifest, appAssetPath, vinextConfig.basePath, vinextConfig.assetPrefix);
|
|
145
148
|
const serializedPagesNextData = {
|
|
146
149
|
...pagesNextData,
|
|
147
150
|
__vinext: {
|
|
@@ -163,6 +166,7 @@ function createPagesPageHandler(opts) {
|
|
|
163
166
|
err,
|
|
164
167
|
applyRequestContexts: applySSRContext,
|
|
165
168
|
buildId,
|
|
169
|
+
deploymentId: process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID,
|
|
166
170
|
createGsspReqRes() {
|
|
167
171
|
return createPagesReqRes({
|
|
168
172
|
body: void 0,
|
|
@@ -203,7 +207,10 @@ function createPagesPageHandler(opts) {
|
|
|
203
207
|
statusCode: renderStatusCode,
|
|
204
208
|
triggerBackgroundRegeneration,
|
|
205
209
|
vinext: serializedPagesNextData.__vinext,
|
|
206
|
-
nextData: serializedPagesNextData
|
|
210
|
+
nextData: serializedPagesNextData,
|
|
211
|
+
userAgent: request.headers.get("user-agent") ?? void 0,
|
|
212
|
+
ifNoneMatch: request.headers.get("if-none-match") ?? void 0,
|
|
213
|
+
requestCacheControl: request.headers.get("cache-control") ?? void 0
|
|
207
214
|
});
|
|
208
215
|
if (pageDataResult.kind === "notFound") {
|
|
209
216
|
const notFoundRoute = findNotFoundRoute();
|
|
@@ -251,7 +258,11 @@ function createPagesPageHandler(opts) {
|
|
|
251
258
|
hasUserCacheControl = true;
|
|
252
259
|
break;
|
|
253
260
|
}
|
|
254
|
-
if (!hasUserCacheControl) init.headers["Cache-Control"] =
|
|
261
|
+
if (!hasUserCacheControl) init.headers["Cache-Control"] = NEVER_CACHE_CONTROL;
|
|
262
|
+
}
|
|
263
|
+
if (routePattern !== "/_error" && routePattern !== "/500") {
|
|
264
|
+
const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
|
|
265
|
+
if (deploymentId) init.headers[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
|
|
255
266
|
}
|
|
256
267
|
return buildNextDataJsonResponse(pageProps, safeJsonStringify, init);
|
|
257
268
|
}
|
|
@@ -263,7 +274,9 @@ function createPagesPageHandler(opts) {
|
|
|
263
274
|
manifest,
|
|
264
275
|
moduleIds: pageModuleIds,
|
|
265
276
|
scriptNonce,
|
|
266
|
-
disableOptimizedLoading: vinextConfig.disableOptimizedLoading
|
|
277
|
+
disableOptimizedLoading: vinextConfig.disableOptimizedLoading,
|
|
278
|
+
basePath: vinextConfig.basePath,
|
|
279
|
+
assetPrefix: vinextConfig.assetPrefix
|
|
267
280
|
}),
|
|
268
281
|
buildId,
|
|
269
282
|
clearSsrContext() {
|
|
@@ -305,7 +318,10 @@ function createPagesPageHandler(opts) {
|
|
|
305
318
|
safeJsonStringify,
|
|
306
319
|
scriptNonce,
|
|
307
320
|
statusCode: renderStatusCode,
|
|
308
|
-
nextData: serializedPagesNextData
|
|
321
|
+
nextData: serializedPagesNextData,
|
|
322
|
+
userAgent: request.headers.get("user-agent") ?? void 0,
|
|
323
|
+
ifNoneMatch: request.headers.get("if-none-match") ?? void 0,
|
|
324
|
+
requestCacheControl: request.headers.get("cache-control") ?? void 0
|
|
309
325
|
});
|
|
310
326
|
} catch (e) {
|
|
311
327
|
console.error("[vinext] SSR error:", e);
|
|
@@ -4,6 +4,28 @@ import { RenderPageEnhancers } from "./pages-document-initial-props.js";
|
|
|
4
4
|
import { ComponentType, ReactNode } from "react";
|
|
5
5
|
|
|
6
6
|
//#region src/server/pages-page-response.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Returns true when the User-Agent belongs to a bot or crawler that cannot
|
|
9
|
+
* reliably consume a streamed HTML response.
|
|
10
|
+
*/
|
|
11
|
+
declare function isPagesStreamingBot(userAgent: string): boolean;
|
|
12
|
+
declare function generatePagesETag(payload: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Mirrors Next.js `sendEtagResponse` semantics (weak/strong comparison).
|
|
15
|
+
*
|
|
16
|
+
* A weak ETag `W/"..."` matches both `W/"..."` and `"..."` in `If-None-Match`.
|
|
17
|
+
* A strong ETag `"..."` only matches the same strong token.
|
|
18
|
+
* `*` always matches.
|
|
19
|
+
*/
|
|
20
|
+
declare function etagMatches(etag: string, ifNoneMatch: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Returns true when a request `Cache-Control` header asks to bypass the 304
|
|
23
|
+
* short-circuit. Mirrors the `fresh` package's check used by Next.js's
|
|
24
|
+
* `sendEtagResponse` (`/(?:^|,)\s*?no-cache\s*?(?:,|$)/`). Shared by the
|
|
25
|
+
* fresh-MISS bot path here and the ISR HIT/STALE paths in
|
|
26
|
+
* `pages-page-data.ts` so the two cannot drift.
|
|
27
|
+
*/
|
|
28
|
+
declare function requestsNoCache(cacheControl: string | undefined): boolean;
|
|
7
29
|
type PagesFontPreload = {
|
|
8
30
|
href: string;
|
|
9
31
|
type: string;
|
|
@@ -82,10 +104,32 @@ type RenderPagesPageResponseOptions = {
|
|
|
82
104
|
statusCode?: number;
|
|
83
105
|
vinext?: VinextNextData["__vinext"];
|
|
84
106
|
nextData?: PagesNextDataExtras;
|
|
107
|
+
/**
|
|
108
|
+
* The request's User-Agent string (from `request.headers.get('user-agent')`).
|
|
109
|
+
* When this matches a known crawler / bot pattern, the response is fully
|
|
110
|
+
* buffered before sending so bots receive a single complete HTML chunk with
|
|
111
|
+
* an ETag header. Omitting this field disables bot-detection (streaming as
|
|
112
|
+
* normal), which is the correct behaviour for non-HTML requests and tests.
|
|
113
|
+
*/
|
|
114
|
+
userAgent?: string;
|
|
115
|
+
/**
|
|
116
|
+
* The incoming request's `If-None-Match` header value. When set and the
|
|
117
|
+
* computed ETag matches (weak-ETag semantics, mirroring Next.js's
|
|
118
|
+
* `sendEtagResponse`), a `304 Not Modified` is returned with an empty body.
|
|
119
|
+
* Only evaluated on bot/buffered responses that carry an ETag.
|
|
120
|
+
*/
|
|
121
|
+
ifNoneMatch?: string;
|
|
122
|
+
/**
|
|
123
|
+
* The incoming request's `Cache-Control` header value. When the value
|
|
124
|
+
* contains `no-cache`, the 304 short-circuit is skipped and a full 200
|
|
125
|
+
* response is always returned — mirroring the `fresh` package used by
|
|
126
|
+
* Next.js's `sendEtagResponse`.
|
|
127
|
+
*/
|
|
128
|
+
requestCacheControl?: string;
|
|
85
129
|
};
|
|
86
130
|
declare function buildPagesNextDataScript(options: Pick<RenderPagesPageResponseOptions, "buildId" | "i18n" | "isFallback" | "pageProps" | "params" | "routePattern" | "safeJsonStringify" | "scriptNonce" | "nextData"> & {
|
|
87
131
|
vinext?: VinextNextData["__vinext"];
|
|
88
132
|
}): string;
|
|
89
133
|
declare function renderPagesPageResponse(options: RenderPagesPageResponseOptions): Promise<Response>;
|
|
90
134
|
//#endregion
|
|
91
|
-
export { PagesGsspResponse, PagesI18nRenderContext, PagesNextDataExtras, buildPagesNextDataScript, renderPagesPageResponse };
|
|
135
|
+
export { PagesGsspResponse, PagesI18nRenderContext, PagesNextDataExtras, buildPagesNextDataScript, etagMatches, generatePagesETag, isPagesStreamingBot, renderPagesPageResponse, requestsNoCache };
|
|
@@ -1,16 +1,64 @@
|
|
|
1
1
|
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
2
2
|
import { reportRequestError } from "./instrumentation.js";
|
|
3
3
|
import { setCacheStateHeaders } from "./cache-headers.js";
|
|
4
|
+
import { fnv1a52 } from "../utils/hash.js";
|
|
4
5
|
import { encodeCacheTag } from "../utils/encode-cache-tag.js";
|
|
6
|
+
import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, applyCdnResponseHeaders } from "./cache-control.js";
|
|
7
|
+
import { buildMissIsrCacheControl } from "./isr-decision.js";
|
|
5
8
|
import { withScriptNonce } from "../shims/script-nonce-context.js";
|
|
6
9
|
import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr } from "./html.js";
|
|
7
10
|
import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
|
|
8
11
|
import { readStreamAsText } from "../utils/text-stream.js";
|
|
9
12
|
import { loadUserDocumentInitialProps, runDocumentRenderPage } from "./pages-document-initial-props.js";
|
|
10
13
|
import { callDocumentGetInitialProps } from "./document-initial-head.js";
|
|
11
|
-
import { applyCdnResponseHeaders, buildRevalidateCacheControl } from "./cache-control.js";
|
|
12
14
|
import React from "react";
|
|
13
15
|
//#region src/server/pages-page-response.ts
|
|
16
|
+
/**
|
|
17
|
+
* Crawlers that cannot handle streamed HTML: they read metadata only from
|
|
18
|
+
* the first network chunk, so streaming would give them an incomplete <head>.
|
|
19
|
+
* Pattern sourced from Next.js html-bots.ts (updated to match the canary).
|
|
20
|
+
*/
|
|
21
|
+
const HTML_LIMITED_BOT_UA_RE = /[\w-]+-Google|Google-[\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight/i;
|
|
22
|
+
/**
|
|
23
|
+
* Googlebot (the main search crawler) executes JavaScript via a headless
|
|
24
|
+
* browser, so it too cannot safely handle mid-stream HTML mutations.
|
|
25
|
+
* Matches "Googlebot" but NOT suffixed variants like "Googlebot-Image".
|
|
26
|
+
*/
|
|
27
|
+
const HEADLESS_BROWSER_BOT_UA_RE = /Googlebot(?!-)|Googlebot$/i;
|
|
28
|
+
/**
|
|
29
|
+
* Returns true when the User-Agent belongs to a bot or crawler that cannot
|
|
30
|
+
* reliably consume a streamed HTML response.
|
|
31
|
+
*/
|
|
32
|
+
function isPagesStreamingBot(userAgent) {
|
|
33
|
+
return HEADLESS_BROWSER_BOT_UA_RE.test(userAgent) || HTML_LIMITED_BOT_UA_RE.test(userAgent);
|
|
34
|
+
}
|
|
35
|
+
function generatePagesETag(payload) {
|
|
36
|
+
return "\"" + fnv1a52(payload).toString(36) + payload.length.toString(36) + "\"";
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Mirrors Next.js `sendEtagResponse` semantics (weak/strong comparison).
|
|
40
|
+
*
|
|
41
|
+
* A weak ETag `W/"..."` matches both `W/"..."` and `"..."` in `If-None-Match`.
|
|
42
|
+
* A strong ETag `"..."` only matches the same strong token.
|
|
43
|
+
* `*` always matches.
|
|
44
|
+
*/
|
|
45
|
+
function etagMatches(etag, ifNoneMatch) {
|
|
46
|
+
if (ifNoneMatch === "*") return true;
|
|
47
|
+
const normalize = (t) => t.replace(/^W\//, "");
|
|
48
|
+
const etagNorm = normalize(etag.trim());
|
|
49
|
+
for (const token of ifNoneMatch.split(",")) if (normalize(token.trim()) === etagNorm) return true;
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Returns true when a request `Cache-Control` header asks to bypass the 304
|
|
54
|
+
* short-circuit. Mirrors the `fresh` package's check used by Next.js's
|
|
55
|
+
* `sendEtagResponse` (`/(?:^|,)\s*?no-cache\s*?(?:,|$)/`). Shared by the
|
|
56
|
+
* fresh-MISS bot path here and the ISR HIT/STALE paths in
|
|
57
|
+
* `pages-page-data.ts` so the two cannot drift.
|
|
58
|
+
*/
|
|
59
|
+
function requestsNoCache(cacheControl) {
|
|
60
|
+
return /(?:^|,)\s*no-cache\s*(?:,|$)/.test(cacheControl ?? "");
|
|
61
|
+
}
|
|
14
62
|
function buildPagesFontHeadHtml(fontLinks, fontPreloads, fontStyles, scriptNonce) {
|
|
15
63
|
let html = "";
|
|
16
64
|
const nonceAttr = createNonceAttribute(scriptNonce);
|
|
@@ -201,21 +249,34 @@ async function renderPagesPageResponse(options) {
|
|
|
201
249
|
}
|
|
202
250
|
const compositeStream = await buildPagesCompositeStream(responseBodyStream, shellPrefix, shellSuffix);
|
|
203
251
|
const userSetCacheControl = responseHeaders.has("Cache-Control");
|
|
204
|
-
if (options.scriptNonce) responseHeaders.set("Cache-Control",
|
|
252
|
+
if (options.scriptNonce) responseHeaders.set("Cache-Control", NO_STORE_CACHE_CONTROL);
|
|
205
253
|
else if (options.isrRevalidateSeconds) {
|
|
206
254
|
const isrPathname = options.routeUrl.split("?")[0];
|
|
207
255
|
const stem = isrPathname.endsWith("/") ? isrPathname.slice(0, -1) : isrPathname;
|
|
208
256
|
applyCdnResponseHeaders(responseHeaders, {
|
|
209
|
-
cacheControl:
|
|
257
|
+
cacheControl: buildMissIsrCacheControl(options.isrRevalidateSeconds, options.expireSeconds),
|
|
210
258
|
tags: [encodeCacheTag(`_N_T_${stem || "/"}`)]
|
|
211
259
|
});
|
|
212
260
|
setCacheStateHeaders(responseHeaders, "MISS");
|
|
213
|
-
} else if (options.gsspRes && !userSetCacheControl) responseHeaders.set("Cache-Control",
|
|
261
|
+
} else if (options.gsspRes && !userSetCacheControl) responseHeaders.set("Cache-Control", NEVER_CACHE_CONTROL);
|
|
214
262
|
if (options.fontLinkHeader) responseHeaders.set("Link", options.fontLinkHeader);
|
|
263
|
+
if (options.userAgent && isPagesStreamingBot(options.userAgent)) {
|
|
264
|
+
const fullHtml = await readStreamAsText(compositeStream);
|
|
265
|
+
const etag = generatePagesETag(fullHtml);
|
|
266
|
+
responseHeaders.set("ETag", etag);
|
|
267
|
+
if (!requestsNoCache(options.requestCacheControl) && options.ifNoneMatch && etagMatches(etag, options.ifNoneMatch)) return new Response(null, {
|
|
268
|
+
status: 304,
|
|
269
|
+
headers: responseHeaders
|
|
270
|
+
});
|
|
271
|
+
return new Response(fullHtml, {
|
|
272
|
+
status: finalStatus,
|
|
273
|
+
headers: responseHeaders
|
|
274
|
+
});
|
|
275
|
+
}
|
|
215
276
|
return Object.assign(new Response(compositeStream, {
|
|
216
277
|
status: finalStatus,
|
|
217
278
|
headers: responseHeaders
|
|
218
279
|
}), { __vinextStreamedHtmlResponse: true });
|
|
219
280
|
}
|
|
220
281
|
//#endregion
|
|
221
|
-
export { buildPagesNextDataScript, renderPagesPageResponse };
|
|
282
|
+
export { buildPagesNextDataScript, etagMatches, generatePagesETag, isPagesStreamingBot, renderPagesPageResponse, requestsNoCache };
|
|
@@ -62,6 +62,20 @@ type PagesPipelineDeps = {
|
|
|
62
62
|
*/
|
|
63
63
|
serveStaticFile?: ((requestPathname: string, stagedHeaders: HeaderRecord) => Promise<boolean>) | null;
|
|
64
64
|
};
|
|
65
|
+
/**
|
|
66
|
+
* Wrap an adapter's `runMiddleware` callback so middleware receives the original
|
|
67
|
+
* (pre-basePath-stripping) URL. Adapters strip the basePath before handing the
|
|
68
|
+
* request to `runPagesRequest`, but Next.js passes the un-stripped URL to the
|
|
69
|
+
* middleware adapter so `request.nextUrl.basePath` reflects whether the URL
|
|
70
|
+
* actually had the basePath prefix. Requests outside the basePath
|
|
71
|
+
* (`hadBasePath === false`) are passed through untouched so middleware sees
|
|
72
|
+
* `nextUrl.basePath === ""` and can redirect them into the basePath
|
|
73
|
+
* (see the middleware-base-path e2e test / #1830).
|
|
74
|
+
*
|
|
75
|
+
* Shared by the Node prod server (prod-server.ts) and the generated Pages
|
|
76
|
+
* Router worker entry (deploy.ts) to keep the two adapters in sync.
|
|
77
|
+
*/
|
|
78
|
+
declare function wrapMiddlewareWithBasePath(runMiddleware: NonNullable<PagesPipelineDeps["runMiddleware"]>, basePath: string, hadBasePath: boolean): NonNullable<PagesPipelineDeps["runMiddleware"]>;
|
|
65
79
|
type PagesPipelineResult = {
|
|
66
80
|
type: "response";
|
|
67
81
|
response: Response;
|
|
@@ -96,4 +110,4 @@ type PagesPipelineResult = {
|
|
|
96
110
|
*/
|
|
97
111
|
declare function runPagesRequest(request: Request, deps: PagesPipelineDeps): Promise<PagesPipelineResult>;
|
|
98
112
|
//#endregion
|
|
99
|
-
export { MiddlewareResult, PagesPipelineDeps, PagesPipelineResult, PagesRenderOptions, runPagesRequest };
|
|
113
|
+
export { MiddlewareResult, PagesPipelineDeps, PagesPipelineResult, PagesRenderOptions, runPagesRequest, wrapMiddlewareWithBasePath };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hasBasePath } from "../utils/base-path.js";
|
|
1
|
+
import { addBasePathToPathname, hasBasePath } from "../utils/base-path.js";
|
|
2
2
|
import { applyMiddlewareRequestHeaders, isExternalUrl, matchRedirect, matchRewrite, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, sanitizeDestination } from "../config/config-matchers.js";
|
|
3
3
|
import { applyConfigHeadersToHeaderRecord, normalizeTrailingSlash } from "./request-pipeline.js";
|
|
4
4
|
import { mergeRewriteQuery } from "../utils/query.js";
|
|
@@ -6,6 +6,27 @@ import { normalizeDefaultLocalePathname, stripI18nLocaleForApiRoute } from "./pa
|
|
|
6
6
|
import { mergeHeaders } from "./worker-utils.js";
|
|
7
7
|
//#region src/server/pages-request-pipeline.ts
|
|
8
8
|
/**
|
|
9
|
+
* Wrap an adapter's `runMiddleware` callback so middleware receives the original
|
|
10
|
+
* (pre-basePath-stripping) URL. Adapters strip the basePath before handing the
|
|
11
|
+
* request to `runPagesRequest`, but Next.js passes the un-stripped URL to the
|
|
12
|
+
* middleware adapter so `request.nextUrl.basePath` reflects whether the URL
|
|
13
|
+
* actually had the basePath prefix. Requests outside the basePath
|
|
14
|
+
* (`hadBasePath === false`) are passed through untouched so middleware sees
|
|
15
|
+
* `nextUrl.basePath === ""` and can redirect them into the basePath
|
|
16
|
+
* (see the middleware-base-path e2e test / #1830).
|
|
17
|
+
*
|
|
18
|
+
* Shared by the Node prod server (prod-server.ts) and the generated Pages
|
|
19
|
+
* Router worker entry (deploy.ts) to keep the two adapters in sync.
|
|
20
|
+
*/
|
|
21
|
+
function wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) {
|
|
22
|
+
if (!hadBasePath || !basePath) return runMiddleware;
|
|
23
|
+
return (request, ctx, opts) => {
|
|
24
|
+
const mwUrl = new URL(request.url);
|
|
25
|
+
mwUrl.pathname = addBasePathToPathname(mwUrl.pathname, basePath);
|
|
26
|
+
return runMiddleware(new Request(mwUrl, request), ctx, opts);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
9
30
|
* Run the Pages Router request pipeline.
|
|
10
31
|
*
|
|
11
32
|
* ASSUMPTION: request already has internal headers filtered and basePath stripped.
|
|
@@ -206,4 +227,4 @@ async function runPagesRequest(request, deps) {
|
|
|
206
227
|
};
|
|
207
228
|
}
|
|
208
229
|
//#endregion
|
|
209
|
-
export { runPagesRequest };
|
|
230
|
+
export { runPagesRequest, wrapMiddlewareWithBasePath };
|
|
@@ -3,6 +3,44 @@ import { resolveRequestHost, trustProxy, trustedHosts } from "./proxy-trust.js";
|
|
|
3
3
|
import { IncomingMessage, ServerResponse } from "node:http";
|
|
4
4
|
|
|
5
5
|
//#region src/server/prod-server.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Import a built server entry module (App Router RSC entry or Pages Router
|
|
8
|
+
* server entry) by absolute file path.
|
|
9
|
+
*
|
|
10
|
+
* The first import of a given path uses the plain file:// URL with NO query
|
|
11
|
+
* string. This is load-bearing: code-split builds emit lazy chunks that
|
|
12
|
+
* import the entry back by bare specifier (default Vite builds on both
|
|
13
|
+
* supported majors — Rollup on Vite 7 and Rolldown on Vite 8 — hoist modules
|
|
14
|
+
* shared between the entry's static graph and lazy route chunks into the
|
|
15
|
+
* entry chunk, which the chunks then import as e.g. "../../index.js").
|
|
16
|
+
* Node keys its ESM cache on the full URL including the query string, so if
|
|
17
|
+
* the server imported the entry as `index.js?t=<mtime>`, a chunk's bare
|
|
18
|
+
* back-import would evaluate the entire server bundle a second time and
|
|
19
|
+
* module-level singletons (db pools, service registries) would silently
|
|
20
|
+
* diverge between the two copies. See
|
|
21
|
+
* https://github.com/cloudflare/vinext/issues/1923.
|
|
22
|
+
*
|
|
23
|
+
* A `?t=<mtime>` query string is appended only when the same path is
|
|
24
|
+
* imported again after a rebuild (different mtime) — e.g. test suites that
|
|
25
|
+
* rebuild a fixture to the same output path within one process — where the
|
|
26
|
+
* bare URL's cache entry would return the stale previous build. Note this
|
|
27
|
+
* rebuild branch trades the single-instance guarantee back: chunks that
|
|
28
|
+
* import the entry by bare path still resolve to the FIRST build's cache
|
|
29
|
+
* entry, so freshness and single-instance only hold together on the first
|
|
30
|
+
* import of a path. Production processes import each entry path exactly
|
|
31
|
+
* once and always get both.
|
|
32
|
+
*
|
|
33
|
+
* The entry is imported via its canonical real path: the bundler
|
|
34
|
+
* canonicalizes module ids with fs.realpathSync.native, so chunks evaluate
|
|
35
|
+
* under realpath-based URLs and their relative imports resolve to realpath
|
|
36
|
+
* URLs too. Importing the entry through a symlinked path (macOS /var/...
|
|
37
|
+
* tmpdirs, symlinked deploy directories) would otherwise create a second
|
|
38
|
+
* instance keyed on the symlinked URL.
|
|
39
|
+
*
|
|
40
|
+
* Exported for direct unit testing of the URL choice.
|
|
41
|
+
*/
|
|
42
|
+
declare function resolveServerEntryImportUrl(entryPath: string): string;
|
|
43
|
+
declare function importServerEntryModule(entryPath: string): Promise<any>;
|
|
6
44
|
type ProdServerOptions = {
|
|
7
45
|
/** Port to listen on */port?: number; /** Host to bind to */
|
|
8
46
|
host?: string; /** Path to the build output directory */
|
|
@@ -105,4 +143,4 @@ declare function resolveAppRouterPrerenderSeeder(entryModule: unknown): AppRoute
|
|
|
105
143
|
*/
|
|
106
144
|
declare function resolveAppRouterAssetPath(pathname: string, assetPathPrefix: string, assetPrefix: string): string | null;
|
|
107
145
|
//#endregion
|
|
108
|
-
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
146
|
+
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
@@ -8,17 +8,14 @@ import { isUnknownRecord } from "../utils/record.js";
|
|
|
8
8
|
import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
|
|
9
9
|
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath, isSafeImageContentType, parseImageParams } from "./image-optimization.js";
|
|
10
10
|
import { installSocketErrorBackstop } from "./socket-error-backstop.js";
|
|
11
|
-
import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix
|
|
11
|
+
import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
|
|
12
12
|
import { CONTENT_TYPES, StaticFileCache, etagFromFilenameHash } from "./static-file-cache.js";
|
|
13
13
|
import { buildNextDataNotFoundResponse, isNextDataPathname, parseNextDataPathname } from "./pages-data-route.js";
|
|
14
14
|
import { collectInlineCssManifest } from "../build/inline-css.js";
|
|
15
15
|
import { mergeHeaders } from "./worker-utils.js";
|
|
16
|
-
import { runPagesRequest } from "./pages-request-pipeline.js";
|
|
17
|
-
import { manifestFileWithBase } from "../utils/manifest-paths.js";
|
|
16
|
+
import { runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
|
|
18
17
|
import { readTrustedPrerenderRouteParamsFromHeaders, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
|
|
19
|
-
import {
|
|
20
|
-
import { findClientEntryFile, findPagesClientEntryFile, readClientBuildManifest } from "../utils/client-build-manifest.js";
|
|
21
|
-
import { findClientEntryFileFromVinextManifest, findPagesClientEntryFileFromVinextManifest, readClientEntryManifest } from "../utils/client-entry-manifest.js";
|
|
18
|
+
import { computeClientRuntimeMetadata } from "../utils/client-runtime-metadata.js";
|
|
22
19
|
import { readPrerenderSecret } from "../build/server-manifest.js";
|
|
23
20
|
import { seedMemoryCacheFromPrerender } from "./seed-cache.js";
|
|
24
21
|
import fs from "node:fs";
|
|
@@ -48,6 +45,68 @@ import { Readable, pipeline } from "node:stream";
|
|
|
48
45
|
* - dist/server/index.js — RSC entry (default export: handler(Request) → Response)
|
|
49
46
|
* - dist/server/ssr/index.js — SSR entry (imported by RSC entry at runtime)
|
|
50
47
|
*/
|
|
48
|
+
/**
|
|
49
|
+
* mtime of the build each bare (query-less) server-entry URL was first
|
|
50
|
+
* imported from in this process. Node's ESM cache pins a bare URL to that
|
|
51
|
+
* build forever, so rebuilds to the same path must be detected and loaded
|
|
52
|
+
* through a cache-busted URL instead.
|
|
53
|
+
*/
|
|
54
|
+
const bareServerEntryMtimes = /* @__PURE__ */ new Map();
|
|
55
|
+
/**
|
|
56
|
+
* Import a built server entry module (App Router RSC entry or Pages Router
|
|
57
|
+
* server entry) by absolute file path.
|
|
58
|
+
*
|
|
59
|
+
* The first import of a given path uses the plain file:// URL with NO query
|
|
60
|
+
* string. This is load-bearing: code-split builds emit lazy chunks that
|
|
61
|
+
* import the entry back by bare specifier (default Vite builds on both
|
|
62
|
+
* supported majors — Rollup on Vite 7 and Rolldown on Vite 8 — hoist modules
|
|
63
|
+
* shared between the entry's static graph and lazy route chunks into the
|
|
64
|
+
* entry chunk, which the chunks then import as e.g. "../../index.js").
|
|
65
|
+
* Node keys its ESM cache on the full URL including the query string, so if
|
|
66
|
+
* the server imported the entry as `index.js?t=<mtime>`, a chunk's bare
|
|
67
|
+
* back-import would evaluate the entire server bundle a second time and
|
|
68
|
+
* module-level singletons (db pools, service registries) would silently
|
|
69
|
+
* diverge between the two copies. See
|
|
70
|
+
* https://github.com/cloudflare/vinext/issues/1923.
|
|
71
|
+
*
|
|
72
|
+
* A `?t=<mtime>` query string is appended only when the same path is
|
|
73
|
+
* imported again after a rebuild (different mtime) — e.g. test suites that
|
|
74
|
+
* rebuild a fixture to the same output path within one process — where the
|
|
75
|
+
* bare URL's cache entry would return the stale previous build. Note this
|
|
76
|
+
* rebuild branch trades the single-instance guarantee back: chunks that
|
|
77
|
+
* import the entry by bare path still resolve to the FIRST build's cache
|
|
78
|
+
* entry, so freshness and single-instance only hold together on the first
|
|
79
|
+
* import of a path. Production processes import each entry path exactly
|
|
80
|
+
* once and always get both.
|
|
81
|
+
*
|
|
82
|
+
* The entry is imported via its canonical real path: the bundler
|
|
83
|
+
* canonicalizes module ids with fs.realpathSync.native, so chunks evaluate
|
|
84
|
+
* under realpath-based URLs and their relative imports resolve to realpath
|
|
85
|
+
* URLs too. Importing the entry through a symlinked path (macOS /var/...
|
|
86
|
+
* tmpdirs, symlinked deploy directories) would otherwise create a second
|
|
87
|
+
* instance keyed on the symlinked URL.
|
|
88
|
+
*
|
|
89
|
+
* Exported for direct unit testing of the URL choice.
|
|
90
|
+
*/
|
|
91
|
+
function resolveServerEntryImportUrl(entryPath) {
|
|
92
|
+
let canonicalEntryPath;
|
|
93
|
+
try {
|
|
94
|
+
canonicalEntryPath = fs.realpathSync.native(entryPath);
|
|
95
|
+
} catch {
|
|
96
|
+
canonicalEntryPath = entryPath;
|
|
97
|
+
}
|
|
98
|
+
const href = pathToFileURL(canonicalEntryPath).href;
|
|
99
|
+
const mtime = fs.statSync(canonicalEntryPath).mtimeMs;
|
|
100
|
+
const bareMtime = bareServerEntryMtimes.get(href);
|
|
101
|
+
if (bareMtime === void 0 || bareMtime === mtime) {
|
|
102
|
+
bareServerEntryMtimes.set(href, mtime);
|
|
103
|
+
return href;
|
|
104
|
+
}
|
|
105
|
+
return `${href}?t=${mtime}`;
|
|
106
|
+
}
|
|
107
|
+
async function importServerEntryModule(entryPath) {
|
|
108
|
+
return import(resolveServerEntryImportUrl(entryPath));
|
|
109
|
+
}
|
|
51
110
|
/** Convert a Node.js IncomingMessage into a ReadableStream for Web Request body. */
|
|
52
111
|
function readNodeStream(req) {
|
|
53
112
|
return new ReadableStream({ start(controller) {
|
|
@@ -158,8 +217,14 @@ const NO_BODY_RESPONSE_STATUSES = new Set([
|
|
|
158
217
|
205,
|
|
159
218
|
304
|
|
160
219
|
]);
|
|
161
|
-
|
|
162
|
-
|
|
220
|
+
const OMIT_BODY_HEADERS = new Set(["content-length", "content-type"]);
|
|
221
|
+
const OMIT_STATIC_RESPONSE_HEADERS = new Set([
|
|
222
|
+
VINEXT_STATIC_FILE_HEADER,
|
|
223
|
+
"content-encoding",
|
|
224
|
+
"content-length",
|
|
225
|
+
"content-type"
|
|
226
|
+
]);
|
|
227
|
+
function omitHeadersCaseInsensitive(headersRecord, targets) {
|
|
163
228
|
const filtered = {};
|
|
164
229
|
for (const [key, value] of Object.entries(headersRecord)) {
|
|
165
230
|
if (targets.has(key.toLowerCase())) continue;
|
|
@@ -172,6 +237,15 @@ function matchesIfNoneMatchHeader(ifNoneMatch, etag) {
|
|
|
172
237
|
if (ifNoneMatch === "*") return true;
|
|
173
238
|
return ifNoneMatch.split(",").map((value) => value.trim()).some((value) => value === etag);
|
|
174
239
|
}
|
|
240
|
+
function installClientBuildManifestGlobals(clientDir, assetBase, assetPrefix) {
|
|
241
|
+
const metadata = computeClientRuntimeMetadata({
|
|
242
|
+
clientDir,
|
|
243
|
+
assetBase,
|
|
244
|
+
assetPrefix
|
|
245
|
+
});
|
|
246
|
+
globalThis.__VINEXT_LAZY_CHUNKS__ = metadata.lazyChunks;
|
|
247
|
+
globalThis.__VINEXT_DYNAMIC_PRELOADS__ = metadata.dynamicPreloads;
|
|
248
|
+
}
|
|
175
249
|
function isNoBodyResponseStatus(status) {
|
|
176
250
|
return NO_BODY_RESPONSE_STATUSES.has(status);
|
|
177
251
|
}
|
|
@@ -212,7 +286,7 @@ function sendCompressed(req, res, body, contentType, statusCode, extraHeaders =
|
|
|
212
286
|
const buf = typeof body === "string" ? Buffer.from(body) : body;
|
|
213
287
|
const baseType = contentType.split(";")[0].trim();
|
|
214
288
|
const encoding = compress ? negotiateEncoding(req) : null;
|
|
215
|
-
const headersWithoutBodyHeaders = omitHeadersCaseInsensitive(extraHeaders,
|
|
289
|
+
const headersWithoutBodyHeaders = omitHeadersCaseInsensitive(extraHeaders, OMIT_BODY_HEADERS);
|
|
216
290
|
const writeHead = (headers) => {
|
|
217
291
|
if (statusText) res.writeHead(statusCode, statusText, headers);
|
|
218
292
|
else res.writeHead(statusCode, headers);
|
|
@@ -619,19 +693,15 @@ function readSsrManifest(clientDir) {
|
|
|
619
693
|
function installPagesClientAssetGlobals(options) {
|
|
620
694
|
const ssrManifest = readSsrManifest(options.clientDir);
|
|
621
695
|
globalThis.__VINEXT_SSR_MANIFEST__ = Object.keys(ssrManifest).length > 0 ? ssrManifest : void 0;
|
|
622
|
-
const
|
|
623
|
-
const clientEntryManifest = readClientEntryManifest(options.clientDir);
|
|
624
|
-
const entryOptions = {
|
|
696
|
+
const metadata = computeClientRuntimeMetadata({
|
|
625
697
|
clientDir: options.clientDir,
|
|
626
|
-
assetsSubdir: options.assetsSubdir,
|
|
627
698
|
assetBase: options.assetBase,
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
} else globalThis.__VINEXT_LAZY_CHUNKS__ = void 0;
|
|
699
|
+
assetPrefix: options.assetPrefix,
|
|
700
|
+
includeClientEntry: options.clientEntryLookup === "pages-client-entry" ? "pages-client-entry" : true
|
|
701
|
+
});
|
|
702
|
+
globalThis.__VINEXT_CLIENT_ENTRY__ = metadata.clientEntryFile;
|
|
703
|
+
globalThis.__VINEXT_LAZY_CHUNKS__ = metadata.lazyChunks;
|
|
704
|
+
globalThis.__VINEXT_DYNAMIC_PRELOADS__ = metadata.dynamicPreloads;
|
|
635
705
|
return ssrManifest;
|
|
636
706
|
}
|
|
637
707
|
/**
|
|
@@ -659,8 +729,7 @@ async function startAppRouterServer(options) {
|
|
|
659
729
|
imageConfig = JSON.parse(fs.readFileSync(imageConfigPath, "utf-8"));
|
|
660
730
|
} catch {}
|
|
661
731
|
const prerenderSecret = readPrerenderSecret(path.dirname(rscEntryPath));
|
|
662
|
-
const
|
|
663
|
-
const rscModule = await import(`${pathToFileURL(rscEntryPath).href}?t=${rscMtime}`);
|
|
732
|
+
const rscModule = await importServerEntryModule(rscEntryPath);
|
|
664
733
|
const rscHandler = resolveAppRouterHandler(rscModule.default);
|
|
665
734
|
const appRouterAssetPrefix = typeof rscModule.__assetPrefix === "string" ? rscModule.__assetPrefix : "";
|
|
666
735
|
const appRouterBasePath = typeof rscModule.__basePath === "string" ? rscModule.__basePath : "";
|
|
@@ -671,10 +740,11 @@ async function startAppRouterServer(options) {
|
|
|
671
740
|
const appAssetBase = appRouterBasePath ? `${appRouterBasePath}/` : "/";
|
|
672
741
|
if (appRouterHasPagesDir) installPagesClientAssetGlobals({
|
|
673
742
|
clientDir,
|
|
674
|
-
|
|
743
|
+
assetPrefix: appRouterAssetPrefix,
|
|
675
744
|
assetBase: appAssetBase,
|
|
676
745
|
clientEntryLookup: "pages-client-entry"
|
|
677
746
|
});
|
|
747
|
+
else installClientBuildManifestGlobals(clientDir, appAssetBase, appRouterAssetPrefix);
|
|
678
748
|
const seededRoutes = await resolveAppRouterPrerenderSeeder(rscModule)(path.dirname(rscEntryPath));
|
|
679
749
|
if (seededRoutes > 0) console.log(`[vinext] Seeded ${seededRoutes} pre-rendered route${seededRoutes !== 1 ? "s" : ""} into memory cache`);
|
|
680
750
|
const staticCache = await StaticFileCache.create(clientDir);
|
|
@@ -745,12 +815,7 @@ async function startAppRouterServer(options) {
|
|
|
745
815
|
} catch {
|
|
746
816
|
staticFilePath = staticFileSignal;
|
|
747
817
|
}
|
|
748
|
-
const staticResponseHeaders = omitHeadersCaseInsensitive(mergeResponseHeaders({}, response),
|
|
749
|
-
VINEXT_STATIC_FILE_HEADER,
|
|
750
|
-
"content-encoding",
|
|
751
|
-
"content-length",
|
|
752
|
-
"content-type"
|
|
753
|
-
]);
|
|
818
|
+
const staticResponseHeaders = omitHeadersCaseInsensitive(mergeResponseHeaders({}, response), OMIT_STATIC_RESPONSE_HEADERS);
|
|
754
819
|
const served = await tryServeStatic(req, res, clientDir, staticFilePath, compress, staticCache, staticResponseHeaders, response.status);
|
|
755
820
|
cancelResponseBody(response);
|
|
756
821
|
if (served) return;
|
|
@@ -804,8 +869,7 @@ function readPagesServerEntryPageRoutes(value) {
|
|
|
804
869
|
*/
|
|
805
870
|
async function startPagesRouterServer(options) {
|
|
806
871
|
const { port, host, clientDir, serverEntryPath, compress, purpose } = options;
|
|
807
|
-
const
|
|
808
|
-
const serverEntry = await import(`${pathToFileURL(serverEntryPath).href}?t=${serverMtime}`);
|
|
872
|
+
const serverEntry = await importServerEntryModule(serverEntryPath);
|
|
809
873
|
const { renderPage, handleApiRoute: handleApi, runMiddleware, vinextConfig, buildId: pagesBuildId } = serverEntry;
|
|
810
874
|
const matchPageRoute = typeof serverEntry.matchPageRoute === "function" ? serverEntry.matchPageRoute : void 0;
|
|
811
875
|
const pageRoutes = readPagesServerEntryPageRoutes(serverEntry.pageRoutes);
|
|
@@ -832,7 +896,7 @@ async function startPagesRouterServer(options) {
|
|
|
832
896
|
} : void 0;
|
|
833
897
|
const ssrManifest = installPagesClientAssetGlobals({
|
|
834
898
|
clientDir,
|
|
835
|
-
|
|
899
|
+
assetPrefix,
|
|
836
900
|
assetBase,
|
|
837
901
|
clientEntryLookup: "any-client-entry"
|
|
838
902
|
});
|
|
@@ -963,7 +1027,7 @@ async function startPagesRouterServer(options) {
|
|
|
963
1027
|
ctx: void 0,
|
|
964
1028
|
rawSearch: rawQs,
|
|
965
1029
|
matchPageRoute: matchPageRoute ?? null,
|
|
966
|
-
runMiddleware: typeof runMiddleware === "function" ? runMiddleware : null,
|
|
1030
|
+
runMiddleware: typeof runMiddleware === "function" ? wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) : null,
|
|
967
1031
|
renderPage: typeof renderPage === "function" ? (request, resolvedUrl, options, stagedHeaders) => renderPage(request, resolvedUrl, ssrManifest, void 0, stagedHeaders, options) : null,
|
|
968
1032
|
handleApi: typeof handleApi === "function" ? (request, apiUrl) => handleApi(request, apiUrl, createNodeExecutionContext()) : null,
|
|
969
1033
|
serveStaticFile: async (requestPathname, stagedHeaders) => {
|
|
@@ -1018,4 +1082,4 @@ async function startPagesRouterServer(options) {
|
|
|
1018
1082
|
};
|
|
1019
1083
|
}
|
|
1020
1084
|
//#endregion
|
|
1021
|
-
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
1085
|
+
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
@@ -3,7 +3,7 @@ import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation }
|
|
|
3
3
|
import { VINEXT_RSC_MARKER_HEADER } from "../server/headers.js";
|
|
4
4
|
import { markDynamicUsage } from "./headers.js";
|
|
5
5
|
import { _registerCacheContextAccessor, _setRequestScopedCacheLife, cacheLifeProfiles, getDataCacheHandler } from "./cache.js";
|
|
6
|
-
import { addCollectedRequestTags } from "./fetch-cache.js";
|
|
6
|
+
import { addCollectedRequestTags, getCurrentFetchSoftTags } from "./fetch-cache.js";
|
|
7
7
|
//#region src/shims/cache-runtime.ts
|
|
8
8
|
/**
|
|
9
9
|
* "use cache" runtime
|
|
@@ -269,7 +269,11 @@ function registerCachedFunction(fn, id, variant, options = {}) {
|
|
|
269
269
|
}
|
|
270
270
|
if (isDev) return executeWithContext(fn, args, cacheVariant);
|
|
271
271
|
const handler = getDataCacheHandler();
|
|
272
|
-
const
|
|
272
|
+
const softTags = getCurrentFetchSoftTags();
|
|
273
|
+
const existing = await handler.get(cacheKey, {
|
|
274
|
+
kind: "FETCH",
|
|
275
|
+
softTags
|
|
276
|
+
});
|
|
273
277
|
if (existing?.value && existing.value.kind === "FETCH" && existing.cacheState !== "stale") try {
|
|
274
278
|
propagateCacheTagsToRequest(existing.value.tags);
|
|
275
279
|
if (rsc && existing.value.data.headers["x-vinext-rsc"] === "1") {
|
|
@@ -321,8 +325,11 @@ function registerCachedFunction(fn, id, variant, options = {}) {
|
|
|
321
325
|
value: fn.length,
|
|
322
326
|
configurable: true
|
|
323
327
|
});
|
|
328
|
+
cachedFn[USE_CACHE_FUNCTION_SYMBOL] = true;
|
|
324
329
|
return cachedFn;
|
|
325
330
|
}
|
|
331
|
+
/** @internal Symbol used to identify "use cache" wrapper functions. */
|
|
332
|
+
const USE_CACHE_FUNCTION_SYMBOL = Symbol.for("vinext.useCacheFunction");
|
|
326
333
|
function throwPrivateUseCacheInsidePublicUseCacheError() {
|
|
327
334
|
const error = /* @__PURE__ */ new Error("\"use cache: private\" must not be used within \"use cache\". It can only be nested inside of another \"use cache: private\".");
|
|
328
335
|
const ctx = getRequestContext();
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/shims/dynamic-preload-chunks.d.ts
|
|
4
|
+
declare function DynamicPreloadChunks(props: {
|
|
5
|
+
moduleIds?: readonly string[];
|
|
6
|
+
}): React.FunctionComponentElement<React.FragmentProps> | null;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { DynamicPreloadChunks };
|