vinext 0.2.0 → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -29
- package/dist/build/client-build-config.d.ts +13 -92
- package/dist/build/client-build-config.js +17 -95
- package/dist/build/css-url-assets.d.ts +3 -1
- package/dist/build/css-url-assets.js +18 -1
- package/dist/build/inject-pregenerated-paths.d.ts +3 -0
- package/dist/build/inject-pregenerated-paths.js +4 -1
- package/dist/build/prerender-paths.d.ts +23 -0
- package/dist/build/prerender-paths.js +296 -0
- package/dist/build/prerender-server-entry.d.ts +1 -0
- package/dist/build/prerender-server-entry.js +49 -0
- package/dist/build/prerender-server-pool.d.ts +44 -0
- package/dist/build/prerender-server-pool.js +194 -0
- package/dist/build/prerender.d.ts +12 -1
- package/dist/build/prerender.js +87 -23
- package/dist/build/run-prerender.js +2 -1
- package/dist/cache/cache-adapters-virtual.d.ts +7 -1
- package/dist/cache/cache-adapters-virtual.js +26 -2
- package/dist/check.d.ts +4 -6
- package/dist/check.js +13 -9
- package/dist/cli.js +34 -54
- package/dist/client/vinext-next-data.d.ts +4 -1
- package/dist/config/config-matchers.js +33 -11
- package/dist/config/next-config.d.ts +36 -6
- package/dist/config/next-config.js +17 -4
- package/dist/config/prerender.d.ts +11 -1
- package/dist/config/prerender.js +19 -1
- package/dist/config/tsconfig-paths.js +5 -1
- package/dist/entries/app-browser-entry.js +5 -1
- package/dist/entries/app-rsc-entry.d.ts +3 -2
- package/dist/entries/app-rsc-entry.js +4 -0
- package/dist/entries/pages-client-entry.js +14 -3
- package/dist/entries/pages-server-entry.js +18 -4
- package/dist/image/image-adapters-virtual.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +549 -152
- package/dist/init-cloudflare.js +12 -3
- package/dist/init-platform.d.ts +10 -1
- package/dist/init-platform.js +78 -12
- package/dist/init.d.ts +11 -3
- package/dist/init.js +129 -31
- package/dist/plugins/fonts.js +1 -1
- package/dist/plugins/ignore-dynamic-requests.js +1 -1
- package/dist/plugins/og-assets.js +2 -1
- package/dist/plugins/optimize-imports.js +1 -1
- package/dist/plugins/rsc-reference-validation-normalizer.d.ts +12 -0
- package/dist/plugins/rsc-reference-validation-normalizer.js +48 -0
- package/dist/plugins/sass.d.ts +17 -1
- package/dist/plugins/sass.js +74 -1
- package/dist/plugins/styled-jsx.d.ts +16 -0
- package/dist/plugins/styled-jsx.js +149 -0
- package/dist/routing/app-route-graph.js +22 -16
- package/dist/routing/file-matcher.d.ts +8 -1
- package/dist/routing/file-matcher.js +15 -3
- package/dist/server/app-browser-entry.js +44 -14
- package/dist/server/app-browser-navigation-controller.js +5 -2
- package/dist/server/app-browser-server-action-client.js +5 -3
- package/dist/server/app-browser-state.d.ts +1 -0
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-layout-param-observation.d.ts +1 -1
- package/dist/server/app-page-boundary.js +2 -1
- package/dist/server/app-page-cache-finalizer.d.ts +1 -0
- package/dist/server/app-page-cache-finalizer.js +3 -3
- package/dist/server/app-page-cache-render.d.ts +1 -1
- package/dist/server/app-page-cache.js +23 -6
- package/dist/server/app-page-dispatch.d.ts +2 -1
- package/dist/server/app-page-dispatch.js +27 -10
- package/dist/server/app-page-element-builder.js +1 -0
- package/dist/server/app-page-execution.js +2 -1
- package/dist/server/app-page-render-identity.d.ts +1 -0
- package/dist/server/app-page-render-identity.js +2 -1
- package/dist/server/app-page-render.d.ts +3 -1
- package/dist/server/app-page-render.js +67 -16
- package/dist/server/app-page-response.d.ts +7 -0
- package/dist/server/app-page-response.js +16 -4
- package/dist/server/app-page-route-wiring.js +3 -3
- package/dist/server/app-page-stream.d.ts +2 -1
- package/dist/server/app-page-stream.js +1 -1
- package/dist/server/app-route-handler-execution.d.ts +1 -1
- package/dist/server/app-route-tree-prefetch.d.ts +43 -0
- package/dist/server/app-route-tree-prefetch.js +187 -0
- package/dist/server/app-router-entry.js +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +2 -1
- package/dist/server/app-rsc-cache-busting.js +9 -5
- package/dist/server/app-rsc-handler.d.ts +6 -0
- package/dist/server/app-rsc-handler.js +88 -10
- package/dist/server/app-rsc-render-mode.d.ts +3 -5
- package/dist/server/app-rsc-render-mode.js +5 -12
- package/dist/server/app-rsc-request-normalization.d.ts +3 -4
- package/dist/server/app-rsc-request-normalization.js +4 -5
- package/dist/server/app-server-action-execution.js +6 -5
- package/dist/server/app-ssr-entry.js +1 -1
- package/dist/server/cache-control.d.ts +3 -1
- package/dist/server/cache-control.js +13 -1
- package/dist/server/dev-module-runner.js +1 -1
- package/dist/server/dev-origin-check.d.ts +2 -2
- package/dist/server/dev-origin-check.js +2 -2
- package/dist/server/dev-server.d.ts +11 -1
- package/dist/server/dev-server.js +82 -24
- package/dist/server/headers.d.ts +6 -2
- package/dist/server/headers.js +11 -5
- package/dist/server/image-optimization.d.ts +12 -1
- package/dist/server/image-optimization.js +13 -1
- package/dist/server/isr-cache.d.ts +13 -4
- package/dist/server/isr-cache.js +8 -4
- package/dist/server/pages-data-route.d.ts +4 -2
- package/dist/server/pages-data-route.js +18 -4
- package/dist/server/pages-dev-module-url.d.ts +2 -1
- package/dist/server/pages-dev-module-url.js +6 -3
- package/dist/server/pages-node-compat.d.ts +12 -1
- package/dist/server/pages-node-compat.js +50 -1
- package/dist/server/pages-page-data.d.ts +9 -0
- package/dist/server/pages-page-data.js +18 -10
- package/dist/server/pages-page-handler.js +19 -7
- package/dist/server/pages-page-response.d.ts +1 -0
- package/dist/server/pages-page-response.js +5 -4
- package/dist/server/pages-request-pipeline.d.ts +8 -6
- package/dist/server/pages-request-pipeline.js +31 -7
- package/dist/server/pages-router-entry.js +1 -1
- package/dist/server/prerender-manifest.d.ts +15 -1
- package/dist/server/prerender-manifest.js +29 -1
- package/dist/server/prod-server.d.ts +10 -7
- package/dist/server/prod-server.js +43 -28
- package/dist/server/request-pipeline.js +1 -1
- package/dist/server/static-file-cache.js +1 -1
- package/dist/shims/cache-handler.js +8 -1
- package/dist/shims/cache.d.ts +1 -1
- package/dist/shims/cache.js +3 -0
- package/dist/shims/cdn-cache.d.ts +2 -7
- package/dist/shims/cdn-cache.js +2 -14
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +77 -52
- package/dist/shims/internal/app-prefetch-fetch-queue.d.ts +10 -0
- package/dist/shims/internal/app-prefetch-fetch-queue.js +61 -0
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +2 -1
- package/dist/shims/internal/hybrid-client-route-owner.js +34 -1
- package/dist/shims/internal/pages-data-fetch-dedup.d.ts +2 -1
- package/dist/shims/internal/pages-data-fetch-dedup.js +13 -2
- package/dist/shims/internal/pages-data-target.d.ts +8 -3
- package/dist/shims/internal/pages-data-target.js +9 -4
- package/dist/shims/link.d.ts +1 -1
- package/dist/shims/link.js +179 -42
- package/dist/shims/navigation.d.ts +15 -3
- package/dist/shims/navigation.js +202 -39
- package/dist/shims/request-context.js +18 -0
- package/dist/shims/request-state-types.d.ts +2 -2
- package/dist/shims/router.js +31 -15
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -1
- package/dist/utils/middleware-request-headers.js +1 -1
- package/dist/utils/project.d.ts +1 -1
- package/dist/utils/protocol-headers.d.ts +7 -1
- package/dist/utils/protocol-headers.js +7 -1
- package/dist/utils/vite-version.d.ts +4 -12
- package/dist/utils/vite-version.js +39 -21
- package/package.json +31 -13
- package/dist/cloudflare/index.d.ts +0 -3
- package/dist/cloudflare/index.js +0 -3
- package/dist/packages/cloudflare/src/cache/cdn-adapter.runtime.js +0 -102
- package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.d.ts +0 -126
- package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.js +0 -435
- package/dist/packages/cloudflare/src/deploy-config.js +0 -150
- package/dist/packages/cloudflare/src/deploy-help.js +0 -55
- package/dist/packages/cloudflare/src/deploy.js +0 -276
- package/dist/packages/cloudflare/src/tpr.d.ts +0 -45
- package/dist/packages/cloudflare/src/tpr.js +0 -561
- package/dist/packages/cloudflare/src/utils/cache-control-metadata.js +0 -20
|
@@ -1,25 +1,18 @@
|
|
|
1
1
|
//#region src/server/app-rsc-render-mode.ts
|
|
2
2
|
const APP_RSC_RENDER_MODE_NAVIGATION = "navigation";
|
|
3
|
+
const APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL = "prefetch-dynamic-shell";
|
|
3
4
|
const APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL = "prefetch-loading-shell";
|
|
4
|
-
const APP_RSC_RENDER_MODE_REFRESH_PRESERVE_UI = "refresh-preserve-ui";
|
|
5
|
-
const APP_RSC_RENDER_MODE_ACTION_RERENDER_PRESERVE_UI = "action-rerender-preserve-ui";
|
|
6
|
-
function shouldSuppressLoadingBoundaries(mode) {
|
|
7
|
-
return mode === "refresh-preserve-ui" || mode === "action-rerender-preserve-ui";
|
|
8
|
-
}
|
|
9
|
-
function shouldUsePreserveUiCacheVariant(mode) {
|
|
10
|
-
return shouldSuppressLoadingBoundaries(mode);
|
|
11
|
-
}
|
|
12
5
|
function getRscRenderModeCacheVariant(mode) {
|
|
6
|
+
if (mode === "prefetch-dynamic-shell") return "prefetch-dynamic-shell";
|
|
13
7
|
if (mode === "prefetch-loading-shell") return "prefetch-loading-shell";
|
|
14
|
-
return
|
|
8
|
+
return null;
|
|
15
9
|
}
|
|
16
10
|
function parseAppRscRenderMode(value) {
|
|
17
11
|
switch (value) {
|
|
12
|
+
case APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL: return APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL;
|
|
18
13
|
case APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL: return APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL;
|
|
19
|
-
case APP_RSC_RENDER_MODE_REFRESH_PRESERVE_UI: return APP_RSC_RENDER_MODE_REFRESH_PRESERVE_UI;
|
|
20
|
-
case APP_RSC_RENDER_MODE_ACTION_RERENDER_PRESERVE_UI: return APP_RSC_RENDER_MODE_ACTION_RERENDER_PRESERVE_UI;
|
|
21
14
|
default: return APP_RSC_RENDER_MODE_NAVIGATION;
|
|
22
15
|
}
|
|
23
16
|
}
|
|
24
17
|
//#endregion
|
|
25
|
-
export {
|
|
18
|
+
export { APP_RSC_RENDER_MODE_NAVIGATION, APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, getRscRenderModeCacheVariant, parseAppRscRenderMode };
|
|
@@ -31,10 +31,9 @@ type NormalizedRscRequest = {
|
|
|
31
31
|
* 4. Collapse double-slashes, resolve `.` and `..` segments (normalizePath)
|
|
32
32
|
* 5. basePath check + strip — 404 when pathname lacks the basePath prefix.
|
|
33
33
|
* `/__vinext/` bypasses this for internal prerender endpoints.
|
|
34
|
-
* 6. RSC detection: `.rsc` suffix
|
|
35
|
-
* `_rsc` cache-busting query
|
|
36
|
-
*
|
|
37
|
-
* store Flight responses under HTML URLs.
|
|
34
|
+
* 6. RSC detection: `.rsc` suffix or Next-style `RSC: 1`. The internal
|
|
35
|
+
* `_rsc` cache-busting query is validated separately so full-route Flight
|
|
36
|
+
* responses do not share the canonical HTML URL in caches that ignore Vary.
|
|
38
37
|
* 7. cleanPathname — pathname with `.rsc` suffix stripped
|
|
39
38
|
* 8. Sanitize X-Vinext-Interception-Context — strip null bytes (header injection)
|
|
40
39
|
* 9. Normalize x-vinext-mounted-slots — dedup and sort for canonical cache keys
|
|
@@ -27,10 +27,9 @@ import { normalizeInterceptionContextHeader } from "./app-interception-context-h
|
|
|
27
27
|
* 4. Collapse double-slashes, resolve `.` and `..` segments (normalizePath)
|
|
28
28
|
* 5. basePath check + strip — 404 when pathname lacks the basePath prefix.
|
|
29
29
|
* `/__vinext/` bypasses this for internal prerender endpoints.
|
|
30
|
-
* 6. RSC detection: `.rsc` suffix
|
|
31
|
-
* `_rsc` cache-busting query
|
|
32
|
-
*
|
|
33
|
-
* store Flight responses under HTML URLs.
|
|
30
|
+
* 6. RSC detection: `.rsc` suffix or Next-style `RSC: 1`. The internal
|
|
31
|
+
* `_rsc` cache-busting query is validated separately so full-route Flight
|
|
32
|
+
* responses do not share the canonical HTML URL in caches that ignore Vary.
|
|
34
33
|
* 7. cleanPathname — pathname with `.rsc` suffix stripped
|
|
35
34
|
* 8. Sanitize X-Vinext-Interception-Context — strip null bytes (header injection)
|
|
36
35
|
* 9. Normalize x-vinext-mounted-slots — dedup and sort for canonical cache keys
|
|
@@ -57,7 +56,7 @@ function normalizeRscRequest(request, basePath, allowOutsideBasePath = false) {
|
|
|
57
56
|
if (!hadBasePath && !pathname.startsWith("/__vinext/") && !allowOutsideBasePath) return notFoundResponse();
|
|
58
57
|
if (hadBasePath) pathname = stripBasePath(pathname, basePath);
|
|
59
58
|
}
|
|
60
|
-
const isRscRequest = pathname.endsWith(".rsc") || request.headers.get("RSC") === "1"
|
|
59
|
+
const isRscRequest = pathname.endsWith(".rsc") || request.headers.get("RSC") === "1";
|
|
61
60
|
const cleanPathname = stripRscSuffix(pathname);
|
|
62
61
|
const interceptionContextHeader = normalizeInterceptionContextHeader(request.headers.get(VINEXT_INTERCEPTION_CONTEXT_HEADER));
|
|
63
62
|
const mountedSlotsHeader = normalizeMountedSlotsHeader(request.headers.get(VINEXT_MOUNTED_SLOTS_HEADER));
|
|
@@ -4,7 +4,7 @@ import { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS
|
|
|
4
4
|
import { isExternalUrl } from "../config/config-matchers.js";
|
|
5
5
|
import { internalServerErrorResponse, payloadTooLargeResponse } from "./http-error-responses.js";
|
|
6
6
|
import { validateCsrfOrigin, validateServerActionPayload } from "./request-pipeline.js";
|
|
7
|
-
import {
|
|
7
|
+
import { APP_RSC_RENDER_MODE_NAVIGATION } from "./app-rsc-render-mode.js";
|
|
8
8
|
import { headersContextFromRequest, isDraftModeRequest, setHeadersContext } from "../shims/headers.js";
|
|
9
9
|
import { getAndClearActionRevalidationKind } from "../shims/cache-request-state.js";
|
|
10
10
|
import { setCurrentFetchCacheMode, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault } from "../shims/fetch-cache.js";
|
|
@@ -684,7 +684,7 @@ async function handleServerActionRscRequest(options) {
|
|
|
684
684
|
request: redirectRenderRequest,
|
|
685
685
|
route: targetMatch.route,
|
|
686
686
|
searchParams: redirectSearchParams,
|
|
687
|
-
renderMode:
|
|
687
|
+
renderMode: APP_RSC_RENDER_MODE_NAVIGATION,
|
|
688
688
|
observeMetadataSearchParamsAccess: redirectDynamicConfig !== "force-static",
|
|
689
689
|
observePageSearchParamsAccess: redirectDynamicConfig !== "force-static"
|
|
690
690
|
});
|
|
@@ -737,6 +737,7 @@ async function handleServerActionRscRequest(options) {
|
|
|
737
737
|
const match = options.matchRoute(options.cleanPathname);
|
|
738
738
|
let element;
|
|
739
739
|
let errorPattern = match ? match.route.pattern : options.cleanPathname;
|
|
740
|
+
const actionRerenderIsRscRequest = true;
|
|
740
741
|
if (match) {
|
|
741
742
|
const { route: actionRoute, params: actionParams } = match;
|
|
742
743
|
const actionRerenderTarget = await resolveAppPageActionRerenderTarget({
|
|
@@ -746,7 +747,7 @@ async function handleServerActionRscRequest(options) {
|
|
|
746
747
|
findIntercept: options.findIntercept,
|
|
747
748
|
getRouteParamNames: options.getRouteParamNames,
|
|
748
749
|
getSourceRoute: options.getSourceRoute,
|
|
749
|
-
isRscRequest:
|
|
750
|
+
isRscRequest: actionRerenderIsRscRequest,
|
|
750
751
|
toInterceptOpts: options.toInterceptOpts
|
|
751
752
|
});
|
|
752
753
|
const resolvedActionNavigationParams = resolveAppPageNavigationParams(actionRerenderTarget.route, actionRerenderTarget.navigationParams, options.cleanPathname, actionRerenderTarget.interceptOpts);
|
|
@@ -771,13 +772,13 @@ async function handleServerActionRscRequest(options) {
|
|
|
771
772
|
const buildActionRerenderElement = () => options.buildPageElement({
|
|
772
773
|
cleanPathname: options.cleanPathname,
|
|
773
774
|
interceptOpts: actionRerenderTarget.interceptOpts,
|
|
774
|
-
isRscRequest:
|
|
775
|
+
isRscRequest: actionRerenderIsRscRequest,
|
|
775
776
|
mountedSlotsHeader: options.mountedSlotsHeader,
|
|
776
777
|
params: actionRerenderTarget.params,
|
|
777
778
|
request: options.request,
|
|
778
779
|
route: actionRerenderTarget.route,
|
|
779
780
|
searchParams: actionRerenderSearchParams,
|
|
780
|
-
renderMode:
|
|
781
|
+
renderMode: APP_RSC_RENDER_MODE_NAVIGATION,
|
|
781
782
|
observeMetadataSearchParamsAccess: actionRerenderDynamicConfig !== "force-static",
|
|
782
783
|
observePageSearchParamsAccess: actionRerenderDynamicConfig !== "force-static"
|
|
783
784
|
});
|
|
@@ -12,11 +12,11 @@ import { runWithNavigationContext } from "../shims/navigation-state.js";
|
|
|
12
12
|
import { withScriptNonce } from "../shims/script-nonce-context.js";
|
|
13
13
|
import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr, safeJsonStringify } from "./html.js";
|
|
14
14
|
import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
|
|
15
|
+
import { setPagesClientAssets } from "./pages-client-assets.js";
|
|
15
16
|
import DefaultGlobalError from "../shims/default-global-error.js";
|
|
16
17
|
import { BfcacheStateKeyMapContext, ElementsContext, Slot } from "../shims/slot.js";
|
|
17
18
|
import { createSsrErrorMetaRenderer } from "./app-ssr-error-meta.js";
|
|
18
19
|
import { createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform } from "./app-ssr-stream.js";
|
|
19
|
-
import { setPagesClientAssets } from "./pages-client-assets.js";
|
|
20
20
|
import { createInitialBfcacheMaps } from "./app-bfcache-identity.js";
|
|
21
21
|
import { RSC_FORM_STATE_GLOBAL } from "./app-browser-hydration.js";
|
|
22
22
|
import { createClientReferencePreloader } from "./app-client-reference-preloader.js";
|
|
@@ -2,8 +2,10 @@ import { CdnCacheableHeaderInput } from "../shims/cdn-cache.js";
|
|
|
2
2
|
|
|
3
3
|
//#region src/server/cache-control.d.ts
|
|
4
4
|
declare const NEVER_CACHE_CONTROL = "private, no-cache, no-store, max-age=0, must-revalidate";
|
|
5
|
+
declare const BROWSER_REVALIDATE_CACHE_CONTROL = "public, max-age=0, must-revalidate";
|
|
5
6
|
declare const STATIC_CACHE_CONTROL = "s-maxage=31536000, stale-while-revalidate";
|
|
6
7
|
declare const NO_STORE_CACHE_CONTROL = "no-store, must-revalidate";
|
|
8
|
+
declare function shouldUseNextDeployCacheControl(): boolean;
|
|
7
9
|
/**
|
|
8
10
|
* Route a cacheable response's headers through the active CDN cache adapter and
|
|
9
11
|
* apply the result to `headers`. The default adapter yields a single
|
|
@@ -34,4 +36,4 @@ declare function buildRevalidateCacheControl(revalidateSeconds: number, expireSe
|
|
|
34
36
|
*/
|
|
35
37
|
declare function buildCachedRevalidateCacheControl(cacheState: "HIT" | "STALE", revalidateSeconds: number, expireSeconds?: number): string;
|
|
36
38
|
//#endregion
|
|
37
|
-
export { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, STATIC_CACHE_CONTROL, applyCdnResponseHeaders, buildCachedRevalidateCacheControl, buildRevalidateCacheControl };
|
|
39
|
+
export { BROWSER_REVALIDATE_CACHE_CONTROL, NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, STATIC_CACHE_CONTROL, applyCdnResponseHeaders, buildCachedRevalidateCacheControl, buildRevalidateCacheControl, shouldUseNextDeployCacheControl };
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
|
|
2
2
|
//#region src/server/cache-control.ts
|
|
3
3
|
const NEVER_CACHE_CONTROL = "private, no-cache, no-store, max-age=0, must-revalidate";
|
|
4
|
+
const BROWSER_REVALIDATE_CACHE_CONTROL = "public, max-age=0, must-revalidate";
|
|
4
5
|
const STATIC_CACHE_CONTROL = "s-maxage=31536000, stale-while-revalidate";
|
|
5
6
|
const STALE_REVALIDATE_CACHE_CONTROL = "s-maxage=0, stale-while-revalidate";
|
|
6
7
|
const NO_STORE_CACHE_CONTROL = "no-store, must-revalidate";
|
|
8
|
+
const SHARED_CACHE_DIRECTIVE_RE = /(?:^|,)\s*s-maxage\s*=/i;
|
|
9
|
+
function shouldUseNextDeployCacheControl() {
|
|
10
|
+
return process.env.VINEXT_NEXT_DEPLOY_CACHE_CONTROL === "1";
|
|
11
|
+
}
|
|
12
|
+
function isSharedCacheControl(cacheControl) {
|
|
13
|
+
return SHARED_CACHE_DIRECTIVE_RE.test(cacheControl);
|
|
14
|
+
}
|
|
7
15
|
/**
|
|
8
16
|
* Route a cacheable response's headers through the active CDN cache adapter and
|
|
9
17
|
* apply the result to `headers`. The default adapter yields a single
|
|
@@ -17,6 +25,10 @@ const NO_STORE_CACHE_CONTROL = "no-store, must-revalidate";
|
|
|
17
25
|
*/
|
|
18
26
|
function applyCdnResponseHeaders(headers, input) {
|
|
19
27
|
headers.delete("Cache-Control");
|
|
28
|
+
if (shouldUseNextDeployCacheControl() && isSharedCacheControl(input.cacheControl)) {
|
|
29
|
+
headers.set("Cache-Control", BROWSER_REVALIDATE_CACHE_CONTROL);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
20
32
|
const map = getCdnCacheAdapter().buildResponseHeaders(input);
|
|
21
33
|
for (const [name, value] of Object.entries(map)) {
|
|
22
34
|
if (value === null) {
|
|
@@ -53,4 +65,4 @@ function buildCachedRevalidateCacheControl(cacheState, revalidateSeconds, expire
|
|
|
53
65
|
return buildRevalidateCacheControl(revalidateSeconds, expireSeconds);
|
|
54
66
|
}
|
|
55
67
|
//#endregion
|
|
56
|
-
export { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, STATIC_CACHE_CONTROL, applyCdnResponseHeaders, buildCachedRevalidateCacheControl, buildRevalidateCacheControl };
|
|
68
|
+
export { BROWSER_REVALIDATE_CACHE_CONTROL, NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL, STATIC_CACHE_CONTROL, applyCdnResponseHeaders, buildCachedRevalidateCacheControl, buildRevalidateCacheControl, shouldUseNextDeployCacheControl };
|
|
@@ -8,7 +8,7 @@ import { ESModulesEvaluator, ModuleRunner, createNodeImportMeta } from "vite/mod
|
|
|
8
8
|
*
|
|
9
9
|
* ## Why this exists
|
|
10
10
|
*
|
|
11
|
-
* Vite
|
|
11
|
+
* Vite's `server.ssrLoadModule()` and the lazy `RunnableDevEnvironment.runner`
|
|
12
12
|
* getter both use `SSRCompatModuleRunner`, which constructs a
|
|
13
13
|
* `createServerModuleRunnerTransport` synchronously. That transport calls
|
|
14
14
|
* `connect()` immediately, which reads `environment.hot.api.outsideEmitter` —
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Prevents external websites from making cross-origin requests to the
|
|
6
6
|
* local dev server and reading the responses (data exfiltration).
|
|
7
7
|
*
|
|
8
|
-
* Vite
|
|
9
|
-
*
|
|
8
|
+
* Vite provides built-in CORS and WebSocket origin protection, but vinext
|
|
9
|
+
* overrides Vite's CORS config to allow OPTIONS passthrough.
|
|
10
10
|
* This module adds origin verification to vinext's own request handlers.
|
|
11
11
|
*/
|
|
12
12
|
/**
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Prevents external websites from making cross-origin requests to the
|
|
6
6
|
* local dev server and reading the responses (data exfiltration).
|
|
7
7
|
*
|
|
8
|
-
* Vite
|
|
9
|
-
*
|
|
8
|
+
* Vite provides built-in CORS and WebSocket origin protection, but vinext
|
|
9
|
+
* overrides Vite's CORS config to allow OPTIONS passthrough.
|
|
10
10
|
* This module adds origin verification to vinext's own request handlers.
|
|
11
11
|
*/
|
|
12
12
|
/**
|
|
@@ -43,7 +43,17 @@ declare function createSSRHandler(server: ViteDevServer, runner: ModuleImporter,
|
|
|
43
43
|
* `next.config`. When undefined or empty, no meta tags are emitted.
|
|
44
44
|
*/
|
|
45
45
|
|
|
46
|
-
clientTraceMetadata?: readonly string[], htmlLimitedBots?: string
|
|
46
|
+
clientTraceMetadata?: readonly string[], htmlLimitedBots?: string,
|
|
47
|
+
/**
|
|
48
|
+
* Whether `reactStrictMode: true` is set in next.config. When true, the dev
|
|
49
|
+
* hydration script sets `window.__VINEXT_REACT_STRICT_MODE__` so
|
|
50
|
+
* `wrapWithRouterContext` wraps the tree in `<React.StrictMode>` on the
|
|
51
|
+
* initial hydration and every navigation. Pages Router default is OFF
|
|
52
|
+
* (Next.js: `reactStrictMode === null ? false`), so callers pass
|
|
53
|
+
* `nextConfig?.reactStrictMode === true`.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
reactStrictMode?: boolean): (req: IncomingMessage, res: ServerResponse, url: string, /** Status code override — propagated from middleware rewrite status. */
|
|
47
57
|
|
|
48
58
|
statusCode?: number,
|
|
49
59
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
|
|
2
|
-
import { createValidFileMatcher, findFileWithExtensions } from "../routing/file-matcher.js";
|
|
2
|
+
import { createValidFileMatcher, findFileWithExtensions, findFileWithExts } from "../routing/file-matcher.js";
|
|
3
3
|
import { patternToNextFormat } from "../routing/route-validation.js";
|
|
4
4
|
import { matchRoute } from "../routing/pages-router.js";
|
|
5
5
|
import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
|
|
@@ -17,7 +17,7 @@ import "../shims/router-state.js";
|
|
|
17
17
|
import { runWithHeadState } from "../shims/head-state.js";
|
|
18
18
|
import { runWithServerInsertedHTMLState } from "../shims/navigation-state.js";
|
|
19
19
|
import { withScriptNonce } from "../shims/script-nonce-context.js";
|
|
20
|
-
import { createInlineScriptTag, createNonceAttribute, safeJsonStringify } from "./html.js";
|
|
20
|
+
import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr, safeJsonStringify } from "./html.js";
|
|
21
21
|
import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
|
|
22
22
|
import { getScriptNonceFromNodeHeaderSources } from "./csp.js";
|
|
23
23
|
import { logRequest, now } from "./request-log.js";
|
|
@@ -31,8 +31,9 @@ import { hasPagesGetInitialProps, loadDevAppInitialProps, loadPagesGetInitialPro
|
|
|
31
31
|
import { isSerializableProps } from "./pages-serializable-props.js";
|
|
32
32
|
import { isBotUserAgent } from "../utils/html-limited-bots.js";
|
|
33
33
|
import { getPagesRouteParams, matchesPagesStaticPath } from "./pages-page-data.js";
|
|
34
|
-
import { createPagesDevModuleUrl } from "./pages-dev-module-url.js";
|
|
35
|
-
import {
|
|
34
|
+
import { createPagesDevAssetUrl, createPagesDevModuleUrl } from "./pages-dev-module-url.js";
|
|
35
|
+
import { getManifestFilesForModule } from "./pages-asset-tags.js";
|
|
36
|
+
import { attachPagesRequestCookies, getPagesPreviewDataFromCookieHeader } from "./pages-node-compat.js";
|
|
36
37
|
import path from "node:path";
|
|
37
38
|
import React from "react";
|
|
38
39
|
import { renderToReadableStream } from "react-dom/server.edge";
|
|
@@ -52,6 +53,35 @@ async function renderToStringAsync(element) {
|
|
|
52
53
|
async function renderIsrPassToStringAsync(element) {
|
|
53
54
|
return await runWithServerInsertedHTMLState(() => runWithHeadState(() => _runWithCacheState(() => runWithPrivateCache(() => runWithFetchCache(async () => renderToStringAsync(element))))));
|
|
54
55
|
}
|
|
56
|
+
const DEV_STYLESHEET_ASSET_RE = /\.(?:css|scss|sass)$/i;
|
|
57
|
+
function createDevInitialStylesheetHeadHTML(options) {
|
|
58
|
+
const { ssrManifest, moduleIds, nonceAttr } = options;
|
|
59
|
+
if (!ssrManifest || moduleIds.length === 0) return "";
|
|
60
|
+
const seen = /* @__PURE__ */ new Set();
|
|
61
|
+
let html = "";
|
|
62
|
+
for (const moduleId of moduleIds) {
|
|
63
|
+
const files = getManifestFilesForModule(ssrManifest, moduleId);
|
|
64
|
+
if (!files) continue;
|
|
65
|
+
for (const file of files) {
|
|
66
|
+
if (!DEV_STYLESHEET_ASSET_RE.test(file) || seen.has(file)) continue;
|
|
67
|
+
seen.add(file);
|
|
68
|
+
const href = createPagesDevAssetUrl(file);
|
|
69
|
+
html += `<link rel="stylesheet"${nonceAttr} href="${escapeHtmlAttr(href)}" />\n `;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return html;
|
|
73
|
+
}
|
|
74
|
+
async function collectDevInitialStylesheetHeadHTML(runner, moduleIds, nonceAttr) {
|
|
75
|
+
try {
|
|
76
|
+
return createDevInitialStylesheetHeadHTML({
|
|
77
|
+
ssrManifest: (await runner.import("virtual:vinext-pages-client-assets")).default?.ssrManifest,
|
|
78
|
+
moduleIds,
|
|
79
|
+
nonceAttr
|
|
80
|
+
});
|
|
81
|
+
} catch {
|
|
82
|
+
return "";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
55
85
|
/**
|
|
56
86
|
* Emit a `getServerSideProps` / `getStaticProps` `{ redirect }` result.
|
|
57
87
|
*
|
|
@@ -102,7 +132,7 @@ const STREAM_BODY_MARKER = "<!--VINEXT_STREAM_BODY-->";
|
|
|
102
132
|
* shell sooner).
|
|
103
133
|
*/
|
|
104
134
|
async function streamPageToResponse(res, element, options) {
|
|
105
|
-
const { url, server, fontHeadHTML, scripts, DocumentComponent, statusCode, extraHeaders, getHeadHTML, enhancePageElement, scriptNonce, documentContext, setDocumentInitialHead, bufferBodyBeforeHeaders = false } = options;
|
|
135
|
+
const { url, server, fontHeadHTML, assetHeadHTML = "", scripts, DocumentComponent, statusCode, extraHeaders, getHeadHTML, enhancePageElement, scriptNonce, documentContext, setDocumentInitialHead, bufferBodyBeforeHeaders = false } = options;
|
|
106
136
|
const documentRenderPage = await runDocumentRenderPage({
|
|
107
137
|
DocumentComponent,
|
|
108
138
|
enhancePageElement,
|
|
@@ -129,7 +159,7 @@ async function streamPageToResponse(res, element, options) {
|
|
|
129
159
|
const docProps = documentRenderPage.status === "skipped" ? await loadUserDocumentInitialProps(DocumentComponent) : documentRenderPage.docProps;
|
|
130
160
|
let docHtml = await renderToStringAsync(docProps ? React.createElement(DocumentComponent, docProps) : React.createElement(DocumentComponent));
|
|
131
161
|
docHtml = docHtml.replace("__NEXT_MAIN__", STREAM_BODY_MARKER);
|
|
132
|
-
if (headHTML || fontHeadHTML) docHtml = docHtml.replace("</head>", ` ${fontHeadHTML}${headHTML}\n</head>`);
|
|
162
|
+
if (headHTML || fontHeadHTML || assetHeadHTML) docHtml = docHtml.replace("</head>", ` ${fontHeadHTML}${headHTML}\n ${assetHeadHTML}\n</head>`);
|
|
133
163
|
docHtml = docHtml.replace("<!-- __NEXT_SCRIPTS__ -->", scripts);
|
|
134
164
|
if (!docHtml.includes("__NEXT_DATA__")) docHtml = docHtml.replace("</body>", ` ${scripts}\n</body>`);
|
|
135
165
|
shellTemplate = docHtml;
|
|
@@ -137,6 +167,7 @@ async function streamPageToResponse(res, element, options) {
|
|
|
137
167
|
<html>
|
|
138
168
|
<head>
|
|
139
169
|
${fontHeadHTML}${headHTML}
|
|
170
|
+
${assetHeadHTML}
|
|
140
171
|
</head>
|
|
141
172
|
<body>
|
|
142
173
|
<div id="__next">${STREAM_BODY_MARKER}</div>
|
|
@@ -149,7 +180,7 @@ async function streamPageToResponse(res, element, options) {
|
|
|
149
180
|
const suffix = transformedShell.slice(markerIdx + 25);
|
|
150
181
|
const bufferedBody = bufferBodyBeforeHeaders ? await new Response(bodyStream).text() : null;
|
|
151
182
|
const headers = {
|
|
152
|
-
"Content-Type": "text/html",
|
|
183
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
153
184
|
"Transfer-Encoding": "chunked"
|
|
154
185
|
};
|
|
155
186
|
if (extraHeaders) for (const [key, val] of Object.entries(extraHeaders)) if (Array.isArray(val)) res.setHeader(key, val);
|
|
@@ -204,7 +235,7 @@ function parseCookieLocale(req, i18nConfig) {
|
|
|
204
235
|
* 4. Render the component to HTML
|
|
205
236
|
* 5. Wrap in _document shell and send response
|
|
206
237
|
*/
|
|
207
|
-
function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false, hasMiddleware = false, hasRewrites = false, clientTraceMetadata, htmlLimitedBots) {
|
|
238
|
+
function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false, hasMiddleware = false, hasRewrites = false, clientTraceMetadata, htmlLimitedBots, reactStrictMode = false) {
|
|
208
239
|
const matcher = fileMatcher ?? createValidFileMatcher();
|
|
209
240
|
const pagePatterns = routes.map((r) => patternToNextFormat(r.pattern));
|
|
210
241
|
const _alsRegistration = Promise.all([runner.import("vinext/head-state"), runner.import("vinext/router-state")]);
|
|
@@ -337,6 +368,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
337
368
|
let isrRevalidateSeconds = null;
|
|
338
369
|
let isFallbackRender = false;
|
|
339
370
|
let shouldPersistFallbackData = false;
|
|
371
|
+
let staticPropsPreviewData = false;
|
|
340
372
|
if (typeof pageModule.getStaticPaths === "function" && route.isDynamic) {
|
|
341
373
|
const pathsResult = await pageModule.getStaticPaths({
|
|
342
374
|
locales: i18nConfig?.locales ?? [],
|
|
@@ -416,6 +448,12 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
416
448
|
__N_SSP: true
|
|
417
449
|
};
|
|
418
450
|
const headersBeforeGSSP = new Set(Object.keys(res.getHeaders()));
|
|
451
|
+
const previewData = getPagesPreviewDataFromCookieHeader(req.headers.cookie);
|
|
452
|
+
const previewContext = previewData === false ? {} : {
|
|
453
|
+
draftMode: true,
|
|
454
|
+
preview: true,
|
|
455
|
+
previewData
|
|
456
|
+
};
|
|
419
457
|
const context = {
|
|
420
458
|
params: userFacingParams,
|
|
421
459
|
req,
|
|
@@ -424,7 +462,8 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
424
462
|
resolvedUrl: gsspResolvedUrl,
|
|
425
463
|
locale: locale ?? currentDefaultLocale,
|
|
426
464
|
locales: i18nConfig?.locales,
|
|
427
|
-
defaultLocale: currentDefaultLocale
|
|
465
|
+
defaultLocale: currentDefaultLocale,
|
|
466
|
+
...previewContext
|
|
428
467
|
};
|
|
429
468
|
const result = await pageModule.getServerSideProps(context);
|
|
430
469
|
if (res.writableEnded) return;
|
|
@@ -480,7 +519,14 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
480
519
|
const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
|
|
481
520
|
const cached = await isrGet(cacheKey);
|
|
482
521
|
const isOnDemandRevalidate = isOnDemandRevalidateRequest(req.headers[PRERENDER_REVALIDATE_HEADER]);
|
|
483
|
-
|
|
522
|
+
const previewData = getPagesPreviewDataFromCookieHeader(req.headers.cookie, { isOnDemandRevalidate });
|
|
523
|
+
staticPropsPreviewData = previewData;
|
|
524
|
+
const previewContext = previewData === false ? {} : {
|
|
525
|
+
draftMode: true,
|
|
526
|
+
preview: true,
|
|
527
|
+
previewData
|
|
528
|
+
};
|
|
529
|
+
if (!isOnDemandRevalidate && cached && !cached.isStale && cached.value.value?.kind === "PAGES" && !cached.value.value.generatedFromDataRequest && !scriptNonce && !isDataReq && previewData === false) {
|
|
484
530
|
const cachedHtml = cached.value.value.html;
|
|
485
531
|
const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
|
|
486
532
|
const { cacheControl: hitCacheControl } = decideIsr({
|
|
@@ -489,7 +535,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
489
535
|
revalidateSeconds: getRevalidateDuration(cacheKey) ?? 60
|
|
490
536
|
});
|
|
491
537
|
const hitHeaders = {
|
|
492
|
-
"Content-Type": "text/html",
|
|
538
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
493
539
|
...buildCacheStateHeaders("HIT"),
|
|
494
540
|
"Cache-Control": hitCacheControl
|
|
495
541
|
};
|
|
@@ -498,7 +544,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
498
544
|
res.end(transformedHtml);
|
|
499
545
|
return;
|
|
500
546
|
}
|
|
501
|
-
if (!isOnDemandRevalidate && cached && cached.isStale && cached.value.value?.kind === "PAGES" && !cached.value.value.generatedFromDataRequest && !scriptNonce && !isDataReq) {
|
|
547
|
+
if (!isOnDemandRevalidate && cached && cached.isStale && cached.value.value?.kind === "PAGES" && !cached.value.value.generatedFromDataRequest && !scriptNonce && !isDataReq && previewData === false) {
|
|
502
548
|
const cachedHtml = cached.value.value.html;
|
|
503
549
|
const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
|
|
504
550
|
triggerBackgroundRegeneration(cacheKey, async () => {
|
|
@@ -663,7 +709,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
663
709
|
revalidateSeconds: getRevalidateDuration(cacheKey) ?? 60
|
|
664
710
|
});
|
|
665
711
|
const staleHeaders = {
|
|
666
|
-
"Content-Type": "text/html",
|
|
712
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
667
713
|
...buildCacheStateHeaders("STALE"),
|
|
668
714
|
"Cache-Control": staleCacheControl
|
|
669
715
|
};
|
|
@@ -677,9 +723,10 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
677
723
|
locale: locale ?? currentDefaultLocale,
|
|
678
724
|
locales: i18nConfig?.locales,
|
|
679
725
|
defaultLocale: currentDefaultLocale,
|
|
680
|
-
revalidateReason: isOnDemandRevalidate ? "on-demand" : "stale"
|
|
726
|
+
revalidateReason: isOnDemandRevalidate ? "on-demand" : "stale",
|
|
727
|
+
...previewContext
|
|
681
728
|
};
|
|
682
|
-
const generatedPageData = !isOnDemandRevalidate && cached?.isStale === false && cached?.value.value?.kind === "PAGES" && cached.value.value.generatedFromDataRequest && isUnknownRecord(cached.value.value.pageData) ? cached.value.value.pageData : null;
|
|
729
|
+
const generatedPageData = !isOnDemandRevalidate && previewData === false && cached?.isStale === false && cached?.value.value?.kind === "PAGES" && cached.value.value.generatedFromDataRequest && isUnknownRecord(cached.value.value.pageData) ? cached.value.value.pageData : null;
|
|
683
730
|
if (!generatedPageData && await loadAppInitialProps()) return;
|
|
684
731
|
const result = generatedPageData ? null : await pageModule.getStaticProps(context);
|
|
685
732
|
if (generatedPageData) {
|
|
@@ -713,8 +760,8 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
713
760
|
return;
|
|
714
761
|
}
|
|
715
762
|
if (result && "props" in result) isSerializableProps(patternToNextFormat(route.pattern), "getStaticProps", pageProps);
|
|
716
|
-
if (typeof result?.revalidate === "number" && result.revalidate > 0) isrRevalidateSeconds = result.revalidate;
|
|
717
|
-
else if (cached?.value.value?.kind === "PAGES" && cached.value.value.generatedFromDataRequest) isrRevalidateSeconds = cached.value.cacheControl?.revalidate ?? 31536e3;
|
|
763
|
+
if (previewData === false && typeof result?.revalidate === "number" && result.revalidate > 0) isrRevalidateSeconds = result.revalidate;
|
|
764
|
+
else if (previewData === false && cached?.value.value?.kind === "PAGES" && cached.value.value.generatedFromDataRequest) isrRevalidateSeconds = cached.value.cacheControl?.revalidate ?? 31536e3;
|
|
718
765
|
}
|
|
719
766
|
if (typeof pageModule.getServerSideProps !== "function" && typeof pageModule.getStaticProps !== "function" && hasAppGetInitialProps) {
|
|
720
767
|
if (await loadAppInitialProps()) return;
|
|
@@ -743,7 +790,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
743
790
|
}
|
|
744
791
|
}
|
|
745
792
|
if (isDataReq) {
|
|
746
|
-
if (shouldPersistFallbackData) {
|
|
793
|
+
if (shouldPersistFallbackData && staticPropsPreviewData === false) {
|
|
747
794
|
const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
|
|
748
795
|
const revalidateSeconds = isrRevalidateSeconds ?? 31536e3;
|
|
749
796
|
await isrSet(cacheKey, {
|
|
@@ -786,6 +833,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
|
|
|
786
833
|
if (typeof dynamicShim.flushPreloads === "function") await dynamicShim.flushPreloads();
|
|
787
834
|
const nonceAttr = createNonceAttribute(scriptNonce);
|
|
788
835
|
let fontHeadHTML = "";
|
|
836
|
+
const assetHeadHTML = await collectDevInitialStylesheetHeadHTML(runner, [AppComponent ? findFileWithExts(pagesDir, "_app", matcher) : null, route.filePath], nonceAttr);
|
|
789
837
|
const allFontStyles = [];
|
|
790
838
|
const allFontPreloads = [];
|
|
791
839
|
try {
|
|
@@ -847,6 +895,9 @@ const rawPageProps = props.pageProps;
|
|
|
847
895
|
const pageProps = rawPageProps && typeof rawPageProps === "object" ? rawPageProps : {};
|
|
848
896
|
window.__VINEXT_PAGE_LOADERS__ = { [nextData.page]: () => import("${pageModuleSource}") };
|
|
849
897
|
window.__VINEXT_APP_LOADER__ = ${appModuleSource ? `() => import("${appModuleSource}")` : "undefined"};
|
|
898
|
+
// reactStrictMode flag — read by wrapWithRouterContext so the <React.StrictMode>
|
|
899
|
+
// wrap is applied on initial hydration and every navigation (matches Next.js).
|
|
900
|
+
window.__VINEXT_REACT_STRICT_MODE__ = ${JSON.stringify(reactStrictMode === true)};
|
|
850
901
|
|
|
851
902
|
async function hydrate() {
|
|
852
903
|
let hydrateRootOptions;
|
|
@@ -923,6 +974,7 @@ hydrate();
|
|
|
923
974
|
url,
|
|
924
975
|
server,
|
|
925
976
|
fontHeadHTML,
|
|
977
|
+
assetHeadHTML,
|
|
926
978
|
scripts: allScripts,
|
|
927
979
|
DocumentComponent,
|
|
928
980
|
statusCode,
|
|
@@ -969,7 +1021,7 @@ hydrate();
|
|
|
969
1021
|
pageProps
|
|
970
1022
|
}) : createElement(pageModule.default, pageProps);
|
|
971
1023
|
if (wrapWithRouterContext) isrElement = wrapWithRouterContext(isrElement);
|
|
972
|
-
const isrHtml = `<!DOCTYPE html><html><head
|
|
1024
|
+
const isrHtml = `<!DOCTYPE html><html><head>${assetHeadHTML}</head><body><div id="__next">${await renderIsrPassToStringAsync(withScriptNonce(isrElement, scriptNonce))}</div>${allScripts}</body></html>`;
|
|
973
1025
|
const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
|
|
974
1026
|
await isrSet(cacheKey, buildPagesCacheValue(isrHtml, pageProps), isrRevalidateSeconds);
|
|
975
1027
|
setRevalidateDuration(cacheKey, isrRevalidateSeconds);
|
|
@@ -1008,14 +1060,15 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
|
|
|
1008
1060
|
const matcher = fileMatcher ?? createValidFileMatcher();
|
|
1009
1061
|
const candidates = statusCode === 404 ? ["404", "_error"] : statusCode === 500 ? ["500", "_error"] : ["_error"];
|
|
1010
1062
|
for (const candidate of candidates) try {
|
|
1011
|
-
const
|
|
1012
|
-
if (!
|
|
1013
|
-
const ErrorComponent = (await importModule(runner,
|
|
1063
|
+
const errorAssetPath = findFileWithExts(pagesDir, candidate, matcher);
|
|
1064
|
+
if (!errorAssetPath) continue;
|
|
1065
|
+
const ErrorComponent = (await importModule(runner, errorAssetPath)).default;
|
|
1014
1066
|
if (!ErrorComponent) continue;
|
|
1015
1067
|
let AppComponent = null;
|
|
1016
1068
|
const appPathErr = path.join(pagesDir, "_app");
|
|
1069
|
+
const appAssetPath = findFileWithExts(pagesDir, "_app", matcher);
|
|
1017
1070
|
if (findFileWithExtensions(appPathErr, matcher)) try {
|
|
1018
|
-
AppComponent = (await importModule(runner, appPathErr)).default ?? null;
|
|
1071
|
+
AppComponent = (await importModule(runner, appAssetPath ?? appPathErr)).default ?? null;
|
|
1019
1072
|
} catch {}
|
|
1020
1073
|
const createElement = React.createElement;
|
|
1021
1074
|
const initialErrorProps = await loadPagesGetInitialProps(ErrorComponent, {
|
|
@@ -1051,12 +1104,16 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
|
|
|
1051
1104
|
const element = createErrorElement(AppComponent, ErrorComponent);
|
|
1052
1105
|
const headShim = await importModule(runner, "next/head");
|
|
1053
1106
|
if (typeof headShim.resetSSRHead === "function") headShim.resetSSRHead();
|
|
1107
|
+
const responseHeaders = typeof res.getHeaders === "function" ? res.getHeaders() : void 0;
|
|
1108
|
+
const nonceAttr = createNonceAttribute(getScriptNonceFromNodeHeaderSources(req.headers, responseHeaders));
|
|
1109
|
+
const assetHeadHTML = await collectDevInitialStylesheetHeadHTML(runner, [appAssetPath, errorAssetPath], nonceAttr);
|
|
1054
1110
|
if (DocumentComponent) {
|
|
1055
1111
|
const errorPathname = candidate === "_error" ? "/_error" : `/${candidate}`;
|
|
1056
1112
|
await streamPageToResponse(res, element, {
|
|
1057
1113
|
url,
|
|
1058
1114
|
server,
|
|
1059
1115
|
fontHeadHTML: "",
|
|
1116
|
+
assetHeadHTML,
|
|
1060
1117
|
scripts: "",
|
|
1061
1118
|
DocumentComponent,
|
|
1062
1119
|
statusCode,
|
|
@@ -1084,13 +1141,14 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
|
|
|
1084
1141
|
<head>
|
|
1085
1142
|
<meta charset="utf-8" />
|
|
1086
1143
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1144
|
+
${assetHeadHTML}
|
|
1087
1145
|
</head>
|
|
1088
1146
|
<body>
|
|
1089
1147
|
<div id="__next">${await renderToStringAsync(element)}</div>
|
|
1090
1148
|
</body>
|
|
1091
1149
|
</html>`;
|
|
1092
1150
|
const transformedHtml = await server.transformIndexHtml(url, html);
|
|
1093
|
-
res.writeHead(statusCode, { "Content-Type": "text/html" });
|
|
1151
|
+
res.writeHead(statusCode, { "Content-Type": "text/html; charset=utf-8" });
|
|
1094
1152
|
res.end(transformedHtml);
|
|
1095
1153
|
}
|
|
1096
1154
|
return;
|
package/dist/server/headers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_SET_COOKIE_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER } from "../utils/protocol-headers.js";
|
|
1
|
+
import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
|
|
2
2
|
|
|
3
3
|
//#region src/server/headers.d.ts
|
|
4
4
|
/**
|
|
@@ -33,6 +33,10 @@ declare const VINEXT_PARAMS_HEADER = "X-Vinext-Params";
|
|
|
33
33
|
declare const VINEXT_MOUNTED_SLOTS_HEADER = "X-Vinext-Mounted-Slots";
|
|
34
34
|
/** Per-page dynamic stale time in seconds for App Router RSC responses. */
|
|
35
35
|
declare const VINEXT_DYNAMIC_STALE_TIME_HEADER = "X-Vinext-Dynamic-Stale-Time";
|
|
36
|
+
/** URL-encoded rendered path and search after middleware/config rewrites. */
|
|
37
|
+
declare const VINEXT_RENDERED_PATH_AND_SEARCH_HEADER = "X-Vinext-Rendered-Path-And-Search";
|
|
38
|
+
/** Prerender-only JSON side channel carrying request cacheLife metadata. */
|
|
39
|
+
declare const VINEXT_PRERENDER_CACHE_LIFE_HEADER = "x-vinext-prerender-cache-life";
|
|
36
40
|
/** Route interception context for parallel/intercepting routes. */
|
|
37
41
|
declare const VINEXT_INTERCEPTION_CONTEXT_HEADER = "X-Vinext-Interception-Context";
|
|
38
42
|
/** RSC render mode (e.g. "navigation", "prefetch"). */
|
|
@@ -101,4 +105,4 @@ declare const INTERNAL_HEADERS: string[];
|
|
|
101
105
|
/** Vinext-only internal headers stripped alongside Next.js protocol internals. */
|
|
102
106
|
declare const VINEXT_INTERNAL_HEADERS: string[];
|
|
103
107
|
//#endregion
|
|
104
|
-
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
|
108
|
+
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
package/dist/server/headers.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_SET_COOKIE_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER } from "../utils/protocol-headers.js";
|
|
1
|
+
import { MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_OVERRIDE_HEADERS, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
|
|
2
2
|
//#region src/server/headers.ts
|
|
3
3
|
/**
|
|
4
4
|
* Internal HTTP header name constants used throughout vinext.
|
|
@@ -32,6 +32,10 @@ const VINEXT_PARAMS_HEADER = "X-Vinext-Params";
|
|
|
32
32
|
const VINEXT_MOUNTED_SLOTS_HEADER = "X-Vinext-Mounted-Slots";
|
|
33
33
|
/** Per-page dynamic stale time in seconds for App Router RSC responses. */
|
|
34
34
|
const VINEXT_DYNAMIC_STALE_TIME_HEADER = "X-Vinext-Dynamic-Stale-Time";
|
|
35
|
+
/** URL-encoded rendered path and search after middleware/config rewrites. */
|
|
36
|
+
const VINEXT_RENDERED_PATH_AND_SEARCH_HEADER = "X-Vinext-Rendered-Path-And-Search";
|
|
37
|
+
/** Prerender-only JSON side channel carrying request cacheLife metadata. */
|
|
38
|
+
const VINEXT_PRERENDER_CACHE_LIFE_HEADER = "x-vinext-prerender-cache-life";
|
|
35
39
|
/** Route interception context for parallel/intercepting routes. */
|
|
36
40
|
const VINEXT_INTERCEPTION_CONTEXT_HEADER = "X-Vinext-Interception-Context";
|
|
37
41
|
/** RSC render mode (e.g. "navigation", "prefetch"). */
|
|
@@ -82,8 +86,6 @@ const MIDDLEWARE_NEXT_HEADER = "x-middleware-next";
|
|
|
82
86
|
const MIDDLEWARE_REWRITE_HEADER = "x-middleware-rewrite";
|
|
83
87
|
/** Redirect URL set by middleware. */
|
|
84
88
|
const MIDDLEWARE_REDIRECT_HEADER = "x-middleware-redirect";
|
|
85
|
-
/** Skip-middleware signal. */
|
|
86
|
-
const MIDDLEWARE_SKIP_HEADER = "x-middleware-skip";
|
|
87
89
|
const NEXT_ROUTER_STATE_TREE_HEADER = "Next-Router-State-Tree";
|
|
88
90
|
const NEXT_ROUTER_PREFETCH_HEADER = "Next-Router-Prefetch";
|
|
89
91
|
const NEXT_ROUTER_SEGMENT_PREFETCH_HEADER = "Next-Router-Segment-Prefetch";
|
|
@@ -120,6 +122,10 @@ const INTERNAL_HEADERS = [
|
|
|
120
122
|
ACTION_FORWARDED_HEADER
|
|
121
123
|
];
|
|
122
124
|
/** Vinext-only internal headers stripped alongside Next.js protocol internals. */
|
|
123
|
-
const VINEXT_INTERNAL_HEADERS = [
|
|
125
|
+
const VINEXT_INTERNAL_HEADERS = [
|
|
126
|
+
VINEXT_PRERENDER_ROUTE_PARAMS_HEADER,
|
|
127
|
+
VINEXT_PRERENDER_SPECULATIVE_HEADER,
|
|
128
|
+
VINEXT_PRERENDER_CACHE_LIFE_HEADER
|
|
129
|
+
];
|
|
124
130
|
//#endregion
|
|
125
|
-
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
|
131
|
+
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_STATIC_FILE_HEADER, VINEXT_TIMING_HEADER };
|
|
@@ -26,7 +26,18 @@ declare const IMAGE_OPTIMIZATION_PATH = "/_next/image";
|
|
|
26
26
|
* for any newly generated URLs.
|
|
27
27
|
*/
|
|
28
28
|
declare const VINEXT_IMAGE_OPTIMIZATION_PATH = "/_vinext/image";
|
|
29
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Returns true when `pathname` is either supported image optimization
|
|
31
|
+
* endpoint.
|
|
32
|
+
*
|
|
33
|
+
* A single trailing slash is accepted (`/_next/image/`): with
|
|
34
|
+
* `trailingSlash: true`, Next.js 308-redirects `/_next/image?url=...` to
|
|
35
|
+
* `/_next/image/?url=...` and then serves the slashed form — its route
|
|
36
|
+
* matching strips a trailing slash before matching internal paths (see
|
|
37
|
+
* getItem in packages/next/src/server/lib/router-utils/filesystem.ts).
|
|
38
|
+
* Rejecting the slashed form 404'd every dev-mode next/image request under
|
|
39
|
+
* `trailingSlash: true`.
|
|
40
|
+
*/
|
|
30
41
|
declare function isImageOptimizationPath(pathname: string): boolean;
|
|
31
42
|
/**
|
|
32
43
|
* Image security configuration from next.config.js `images` section.
|