vinext 0.1.8 → 0.2.1

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 (285) hide show
  1. package/README.md +51 -30
  2. package/dist/build/css-url-assets.d.ts +3 -1
  3. package/dist/build/css-url-assets.js +18 -1
  4. package/dist/build/inject-pregenerated-paths.d.ts +7 -0
  5. package/dist/build/inject-pregenerated-paths.js +21 -0
  6. package/dist/build/layout-classification.js +1 -1
  7. package/dist/build/nitro-route-rules.js +1 -1
  8. package/dist/build/pages-client-assets-module.d.ts +11 -0
  9. package/dist/build/pages-client-assets-module.js +27 -0
  10. package/dist/build/prerender-server-entry.d.ts +1 -0
  11. package/dist/build/prerender-server-entry.js +49 -0
  12. package/dist/build/prerender-server-pool.d.ts +44 -0
  13. package/dist/build/prerender-server-pool.js +194 -0
  14. package/dist/build/prerender.d.ts +2 -1
  15. package/dist/build/prerender.js +98 -27
  16. package/dist/build/report.d.ts +13 -2
  17. package/dist/build/report.js +99 -2
  18. package/dist/build/run-prerender.d.ts +7 -0
  19. package/dist/build/run-prerender.js +12 -2
  20. package/dist/build/standalone.js +2 -0
  21. package/dist/cache/cache-adapters-virtual.js +3 -3
  22. package/dist/check.d.ts +4 -6
  23. package/dist/check.js +48 -11
  24. package/dist/cli-args.d.ts +1 -2
  25. package/dist/cli-args.js +1 -1
  26. package/dist/cli-dev-config.d.ts +12 -0
  27. package/dist/cli-dev-config.js +23 -0
  28. package/dist/cli.js +112 -96
  29. package/dist/{server → client}/dev-error-overlay-store.d.ts +1 -1
  30. package/dist/{server → client}/dev-error-overlay-store.js +1 -1
  31. package/dist/{server → client}/dev-error-overlay.d.ts +1 -1
  32. package/dist/{server → client}/dev-error-overlay.js +2 -2
  33. package/dist/client/navigation-runtime.d.ts +2 -0
  34. package/dist/client/navigation-runtime.js +3 -1
  35. package/dist/client/vinext-next-data.d.ts +4 -1
  36. package/dist/client/window-next.d.ts +2 -1
  37. package/dist/cloudflare/index.d.ts +2 -2
  38. package/dist/cloudflare/index.js +2 -2
  39. package/dist/config/config-matchers.js +45 -19
  40. package/dist/config/next-config.d.ts +41 -9
  41. package/dist/config/next-config.js +60 -3
  42. package/dist/config/prerender.d.ts +34 -0
  43. package/dist/config/prerender.js +55 -0
  44. package/dist/entries/app-browser-entry.js +5 -1
  45. package/dist/entries/app-rsc-entry.d.ts +5 -3
  46. package/dist/entries/app-rsc-entry.js +44 -10
  47. package/dist/entries/app-rsc-manifest.js +2 -0
  48. package/dist/entries/pages-client-entry.d.ts +3 -0
  49. package/dist/entries/pages-client-entry.js +28 -3
  50. package/dist/entries/pages-server-entry.js +23 -6
  51. package/dist/image/image-adapters-virtual.d.ts +59 -0
  52. package/dist/image/image-adapters-virtual.js +59 -0
  53. package/dist/index.d.ts +33 -0
  54. package/dist/index.js +482 -120
  55. package/dist/init-cloudflare.d.ts +43 -0
  56. package/dist/init-cloudflare.js +839 -0
  57. package/dist/init-platform.d.ts +40 -0
  58. package/dist/init-platform.js +195 -0
  59. package/dist/init.d.ts +16 -38
  60. package/dist/init.js +209 -99
  61. package/dist/node_modules/.pnpm/am-i-vibing@0.5.0/node_modules/am-i-vibing/dist/detector-1yx2Hoe0.js +294 -0
  62. package/dist/node_modules/.pnpm/process-ancestry@0.1.0/node_modules/process-ancestry/dist/index.js +94 -0
  63. package/dist/{cloudflare → packages/cloudflare}/src/cache/cdn-adapter.runtime.js +1 -1
  64. package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.d.ts +2 -2
  65. package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.js +9 -2
  66. package/dist/packages/cloudflare/src/deploy-config.js +150 -0
  67. package/dist/packages/cloudflare/src/deploy-help.js +55 -0
  68. package/dist/packages/cloudflare/src/deploy.js +272 -0
  69. package/dist/{cloudflare → packages/cloudflare/src}/tpr.d.ts +4 -41
  70. package/dist/{cloudflare → packages/cloudflare/src}/tpr.js +44 -20
  71. package/dist/plugins/ast-scope.d.ts +16 -0
  72. package/dist/plugins/ast-scope.js +62 -0
  73. package/dist/plugins/ast-utils.d.ts +27 -1
  74. package/dist/plugins/ast-utils.js +32 -1
  75. package/dist/plugins/css-module-imports.d.ts +14 -0
  76. package/dist/plugins/css-module-imports.js +59 -0
  77. package/dist/plugins/extensionless-dynamic-import.js +2 -2
  78. package/dist/plugins/fonts.js +1 -1
  79. package/dist/plugins/ignore-dynamic-requests.d.ts +11 -0
  80. package/dist/plugins/ignore-dynamic-requests.js +530 -0
  81. package/dist/plugins/og-assets.js +2 -1
  82. package/dist/plugins/optimize-imports.js +1 -1
  83. package/dist/plugins/rsc-reference-validation-normalizer.d.ts +12 -0
  84. package/dist/plugins/rsc-reference-validation-normalizer.js +48 -0
  85. package/dist/plugins/sass.d.ts +17 -1
  86. package/dist/plugins/sass.js +74 -1
  87. package/dist/plugins/styled-jsx.d.ts +16 -0
  88. package/dist/plugins/styled-jsx.js +149 -0
  89. package/dist/plugins/typeof-window.d.ts +1 -1
  90. package/dist/plugins/typeof-window.js +28 -56
  91. package/dist/routing/app-route-graph.d.ts +23 -2
  92. package/dist/routing/app-route-graph.js +138 -38
  93. package/dist/routing/file-matcher.d.ts +11 -1
  94. package/dist/routing/file-matcher.js +18 -3
  95. package/dist/routing/pages-router.js +2 -2
  96. package/dist/server/api-handler.js +9 -14
  97. package/dist/server/app-browser-action-result.d.ts +4 -1
  98. package/dist/server/app-browser-action-result.js +8 -1
  99. package/dist/server/app-browser-entry.js +198 -57
  100. package/dist/server/app-browser-interception-context.d.ts +1 -0
  101. package/dist/server/app-browser-interception-context.js +4 -2
  102. package/dist/server/app-browser-navigation-controller.d.ts +2 -0
  103. package/dist/server/app-browser-navigation-controller.js +60 -31
  104. package/dist/server/app-browser-server-action-client.d.ts +1 -1
  105. package/dist/server/app-browser-server-action-client.js +8 -7
  106. package/dist/server/app-browser-state.d.ts +12 -1
  107. package/dist/server/app-browser-state.js +25 -8
  108. package/dist/server/app-browser-visible-commit.d.ts +6 -1
  109. package/dist/server/app-browser-visible-commit.js +42 -24
  110. package/dist/server/app-elements-wire.d.ts +9 -2
  111. package/dist/server/app-elements-wire.js +8 -1
  112. package/dist/server/app-elements.d.ts +2 -2
  113. package/dist/server/app-elements.js +4 -3
  114. package/dist/server/app-fallback-renderer.d.ts +2 -1
  115. package/dist/server/app-fallback-renderer.js +10 -6
  116. package/dist/server/app-hydration-cache-publication.d.ts +11 -0
  117. package/dist/server/app-hydration-cache-publication.js +43 -0
  118. package/dist/server/app-middleware.d.ts +4 -3
  119. package/dist/server/app-middleware.js +8 -3
  120. package/dist/server/app-optimistic-routing.js +11 -4
  121. package/dist/server/app-page-boundary.js +2 -1
  122. package/dist/server/app-page-cache-finalizer.d.ts +1 -0
  123. package/dist/server/app-page-cache-finalizer.js +5 -4
  124. package/dist/server/app-page-cache-render.d.ts +1 -0
  125. package/dist/server/app-page-cache-render.js +8 -4
  126. package/dist/server/app-page-cache.d.ts +1 -0
  127. package/dist/server/app-page-cache.js +27 -7
  128. package/dist/server/app-page-dispatch.d.ts +1 -0
  129. package/dist/server/app-page-dispatch.js +26 -8
  130. package/dist/server/app-page-element-builder.d.ts +2 -1
  131. package/dist/server/app-page-element-builder.js +59 -4
  132. package/dist/server/app-page-execution.js +2 -1
  133. package/dist/server/app-page-probe.js +5 -0
  134. package/dist/server/app-page-render-identity.d.ts +2 -0
  135. package/dist/server/app-page-render-identity.js +3 -2
  136. package/dist/server/app-page-render.d.ts +2 -0
  137. package/dist/server/app-page-render.js +85 -16
  138. package/dist/server/app-page-request.d.ts +14 -0
  139. package/dist/server/app-page-request.js +69 -4
  140. package/dist/server/app-page-response.d.ts +7 -0
  141. package/dist/server/app-page-response.js +16 -4
  142. package/dist/server/app-page-route-wiring.d.ts +8 -1
  143. package/dist/server/app-page-route-wiring.js +32 -18
  144. package/dist/server/app-page-segment-state.d.ts +2 -1
  145. package/dist/server/app-page-segment-state.js +3 -1
  146. package/dist/server/app-page-stream.d.ts +7 -1
  147. package/dist/server/app-page-stream.js +3 -1
  148. package/dist/server/app-route-handler-execution.js +5 -1
  149. package/dist/server/app-route-handler-response.js +1 -0
  150. package/dist/server/app-route-handler-runtime.js +1 -1
  151. package/dist/server/app-route-tree-prefetch.d.ts +43 -0
  152. package/dist/server/app-route-tree-prefetch.js +187 -0
  153. package/dist/server/app-router-entry.js +9 -2
  154. package/dist/server/app-rsc-cache-busting.d.ts +2 -1
  155. package/dist/server/app-rsc-cache-busting.js +9 -4
  156. package/dist/server/app-rsc-handler.d.ts +7 -0
  157. package/dist/server/app-rsc-handler.js +148 -39
  158. package/dist/server/app-rsc-render-mode.d.ts +3 -2
  159. package/dist/server/app-rsc-render-mode.js +4 -1
  160. package/dist/server/app-rsc-request-normalization.d.ts +6 -6
  161. package/dist/server/app-rsc-request-normalization.js +10 -8
  162. package/dist/server/app-segment-config.d.ts +11 -0
  163. package/dist/server/app-segment-config.js +62 -6
  164. package/dist/server/app-server-action-execution.d.ts +1 -0
  165. package/dist/server/app-server-action-execution.js +61 -30
  166. package/dist/server/app-ssr-entry.d.ts +3 -0
  167. package/dist/server/app-ssr-entry.js +13 -5
  168. package/dist/server/app-ssr-stream.d.ts +7 -3
  169. package/dist/server/app-ssr-stream.js +10 -6
  170. package/dist/server/app-visited-response-cache.d.ts +6 -0
  171. package/dist/server/app-visited-response-cache.js +3 -1
  172. package/dist/server/cache-control.d.ts +3 -1
  173. package/dist/server/cache-control.js +13 -1
  174. package/dist/server/client-trace-metadata.js +26 -0
  175. package/dist/server/default-global-not-found-module.d.ts +14 -0
  176. package/dist/server/default-global-not-found-module.js +14 -0
  177. package/dist/server/dev-route-files.js +4 -0
  178. package/dist/server/dev-server.d.ts +11 -1
  179. package/dist/server/dev-server.js +94 -35
  180. package/dist/server/dev-stack-sourcemap.d.ts +1 -1
  181. package/dist/server/dev-stack-sourcemap.js +1 -1
  182. package/dist/server/fetch-handler.d.ts +2 -0
  183. package/dist/server/fetch-handler.js +18 -0
  184. package/dist/server/headers.d.ts +7 -15
  185. package/dist/server/headers.js +11 -18
  186. package/dist/server/image-optimization.d.ts +51 -1
  187. package/dist/server/image-optimization.js +52 -2
  188. package/dist/server/implicit-tags.js +1 -1
  189. package/dist/server/isr-cache.d.ts +3 -2
  190. package/dist/server/isr-cache.js +9 -5
  191. package/dist/server/metadata-routes.d.ts +3 -1
  192. package/dist/server/metadata-routes.js +12 -2
  193. package/dist/server/middleware-runtime.js +6 -1
  194. package/dist/server/navigation-planner.d.ts +1 -0
  195. package/dist/server/navigation-planner.js +14 -3
  196. package/dist/server/pages-api-route.js +2 -2
  197. package/dist/server/pages-asset-tags.d.ts +4 -6
  198. package/dist/server/pages-asset-tags.js +12 -12
  199. package/dist/server/pages-client-assets.d.ts +12 -0
  200. package/dist/server/pages-client-assets.js +10 -0
  201. package/dist/server/pages-data-route.d.ts +4 -2
  202. package/dist/server/pages-data-route.js +18 -4
  203. package/dist/server/pages-dev-module-url.d.ts +2 -1
  204. package/dist/server/pages-dev-module-url.js +6 -3
  205. package/dist/server/pages-node-compat.d.ts +19 -1
  206. package/dist/server/pages-node-compat.js +81 -4
  207. package/dist/server/pages-page-data.d.ts +33 -1
  208. package/dist/server/pages-page-data.js +62 -33
  209. package/dist/server/pages-page-handler.d.ts +2 -1
  210. package/dist/server/pages-page-handler.js +38 -19
  211. package/dist/server/pages-page-response.d.ts +8 -0
  212. package/dist/server/pages-page-response.js +7 -3
  213. package/dist/server/pages-request-pipeline.d.ts +9 -5
  214. package/dist/server/pages-request-pipeline.js +55 -7
  215. package/dist/server/pages-router-entry.d.ts +30 -0
  216. package/dist/server/pages-router-entry.js +108 -0
  217. package/dist/server/prerender-manifest.d.ts +3 -1
  218. package/dist/server/prerender-route-params.js +1 -1
  219. package/dist/server/prod-server.d.ts +5 -3
  220. package/dist/server/prod-server.js +68 -39
  221. package/dist/server/request-pipeline.js +2 -1
  222. package/dist/server/seed-cache.js +4 -4
  223. package/dist/shims/app-router-scroll-state.d.ts +1 -0
  224. package/dist/shims/app-router-scroll-state.js +1 -0
  225. package/dist/shims/app-router-scroll.js +2 -1
  226. package/dist/shims/cache-handler.js +8 -1
  227. package/dist/shims/cache.js +22 -15
  228. package/dist/shims/cdn-cache.js +1 -1
  229. package/dist/shims/dynamic-preload-chunks.js +2 -1
  230. package/dist/shims/fetch-cache.d.ts +3 -1
  231. package/dist/shims/fetch-cache.js +77 -52
  232. package/dist/shims/form.d.ts +4 -2
  233. package/dist/shims/form.js +37 -43
  234. package/dist/shims/headers.d.ts +7 -1
  235. package/dist/shims/headers.js +17 -3
  236. package/dist/shims/image.js +75 -14
  237. package/dist/shims/internal/app-prefetch-fetch-queue.d.ts +10 -0
  238. package/dist/shims/internal/app-prefetch-fetch-queue.js +61 -0
  239. package/dist/shims/internal/app-route-detection.d.ts +2 -17
  240. package/dist/shims/internal/app-route-detection.js +4 -17
  241. package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +23 -0
  242. package/dist/shims/internal/hybrid-client-route-owner-direct.js +51 -0
  243. package/dist/shims/internal/hybrid-client-route-owner.d.ts +3 -15
  244. package/dist/shims/internal/hybrid-client-route-owner.js +40 -58
  245. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +10 -5
  246. package/dist/shims/internal/pages-data-fetch-dedup.js +84 -14
  247. package/dist/shims/internal/pages-data-target.d.ts +18 -12
  248. package/dist/shims/internal/pages-data-target.js +97 -13
  249. package/dist/shims/internal/pages-router-components.d.ts +7 -0
  250. package/dist/shims/internal/pages-router-components.js +13 -0
  251. package/dist/shims/layout-segment-context.d.ts +4 -1
  252. package/dist/shims/layout-segment-context.js +18 -4
  253. package/dist/shims/link.d.ts +1 -1
  254. package/dist/shims/link.js +224 -58
  255. package/dist/shims/navigation.d.ts +19 -3
  256. package/dist/shims/navigation.js +245 -47
  257. package/dist/shims/request-context.js +18 -0
  258. package/dist/shims/root-params.d.ts +15 -1
  259. package/dist/shims/root-params.js +21 -1
  260. package/dist/shims/router.d.ts +5 -7
  261. package/dist/shims/router.js +387 -102
  262. package/dist/shims/server.js +3 -2
  263. package/dist/shims/slot.js +3 -6
  264. package/dist/shims/unified-request-context.js +1 -0
  265. package/dist/typegen.js +1 -1
  266. package/dist/utils/client-runtime-metadata.d.ts +2 -18
  267. package/dist/utils/client-runtime-metadata.js +31 -22
  268. package/dist/utils/dev-stack-sourcemap-endpoint.d.ts +4 -0
  269. package/dist/{server → utils}/dev-stack-sourcemap-endpoint.js +1 -1
  270. package/dist/utils/domain-locale.d.ts +6 -3
  271. package/dist/{server → utils}/middleware-request-headers.d.ts +1 -1
  272. package/dist/{server → utils}/middleware-request-headers.js +3 -3
  273. package/dist/utils/project.d.ts +48 -2
  274. package/dist/utils/project.js +225 -4
  275. package/dist/utils/protocol-headers.d.ts +23 -0
  276. package/dist/utils/protocol-headers.js +23 -0
  277. package/dist/utils/react-version.d.ts +4 -0
  278. package/dist/utils/react-version.js +44 -0
  279. package/dist/utils/vite-version.d.ts +2 -0
  280. package/dist/utils/vite-version.js +11 -1
  281. package/package.json +47 -2
  282. package/dist/deploy.d.ts +0 -197
  283. package/dist/deploy.js +0 -1040
  284. package/dist/server/dev-stack-sourcemap-endpoint.d.ts +0 -4
  285. /package/dist/{cloudflare → packages/cloudflare}/src/utils/cache-control-metadata.js +0 -0
