vinext 1.0.0-beta.2 → 1.0.0-beta.3

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.
Files changed (126) hide show
  1. package/README.md +6 -9
  2. package/dist/build/client-build-config.js +7 -1
  3. package/dist/check.js +4 -0
  4. package/dist/config/config-matchers.js +4 -1
  5. package/dist/config/next-config.d.ts +12 -3
  6. package/dist/config/next-config.js +4 -0
  7. package/dist/entries/app-browser-entry.d.ts +4 -10
  8. package/dist/entries/app-browser-entry.js +20 -4
  9. package/dist/entries/app-rsc-entry.js +3 -0
  10. package/dist/entries/app-rsc-manifest.js +19 -0
  11. package/dist/entries/pages-client-entry.js +0 -1
  12. package/dist/entries/pages-server-entry.js +10 -3
  13. package/dist/index.js +112 -43
  14. package/dist/init-cloudflare.d.ts +3 -1
  15. package/dist/init-cloudflare.js +59 -5
  16. package/dist/plugins/extensionless-dynamic-import.js +23 -18
  17. package/dist/plugins/ignore-dynamic-requests.js +3 -1
  18. package/dist/plugins/import-meta-url.js +17 -6
  19. package/dist/plugins/require-context.js +20 -15
  20. package/dist/plugins/transform-cache.d.ts +19 -0
  21. package/dist/plugins/transform-cache.js +36 -0
  22. package/dist/routing/app-route-graph.d.ts +14 -6
  23. package/dist/routing/app-route-graph.js +106 -16
  24. package/dist/server/api-handler.d.ts +2 -0
  25. package/dist/server/api-handler.js +3 -3
  26. package/dist/server/app-browser-entry.js +103 -31
  27. package/dist/server/app-browser-navigation-controller.d.ts +23 -21
  28. package/dist/server/app-browser-navigation-controller.js +1 -1
  29. package/dist/server/app-page-cache-finalizer.js +1 -1
  30. package/dist/server/app-page-cache.d.ts +1 -1
  31. package/dist/server/app-page-cache.js +16 -2
  32. package/dist/server/app-page-dispatch.d.ts +8 -0
  33. package/dist/server/app-page-dispatch.js +31 -6
  34. package/dist/server/app-page-element-builder.d.ts +2 -0
  35. package/dist/server/app-page-element-builder.js +33 -7
  36. package/dist/server/app-page-head.d.ts +3 -3
  37. package/dist/server/app-page-head.js +58 -13
  38. package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
  39. package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
  40. package/dist/server/app-page-probe.d.ts +4 -0
  41. package/dist/server/app-page-probe.js +6 -2
  42. package/dist/server/app-page-request.d.ts +3 -0
  43. package/dist/server/app-page-request.js +1 -1
  44. package/dist/server/app-page-response.js +1 -1
  45. package/dist/server/app-page-route-wiring.d.ts +7 -0
  46. package/dist/server/app-page-route-wiring.js +211 -49
  47. package/dist/server/app-pages-bridge.d.ts +1 -1
  48. package/dist/server/app-pages-bridge.js +2 -1
  49. package/dist/server/app-route-handler-cache.js +4 -0
  50. package/dist/server/app-route-handler-dispatch.js +9 -3
  51. package/dist/server/app-route-handler-response.js +1 -1
  52. package/dist/server/app-route-module-loader.d.ts +6 -0
  53. package/dist/server/app-route-module-loader.js +3 -0
  54. package/dist/server/app-router-entry.js +6 -3
  55. package/dist/server/app-rsc-errors.js +10 -0
  56. package/dist/server/app-rsc-handler.js +21 -9
  57. package/dist/server/app-rsc-route-matching.d.ts +9 -0
  58. package/dist/server/app-rsc-route-matching.js +6 -0
  59. package/dist/server/app-ssr-entry.js +4 -4
  60. package/dist/server/cache-control.d.ts +2 -2
  61. package/dist/server/cache-control.js +2 -1
  62. package/dist/server/dev-server.d.ts +3 -1
  63. package/dist/server/dev-server.js +145 -312
  64. package/dist/server/headers.d.ts +2 -2
  65. package/dist/server/headers.js +4 -3
  66. package/dist/server/isr-cache.d.ts +10 -33
  67. package/dist/server/isr-cache.js +33 -43
  68. package/dist/server/isr-decision.d.ts +2 -2
  69. package/dist/server/pages-api-route.d.ts +2 -0
  70. package/dist/server/pages-api-route.js +4 -1
  71. package/dist/server/pages-dev-hydration.js +0 -1
  72. package/dist/server/pages-get-initial-props.d.ts +1 -1
  73. package/dist/server/pages-get-initial-props.js +1 -4
  74. package/dist/server/pages-i18n.js +64 -16
  75. package/dist/server/pages-node-compat.d.ts +2 -0
  76. package/dist/server/pages-node-compat.js +7 -3
  77. package/dist/server/pages-page-data.d.ts +61 -9
  78. package/dist/server/pages-page-data.js +334 -74
  79. package/dist/server/pages-page-handler.d.ts +5 -1
  80. package/dist/server/pages-page-handler.js +115 -19
  81. package/dist/server/pages-page-response.d.ts +5 -3
  82. package/dist/server/pages-page-response.js +23 -26
  83. package/dist/server/pages-request-pipeline.d.ts +7 -0
  84. package/dist/server/pages-request-pipeline.js +5 -1
  85. package/dist/server/pages-revalidate.d.ts +1 -1
  86. package/dist/server/pages-revalidate.js +54 -8
  87. package/dist/server/pages-router-entry.d.ts +1 -0
  88. package/dist/server/pages-router-entry.js +10 -4
  89. package/dist/server/prod-server.d.ts +2 -1
  90. package/dist/server/prod-server.js +37 -13
  91. package/dist/server/revalidation-host.d.ts +11 -0
  92. package/dist/server/revalidation-host.js +19 -0
  93. package/dist/server/worker-revalidation-context.d.ts +13 -0
  94. package/dist/server/worker-revalidation-context.js +29 -0
  95. package/dist/shims/cache-handler.d.ts +1 -1
  96. package/dist/shims/cache-handler.js +10 -7
  97. package/dist/shims/cache-runtime.d.ts +6 -0
  98. package/dist/shims/cache-runtime.js +4 -1
  99. package/dist/shims/cache.js +1 -1
  100. package/dist/shims/error.js +1 -1
  101. package/dist/shims/fetch-cache.js +1 -1
  102. package/dist/shims/font-google-base.js +17 -8
  103. package/dist/shims/font-local.js +41 -10
  104. package/dist/shims/headers.d.ts +10 -13
  105. package/dist/shims/headers.js +85 -36
  106. package/dist/shims/internal/cookie-serialize.d.ts +10 -9
  107. package/dist/shims/internal/cookie-serialize.js +9 -8
  108. package/dist/shims/internal/interpolate-as.d.ts +15 -1
  109. package/dist/shims/internal/interpolate-as.js +27 -2
  110. package/dist/shims/link.js +18 -6
  111. package/dist/shims/metadata.d.ts +7 -17
  112. package/dist/shims/metadata.js +74 -24
  113. package/dist/shims/navigation.js +1 -1
  114. package/dist/shims/request-context.d.ts +4 -1
  115. package/dist/shims/router.js +18 -7
  116. package/dist/shims/script.js +1 -1
  117. package/dist/shims/server.d.ts +7 -6
  118. package/dist/shims/server.js +156 -57
  119. package/dist/shims/unified-request-context.d.ts +21 -2
  120. package/dist/shims/unified-request-context.js +82 -1
  121. package/dist/utils/cache-control-metadata.d.ts +2 -1
  122. package/dist/utils/cache-control-metadata.js +5 -1
  123. package/dist/utils/html-limited-bots.js +1 -1
  124. package/dist/utils/protocol-headers.d.ts +7 -1
  125. package/dist/utils/protocol-headers.js +7 -1
  126. package/package.json +1 -1