@@ -1,18 +1,21 @@
1
1
  import { createValidFileMatcher, findFileWithExtensions } from "../routing/file-matcher.js";
2
- import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER } from "../server/headers.js";
3
- import { normalizeStaticPathsEntry } from "../routing/route-pattern.js";
2
+ import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
3
+ import { VINEXT_PRERENDER_CACHE_LIFE_HEADER } from "../server/headers.js";
4
+ import { classifyAppRoute, classifyPagesRoute, getAppRouteRenderEntryPath } from "./report.js";
4
5
  import { NoOpCacheHandler, getCacheHandler, setCacheHandler } from "../shims/cache-handler.js";
5
6
  import { headersContextFromRequest, runWithHeadersContext } from "../shims/headers.js";
6
7
  import { _consumeRequestScopedCacheLife } from "../shims/cache-request-state.js";
7
- import { classifyAppRoute, classifyPagesRoute, getAppRouteRenderEntryPath } from "./report.js";
8
+ import { normalizeStaticPathsEntry } from "../routing/route-pattern.js";
8
9
  import { BLOCKED_PAGES } from "../shims/constants.js";
10
+ import { buildPregeneratedConcretePathTable } from "../server/prerender-manifest.js";
9
11
  import { concatUint8Arrays, decodeRscEmbeddedChunk } from "../server/app-rsc-embedded-chunks.js";
10
12
  import { navigationRuntimeRscBootstrapExpression } from "../server/app-ssr-stream.js";
11
- import { createAppPprFallbackShells, markAppPprDynamicFallbackShellHtml } from "../server/app-ppr-fallback-shell.js";
12
13
  import { encodePrerenderRouteParams, serializePrerenderRouteParamsHeader } from "../server/prerender-route-params.js";
13
14
  import { readPrerenderSecret } from "./server-manifest.js";
15
+ import { createAppPprFallbackShells, markAppPprDynamicFallbackShellHtml } from "../server/app-ppr-fallback-shell.js";
14
16
  import { getOutputPath, getRscOutputPath } from "../utils/prerender-output-paths.js";
15
17
  import { startProdServer } from "../server/prod-server.js";
18
+ import { prerenderPoolAvailable, resolvePrerenderPoolSize, startPrerenderServerPool } from "./prerender-server-pool.js";
16
19
  import fs from "node:fs";
17
20
  import path from "node:path";
18
21
  import os from "node:os";
@@ -41,6 +44,14 @@ function isExperimentalPprFallbackShellGenerationEnabled(env = process.env) {
41
44
  function getErrorMessageWithStack(err) {
42
45
  return err.stack || err.message;
43
46
  }
47
+ async function startOptionalPrerenderServerPool(outDir, poolSize) {
48
+ try {
49
+ return await startPrerenderServerPool(outDir, poolSize);
50
+ } catch (e) {
51
+ console.warn(`[vinext] prerender render pool failed to start; falling back to single-process render: ${e instanceof Error ? e.message : String(e)}`);
52
+ return null;
53
+ }
54
+ }
44
55
  var PrerenderUserFunctionError = class extends Error {};
45
56
  function parsePrerenderEndpointError(text) {
46
57
  try {
@@ -80,7 +91,7 @@ function extractRscPayloadFromPrerenderedHtml(html) {
80
91
  let match;
81
92
  while ((match = scriptPattern.exec(html)) !== null) {
82
93
  const script = (match[1] ?? "").trim().replace(/;$/, "");
83
- if (script === RSC_RUNTIME_DONE_SCRIPT || script === RSC_LEGACY_DONE_SCRIPT) {
94
+ if (script === RSC_RUNTIME_DONE_SCRIPT || script === RSC_LEGACY_DONE_SCRIPT || script.endsWith(`;${RSC_RUNTIME_DONE_SCRIPT}`)) {
84
95
  sawDone = true;
85
96
  continue;
86
97
  }
@@ -256,11 +267,12 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
256
267
  const previousPrerenderFlag = process.env.VINEXT_PRERENDER;
257
268
  process.env.VINEXT_PRERENDER = "1";
258
269
  let ownedProdServerHandle = null;
270
+ let renderPool = null;
259
271
  try {
260
272
  let prerenderSecret = options._prerenderSecret;
261
273
  if (!prerenderSecret && pagesBundlePath) prerenderSecret = readPrerenderSecret(path.dirname(pagesBundlePath));
262
274
  if (!prerenderSecret) console.warn("[vinext] Warning: prerender secret not found. /__vinext/prerender/* endpoints will return 403 and dynamic routes will produce no paths. Run `vinext build` to regenerate the secret.");
263
- const baseUrl = `http://127.0.0.1:${(options._prodServer ? options._prodServer : await (async () => {
275
+ const prodServer = options._prodServer ? options._prodServer : await (async () => {
264
276
  const srv = await startProdServer({
265
277
  port: 0,
266
278
  host: "127.0.0.1",
@@ -270,12 +282,18 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
270
282
  });
271
283
  ownedProdServerHandle = srv;
272
284
  return srv;
273
- })()).port}`;
285
+ })();
286
+ const baseUrl = `http://127.0.0.1:${prodServer.port}`;
274
287
  const secretHeaders = prerenderSecret ? { [VINEXT_PRERENDER_SECRET_HEADER]: prerenderSecret } : {};
275
- const renderPage = (urlPath) => fetch(`${baseUrl}${urlPath}`, {
276
- headers: secretHeaders,
277
- redirect: "manual"
278
- });
288
+ let renderPorts = [prodServer.port];
289
+ let renderRoundRobin = 0;
290
+ const renderPage = (urlPath) => {
291
+ const port = renderPorts[renderRoundRobin++ % renderPorts.length];
292
+ return fetch(`http://127.0.0.1:${port}${urlPath}`, {
293
+ headers: secretHeaders,
294
+ redirect: "manual"
295
+ });
296
+ };
279
297
  const bundlePageRoutes = routes.map((r) => ({
280
298
  pattern: r.pattern,
281
299
  isDynamic: r.isDynamic ?? false,
@@ -396,6 +414,13 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
396
414
  revalidate
397
415
  });
398
416
  }
417
+ if (!options._prodServer && pagesBundlePath && prerenderPoolAvailable()) {
418
+ const poolSize = resolvePrerenderPoolSize(pagesToRender.length, concurrency);
419
+ if (poolSize > 1) {
420
+ renderPool = await startOptionalPrerenderServerPool(path.dirname(path.dirname(pagesBundlePath)), poolSize);
421
+ if (renderPool) renderPorts = renderPool.ports;
422
+ }
423
+ }
399
424
  let completed = 0;
400
425
  const pageResults = await runWithConcurrency(pagesToRender, concurrency, async ({ route, urlPath, revalidate }) => {
401
426
  let result;
@@ -426,6 +451,7 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
426
451
  ...urlPath !== route.pattern ? { path: urlPath } : {}
427
452
  };
428
453
  } catch (e) {
454
+ renderPool?.recordRenderError(e);
429
455
  const err = e;
430
456
  result = {
431
457
  route: route.pattern,
@@ -442,6 +468,7 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
442
468
  return result;
443
469
  });
444
470
  results.push(...pageResults);
471
+ renderPool?.assertHealthy();
445
472
  const hasCustom404 = findFileWithExtensions(path.join(pagesDir, "404"), fileMatcher);
446
473
  const hasErrorPage = findFileWithExtensions(path.join(pagesDir, "_error"), fileMatcher);
447
474
  if (hasCustom404 || hasErrorPage) try {
@@ -459,13 +486,17 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
459
486
  router: "pages"
460
487
  });
461
488
  }
462
- } catch {}
489
+ } catch (e) {
490
+ renderPool?.recordRenderError(e);
491
+ }
492
+ renderPool?.assertHealthy();
463
493
  if (!skipManifest) writePrerenderIndex(results, manifestDir, {
464
494
  buildId: config.buildId,
465
495
  trailingSlash: config.trailingSlash
466
496
  });
467
497
  return { routes: results };
468
498
  } finally {
499
+ if (renderPool) await renderPool.close();
469
500
  setCacheHandler(previousHandler);
470
501
  if (previousPrerenderFlag === void 0) delete process.env.VINEXT_PRERENDER;
471
502
  else process.env.VINEXT_PRERENDER = previousPrerenderFlag;
@@ -504,10 +535,13 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
504
535
  let rscHandler;
505
536
  let staticParamsMap = {};
506
537
  let ownedProdServerHandle = null;
538
+ let renderPool = null;
539
+ let renderPorts = [];
540
+ let renderRoundRobin = 0;
507
541
  try {
508
542
  const prerenderSecret = readPrerenderSecret(serverDir);
509
543
  if (!prerenderSecret) console.warn("[vinext] Warning: prerender secret not found. /__vinext/prerender/* endpoints will return 403 and generateStaticParams will not be called. Run `vinext build` to regenerate the secret.");
510
- const baseUrl = `http://127.0.0.1:${(options._prodServer ? options._prodServer : await (async () => {
544
+ const prodServer = options._prodServer ? options._prodServer : await (async () => {
511
545
  const srv = await startProdServer({
512
546
  port: 0,
513
547
  host: "127.0.0.1",
@@ -517,11 +551,13 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
517
551
  });
518
552
  ownedProdServerHandle = srv;
519
553
  return srv;
520
- })()).port}`;
554
+ })();
555
+ const baseUrl = `http://127.0.0.1:${prodServer.port}`;
556
+ renderPorts = [prodServer.port];
521
557
  const secretHeaders = prerenderSecret ? { [VINEXT_PRERENDER_SECRET_HEADER]: prerenderSecret } : {};