@@ -1,23 +1,19 @@
1
- import { createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
1
+ import { closeAfterResponse, createRequestContext, runWithRequestContext } from "../shims/unified-request-context.js";
2
2
  import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
3
3
  import { createValidFileMatcher, findFileWithExtensions, findFileWithExts } from "../routing/file-matcher.js";
4
4
  import { patternToNextFormat } from "../routing/route-validation.js";
5
5
  import { matchRoute } from "../routing/pages-router.js";
6
- import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
6
+ import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
7
+ import { NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
7
8
  import { isDangerousScheme } from "../shims/url-safety.js";
9
+ import { sanitizeDestination } from "../config/config-matchers.js";
8
10
  import { importModule, reportRequestError } from "./instrumentation.js";
9
- import { buildCacheStateHeaders } from "./cache-headers.js";
10
- import { isUnknownRecord } from "../utils/record.js";
11
- import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL } from "./cache-control.js";
12
- import { buildMissIsrCacheControl, decideIsr } from "./isr-decision.js";
13
- import { PRERENDER_REVALIDATE_HEADER, buildPagesCacheValue, getRevalidateDuration, isOnDemandRevalidateRequest, isrCacheKey, isrGet, isrSet, setRevalidateDuration, triggerBackgroundRegeneration } from "./isr-cache.js";
14
- import { _runWithCacheState } from "../shims/cache-request-state.js";
15
- import { ensureFetchPatch, runWithFetchCache } from "../shims/fetch-cache.js";
16
- import { runWithPrivateCache } from "../shims/cache-runtime.js";
11
+ import { NEVER_CACHE_CONTROL } from "./cache-control.js";
12
+ import { buildMissIsrCacheControl } from "./isr-decision.js";
13
+ import { isOnDemandRevalidateRequest } from "./isr-cache.js";
14
+ import { ensureFetchPatch } from "../shims/fetch-cache.js";
17
15
  import { mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
18
16
  import "../shims/router-state.js";
19
- import { runWithHeadState } from "../shims/head-state.js";
20
- import { runWithServerInsertedHTMLState } from "../shims/navigation-state.js";
21
17
  import { withScriptNonce } from "../shims/script-nonce-context.js";
22
18
  import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr, safeJsonStringify } from "./html.js";
23
19
  import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
@@ -29,10 +25,10 @@ import { buildPagesReadinessNextData } from "./pages-readiness.js";
29
25
  import { resolvePagesPageMethodResponse } from "./pages-page-method.js";
30
26
  import { loadUserDocumentInitialProps, runDocumentRenderPage } from "./pages-document-initial-props.js";
31
27
  import { callDocumentGetInitialProps } from "./document-initial-head.js";
28
+ import { isBotUserAgent } from "../utils/html-limited-bots.js";
32
29
  import { hasPagesGetInitialProps, loadDevAppInitialProps, loadPagesGetInitialProps } from "./pages-get-initial-props.js";
33
30
  import { isSerializableProps } from "./pages-serializable-props.js";
34
- import { isBotUserAgent } from "../utils/html-limited-bots.js";
35
- import { getPagesRouteParams, matchesPagesStaticPath } from "./pages-page-data.js";
31
+ import { assertPages404DoesNotReturnNotFound, buildPagesRedirectProps, getPagesRouteParams, matchesPagesStaticPath, mergePagesDataProps, resolvePagesRedirect, resolvePagesRedirectLocation, resolvePagesRevalidateSeconds } from "./pages-page-data.js";
36
32
  import { createPagesDevAssetUrl, createPagesDevModuleUrl } from "./pages-dev-module-url.js";
37
33
  import { createPagesDevHydrationScript } from "./pages-dev-hydration.js";
38
34
  import { getManifestFilesForModule } from "./pages-asset-tags.js";
@@ -78,8 +74,15 @@ function applyDevPagesPreviewResponse(res, preview) {
78
74
  applyDevPagesPreviewHeaders(headers, preview);
79
75
  for (const [name, value] of Object.entries(headers)) res.setHeader(name, value);
80
76
  }
81
- async function renderIsrPassToStringAsync(element) {
82
- return await runWithServerInsertedHTMLState(() => runWithHeadState(() => _runWithCacheState(() => runWithPrivateCache(() => runWithFetchCache(async () => renderToStringAsync(element))))));
77
+ const DEV_PAGES_CACHE_CONTROL = "no-cache, must-revalidate";
78
+ const PAGES_CACHE_ONE_YEAR_SECONDS = 31536e3;
79
+ function applyDevPagesCacheHeaders(res, state, cacheControl = DEV_PAGES_CACHE_CONTROL) {
80
+ res.setHeader(NEXTJS_CACHE_HEADER, state);
81
+ res.setHeader("Cache-Control", cacheControl);
82
+ }
83
+ function buildDevPagesTerminalCacheControl(revalidateSeconds, expireSeconds) {
84
+ if (revalidateSeconds === false) return `s-maxage=${PAGES_CACHE_ONE_YEAR_SECONDS}`;
85
+ return buildMissIsrCacheControl(revalidateSeconds, expireSeconds);
83
86
  }
84
87
  const DEV_STYLESHEET_ASSET_RE = /\.(?:css|scss|sass)$/i;
85
88
  const transformedStylesheetAssetsCache = /* @__PURE__ */ new WeakMap();
@@ -172,11 +175,17 @@ async function collectDevInitialStylesheetHeadHTML(server, runner, moduleIds, no
172
175
  * (`buildPagesRedirectResponse`) and Next.js's `render.tsx` `__N_REDIRECT`
173
176
  * handling. See AGENTS.md "dev and prod server parity".
174
177
  */
175
- function writeGsspRedirect(res, redirect, isDataReq, props) {
176
- const status = redirect.statusCode ?? (redirect.permanent ? 308 : 307);
177
- let dest = redirect.destination;
178
- if (!dest.startsWith("http://") && !dest.startsWith("https://")) dest = dest.replace(/^[\\/]+/, "/");
179
- if (isDangerousScheme(dest)) {
178
+ function writeGsspRedirect(res, redirect, isDataReq, props, options) {
179
+ const resolved = resolvePagesRedirect(redirect, {
180
+ method: options.method,
181
+ routeUrl: options.routeUrl,
182
+ sanitizeDestination
183
+ });
184
+ const redirectProps = buildPagesRedirectProps(resolved, {
185
+ ...props,
186
+ pageProps: props.pageProps
187
+ });
188
+ if (isDangerousScheme(resolved.destination)) {
180
189
  const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
181
190
  const headers = {
182
191
  "Cache-Control": "private, no-cache, no-store, max-age=0, must-revalidate",
@@ -192,18 +201,15 @@ function writeGsspRedirect(res, redirect, isDataReq, props) {
192
201
  const dataHeaders = { "Content-Type": "application/json" };
193
202
  if (deploymentId) dataHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
194
203
  res.writeHead(200, dataHeaders);
195
- res.end(JSON.stringify({
196
- ...props,
197
- pageProps: {
198
- ...isUnknownRecord(props.pageProps) ? props.pageProps : {},
199
- __N_REDIRECT: dest,
200
- __N_REDIRECT_STATUS: status
201
- }
202
- }));
204
+ res.end(JSON.stringify({ ...redirectProps }));
203
205
  return;
204
206
  }
205
- res.writeHead(status, { Location: dest });
206
- res.end();
207
+ const location = resolvePagesRedirectLocation(resolved, options.basePath);
208
+ res.writeHead(resolved.statusCode, {
209
+ Location: location,
210
+ ...resolved.statusCode === 308 ? { Refresh: `0;url=${location}` } : {}
211
+ });
212
+ res.end(location);
207
213
  }
208
214
  /** Body placeholder used to split the document shell for streaming. */
209
215
  const STREAM_BODY_MARKER = "<!--VINEXT_STREAM_BODY-->";
@@ -324,7 +330,7 @@ function parseCookieLocale(req, i18nConfig) {
324
330
  * 4. Render the component to HTML
325
331
  * 5. Wrap in _document shell and send response
326
332
  */
327
- function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false, hasMiddleware = false, hasRewrites = false, clientTraceMetadata, htmlLimitedBots, reactStrictMode = false) {
333
+ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false, hasMiddleware = false, hasRewrites = false, clientTraceMetadata, htmlLimitedBots, reactStrictMode = false, expireTime = PAGES_CACHE_ONE_YEAR_SECONDS) {
328
334
  const matcher = fileMatcher ?? createValidFileMatcher();
329
335
  const pagePatterns = routes.map((r) => patternToNextFormat(r.pattern));
330
336
  const _alsRegistration = Promise.all([runner.import("vinext/head-state"), runner.import("vinext/router-state")]);
@@ -350,22 +356,24 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
350
356
  let locale;
351
357
  let localeStrippedUrl = url;
352
358
  let currentDefaultLocale;
353
- let currentDomainLocaleDomain;
354
359
  const domainLocales = i18nConfig?.domains;
355
360
  if (i18nConfig) {
356
361
  const resolved = resolvePagesI18nRequest(url, i18nConfig, req.headers, req.headers.host, basePath, trailingSlash);
357
362
  locale = resolved.locale;
358
363
  localeStrippedUrl = resolved.url;
359
364
  currentDefaultLocale = resolved.domainLocale?.defaultLocale ?? i18nConfig.defaultLocale;
360
- currentDomainLocaleDomain = resolved.domainLocale?.domain;
361
365
  if (resolved.redirectUrl) {
362
366
  res.writeHead(307, { Location: resolved.redirectUrl });
363
367
  res.end();
364
368
  return;
365
369
  }
366
370
  }
367
- const i18nCacheVariant = i18nConfig ? currentDomainLocaleDomain ? "domain:" + currentDomainLocaleDomain.toLowerCase() : "locale:" + String(locale) : null;
368
- const pagesIsrCacheKey = i18nCacheVariant ? (pathname) => isrCacheKey("pages", pathname + "::i18n=" + encodeURIComponent(i18nCacheVariant), process.env.__VINEXT_BUILD_ID) : (pathname) => isrCacheKey("pages", pathname, process.env.__VINEXT_BUILD_ID);
371
+ const errorPageContext = {
372
+ basePath,
373
+ locale: locale ?? currentDefaultLocale,
374
+ locales: i18nConfig?.locales,
375
+ defaultLocale: currentDefaultLocale
376
+ };
369
377
  const match = matchRoute(localeStrippedUrl, routes);
370
378
  if (!match) {
371
379
  if (isDataReq) {
@@ -377,9 +385,13 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
377
385
  res.end("{}");
378
386
  return;
379
387
  }
380
- await runWithRequestContext(createRequestContext(), async () => {
388
+ const requestContext = createRequestContext();
389
+ const closeRequest = () => void closeAfterResponse(requestContext);
390
+ res.on("finish", closeRequest);
391
+ res.on("close", closeRequest);
392
+ await runWithRequestContext(requestContext, async () => {
381
393
  await _alsRegistration;
382
- await renderErrorPage(server, runner, req, res, url, pagesDir, 404, void 0, matcher, void 0, reactStrictMode);
394
+ await renderErrorPage(server, runner, req, res, url, pagesDir, 404, void 0, matcher, void 0, reactStrictMode, errorPageContext);
383
395
  });
384
396
  return;
385
397
  }
@@ -391,7 +403,11 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
391
403
  let requestAsPath = isDataReq ? gsspResolvedUrl : i18nConfig ? extractLocaleFromUrl$1(originalUrl, i18nConfig, locale).url : originalUrl;
392
404
  const userFacingParams = route.isDynamic ? params : null;
393
405
  let query = mergeRouteParamsIntoQuery(parseQueryString(url), params);
394
- return runWithRequestContext(createRequestContext(), async () => {
406
+ const requestContext = createRequestContext();
407
+ const closeRequest = () => void closeAfterResponse(requestContext);
408
+ res.on("finish", closeRequest);
409
+ res.on("close", closeRequest);
410
+ return runWithRequestContext(requestContext, async () => {
395
411
  ensureFetchPatch();
396
412
  try {
397
413
  await _alsRegistration;
@@ -472,10 +488,9 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
472
488
  let pageProps = {};
473
489
  let renderProps = { pageProps };
474
490
  if (requestPreviewData !== false) renderProps.__N_PREVIEW = true;
475
- let isrRevalidateSeconds = null;
491
+ let isOnDemandRevalidate = false;
492
+ let staticPropsRevalidateSeconds = false;
476
493
  let isFallbackRender = false;
477
- let shouldPersistFallbackData = false;
478
- let staticPropsPreviewData = requestPreviewData;
479
494
  if (typeof pageModule.getStaticPaths === "function" && route.isDynamic) {
480
495
  const pathsResult = await pageModule.getStaticPaths({
481
496
  locales: i18nConfig?.locales ?? [],
@@ -491,17 +506,17 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
491
506
  const notFoundHeaders = { "Content-Type": "application/json" };
492
507
  if (deploymentId) notFoundHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
493
508
  res.writeHead(404, notFoundHeaders);
494
- res.end("{}");
509
+ res.end("{\"notFound\":true}");
495
510
  return;
496
511
  }
497
- await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, matcher, void 0, reactStrictMode);
512
+ await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, matcher, void 0, reactStrictMode, errorPageContext);
498
513
  return;
499
514
  }
500
515
  const userAgentHeader = req.headers["user-agent"];
501
516
  const userAgent = Array.isArray(userAgentHeader) ? userAgentHeader[0] : userAgentHeader;
502
517
  const isBotRequest = !!userAgent && isBotUserAgent(userAgent, htmlLimitedBots);
503
518
  if (fallback === true && !isValidPath && !isDataReq && !isBotRequest && requestPreviewData === false) {
504
- isFallbackRender = (await isrGet(pagesIsrCacheKey(url.split("?")[0])))?.value.value?.kind !== "PAGES";
519
+ isFallbackRender = true;
505
520
  if (isFallbackRender && typeof routerShim.setSSRContext === "function") routerShim.setSSRContext({
506
521
  pathname: patternToNextFormat(route.pattern),
507
522
  query: {},
@@ -514,7 +529,6 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
514
529
  isFallback: true
515
530
  });
516
531
  }
517
- shouldPersistFallbackData = fallback === true && !isValidPath && isDataReq;
518
532
  }
519
533
  const gsspExtraHeaders = {};
520
534
  const hasAppGetInitialProps = hasPagesGetInitialProps(AppComponent);
@@ -526,7 +540,6 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
526
540
  const appTree = React.createElement(AppComponent, {
527
541
  ...appTreeProps,
528
542
  Component: PageComponent,
529
- pageProps: appTreeProps.pageProps,
530
543
  router: routerShim.default
531
544
  });
532
545
  return typeof routerShim.wrapWithRouterContext === "function" ? routerShim.wrapWithRouterContext(appTree) : appTree;
@@ -577,17 +590,18 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
577
590
  const result = await pageModule.getServerSideProps(context);
578
591
  if (res.writableEnded) return;
579
592
  if (result && "props" in result) {
580
- pageProps = {
581
- ...pageProps,
582
- ...await Promise.resolve(result.props)
583
- };
593
+ pageProps = mergePagesDataProps(renderProps.pageProps, await Promise.resolve(result.props));
584
594
  renderProps = {
585
595
  ...renderProps,
586
596
  pageProps
587
597
  };
588
598
  }
589
599
  if (result && "redirect" in result) {
590
- writeGsspRedirect(res, result.redirect, isDataReq, renderProps);
600
+ writeGsspRedirect(res, result.redirect, isDataReq, renderProps, {
601
+ basePath,
602
+ method: "getServerSideProps",
603
+ routeUrl: url
604
+ });
591
605
  return;
592
606
  }
593
607
  if (result && "notFound" in result && result.notFound) {
@@ -597,10 +611,10 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
597
611
  const notFoundHeaders = { "Content-Type": "application/json" };
598
612
  if (deploymentId) notFoundHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
599
613
  res.writeHead(404, notFoundHeaders);
600
- res.end("{}");
614
+ res.end("{\"notFound\":true}");
601
615
  return;
602
616
  }
603
- await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, void 0, void 0, reactStrictMode);
617
+ await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, void 0, void 0, reactStrictMode, errorPageContext);
604
618
  return;
605
619
  }
606
620
  if (result && "props" in result) isSerializableProps(patternToNextFormat(route.pattern), "getServerSideProps", pageProps);
@@ -616,216 +630,19 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
616
630
  }
617
631
  const responseHeaders = typeof res.getHeaders === "function" ? res.getHeaders() : void 0;
618
632
  const scriptNonce = getScriptNonceFromNodeHeaderSources(req.headers, responseHeaders);
619
- let earlyFontLinkHeader = "";
620
- try {
621
- const earlyPreloads = [];
622
- const fontGoogleEarly = await importModule(runner, "next/font/google");
623
- if (typeof fontGoogleEarly.getSSRFontPreloads === "function") earlyPreloads.push(...fontGoogleEarly.getSSRFontPreloads());
624
- const fontLocalEarly = await importModule(runner, "next/font/local");
625
- if (typeof fontLocalEarly.getSSRFontPreloads === "function") earlyPreloads.push(...fontLocalEarly.getSSRFontPreloads());
626
- if (earlyPreloads.length > 0) earlyFontLinkHeader = earlyPreloads.map((p) => `<${p.href}>; rel=preload; as=font; type=${p.type}; crossorigin`).join(", ");
627
- } catch {}
628
633
  if (typeof pageModule.getStaticProps === "function" && !isFallbackRender) {
629
- const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
630
- const cached = await isrGet(cacheKey);
631
- const isOnDemandRevalidate = isOnDemandRevalidateRequest(req.headers[PRERENDER_REVALIDATE_HEADER]);
634
+ isOnDemandRevalidate = isOnDemandRevalidateRequest(req.headers[PRERENDER_REVALIDATE_HEADER]);
635
+ if (isOnDemandRevalidate && req.headers["x-prerender-revalidate-if-generated"] !== void 0) {
636
+ res.writeHead(404, { [NEXTJS_CACHE_HEADER]: "REVALIDATED" });
637
+ res.end("This page could not be found");
638
+ return;
639
+ }
632
640
  const previewData = requestPreviewData;
633
- staticPropsPreviewData = previewData;
634
641
  const previewContext = previewData === false ? {} : {
635
642
  draftMode: true,
636
643
  preview: true,
637
644
  previewData
638
645
  };
639
- if (!isOnDemandRevalidate && cached && !cached.isStale && cached.value.value?.kind === "PAGES" && !cached.value.value.generatedFromDataRequest && !scriptNonce && !isDataReq && previewData === false) {
640
- const cachedHtml = cached.value.value.html;
641
- const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
642
- const { cacheControl: hitCacheControl } = decideIsr({
643
- cacheState: "HIT",
644
- kind: "dev",
645
- revalidateSeconds: getRevalidateDuration(cacheKey) ?? 60
646
- });
647
- const hitHeaders = {
648
- "Content-Type": "text/html; charset=utf-8",
649
- ...buildCacheStateHeaders("HIT"),
650
- "Cache-Control": hitCacheControl
651
- };
652
- if (earlyFontLinkHeader) hitHeaders["Link"] = earlyFontLinkHeader;
653
- res.writeHead(200, hitHeaders);
654
- res.end(transformedHtml);
655
- return;
656
- }
657
- if (!isOnDemandRevalidate && cached && cached.isStale && cached.value.value?.kind === "PAGES" && !cached.value.value.generatedFromDataRequest && !scriptNonce && !isDataReq && previewData === false) {
658
- const cachedHtml = cached.value.value.html;
659
- const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
660
- triggerBackgroundRegeneration(cacheKey, async () => {
661
- return runWithRequestContext(createRequestContext({
662
- executionContext: null,
663
- ssrContext: {
664
- pathname: patternToNextFormat(route.pattern),
665
- query,
666
- asPath: requestAsPath,
667
- navigationIsReady,
668
- locale: locale ?? currentDefaultLocale,
669
- locales: i18nConfig?.locales,
670
- defaultLocale: currentDefaultLocale
671
- },
672
- i18nContext: i18nConfig ? {
673
- locale: locale ?? currentDefaultLocale,
674
- locales: i18nConfig.locales,
675
- defaultLocale: currentDefaultLocale,
676
- domainLocales,
677
- hostname: req.headers.host?.split(":", 1)[0]
678
- } : null
679
- }), async () => {
680
- ensureFetchPatch();
681
- let freshPageProps = {};
682
- let freshRenderProps = { pageProps: freshPageProps };
683
- let RegenApp = null;
684
- const appPath = path.join(pagesDir, "_app");
685
- if (findFileWithExtensions(appPath, matcher)) try {
686
- RegenApp = (await runner.import(appPath)).default ?? null;
687
- } catch {}
688
- if (RegenApp && hasPagesGetInitialProps(RegenApp)) {
689
- const regenReq = {
690
- url: req.url,
691
- headers: req.headers,
692
- method: req.method
693
- };
694
- const regenRes = {
695
- headersSent: false,
696
- writableEnded: false,
697
- statusCode: 200,
698
- getHeaders() {
699
- return {};
700
- }
701
- };
702
- const initialProps = await loadPagesGetInitialProps(RegenApp, {
703
- AppTree: (appTreeProps) => {
704
- const appTree = React.createElement(RegenApp, {
705
- ...appTreeProps,
706
- Component: pageModule.default,
707
- pageProps: appTreeProps.pageProps,
708
- router: routerShim.default
709
- });
710
- return typeof routerShim.wrapWithRouterContext === "function" ? routerShim.wrapWithRouterContext(appTree) : appTree;
711
- },
712
- Component: pageModule.default,
713
- router: routerShim.default,
714
- ctx: {
715
- req: regenReq,
716
- res: regenRes,
717
- pathname: patternToNextFormat(route.pattern),
718
- query,
719
- asPath: requestAsPath,
720
- locale: locale ?? currentDefaultLocale,
721
- locales: i18nConfig?.locales,
722
- defaultLocale: currentDefaultLocale
723
- }
724
- });
725
- if (regenRes.headersSent || regenRes.writableEnded) return;
726
- if (initialProps) {
727
- freshRenderProps = initialProps;
728
- freshPageProps = isUnknownRecord(initialProps.pageProps) ? initialProps.pageProps : {};
729
- }
730
- }
731
- const freshResult = await pageModule.getStaticProps({
732
- params: userFacingParams,
733
- locale: locale ?? currentDefaultLocale,
734
- locales: i18nConfig?.locales,
735
- defaultLocale: currentDefaultLocale,
736
- revalidateReason: "stale"
737
- });
738
- if (freshResult && "props" in freshResult) {
739
- const revalidate = typeof freshResult.revalidate === "number" ? freshResult.revalidate : cached.value.cacheControl?.revalidate ?? 0;
740
- if (revalidate > 0) {
741
- freshPageProps = {
742
- ...freshPageProps,
743
- ...freshResult.props
744
- };
745
- freshRenderProps = {
746
- ...freshRenderProps,
747
- pageProps: freshPageProps
748
- };
749
- if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext({
750
- pathname: patternToNextFormat(route.pattern),
751
- query,
752
- asPath: requestAsPath,
753
- navigationIsReady,
754
- locale: locale ?? currentDefaultLocale,
755
- locales: i18nConfig?.locales,
756
- defaultLocale: currentDefaultLocale,
757
- domainLocales
758
- });
759
- if (i18nConfig) {
760
- await runner.import("vinext/i18n-state");
761
- const i18nCtx = await importModule(runner, "vinext/i18n-context");
762
- if (typeof i18nCtx.setI18nContext === "function") i18nCtx.setI18nContext({
763
- locale: locale ?? currentDefaultLocale,
764
- locales: i18nConfig.locales,
765
- defaultLocale: currentDefaultLocale,
766
- domainLocales,
767
- hostname: req.headers.host?.split(":", 1)[0]
768
- });
769
- }
770
- let el = RegenApp ? React.createElement(RegenApp, {
771
- ...freshRenderProps,
772
- Component: pageModule.default,
773
- pageProps: freshRenderProps.pageProps,
774
- router: routerShim.default
775
- }) : React.createElement(pageModule.default, freshPageProps);
776
- if (routerShim.wrapWithRouterContext) el = routerShim.wrapWithRouterContext(el);
777
- const freshBody = await renderIsrPassToStringAsync(withScriptNonce(el, scriptNonce));
778
- const viteRoot = server.config.root;
779
- const viteBase = server.config.base;
780
- const regenPageUrl = createPagesDevModuleUrl(viteRoot, route.filePath, viteBase);
781
- const regenAppUrl = RegenApp ? createPagesDevModuleUrl(viteRoot, path.join(pagesDir, "_app"), viteBase) : null;
782
- const freshPagesNextData = {
783
- ...pagesNextData,
784
- __vinext: {
785
- ...pagesNextData.__vinext,
786
- pageModuleUrl: regenPageUrl,
787
- appModuleUrl: regenAppUrl,
788
- hasMiddleware,
789
- routeUrl: requestAsPath
790
- }
791
- };
792
- const freshHtml = `<!DOCTYPE html><html><head></head><body><div id="__next">${freshBody}</div>${`<script id="__NEXT_DATA__" type="application/json">${safeJsonStringify({
793
- props: freshRenderProps,
794
- page: patternToNextFormat(route.pattern),
795
- query: params,
796
- buildId: process.env.__VINEXT_BUILD_ID,
797
- isFallback: false,
798
- locale: locale ?? currentDefaultLocale,
799
- locales: i18nConfig?.locales,
800
- defaultLocale: currentDefaultLocale,
801
- domainLocales,
802
- ...freshPagesNextData
803
- })}<\/script>`}\n ${cachedHtml.match(/<script type="module">[\s\S]*?<\/script>/)?.[0] ?? ""}</body></html>`;
804
- await isrSet(cacheKey, buildPagesCacheValue(freshHtml, freshRenderProps), revalidate);
805
- setRevalidateDuration(cacheKey, revalidate);
806
- }
807
- }
808
- });
809
- }, {
810
- routerKind: "Pages Router",
811
- routePath: route.pattern,
812
- routeType: "render"
813
- });
814
- const { cacheControl: staleCacheControl } = decideIsr({
815
- cacheState: "STALE",
816
- kind: "dev",
817
- revalidateSeconds: getRevalidateDuration(cacheKey) ?? 60
818
- });
819
- const staleHeaders = {
820
- "Content-Type": "text/html; charset=utf-8",
821
- ...buildCacheStateHeaders("STALE"),
822
- "Cache-Control": staleCacheControl
823
- };
824
- if (earlyFontLinkHeader) staleHeaders["Link"] = earlyFontLinkHeader;
825
- res.writeHead(200, staleHeaders);
826
- res.end(transformedHtml);
827
- return;
828
- }
829
646
  const context = {
830
647
  params: userFacingParams,
831
648
  locale: locale ?? currentDefaultLocale,
@@ -834,43 +651,47 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
834
651
  revalidateReason: isOnDemandRevalidate ? "on-demand" : "stale",
835
652
  ...previewContext
836
653
  };
837
- 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;
838
- if (!generatedPageData && await loadAppInitialProps()) return;
839
- const result = generatedPageData ? null : await pageModule.getStaticProps(context);
840
- if (generatedPageData) {
841
- renderProps = generatedPageData;
842
- pageProps = isUnknownRecord(renderProps.pageProps) ? renderProps.pageProps : {};
843
- }
654
+ if (await loadAppInitialProps()) return;
655
+ const result = await pageModule.getStaticProps(context);
656
+ const routePattern = patternToNextFormat(route.pattern);
657
+ assertPages404DoesNotReturnNotFound(routePattern, result);
658
+ if (result) staticPropsRevalidateSeconds = resolvePagesRevalidateSeconds(result, routePattern);
844
659
  if (result && "props" in result) {
845
- pageProps = {
846
- ...pageProps,
847
- ...await Promise.resolve(result.props)
848
- };
660
+ pageProps = mergePagesDataProps(renderProps.pageProps, await Promise.resolve(result.props));
849
661
  renderProps = {
850
662
  ...renderProps,
851
663
  pageProps
852
664
  };
853
665
  }
854
666
  if (result && "redirect" in result) {
855
- writeGsspRedirect(res, result.redirect, isDataReq, renderProps);
667
+ if (previewData === false) resolvePagesRedirect(result.redirect, {
668
+ method: "getStaticProps",
669
+ routeUrl: url,
670
+ sanitizeDestination
671
+ });
672
+ applyDevPagesCacheHeaders(res, isOnDemandRevalidate ? "REVALIDATED" : "HIT", buildDevPagesTerminalCacheControl(staticPropsRevalidateSeconds, expireTime));
673
+ writeGsspRedirect(res, result.redirect, isDataReq, renderProps, {
674
+ basePath,
675
+ method: "getStaticProps",
676
+ routeUrl: url
677
+ });
856
678
  return;
857
679
  }
858
680
  if (result && "notFound" in result && result.notFound) {
681
+ applyDevPagesCacheHeaders(res, isOnDemandRevalidate ? "REVALIDATED" : "HIT", isDataReq ? buildDevPagesTerminalCacheControl(staticPropsRevalidateSeconds, expireTime) : DEV_PAGES_CACHE_CONTROL);
859
682
  applyDevPagesPreviewResponse(res, requestPreview);
860
683
  if (isDataReq) {
861
684
  const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
862
685
  const notFoundHeaders = { "Content-Type": "application/json" };
863
686
  if (deploymentId) notFoundHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
864
687
  res.writeHead(404, notFoundHeaders);
865
- res.end("{}");
688
+ res.end("{\"notFound\":true}");
866
689
  return;
867
690
  }
868
- await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, void 0, void 0, reactStrictMode);
691
+ await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, void 0, void 0, reactStrictMode, errorPageContext);
869
692
  return;
870
693
  }
871
- if (result && "props" in result) isSerializableProps(patternToNextFormat(route.pattern), "getStaticProps", pageProps);
872
- if (previewData === false && typeof result?.revalidate === "number" && result.revalidate > 0) isrRevalidateSeconds = result.revalidate;
873
- else if (previewData === false && cached?.value.value?.kind === "PAGES" && cached.value.value.generatedFromDataRequest) isrRevalidateSeconds = cached.value.cacheControl?.revalidate ?? 31536e3;
694
+ if (result && "props" in result) isSerializableProps(routePattern, "getStaticProps", pageProps);
874
695
  }
875
696
  if (typeof pageModule.getServerSideProps !== "function" && typeof pageModule.getStaticProps !== "function" && hasAppGetInitialProps) {
876
697
  if (await loadAppInitialProps()) return;
@@ -899,20 +720,11 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
899
720
  }
900
721
  }
901
722
  if (isDataReq) {
902
- if (shouldPersistFallbackData && staticPropsPreviewData === false) {
903
- const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
904
- const revalidateSeconds = isrRevalidateSeconds ?? 31536e3;
905
- await isrSet(cacheKey, {
906
- kind: "PAGES",
907
- html: "",
908
- pageData: renderProps,
909
- generatedFromDataRequest: true,
910
- headers: void 0,
911
- status: void 0
912
- }, revalidateSeconds);
913
- setRevalidateDuration(cacheKey, revalidateSeconds);
914
- }
915
723
  const dataHeaders = { "Content-Type": "application/json" };
724
+ if (typeof pageModule.getStaticProps === "function") {
725
+ dataHeaders["Cache-Control"] = DEV_PAGES_CACHE_CONTROL;
726
+ dataHeaders[NEXTJS_CACHE_HEADER] = isOnDemandRevalidate ? "REVALIDATED" : "HIT";
727
+ }
916
728
  if ((statusCode ?? 200) === 200) dataHeaders["x-nextjs-matched-path"] = `${locale ? `/${locale}` : ""}${patternToNextFormat(route.pattern)}`;
917
729
  if (gsspExtraHeaders) for (const [k, v] of Object.entries(gsspExtraHeaders)) dataHeaders[k] = v;
918
730
  applyDevPagesPreviewHeaders(dataHeaders, requestPreview);
@@ -932,7 +744,6 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
932
744
  if (AppComponent) element = createElement(AppComponent, {
933
745
  ...renderProps,
934
746
  Component: PageComponent,
935
- pageProps: renderProps.pageProps,
936
747
  router: routerShim.default
937
748
  });
938
749
  else element = createElement(PageComponent, pageProps);
@@ -1011,10 +822,9 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
1011
822
  } catch {}
1012
823
  const allScripts = `${nextDataScript}\n ${createInlineScriptTag(`window.__VINEXT_PAGE_PATTERNS__=${safeJsonStringify(pagePatterns)}`, scriptNonce)}\n ${hydrationScript}`;
1013
824
  const extraHeaders = { ...gsspExtraHeaders };
1014
- if (requestPreviewData === false && isrRevalidateSeconds) if (scriptNonce) extraHeaders["Cache-Control"] = NO_STORE_CACHE_CONTROL;
1015
- else {
1016
- extraHeaders["Cache-Control"] = buildMissIsrCacheControl(isrRevalidateSeconds);
1017
- Object.assign(extraHeaders, buildCacheStateHeaders("MISS"));
825
+ if (typeof pageModule.getStaticProps === "function") {
826
+ extraHeaders["Cache-Control"] = DEV_PAGES_CACHE_CONTROL;
827
+ extraHeaders[NEXTJS_CACHE_HEADER] = isFallbackRender ? "MISS" : isOnDemandRevalidate ? "REVALIDATED" : "HIT";
1018
828
  }
1019
829
  applyDevPagesPreviewHeaders(extraHeaders, requestPreview);
1020
830
  if (allFontPreloads.length > 0) extraHeaders["Link"] = allFontPreloads.map((p) => `<${p.href}>; rel=preload; as=font; type=${p.type}; crossorigin`).join(", ");
@@ -1046,7 +856,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
1046
856
  if (FinalApp) enhancedElement = createElement(FinalApp, {
1047
857
  ...renderProps,
1048
858
  Component: FinalComp,
1049
- pageProps
859
+ router: routerShim.default
1050
860
  });
1051
861
  else enhancedElement = createElement(FinalComp, pageProps);
1052
862
  if (wrapWithRouterContext) enhancedElement = wrapWithRouterContext(enhancedElement);
@@ -1062,18 +872,6 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
1062
872
  });
1063
873
  _renderEnd = now();
1064
874
  if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext(null);
1065
- if (!scriptNonce && isrRevalidateSeconds !== null && isrRevalidateSeconds > 0) {
1066
- let isrElement = AppComponent ? createElement(AppComponent, {
1067
- ...renderProps,
1068
- Component: pageModule.default,
1069
- pageProps
1070
- }) : createElement(pageModule.default, pageProps);
1071
- if (wrapWithRouterContext) isrElement = wrapWithRouterContext(isrElement);
1072
- const isrHtml = `<!DOCTYPE html><html><head>${assetHeadHTML}</head><body><div id="__next">${await renderIsrPassToStringAsync(withScriptNonce(isrElement, scriptNonce))}</div>${allScripts}</body></html>`;
1073
- const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
1074
- await isrSet(cacheKey, buildPagesCacheValue(isrHtml, pageProps), isrRevalidateSeconds);
1075
- setRevalidateDuration(cacheKey, isrRevalidateSeconds);
1076
- }
1077
875
  } catch (e) {
1078
876
  console.error(e);
1079
877
  reportRequestError(e instanceof Error ? e : new Error(String(e)), {
@@ -1086,7 +884,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
1086
884
  routeType: "render"
1087
885
  }).catch(() => {});
1088
886
  try {
1089
- await renderErrorPage(server, runner, req, res, url, pagesDir, 500, void 0, matcher, e instanceof Error ? e : new Error(String(e)), reactStrictMode);
887
+ await renderErrorPage(server, runner, req, res, url, pagesDir, 500, void 0, matcher, e instanceof Error ? e : new Error(String(e)), reactStrictMode, errorPageContext);
1090
888
  } catch (fallbackErr) {
1091
889
  res.statusCode = 500;
1092
890
  res.end(`Internal Server Error: ${fallbackErr.message}`);
@@ -1103,16 +901,20 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
1103
901
  * - 500: pages/500.tsx -> pages/_error.tsx -> default
1104
902
  * - other: pages/_error.tsx -> default
1105
903
  */
1106
- async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCode, wrapWithRouterContext, fileMatcher, err, reactStrictMode = false) {
904
+ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCode, wrapWithRouterContext, fileMatcher, err, reactStrictMode = false, context = { basePath: "" }) {
1107
905
  attachPagesRequestCookies(req);
1108
906
  const matcher = fileMatcher ?? createValidFileMatcher();
1109
907
  const candidates = statusCode === 404 ? ["404", "_error"] : statusCode === 500 ? ["500", "_error"] : ["_error"];
1110
908
  for (const candidate of candidates) {
1111
909
  let errorRouterShim = null;
910
+ let candidateLoaded = false;
911
+ let errorAssetPath = null;
1112
912
  try {
1113
- const errorAssetPath = findFileWithExts(pagesDir, candidate, matcher);
913
+ errorAssetPath = findFileWithExts(pagesDir, candidate, matcher);
1114
914
  if (!errorAssetPath && candidate !== "_error") continue;
1115
- const ErrorComponent = (await importModule(runner, errorAssetPath ?? "next/error")).default;
915
+ const errorModule = await importModule(runner, errorAssetPath ?? "next/error");
916
+ candidateLoaded = true;
917
+ const ErrorComponent = errorModule.default;
1116
918
  if (!ErrorComponent) continue;
1117
919
  let AppComponent = null;
1118
920
  const appPathErr = path.join(pagesDir, "_app");
@@ -1146,10 +948,36 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
1146
948
  asPath: url
1147
949
  });
1148
950
  if (res.headersSent || res.writableEnded) return;
1149
- const errorProps = {
951
+ let errorProps = {
1150
952
  ...initialErrorProps,
1151
953
  statusCode
1152
954
  };
955
+ if (typeof errorModule.getStaticProps === "function") {
956
+ const isOnDemandRevalidate = isOnDemandRevalidateRequest(req.headers[PRERENDER_REVALIDATE_HEADER]);
957
+ const staticResult = await errorModule.getStaticProps({
958
+ locale: context.locale,
959
+ locales: context.locales,
960
+ defaultLocale: context.defaultLocale,
961
+ revalidateReason: isOnDemandRevalidate ? "on-demand" : "stale"
962
+ });
963
+ assertPages404DoesNotReturnNotFound(errorPage, staticResult);
964
+ if (staticResult?.redirect) {
965
+ applyDevPagesCacheHeaders(res, isOnDemandRevalidate ? "REVALIDATED" : "HIT", NEVER_CACHE_CONTROL);
966
+ writeGsspRedirect(res, staticResult.redirect, false, { pageProps: errorProps }, {
967
+ basePath: context.basePath,
968
+ method: "getStaticProps",
969
+ routeUrl: url
970
+ });
971
+ return;
972
+ }
973
+ if (staticResult?.props !== void 0) {
974
+ const staticProps = await Promise.resolve(staticResult.props);
975
+ isSerializableProps(errorPage, "getStaticProps", staticProps);
976
+ errorProps = mergePagesDataProps(errorProps, staticProps);
977
+ }
978
+ if (staticResult) resolvePagesRevalidateSeconds(staticResult, errorPage);
979
+ applyDevPagesCacheHeaders(res, isOnDemandRevalidate ? "REVALIDATED" : "HIT");
980
+ }
1153
981
  let renderProps;
1154
982
  if (AppComponent && hasPagesGetInitialProps(AppComponent)) {
1155
983
  const appInitialProps = await loadPagesGetInitialProps(AppComponent, {
@@ -1173,7 +1001,11 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
1173
1001
  }
1174
1002
  });
1175
1003
  if (res.headersSent || res.writableEnded) return;
1176
- renderProps = appInitialProps ?? {};
1004
+ const appRenderProps = appInitialProps ?? {};
1005
+ renderProps = Object.hasOwn(appRenderProps, "pageProps") ? {
1006
+ ...appRenderProps,
1007
+ pageProps: mergePagesDataProps(appRenderProps.pageProps, errorProps)
1008
+ } : appRenderProps;
1177
1009
  } else renderProps = { pageProps: errorProps };
1178
1010
  let DocumentComponent = null;
1179
1011
  const docPathErr = path.join(pagesDir, "_document");
@@ -1257,8 +1089,9 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
1257
1089
  res.end(transformedHtml);
1258
1090
  }
1259
1091
  return;
1260
- } catch {
1092
+ } catch (error) {
1261
1093
  if (res.headersSent || res.writableEnded) return;
1094
+ if (errorAssetPath || candidateLoaded) throw error;
1262
1095
  continue;
1263
1096
  } finally {
1264
1097
  if (typeof errorRouterShim?.setSSRContext === "function") errorRouterShim.setSSRContext(null);