522
558
  rscHandler = (req) => {
523
559
  const parsed = new URL(req.url);
524
- const url = `${baseUrl}${parsed.pathname}${parsed.search}`;
560
+ const url = `http://127.0.0.1:${renderPorts[renderRoundRobin++ % renderPorts.length]}${parsed.pathname}${parsed.search}`;
525
561
  return fetch(url, {
526
562
  method: req.method,
527
563
  headers: {
@@ -717,25 +753,32 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
717
753
  const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(prerenderRouteParams);
718
754
  const htmlHeaders = new Headers();
719
755
  if (prerenderRouteParamsHeader !== null) htmlHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
756
+ if (isSpeculative) htmlHeaders.set(VINEXT_PRERENDER_SPECULATIVE_HEADER, "1");
720
757
  const htmlRequest = new Request(`http://localhost${urlPath}`, { headers: htmlHeaders });
721
758
  const htmlRender = await runWithHeadersContext(headersContextFromRequest(htmlRequest), async () => {
722
759
  const response = await rscHandler(htmlRequest);
723
760
  const cacheControl = response.headers.get("cache-control") ?? "";
724
- if (!response.ok || isSpeculative && cacheControl.includes("no-store")) {
761
+ const linkHeader = response.headers.get("link");
762
+ const responseCacheLife = readPrerenderCacheLifeHeader(response.headers);
763
+ if (!response.ok || cacheControl.includes("no-store")) {
725
764
  await response.body?.cancel();
726
765
  return {
727
766
  cacheControl,
767
+ linkHeader,
728
768
  html: null,
729
769
  ok: response.ok,
730
770
  requestCacheLife: null,
731
771
  status: response.status
732
772
  };
733
773
  }
774
+ const html = await response.text();
775
+ const processCacheLife = _consumeRequestScopedCacheLife();
734
776
  return {
735
777
  cacheControl,
736
- html: await response.text(),
778
+ linkHeader,
779
+ html,
737
780
  ok: true,
738
- requestCacheLife: _consumeRequestScopedCacheLife(),
781
+ requestCacheLife: responseCacheLife ?? processCacheLife,
739
782
  status: response.status
740
783
  };
741
784
  });
@@ -752,13 +795,11 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
752
795
  error: `RSC handler returned ${htmlRender.status}`
753
796
  };
754
797
  }
755
- if (isSpeculative) {
756
- if (htmlCacheControl.includes("no-store")) return {
757
- route: routePattern,
758
- status: "skipped",
759
- reason: "dynamic"
760
- };
761
- }
798
+ if (htmlCacheControl.includes("no-store")) return {
799
+ route: routePattern,
800
+ status: "skipped",
801
+ reason: "dynamic"
802
+ };
762
803
  if (htmlRender.html === null) return {
763
804
  route: routePattern,
764
805
  status: "error",
@@ -772,6 +813,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
772
813
  RSC: "1"
773
814
  });
774
815
  if (prerenderRouteParamsHeader !== null) rscHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
816
+ if (isSpeculative) rscHeaders.set(VINEXT_PRERENDER_SPECULATIVE_HEADER, "1");
775
817
  const rscRequest = new Request(`http://localhost${urlPath}`, { headers: rscHeaders });
776
818
  const rscRes = await runWithHeadersContext(headersContextFromRequest(rscRequest), () => rscHandler(rscRequest));
777
819
  if (!rscRes.ok) {
@@ -800,10 +842,12 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
800
842
  revalidate: renderedRevalidate,
801
843
  ...typeof renderedRevalidate === "number" ? { expire: renderedCacheControl.expire } : {},
802
844
  router: "app",
845
+ ...htmlRender.linkHeader ? { headers: { link: htmlRender.linkHeader } } : {},
803
846
  ...urlPath !== routePattern ? { path: urlPath } : {},
804
847
  ...isFallback ? { fallback: true } : {}
805
848
  };
806
849
  } catch (e) {
850
+ renderPool?.recordRenderError(e);
807
851
  if (isSpeculative) return {
808
852
  route: routePattern,
809
853
  status: "skipped",
@@ -818,6 +862,13 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
818
862
  };
819
863
  }
820
864
  }
865
+ if (!options._prodServer && prerenderPoolAvailable()) {
866
+ const poolSize = resolvePrerenderPoolSize(urlsToRender.length, concurrency);
867
+ if (poolSize > 1) {
868
+ renderPool = await startOptionalPrerenderServerPool(path.dirname(serverDir), poolSize);
869
+ if (renderPool) renderPorts = renderPool.ports;
870
+ }
871
+ }
821
872
  let completedApp = 0;
822
873
  const appResults = await runWithConcurrency(urlsToRender, concurrency, async (urlToRender) => {
823
874
  const result = await renderUrl(urlToRender);
@@ -830,6 +881,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
830
881
  return result;
831
882
  });
832
883
  results.push(...appResults);
884
+ renderPool?.assertHealthy();
833
885
  const outputFiles = mode === "export" && metadataRoutes.length > 0 ? emitStaticMetadataFiles(metadataRoutes, outDir) : [];
834
886
  try {
835
887
  const notFoundRequest = new Request(`http://localhost${NOT_FOUND_SENTINEL_PATH}`);
@@ -847,7 +899,10 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
847
899
  router: "app"
848
900
  });
849
901
  }
850
- } catch {}
902
+ } catch (e) {
903
+ renderPool?.recordRenderError(e);
904
+ }
905
+ renderPool?.assertHealthy();
851
906
  if (!skipManifest) writePrerenderIndex(results, manifestDir, {
852
907
  buildId: config.buildId,
853
908
  trailingSlash: config.trailingSlash
@@ -857,6 +912,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
857
912
  ...outputFiles.length > 0 ? { outputFiles } : {}
858
913
  };
859
914
  } finally {
915
+ if (renderPool) await renderPool.close();
860
916
  setCacheHandler(previousHandler);
861
917
  if (previousPrerenderFlag === void 0) delete process.env.VINEXT_PRERENDER;
862
918
  else process.env.VINEXT_PRERENDER = previousPrerenderFlag;
@@ -876,6 +932,19 @@ function resolveRenderedCacheControl(requestCacheLife, cacheControl, fallbackExp
876
932
  ...revalidate === void 0 ? {} : { revalidate }
877
933
  };
878
934
  }
935
+ function readPrerenderCacheLifeHeader(headers) {
936
+ const value = headers.get(VINEXT_PRERENDER_CACHE_LIFE_HEADER);
937
+ if (!value) return null;
938
+ try {
939
+ const parsed = JSON.parse(value);
940
+ const cacheLife = {};
941
+ if (typeof parsed.revalidate === "number" && Number.isFinite(parsed.revalidate)) cacheLife.revalidate = parsed.revalidate;
942
+ if (typeof parsed.expire === "number" && Number.isFinite(parsed.expire)) cacheLife.expire = parsed.expire;
943
+ return cacheLife.revalidate === void 0 && cacheLife.expire === void 0 ? null : cacheLife;
944
+ } catch {
945
+ return null;
946
+ }
947
+ }
879
948
  function resolveRenderedExpireSeconds(options) {
880
949
  const { fallbackExpireSeconds, sMaxage, staleWhileRevalidate } = options;
881
950
  if (sMaxage === void 0 || staleWhileRevalidate === void 0) return fallbackExpireSeconds;
@@ -907,6 +976,7 @@ function writePrerenderIndex(routes, outDir, options) {
907
976
  revalidate: r.revalidate,
908
977
  ...typeof r.revalidate === "number" ? { expire: r.expire } : {},
909
978
  router: r.router,
979
+ ...r.headers ? { headers: r.headers } : {},
910
980
  ...r.path ? { path: r.path } : {},
911
981
  ...r.fallback ? { fallback: true } : {}
912
982
  };
@@ -924,7 +994,8 @@ function writePrerenderIndex(routes, outDir, options) {
924
994
  const index = {
925
995
  ...buildId ? { buildId } : {},
926
996
  ...typeof trailingSlash === "boolean" ? { trailingSlash } : {},
927
- routes: indexRoutes
997
+ routes: indexRoutes,
998
+ pregeneratedConcretePaths: buildPregeneratedConcretePathTable({ routes: indexRoutes })
928
999
  };
929
1000
  fs.writeFileSync(path.join(outDir, "vinext-prerender.json"), JSON.stringify(index, null, 2), "utf-8");
930
1001
  }
@@ -17,6 +17,13 @@ type RouteRow = {
17
17
  prerendered?: boolean;
18
18
  };
19
19
  type AppRouteRenderEntry = Pick<AppRoute, "pagePath" | "routePath" | "parallelSlots">;
20
+ type StaticMiddlewareMatcherObject = {
21
+ source: string;
22
+ locale?: false;
23
+ has?: Array<Record<string, string>>;
24
+ missing?: Array<Record<string, string>>;
25
+ };
26
+ type StaticMiddlewareMatcher = string | Array<string | StaticMiddlewareMatcherObject>;
20
27
  declare function getAppRouteRenderEntryPath(route: AppRouteRenderEntry): string | null;
21
28
  /**
22
29
  * Returns true if the source code contains an export declaration with the given name.
@@ -28,12 +35,15 @@ declare function getAppRouteRenderEntryPath(route: AppRouteRenderEntry): string
28
35
  * export { foo }
29
36
  */
30
37
  declare function hasNamedExport(code: string, name: string): boolean;
38
+ /** Returns true when Next.js' analyzer recognizes the requested export name. */
39
+ declare function hasExportedName(code: string, name: string): boolean;
31
40
  /**
32
41
  * Extracts the string value of `export const <name> = "value"`.
33
42
  * Handles TypeScript annotations/assertions and no-substitution template literals.
34
43
  * Returns null if the export is absent or not a string literal.
35
44
  */
36
45
  declare function extractExportConstString(code: string, name: string): string | null;
46
+ declare function extractMiddlewareMatcherConfig(filePath: string): StaticMiddlewareMatcher | undefined;
37
47
  /**
38
48
  * Extracts the numeric value of `export const <name> = <number|false>`.
39
49
  * Supports integers, decimals, negative values, `Infinity`, and `false`.
@@ -121,7 +131,8 @@ declare function formatBuildReport(rows: RouteRow[], routerLabel?: string): stri
121
131
  * Scans the project at `root`, classifies all routes, and prints the
122
132
  * Next.js-style build report to stdout.
123
133
  *
124
- * Called at the end of `vinext build` in cli.ts.
134
+ * `root` must be forward-slash it is passed to `findDir`. The caller (the
135
+ * `vinext build` entry in cli.ts) normalizes it.
125
136
  */
126
137
  declare function printBuildReport(options: {
127
138
  root: string;
@@ -129,4 +140,4 @@ declare function printBuildReport(options: {
129
140
  prerenderResult?: PrerenderResult;
130
141
  }): Promise<void>;
131
142
  //#endregion
132
- export { RouteRow, RouteType, buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, formatBuildReport, getAppRouteRenderEntryPath, hasNamedExport, printBuildReport };
143
+ export { RouteRow, RouteType, StaticMiddlewareMatcher, buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
@@ -22,6 +22,7 @@ import { parseSync } from "vite";
22
22
  * `export const revalidate` are classified as "unknown" rather than "static"
23
23
  * to avoid false confidence.
24
24
  */
25
+ const UNSUPPORTED_STATIC_VALUE = Symbol("unsupported static value");
25
26
  function getAppRouteRenderEntryPath(route) {
26
27
  if (route.pagePath) return route.pagePath;
27
28
  if (route.routePath) return null;
@@ -72,6 +73,21 @@ function hasNamedExport(code, name) {
72
73
  if (!program) return false;
73
74
  return hasNamedExportInProgram(program, name);
74
75
  }
76
+ /** Returns true when Next.js' analyzer recognizes the requested export name. */
77
+ function hasExportedName(code, name) {
78
+ const program = parseRouteModule(code);
79
+ if (!program) return false;
80
+ for (const node of program.body) {
81
+ if (node.type !== "ExportNamedDeclaration") continue;
82
+ if (node.exportKind === "type") continue;
83
+ if (declarationHasBindingName(node.declaration, name)) return true;
84
+ for (const specifier of node.specifiers) {
85
+ if (specifier.exportKind === "type") continue;
86
+ if (moduleExportNameValue(specifier.local) === name) return true;
87
+ }
88
+ }
89
+ return false;
90
+ }
75
91
  function hasNamedExportInProgram(program, name) {
76
92
  for (const node of program.body) {
77
93
  if (node.type !== "ExportNamedDeclaration") continue;
@@ -117,6 +133,86 @@ function extractStringFromConstInitializer(initializer) {
117
133
  if (expression.type === "TemplateLiteral" && expression.expressions.length === 0) return expression.quasis[0]?.value.cooked ?? expression.quasis[0]?.value.raw ?? null;
118
134
  return null;
119
135
  }
136
+ function extractMiddlewareMatcherConfig(filePath) {
137
+ let code;
138
+ try {
139
+ code = fs.readFileSync(filePath, "utf8");
140
+ } catch {
141
+ return;
142
+ }
143
+ const initializer = findExportedConstInitializer(code, "config");
144
+ if (!initializer) return void 0;
145
+ const config = unwrapStaticExpression(initializer);
146
+ if (config.type !== "ObjectExpression") return void 0;
147
+ const matcherExpression = objectPropertyValue(config, "matcher");
148
+ if (!matcherExpression) return void 0;
149
+ const value = extractStaticJsonValue(matcherExpression);
150
+ return isStaticMiddlewareMatcher(value) ? value : void 0;
151
+ }
152
+ function objectPropertyValue(object, key) {
153
+ for (const property of object.properties) {
154
+ if (property.type !== "Property" || property.computed) continue;
155
+ if (propertyKeyName(property.key) !== key) continue;
156
+ return property.value;
157
+ }
158
+ return null;
159
+ }
160
+ function propertyKeyName(key) {
161
+ if (key.type === "Identifier") return key.name;
162
+ if (key.type === "Literal" && typeof key.value === "string") return key.value;
163
+ return null;
164
+ }
165
+ function extractStaticJsonValue(expression) {
166
+ const value = unwrapStaticExpression(expression);
167
+ if (value.type === "Literal") {
168
+ if (typeof value.value === "string" || typeof value.value === "number" || typeof value.value === "boolean" || value.value === null) return value.value;
169
+ return UNSUPPORTED_STATIC_VALUE;
170
+ }
171
+ if (value.type === "TemplateLiteral" && value.expressions.length === 0) return value.quasis[0]?.value.cooked ?? value.quasis[0]?.value.raw ?? "";
172
+ if (value.type === "ArrayExpression") {
173
+ const items = [];
174
+ for (const element of value.elements) {
175
+ if (!element || element.type === "SpreadElement") return UNSUPPORTED_STATIC_VALUE;
176
+ const item = extractStaticJsonValue(element);
177
+ if (item === UNSUPPORTED_STATIC_VALUE) return UNSUPPORTED_STATIC_VALUE;
178
+ items.push(item);
179
+ }
180
+ return items;
181
+ }
182
+ if (value.type === "ObjectExpression") {
183
+ const object = {};
184
+ for (const property of value.properties) {
185
+ if (property.type !== "Property" || property.computed) return UNSUPPORTED_STATIC_VALUE;
186
+ const key = propertyKeyName(property.key);
187
+ if (!key) return UNSUPPORTED_STATIC_VALUE;
188
+ const propertyValue = extractStaticJsonValue(property.value);
189
+ if (propertyValue === UNSUPPORTED_STATIC_VALUE) return UNSUPPORTED_STATIC_VALUE;
190
+ object[key] = propertyValue;
191
+ }
192
+ return object;
193
+ }
194
+ return UNSUPPORTED_STATIC_VALUE;
195
+ }
196
+ function isStaticMiddlewareMatcher(value) {
197
+ if (typeof value === "string") return true;
198
+ if (!Array.isArray(value)) return false;
199
+ return value.every((item) => typeof item === "string" || isStaticMiddlewareMatcherObject(item));
200
+ }
201
+ function isStaticMiddlewareMatcherObject(value) {
202
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
203
+ const record = value;
204
+ if (typeof record.source !== "string") return false;
205
+ if (record.locale !== void 0 && record.locale !== false) return false;
206
+ return isStaticMatcherConditions(record.has) && isStaticMatcherConditions(record.missing);
207
+ }
208
+ function isStaticMatcherConditions(value) {
209
+ if (value === void 0) return true;
210
+ if (!Array.isArray(value)) return false;
211
+ return value.every((item) => {
212
+ if (!item || typeof item !== "object" || Array.isArray(item)) return false;
213
+ return Object.values(item).every((entry) => typeof entry === "string");
214
+ });
215
+ }
120
216
  /**
121
217
  * Extracts the numeric value of `export const <name> = <number|false>`.
122
218
  * Supports integers, decimals, negative values, `Infinity`, and `false`.
@@ -474,7 +570,8 @@ function formatBuildReport(rows, routerLabel = "app") {
474
570
  * Scans the project at `root`, classifies all routes, and prints the
475
571
  * Next.js-style build report to stdout.
476
572
  *
477
- * Called at the end of `vinext build` in cli.ts.
573
+ * `root` must be forward-slash it is passed to `findDir`. The caller (the
574
+ * `vinext build` entry in cli.ts) normalizes it.
478
575
  */
479
576
  async function printBuildReport(options) {
480
577
  const { root } = options;
@@ -501,4 +598,4 @@ async function printBuildReport(options) {
501
598
  }
502
599
  }
503
600
  //#endregion
504
- export { buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, formatBuildReport, getAppRouteRenderEntryPath, hasNamedExport, printBuildReport };
601
+ export { buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
@@ -56,6 +56,13 @@ type RunPrerenderOptions = {
56
56
  * `output: 'export'`. Exported for direct unit testing. Refs cloudflare/vinext#1982
57
57
  */
58
58
  declare function assertNoFatalPrerenderRoutes(routes: readonly PrerenderRouteResult[]): void;
59
+ /**
60
+ * Statically generate routes and return the prerender result.
61
+ *
62
+ * `options.root` must be forward-slash — it is passed to `findDir` and flows
63
+ * into the route model. The caller (the `vinext build` entry in cli.ts)
64
+ * normalizes it.
65
+ */
59
66
  declare function runPrerender(options: RunPrerenderOptions): Promise<PrerenderResult | null>;
60
67
  //#endregion
61
68
  export { assertNoFatalPrerenderRoutes, runPrerender };
@@ -4,14 +4,15 @@ import { appRouter } from "../routing/app-router.js";
4
4
  import { scanMetadataFiles } from "../server/metadata-routes.js";
5
5
  import { loadNextConfig, resolveNextConfig } from "../config/next-config.js";
6
6
  import { readPrerenderSecret } from "./server-manifest.js";
7
- import { startProdServer } from "../server/prod-server.js";
7
+ import { rememberCurrentServerEntryImportMtime, startProdServer } from "../server/prod-server.js";
8
8
  import { prerenderApp, prerenderPages, writePrerenderIndex } from "./prerender.js";
9
+ import { injectPregeneratedConcretePaths } from "./inject-pregenerated-paths.js";
9
10
  import fs from "node:fs";
10
11
  import path from "node:path";
11
12
  //#region src/build/run-prerender.ts
12
13
  /**
13
14
  * Shared prerender runner used by both `vinext build` (cli.ts) and
14
- * `vinext deploy --prerender-all` (deploy.ts).
15
+ * `vinext-cloudflare deploy --prerender-all` (deploy.ts).
15
16
  *
16
17
  * `runPrerender` handles route scanning, dynamic imports, progress reporting,
17
18
  * and result summarisation.
@@ -95,6 +96,13 @@ function assertNoFatalPrerenderRoutes(routes) {
95
96
  const fatalList = fatalRoutes.map((r) => ` ${r.route}: ${r.error}`).join("\n");
96
97
  throw new Error(`Prerender failed: ${fatalRoutes.length} route${fatalRoutes.length !== 1 ? "s" : ""} errored during static generation.\n${fatalList}`);
97
98
  }
99
+ /**
100
+ * Statically generate routes and return the prerender result.
101
+ *
102
+ * `options.root` must be forward-slash — it is passed to `findDir` and flows
103
+ * into the route model. The caller (the `vinext build` entry in cli.ts)
104
+ * normalizes it.
105
+ */
98
106
  async function runPrerender(options) {
99
107
  const { root } = options;
100
108
  const appDir = findDir(root, "app", "src/app");
@@ -215,6 +223,8 @@ async function runPrerender(options) {
215
223
  const errorRoutes = allRoutes.filter((r) => r.status === "error").map((r) => ` ${r.route}: ${r.error}`).join("\n");
216
224
  throw new Error(`Static export failed: ${errors} route${errors !== 1 ? "s" : ""} cannot be statically exported.\n${errorRoutes}\n\nRemove server-side data fetching (getServerSideProps, force-dynamic, revalidate) from these routes, or remove \`output: "export"\` from next.config.js.`);
217
225
  }
226
+ injectPregeneratedConcretePaths(root);
227
+ if (fs.existsSync(rscBundlePath)) rememberCurrentServerEntryImportMtime(rscBundlePath);
218
228
  return {
219
229
  routes: allRoutes,
220
230
  ...allOutputFiles.length > 0 ? { outputFiles: allOutputFiles } : {}
@@ -167,6 +167,8 @@ function emitStandaloneOutput(options) {
167
167
  dereference: true,
168
168
  filter: (src) => !path.relative(serverDir, src).split(path.sep).includes("node_modules")
169
169
  });
170
+ const clientAssetsSidecar = path.join(outDir, "vinext-client-assets.js");
171
+ if (fs.existsSync(clientAssetsSidecar)) fs.copyFileSync(clientAssetsSidecar, path.join(standaloneDistDir, "vinext-client-assets.js"));
170
172
  const publicDir = path.join(root, "public");
171
173
  if (fs.existsSync(publicDir)) fs.cpSync(publicDir, path.join(standaloneDir, "public"), {
172
174
  recursive: true,
@@ -35,9 +35,9 @@ function generateCacheAdaptersModule(cache) {
35
35
  lines.push(`import __vinextCdnAdapterFactory from ${JSON.stringify(cdn.adapter)};`);
36
36
  lines.push(`import { setCdnCacheAdapter } from "vinext/shims/cdn-cache";`);
37
37
  }
38
- lines.push("", "// A factory that throws (e.g. a missing binding on an incompatible runtime)", "// is logged and skipped so the default handler stays in place.", "let __vinextCacheAdaptersRegistered = false;", "", "export function registerConfiguredCacheAdapters(env) {", " if (__vinextCacheAdaptersRegistered) return;", " __vinextCacheAdaptersRegistered = true;");
39
- if (data?.adapter) lines.push(" try {", ` setDataCacheHandler(__vinextDataAdapterFactory({ env, options: ${inlineOptions(data.adapter, data.options)} }));`, " } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured data cache adapter; using the default handler.\", error);", " }");
40
- if (cdn?.adapter) lines.push(" try {", ` setCdnCacheAdapter(__vinextCdnAdapterFactory({ env, options: ${inlineOptions(cdn.adapter, cdn.options)} }));`, " } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured CDN cache adapter; using the default adapter.\", error);", " }");
38
+ lines.push("", "// A factory that throws (e.g. a missing binding on an incompatible runtime)", "// is logged and skipped so the default handler stays in place.", "function __vinextFormatAdapterError(error) {", " if (error instanceof Error && error.message) return error.message;", " try {", " return String(error);", " } catch {", " return '<unknown error>';", " }", "}", "", "let __vinextCacheAdaptersRegistered = false;", "", "export function registerConfiguredCacheAdapters(env) {", " if (__vinextCacheAdaptersRegistered) return;", " __vinextCacheAdaptersRegistered = true;");
39
+ if (data?.adapter) lines.push(" try {", ` setDataCacheHandler(__vinextDataAdapterFactory({ env, options: ${inlineOptions(data.adapter, data.options)} }));`, " } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured data cache adapter; using the default handler.\\n\" + __vinextFormatAdapterError(error));", " }");
40
+ if (cdn?.adapter) lines.push(" try {", ` setCdnCacheAdapter(__vinextCdnAdapterFactory({ env, options: ${inlineOptions(cdn.adapter, cdn.options)} }));`, " } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured CDN cache adapter; using the default adapter.\\n\" + __vinextFormatAdapterError(error));", " }");
41
41
  lines.push("}", "");
42
42
  return lines.join("\n");
43
43
  }
package/dist/check.d.ts CHANGED
@@ -60,9 +60,8 @@ declare function hasFreeCjsGlobal(content: string): boolean;
60
60
  /**
61
61
  * Scan source files for `import ... from 'next/...'` statements.
62
62
  *
63
- * `root` must be forward-slash: it is passed to `findSourceFiles` (which
64
- * requires it) and used as the base of `path.posix.relative`, which only yields
65
- * a canonical relative path when both operands are forward-slash.
63
+ * `root` must be forward-slash: it is passed to `findSourceFiles`, which
64
+ * requires it.
66
65
  */
67
66
  declare function scanImports(root: string): CheckItem[];
68
67
  /**
@@ -82,9 +81,8 @@ declare function checkLibraries(root: string): CheckItem[];
82
81
  /**
83
82
  * Check file conventions (pages, app directory, middleware, etc.)
84
83
  *
85
- * `root` must be forward-slash — joined with `path.posix.join`, passed to
86
- * `findDir`, and used as the base of `path.posix.relative`. Only called from
87
- * `runCheck`, which normalizes it.
84
+ * `root` must be forward-slash — joined with `path.posix.join` and passed to
85
+ * `findDir`. Only called from `runCheck`, which normalizes it.
88
86
  */
89
87
  declare function checkConventions(root: string): CheckItem[];
90
88
  /**