@wular/pnext 0.1.6 → 0.1.8

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 (337) hide show
  1. package/bin/pnext +9 -2
  2. package/dist/server/_chunks/aliases-pycbtjt1.js +46 -0
  3. package/dist/server/_chunks/analyze-4d9e00g9.js +449 -0
  4. package/dist/server/_chunks/boot-bhemypxz.js +38 -0
  5. package/dist/server/_chunks/build-c991dv65.js +86 -0
  6. package/dist/server/_chunks/build-kh5e3x48.js +3341 -0
  7. package/dist/server/_chunks/build-tier-0a96kmx3.js +2456 -0
  8. package/dist/server/_chunks/compile-040r79gj.js +22 -0
  9. package/dist/server/_chunks/config-0tvz03a6.js +24 -0
  10. package/dist/server/_chunks/config-27rj7830.js +36 -0
  11. package/dist/server/_chunks/create-9rvwshsp.js +135 -0
  12. package/dist/server/_chunks/dev-ex9exynt.js +220 -0
  13. package/dist/server/_chunks/esbuild-yyktfrtt.js +15 -0
  14. package/dist/server/_chunks/generate-myw9hef7.js +22 -0
  15. package/dist/server/_chunks/index-0r5eqfc7.js +279 -0
  16. package/dist/server/_chunks/index-0yjv5v47.js +105 -0
  17. package/dist/server/_chunks/index-11xpbfej.js +17 -0
  18. package/dist/server/_chunks/index-141vcwk6.js +109 -0
  19. package/dist/server/_chunks/index-1pzm1hdy.js +4130 -0
  20. package/dist/server/_chunks/index-29pq0kax.js +590 -0
  21. package/dist/server/_chunks/index-2vz0wzdn.js +59 -0
  22. package/dist/server/_chunks/index-2wfv4kvx.js +182 -0
  23. package/dist/server/_chunks/index-38xpdxwd.js +194 -0
  24. package/dist/server/_chunks/index-3e075kny.js +128 -0
  25. package/dist/server/_chunks/index-3ycf5ckv.js +472 -0
  26. package/dist/server/_chunks/index-4399bvd2.js +18 -0
  27. package/dist/server/_chunks/index-4da76t62.js +297 -0
  28. package/dist/server/_chunks/index-4t92hdxd.js +172 -0
  29. package/dist/server/_chunks/index-4zzebszd.js +10 -0
  30. package/dist/server/_chunks/index-52k54ykj.js +95 -0
  31. package/dist/server/_chunks/index-5hr4062s.js +70 -0
  32. package/dist/server/_chunks/index-68m3mwbd.js +84 -0
  33. package/dist/server/_chunks/index-6b009mt0.js +56 -0
  34. package/dist/server/_chunks/index-6gex3pfa.js +15 -0
  35. package/dist/server/_chunks/index-6kp9r0d9.js +11 -0
  36. package/dist/server/_chunks/index-6q7zvrap.js +134 -0
  37. package/dist/server/_chunks/index-71885zaq.js +284 -0
  38. package/dist/server/_chunks/index-73z4esab.js +38 -0
  39. package/dist/server/_chunks/index-79ngvam8.js +60 -0
  40. package/dist/server/_chunks/index-7wc9n8be.js +25 -0
  41. package/dist/server/_chunks/index-8ct7rqy3.js +198 -0
  42. package/dist/server/_chunks/index-8kcxdfya.js +206 -0
  43. package/dist/server/_chunks/index-9ch50vtq.js +18 -0
  44. package/dist/server/_chunks/index-9f49w76j.js +213 -0
  45. package/dist/server/_chunks/index-9hz3s0m8.js +851 -0
  46. package/dist/server/_chunks/index-akkrvjn6.js +535 -0
  47. package/dist/server/_chunks/index-b6wtkdp4.js +24 -0
  48. package/dist/server/_chunks/index-b7j671dj.js +207 -0
  49. package/dist/server/_chunks/index-bcws5eh2.js +35 -0
  50. package/dist/server/_chunks/index-bzd2ef7t.js +10 -0
  51. package/dist/server/_chunks/index-cjgqs13g.js +65 -0
  52. package/dist/server/_chunks/index-ckkdpht5.js +41 -0
  53. package/dist/server/_chunks/index-cn86z041.js +296 -0
  54. package/dist/server/_chunks/index-cvr4z22m.js +92 -0
  55. package/dist/server/_chunks/index-cyfednwb.js +87 -0
  56. package/dist/server/_chunks/index-d158y67j.js +152 -0
  57. package/dist/server/_chunks/index-d1jsmhhk.js +955 -0
  58. package/dist/server/_chunks/index-dg0f05zs.js +199 -0
  59. package/dist/server/_chunks/index-dgr390q7.js +9438 -0
  60. package/dist/server/_chunks/index-drwv61cc.js +185 -0
  61. package/dist/server/_chunks/index-e3a6yx84.js +18 -0
  62. package/dist/server/_chunks/index-e88tyv35.js +45 -0
  63. package/dist/server/_chunks/index-en7n9f7m.js +22 -0
  64. package/dist/server/_chunks/index-entm8bmz.js +28 -0
  65. package/dist/server/_chunks/index-evhd413j.js +57 -0
  66. package/dist/server/_chunks/index-ezgj0tr1.js +88 -0
  67. package/dist/server/_chunks/index-fjv96re1.js +5295 -0
  68. package/dist/server/_chunks/index-fn1chkkd.js +214 -0
  69. package/dist/server/_chunks/index-fp8r0sqs.js +64 -0
  70. package/dist/server/_chunks/index-gcmj16xy.js +92 -0
  71. package/dist/server/_chunks/index-gkndxkxf.js +736 -0
  72. package/dist/server/_chunks/index-grta8ap3.js +238 -0
  73. package/dist/server/_chunks/index-h2a70tg4.js +7 -0
  74. package/dist/server/_chunks/index-h4xy648y.js +51 -0
  75. package/dist/server/_chunks/index-hdbwtdv2.js +352 -0
  76. package/dist/server/_chunks/index-he6n2bvp.js +1270 -0
  77. package/dist/server/_chunks/index-j0m1jxqa.js +1253 -0
  78. package/dist/server/_chunks/index-j2zrcp1k.js +66 -0
  79. package/dist/server/_chunks/index-j681c48a.js +2530 -0
  80. package/dist/server/_chunks/index-jbj7ehef.js +50 -0
  81. package/dist/server/_chunks/index-jcsjq3d1.js +20 -0
  82. package/dist/server/_chunks/index-jks0x0a1.js +14 -0
  83. package/dist/server/_chunks/index-jn9rbhrx.js +1599 -0
  84. package/dist/server/_chunks/index-jt8e1pje.js +12 -0
  85. package/dist/server/_chunks/index-kdhxpwyq.js +483 -0
  86. package/dist/server/_chunks/index-knp0s912.js +7 -0
  87. package/dist/server/_chunks/index-kqyy1d4f.js +50 -0
  88. package/dist/server/_chunks/index-kresp2n8.js +445 -0
  89. package/dist/server/_chunks/index-m63czgq5.js +590 -0
  90. package/dist/server/_chunks/index-ms9v9tnx.js +31 -0
  91. package/dist/server/_chunks/index-n4hxpng2.js +11 -0
  92. package/dist/server/_chunks/index-n629dy38.js +1071 -0
  93. package/dist/server/_chunks/index-nf3df4jy.js +266 -0
  94. package/dist/server/_chunks/index-nqh3xhdc.js +57 -0
  95. package/dist/server/_chunks/index-pym1cezq.js +39 -0
  96. package/dist/server/_chunks/index-q3r9z8mq.js +213 -0
  97. package/dist/server/_chunks/index-q738kkhj.js +100 -0
  98. package/dist/server/_chunks/index-qcrvt6er.js +112 -0
  99. package/dist/server/_chunks/index-qjj6gddn.js +54 -0
  100. package/dist/server/_chunks/index-qpwsgt3a.js +158 -0
  101. package/dist/server/_chunks/index-r97ptz1q.js +97 -0
  102. package/dist/server/_chunks/index-ras0sdab.js +26 -0
  103. package/dist/server/_chunks/index-rcs322t1.js +19 -0
  104. package/dist/server/_chunks/index-rgj5mx84.js +101 -0
  105. package/dist/server/_chunks/index-rjegyfx4.js +1435 -0
  106. package/dist/server/_chunks/index-rt9qq1va.js +4 -0
  107. package/dist/server/_chunks/index-rthrsed5.js +356 -0
  108. package/dist/server/_chunks/index-sq3kvz3g.js +200 -0
  109. package/dist/server/_chunks/index-stbk3jhn.js +31 -0
  110. package/dist/server/_chunks/index-tf4j19ge.js +130 -0
  111. package/dist/server/_chunks/index-tgcakdxc.js +125 -0
  112. package/dist/server/_chunks/index-tgsfjmep.js +74 -0
  113. package/dist/server/_chunks/index-tq1f96v7.js +51 -0
  114. package/dist/server/_chunks/index-tq3b39a5.js +107 -0
  115. package/dist/server/_chunks/index-typcbcsv.js +819 -0
  116. package/dist/server/_chunks/index-v1ags1na.js +132 -0
  117. package/dist/server/_chunks/index-vmr35jjw.js +170 -0
  118. package/dist/server/_chunks/index-vvq7rqtn.js +329 -0
  119. package/dist/server/_chunks/index-vz0km5qk.js +33 -0
  120. package/dist/server/_chunks/index-w6h8m6vp.js +138 -0
  121. package/dist/server/_chunks/index-web361b8.js +138 -0
  122. package/dist/server/_chunks/index-wm36rj4v.js +311 -0
  123. package/dist/server/_chunks/index-x0y005nh.js +283 -0
  124. package/dist/server/_chunks/index-x2m17v9c.js +6166 -0
  125. package/dist/server/_chunks/index-x3yn4ywm.js +22 -0
  126. package/dist/server/_chunks/index-x9c55pzy.js +871 -0
  127. package/dist/server/_chunks/index-xh1nbk46.js +1299 -0
  128. package/dist/server/_chunks/index-xvj80c3a.js +30 -0
  129. package/dist/server/_chunks/index-xywjg2vc.js +100 -0
  130. package/dist/server/_chunks/index-y4d02znn.js +66 -0
  131. package/dist/server/_chunks/index-y7mzvq2q.js +110 -0
  132. package/dist/server/_chunks/index-ye9hmsvf.js +47 -0
  133. package/dist/server/_chunks/index-yhxkxghe.js +56 -0
  134. package/dist/server/_chunks/index-yrjkqtfj.js +86 -0
  135. package/dist/server/_chunks/index-z88qv16t.js +11 -0
  136. package/dist/server/_chunks/index-zfrwq472.js +154 -0
  137. package/dist/server/_chunks/index-zqpqyg8p.js +666 -0
  138. package/dist/server/_chunks/loader-5en7wpt5.js +80 -0
  139. package/dist/server/_chunks/modules-t92favpj.js +76 -0
  140. package/dist/server/_chunks/pipeline-4t9q43ht.js +836 -0
  141. package/dist/server/_chunks/production-385bw8df.js +55 -0
  142. package/dist/server/_chunks/proxy-config-60gprphe.js +23 -0
  143. package/dist/server/_chunks/proxy-gfhmktbk.js +35 -0
  144. package/dist/server/_chunks/router-xnrva921.js +501 -0
  145. package/dist/server/_chunks/routes-ht868pdc.js +66 -0
  146. package/dist/server/_chunks/run-d40d63ag.js +697 -0
  147. package/dist/server/_chunks/server-2p8fgqq2.js +1839 -0
  148. package/dist/server/_chunks/tsconfig-defaults-bcn34eqb.js +21 -0
  149. package/dist/server/_chunks/typegen-dvtcjtaw.js +37 -0
  150. package/dist/server/api/client-cache.js +14 -0
  151. package/dist/server/cli/index.js +375 -0
  152. package/dist/server/cli/start.js +158 -0
  153. package/dist/server/client/reference.js +16 -0
  154. package/dist/server/compat/next/cache.js +56 -0
  155. package/dist/server/compat/next/canonical-url.js +13 -0
  156. package/dist/server/compat/next/client-cache.js +57 -0
  157. package/dist/server/compat/next/client-navigation.js +232 -0
  158. package/dist/server/compat/next/client-only.js +1 -0
  159. package/dist/server/compat/next/client-script.js +178 -0
  160. package/dist/server/compat/next/client-server.js +34 -0
  161. package/dist/server/compat/next/config-headers.js +34 -0
  162. package/dist/server/compat/next/config-loader.js +25 -0
  163. package/dist/server/compat/next/config.js +10 -0
  164. package/dist/server/compat/next/constants.js +18 -0
  165. package/dist/server/compat/next/custom-server.js +10 -0
  166. package/dist/server/compat/next/dist/client/components/app-router-headers.js +48 -0
  167. package/dist/server/compat/next/dist/server/web/spec-extension/revalidate.js +27 -0
  168. package/dist/server/compat/next/dist/server/web/spec-extension/unstable-cache.js +25 -0
  169. package/dist/server/compat/next/dist/server/web/spec-extension/unstable-no-store.js +25 -0
  170. package/dist/server/compat/next/dynamic.js +21 -0
  171. package/dist/server/compat/next/error.js +112 -0
  172. package/dist/server/compat/next/font/cache.js +16 -0
  173. package/dist/server/compat/next/font/google.js +3 -0
  174. package/dist/server/compat/next/font/index.js +1 -0
  175. package/dist/server/compat/next/font/local.js +24 -0
  176. package/dist/server/compat/next/font/runtime-client.js +33 -0
  177. package/dist/server/compat/next/font/runtime.js +43 -0
  178. package/dist/server/compat/next/font/shared.js +22 -0
  179. package/dist/server/compat/next/form.js +129 -0
  180. package/dist/server/compat/next/head.js +10 -0
  181. package/dist/server/compat/next/headers.js +30 -0
  182. package/dist/server/compat/next/image/client.js +11 -0
  183. package/dist/server/compat/next/image/config.js +18 -0
  184. package/dist/server/compat/next/image/optimizer.js +25 -0
  185. package/dist/server/compat/next/image/patterns.js +12 -0
  186. package/dist/server/compat/next/image/props.js +16 -0
  187. package/dist/server/compat/next/image/shared.js +86 -0
  188. package/dist/server/compat/next/image/static-metadata.js +32 -0
  189. package/dist/server/compat/next/image/validate.js +8 -0
  190. package/dist/server/compat/next/image.js +59 -0
  191. package/dist/server/compat/next/index.js +1 -0
  192. package/dist/server/compat/next/legacy-image.js +78 -0
  193. package/dist/server/compat/next/legacy-request-apis.js +24 -0
  194. package/dist/server/compat/next/link-transform.js +15 -0
  195. package/dist/server/compat/next/link.js +14 -0
  196. package/dist/server/compat/next/navigation.js +191 -0
  197. package/dist/server/compat/next/offline.js +45 -0
  198. package/dist/server/compat/next/og.js +8 -0
  199. package/dist/server/compat/next/preferred-region.js +8 -0
  200. package/dist/server/compat/next/redirects.js +32 -0
  201. package/dist/server/compat/next/resource-hints.js +30 -0
  202. package/dist/server/compat/next/rewrites.js +43 -0
  203. package/dist/server/compat/next/root-params.js +121 -0
  204. package/dist/server/compat/next/route-state.js +23 -0
  205. package/dist/server/compat/next/router.js +103 -0
  206. package/dist/server/compat/next/script.js +37 -0
  207. package/dist/server/compat/next/server-only.js +1 -0
  208. package/dist/server/compat/next/server.js +131 -0
  209. package/dist/server/compat/next/svgr.js +14 -0
  210. package/dist/server/compat/next/telemetry.js +10 -0
  211. package/dist/server/compat/next/usage.js +13 -0
  212. package/dist/server/compat/next/user-agent.js +12 -0
  213. package/dist/server/compat/next/web-vitals.js +35 -0
  214. package/dist/server/compat/react/action-state.js +11 -0
  215. package/dist/server/compat/react/client-lite.js +83 -0
  216. package/dist/server/compat/react/client.js +131 -0
  217. package/dist/server/compat/react/compiler-runtime.js +42 -0
  218. package/dist/server/compat/react/dom-client.js +106 -0
  219. package/dist/server/compat/react/dom-react-server.js +37 -0
  220. package/dist/server/compat/react/dom-server.js +40 -0
  221. package/dist/server/compat/react/dom.js +52 -0
  222. package/dist/server/compat/react/hooks-extra.js +12 -0
  223. package/dist/server/compat/react/internals.js +14 -0
  224. package/dist/server/compat/react/parity.js +11 -0
  225. package/dist/server/compat/react/preact.js +113 -0
  226. package/dist/server/compat/react/react-server.js +133 -0
  227. package/dist/server/compat/react/router-shim.js +8 -0
  228. package/dist/server/compat/react/server-component-use.js +14 -0
  229. package/dist/server/compat/react/server-inserted-html.js +14 -0
  230. package/dist/server/compat/react/server.js +194 -0
  231. package/dist/server/compat/react/style-values.js +10 -0
  232. package/dist/server/compat/react/use.js +10 -0
  233. package/dist/server/compat/react/view-transition.js +12 -0
  234. package/dist/server/generation.json +1 -0
  235. package/package.json +4 -1
  236. package/reference/compat.md +1 -1
  237. package/reference/config.md +2 -2
  238. package/reference/dev.md +1 -1
  239. package/reference/getting-started.md +1 -1
  240. package/reference/rendering.md +4 -4
  241. package/src/api/dynamic.tsx +86 -4
  242. package/src/api/server.ts +24 -6
  243. package/src/cli/adapters/vercel-warm.ts +327 -62
  244. package/src/cli/adapters/vercel.ts +164 -1054
  245. package/src/cli/analyze.ts +3 -2
  246. package/src/cli/build/cache.ts +6 -6
  247. package/src/cli/build.ts +78 -45
  248. package/src/cli/index.ts +21 -3
  249. package/src/cli/serve/immutable.ts +31 -0
  250. package/src/cli/serve/pipeline.ts +97 -93
  251. package/src/cli/serve/standalone-worker.ts +8 -0
  252. package/src/cli/serve/standalone.ts +703 -0
  253. package/src/cli/start.ts +31 -8
  254. package/src/client/build.ts +1 -1
  255. package/src/client/chunk-fold.ts +19 -6
  256. package/src/client/react-tier.ts +3 -2
  257. package/src/client/reference-stub.ts +23 -36
  258. package/src/compat/actions/client-plugin.ts +4 -2
  259. package/src/compat/actions/config.ts +6 -5
  260. package/src/compat/actions/extensions.ts +5 -5
  261. package/src/compat/aliases.ts +8 -1
  262. package/src/compat/bundler/config.ts +88 -0
  263. package/src/compat/bundler/optimize-package-imports.ts +86 -26
  264. package/src/compat/bundler/relay-transform.ts +15 -3
  265. package/src/compat/bundler/require-context.ts +2 -1
  266. package/src/compat/bundler/webpack-loaders.ts +3 -2
  267. package/src/compat/bundler/worker.ts +2 -2
  268. package/src/compat/cache-control.ts +16 -8
  269. package/src/compat/css/inline-css.ts +1 -1
  270. package/src/compat/css/modules.ts +18 -5
  271. package/src/compat/export/build-complete.ts +1 -1
  272. package/src/compat/export/emit.ts +4 -4
  273. package/src/compat/export/standalone.ts +30 -65
  274. package/src/compat/image-optimizer/optimize.ts +1 -1
  275. package/src/compat/lifecycle/error-funnel.ts +1 -1
  276. package/src/compat/lifecycle/instrumentation-client.ts +4 -3
  277. package/src/compat/lifecycle/instrumentation.ts +117 -24
  278. package/src/compat/lifecycle/testmode.ts +3 -2
  279. package/src/compat/metadata-artifacts.ts +1 -1
  280. package/src/compat/next/config-loader.ts +140 -13
  281. package/src/compat/next/dynamic.tsx +10 -23
  282. package/src/compat/next/font/cache.ts +2 -1
  283. package/src/compat/next/font/runtime.ts +23 -3
  284. package/src/compat/next/og.ts +3 -3
  285. package/src/compat/next/usage.ts +3 -2
  286. package/src/compat/pages/api.ts +83 -14
  287. package/src/compat/pages/router.ts +30 -14
  288. package/src/compat/register/bundler.ts +3 -1
  289. package/src/compat/register/image.ts +1 -1
  290. package/src/compat/register/middleware.ts +1 -1
  291. package/src/compat/register/otel.ts +5 -9
  292. package/src/compat/register/render.ts +3 -1
  293. package/src/compat/register/use-cache.ts +3 -1
  294. package/src/compat/release.ts +46 -0
  295. package/src/compat/segment/serve.ts +1 -1
  296. package/src/compat/typecheck/check.ts +6 -4
  297. package/src/compat/validation/validate.ts +10 -7
  298. package/src/config.ts +76 -9
  299. package/src/css/assets.ts +213 -0
  300. package/src/css/build.ts +190 -247
  301. package/src/dev/client-chunk-store.ts +2 -1
  302. package/src/dev/restart/client-key.ts +2 -1
  303. package/src/dev/restart/global-css.ts +2 -1
  304. package/src/dev/restart/route-bundle-key.ts +2 -1
  305. package/src/dev/restart/route-facts.ts +2 -1
  306. package/src/dev/server.ts +34 -14
  307. package/src/extensions.ts +4 -0
  308. package/src/out-paths.ts +10 -0
  309. package/src/render/hooks.ts +13 -0
  310. package/src/render/renderer.ts +53 -33
  311. package/src/request/context.ts +9 -0
  312. package/src/resolve/dynamic.ts +7 -1
  313. package/src/resolve/engine.ts +2 -12
  314. package/src/resolve/imports.ts +12 -52
  315. package/src/resolve/package-json.ts +56 -0
  316. package/src/resolve/scan-facts.ts +250 -1
  317. package/src/routing/href.ts +10 -3
  318. package/src/routing/match.ts +287 -0
  319. package/src/routing/metadata-files.ts +20 -6
  320. package/src/routing/proxy-config.ts +137 -0
  321. package/src/routing/proxy.ts +35 -134
  322. package/src/routing/routes.ts +22 -281
  323. package/src/runtime/fingerprint.ts +23 -8
  324. package/src/runtime/load.ts +170 -0
  325. package/src/runtime/loader.ts +218 -44
  326. package/src/runtime/module-cache.ts +15 -1
  327. package/src/runtime/modules.ts +405 -82
  328. package/src/runtime/prebundle.ts +101 -0
  329. package/src/runtime/production.ts +456 -0
  330. package/src/runtime/release-graph.ts +342 -0
  331. package/src/runtime/vendor-build.ts +750 -17
  332. package/src/runtime/vendor.ts +16 -3
  333. package/src/types.ts +4 -3
  334. package/src/utils/directive.ts +40 -0
  335. package/src/utils/fs.ts +95 -4
  336. package/src/utils/resource-query.ts +12 -0
  337. package/src/cli/serve/entry.ts +0 -45
@@ -57,7 +57,10 @@ function isAppConvention(name: string): boolean {
57
57
  * outside the source app preserves native App routes and avoids hybrid-route
58
58
  * conflict validation; linked sources and node_modules keep normal resolution.
59
59
  */
60
- export async function materializePagesApp(root: string): Promise<string | undefined> {
60
+ export async function materializePagesApp(
61
+ root: string,
62
+ base = path.join(tmpdir(), 'pnext-pages-compat'),
63
+ ): Promise<string | undefined> {
61
64
  const pagesPath = path.join(root, 'pages')
62
65
  if (!existsSync(pagesPath)) return undefined
63
66
 
@@ -71,11 +74,7 @@ export async function materializePagesApp(root: string): Promise<string | undefi
71
74
  // one shared directory let one process scan a half-materialized tree ("page.js doesn't have a root
72
75
  // layout" on a build that passes on the next run). Naming the directory after what goes in it makes
73
76
  // the second writer a no-op, and staging + rename means a reader sees the whole tree or nothing.
74
- const generatedRoot = path.join(
75
- tmpdir(),
76
- 'pnext-pages-compat',
77
- materializationKey(root, nativeApp, pages, apiRoutes),
78
- )
77
+ const generatedRoot = path.join(base, materializationKey(root, nativeApp, pages, apiRoutes))
79
78
  const appPath = path.join(generatedRoot, 'app')
80
79
  if (existsSync(appPath)) return appPath
81
80
 
@@ -88,10 +87,20 @@ export async function materializePagesApp(root: string): Promise<string | undefi
88
87
  await rm(staging, { recursive: true, force: true })
89
88
  if (!existsSync(appPath)) throw new Error(`pnext: could not materialize ${root}/pages`)
90
89
  }
91
- void sweepStaleMaterializations(path.dirname(generatedRoot))
90
+ void sweepStaleMaterializations(
91
+ base,
92
+ isInsideApp(root, generatedRoot) ? path.basename(generatedRoot) : undefined,
93
+ )
92
94
  return appPath
93
95
  }
94
96
 
97
+ const isInsideApp = (root: string, dir: string) => !path.relative(root, dir).startsWith('..')
98
+
99
+ /** A materialized tree's link to app source: relative when the tree lives inside the app. */
100
+ function materializedLinkTarget(root: string, generatedRoot: string, target: string) {
101
+ return isInsideApp(root, generatedRoot) ? path.relative(generatedRoot, target) : target
102
+ }
103
+
95
104
  /**
96
105
  * Everything the materialized tree is derived from: the pages facts (already parsed out of the
97
106
  * sources), the native app tree's shape, and this module — whose `wrapperSource` writes the files.
@@ -134,10 +143,12 @@ function materializationKey(
134
143
  // a dev server holds its materialized tree open for as long as it runs.
135
144
  const STALE_MATERIALIZATION_MS = 7 * 24 * 60 * 60_000
136
145
 
137
- async function sweepStaleMaterializations(base: string) {
146
+ // A build's own output keeps only its `current` tree: nothing else serves from it.
147
+ async function sweepStaleMaterializations(base: string, current?: string) {
138
148
  try {
139
- const cutoff = Date.now() - STALE_MATERIALIZATION_MS
149
+ const cutoff = current ? Infinity : Date.now() - STALE_MATERIALIZATION_MS
140
150
  for (const entry of await readdir(base)) {
151
+ if (current && (entry === current || entry.endsWith('.staging'))) continue
141
152
  const dir = path.join(base, entry)
142
153
  const info = statSync(dir, { throwIfNoEntry: false })
143
154
  if (info && info.mtimeMs < cutoff) await rm(dir, { recursive: true, force: true })
@@ -158,23 +169,28 @@ async function buildMaterializedApp(
158
169
  const appPath = path.join(generatedRoot, 'app')
159
170
  const sourcePages = path.join(generatedRoot, 'source-pages')
160
171
  const sourceApp = path.join(generatedRoot, 'source-app')
172
+ // Inside a build's output the links stay relative, so a moved build names no build-machine path.
173
+ const link = (target: string, at: string) =>
174
+ symlink(materializedLinkTarget(root, generatedRoot, target), at, 'dir')
161
175
 
162
176
  await rm(generatedRoot, { recursive: true, force: true })
163
177
  await mkdir(appPath, { recursive: true })
164
- await symlink(pagesPath, sourcePages, 'dir')
178
+ await link(pagesPath, sourcePages)
179
+ // Inside the app, packages resolve up to the app's own node_modules under their real paths.
165
180
  const nodeModules = path.join(root, 'node_modules')
166
- if (existsSync(nodeModules))
167
- await symlink(nodeModules, path.join(generatedRoot, 'node_modules'), 'dir')
181
+ if (existsSync(nodeModules) && !isInsideApp(root, generatedRoot)) {
182
+ await link(nodeModules, path.join(generatedRoot, 'node_modules'))
183
+ }
168
184
 
169
185
  // Static assets referenced from materialized routes (e.g. an app route's
170
186
  // `new URL('../../public/vercel.png', import.meta.url)` or a `public/*` asset
171
187
  // import) resolve relative to the generated root; without the symlink the
172
188
  // bundler can't find them and the route 500s.
173
189
  const publicDir = path.join(root, 'public')
174
- if (existsSync(publicDir)) await symlink(publicDir, path.join(generatedRoot, 'public'), 'dir')
190
+ if (existsSync(publicDir)) await link(publicDir, path.join(generatedRoot, 'public'))
175
191
 
176
192
  if (existsSync(nativeApp)) {
177
- await symlink(nativeApp, sourceApp, 'dir')
193
+ await link(nativeApp, sourceApp)
178
194
  await materializeNativeApp(sourceApp, appPath)
179
195
  }
180
196
  if (!existsSync(path.join(appPath, 'layout.js'))) {
@@ -46,7 +46,7 @@ import {
46
46
  setResolveExtensions,
47
47
  setTsConfigPath,
48
48
  } from '../../resolve/imports'
49
- import { setServerBundleConditions } from '../../runtime/loader'
49
+ import { setServerBundleConditions } from '../../runtime/load'
50
50
  import { escapeRegex, specifierSniffTokens } from '../../utils/code'
51
51
  import {
52
52
  cacheComponentsEnabled,
@@ -59,6 +59,7 @@ import {
59
59
  relayCompilerConfig,
60
60
  removeConsoleTargets,
61
61
  resolveExtensions,
62
+ builtInServerExternalPackages,
62
63
  serverExternalPackages,
63
64
  swcEnvUsageMode,
64
65
  tsconfigPath,
@@ -160,6 +161,7 @@ export function registerBundlerExtensions(config: ResolvedConfig): void {
160
161
  // Linked workspace packages are deliberately absent: esbuild resolves their
161
162
  // symlinks to real paths, so they never reach a filter as node_modules.
162
163
  transpiled: () => [...transpile].filter(name => !externals.has(name)),
164
+ releaseExternal: name => builtInServerExternalPackages().has(name) && !transpile.has(name),
163
165
  esmExternals,
164
166
  })
165
167
  setExternalLoadResolver(({ root, fromFile, specifier, target }) => {
@@ -122,7 +122,7 @@ async function writeImagesManifestStep(ctx: BuildStepContext): Promise<void> {
122
122
  async function mirrorStaticMedia(config: ResolvedConfig): Promise<void> {
123
123
  const { cp } = await import('node:fs/promises')
124
124
  for (const dir of ['media', 'immutable/media']) {
125
- const source = path.join(config.outPath, 'public', '_next', 'static', dir)
125
+ const source = path.join(config.outPath, 'static', '_next', 'static', dir)
126
126
  const target = path.join(config.root, '.next', 'static', dir)
127
127
  try {
128
128
  await cp(source, target, { recursive: true, force: true })
@@ -14,7 +14,7 @@ import {
14
14
  type ProxyConfig,
15
15
  type ProxyModule,
16
16
  } from '../../routing/proxy'
17
- import { devServerModuleHref } from '../../runtime/modules'
17
+ import { devServerModuleHref } from '../../runtime/load'
18
18
  import { pathToFileHref, type ResolvedConfig } from '../../config'
19
19
  import { toPosixPath } from '../../utils/fs'
20
20
  import type { RouteManifestEntry } from '../../types'
@@ -14,8 +14,7 @@
14
14
  // opentelemetry suite. Everything is inert unless next compat is on AND @opentelemetry/api resolves from
15
15
  // the app.
16
16
 
17
- import { readFileSync } from 'node:fs'
18
- import path from 'node:path'
17
+ import { isMaterializedPagesRoute } from '../pages/api'
19
18
  import type { ResolvedConfig } from '../../config'
20
19
  import type { RouteManifestEntry } from '../../types'
21
20
  import {
@@ -30,7 +29,7 @@ import {
30
29
  } from '../../extensions'
31
30
  import { nextCompatEnabled } from '../aliases'
32
31
  import { getRequestRuntime } from '../../routing/request-environment'
33
- import { selectRouteForRequest } from '../../routing/routes'
32
+ import { selectRouteForRequest } from '../../routing/match'
34
33
  import { queueAfterTask } from '../../request/context'
35
34
  import {
36
35
  getDocumentScriptExtensions,
@@ -247,14 +246,11 @@ function routeEntryFor(routePattern: string): RouteManifestEntry | undefined {
247
246
  // stable generator-owned marker; cache per file (routes don't change at start).
248
247
  const pagesCompatFileCache = new Map<string, boolean>()
249
248
  function isPagesCompatEntry(entry: RouteManifestEntry | undefined): boolean {
250
- if (!entry?.file.includes(`${path.sep}pnext-pages-compat${path.sep}`)) return false
249
+ const config = getRequestRuntime()?.config
250
+ if (!entry || !config) return false
251
251
  let cached = pagesCompatFileCache.get(entry.file)
252
252
  if (cached === undefined) {
253
- try {
254
- cached = readFileSync(entry.file, 'utf8').includes('source-pages/')
255
- } catch {
256
- cached = false
257
- }
253
+ cached = isMaterializedPagesRoute(config, entry.file)
258
254
  pagesCompatFileCache.set(entry.file, cached)
259
255
  }
260
256
  return cached
@@ -32,7 +32,7 @@ import {
32
32
  setStreamRouteExtensions,
33
33
  setSuspenseExtensions,
34
34
  } from '../../render/hooks'
35
- import { withAssetPrefix } from '../../css/build'
35
+ import { withAssetPrefix } from '../../css/assets'
36
36
  import { polyfillsChunkPath } from '../bundler/polyfill'
37
37
  import { nextRouterShimScript } from '../react/router-shim'
38
38
  import { renderPartytownHeadScripts } from '../next/script'
@@ -43,6 +43,7 @@ import {
43
43
  fontPreloadDomScript,
44
44
  fontSizeAdjustMeta,
45
45
  prewarmFonts,
46
+ releaseFonts,
46
47
  runWithFontScope,
47
48
  } from '../next/font/runtime'
48
49
  import {
@@ -117,6 +118,7 @@ export function registerRenderExtensions(config: ResolvedConfig): void {
117
118
  runWithFontScope,
118
119
  collectFontAssets: fontAssets,
119
120
  prewarmFontAssets: prewarmFonts,
121
+ releaseFonts,
120
122
  })
121
123
  setDocumentScriptExtensions({
122
124
  documentBodyScripts: cfg =>
@@ -14,6 +14,7 @@
14
14
  // lazy-static MISS responses. Emitting it for pure static HITs needs the route's cacheLife persisted in
15
15
  // the build manifest and read by the static-serve path in src/cli/start.ts.
16
16
 
17
+ import { productionRelease } from '../../runtime/production'
17
18
  import type { ResolvedConfig } from '../../config'
18
19
  import { nextCompatEnabled } from '../../compat/aliases'
19
20
  import {
@@ -84,7 +85,8 @@ export function registerUseCacheExtensions(config: ResolvedConfig): void {
84
85
  // Seed the root-param NAME set for cache-key participation (Stage B-3): a
85
86
  // `use cache` entry's conservative first-call key may only discriminate by
86
87
  // root params, so the runtime needs the scanned names at build AND serve.
87
- setUseCacheRootParamNames(scanRootParams(config.appPath))
88
+ const released = productionRelease(config)?.compat?.rootParams
89
+ setUseCacheRootParamNames(released ? new Set(released) : scanRootParams(config.appPath))
88
90
 
89
91
  // Arm `use cache` build-time validation for this build's prerender pass.
90
92
  // Registered as a build STEP so it runs before any page is prerendered; the
@@ -0,0 +1,46 @@
1
+ // Compat facts a production release carries (COMPAT): compiled next.config, cache handlers and
2
+ // instrumentation, plus the app facts compat would otherwise rescan from source at boot.
3
+ import path from 'node:path'
4
+ import type { ResolvedConfig } from '../config'
5
+ import type { CompatRelease } from '../runtime/production'
6
+ import { pagesRevalidateFacts } from './cache-control'
7
+ import { emitReleaseInstrumentation } from './lifecycle/instrumentation'
8
+ import { emitNextConfigBundle } from './next/config-loader'
9
+ import { pagesApiFacts, pagesRouteFacts } from './pages/api'
10
+ import { scanRootParams } from './ppr/root-params-scan'
11
+
12
+ export async function compatReleaseFacts(
13
+ config: ResolvedConfig,
14
+ outDir: string,
15
+ ): Promise<CompatRelease> {
16
+ const [nextConfig, instrumentation] = await Promise.all([
17
+ emitNextConfigBundle(config.root, outDir),
18
+ emitReleaseInstrumentation(config, outDir),
19
+ ])
20
+ const toPosix = (file: string) => file.split(path.sep).join('/')
21
+ return {
22
+ ...(nextConfig ? { nextConfig: toPosix(path.relative(config.outPath, nextConfig.file)) } : {}),
23
+ ...(instrumentation
24
+ ? {
25
+ instrumentation: {
26
+ file: toPosix(instrumentation.file),
27
+ ...(instrumentation.edge ? { edge: toPosix(instrumentation.edge) } : {}),
28
+ },
29
+ }
30
+ : {}),
31
+ compat: {
32
+ rootParams: [...scanRootParams(config.appPath)],
33
+ pagesRevalidate: pagesRevalidateFacts(config.root).map(
34
+ ([file, seconds]): [string, number] => [toPosix(file), seconds],
35
+ ),
36
+ pagesApi: pagesApiFacts(config.root),
37
+ pagesRoutes: pagesRouteFacts(config),
38
+ cacheHandlers: Object.fromEntries(
39
+ Object.entries(nextConfig?.handlers ?? {}).map(([name, file]) => [
40
+ name,
41
+ toPosix(path.relative(config.outPath, file)),
42
+ ]),
43
+ ),
44
+ },
45
+ }
46
+ }
@@ -30,7 +30,7 @@ import {
30
30
  } from '../../extensions'
31
31
  import { getRequestRuntime } from '../../routing/request-environment'
32
32
  import { takeCacheLifeStash } from '../cache/use-cache'
33
- import { matchInterception, parseNavState, selectRouteForRequest } from '../../routing/routes'
33
+ import { matchInterception, parseNavState, selectRouteForRequest } from '../../routing/match'
34
34
  import {
35
35
  currentPprShellHtml,
36
36
  pprShellPath,
@@ -42,6 +42,7 @@ export function typecheckDisabled(): boolean {
42
42
  export interface TypecheckWorkerInput {
43
43
  root: string
44
44
  outPath: string
45
+ outRootPath: string
45
46
  compat: ResolvedConfig['compat']
46
47
  nextConfig: NextConfigObject
47
48
  }
@@ -64,6 +65,7 @@ export async function validateTypesOffThread(config: ResolvedConfig): Promise<vo
64
65
  const input: TypecheckWorkerInput = {
65
66
  root: config.root,
66
67
  outPath: config.outPath,
68
+ outRootPath: config.outRootPath,
67
69
  compat: config.compat,
68
70
  nextConfig: cloneable({
69
71
  typescript: next.typescript,
@@ -210,12 +212,12 @@ function appDiagnostic(file: string | undefined, config: ResolvedConfig): boolea
210
212
  if (resolved === generatedProjectPath(config)) return true
211
213
  if (!resolved.startsWith(config.root + path.sep)) return false
212
214
  if (resolved.includes(`${path.sep}node_modules${path.sep}`)) return false
213
- if (resolved.startsWith(path.resolve(config.outPath) + path.sep)) return false
215
+ if (resolved.startsWith(path.resolve(config.outRootPath) + path.sep)) return false
214
216
  return true
215
217
  }
216
218
 
217
219
  function generatedProjectPath(config: ResolvedConfig): string {
218
- return path.resolve(config.outPath, 'typecheck', 'tsconfig.json')
220
+ return path.resolve(config.outRootPath, 'typecheck', 'tsconfig.json')
219
221
  }
220
222
 
221
223
  /**
@@ -292,7 +294,7 @@ async function writeTypecheckProject(
292
294
  config: ResolvedConfig,
293
295
  native: string | undefined,
294
296
  ): Promise<string> {
295
- const dir = path.join(config.outPath, 'typecheck')
297
+ const dir = path.join(config.outRootPath, 'typecheck')
296
298
  await mkdir(dir, { recursive: true })
297
299
  const reactTypes = resolveTypesDir('react', config.root)
298
300
  const hasReactTypes = reactTypes !== undefined
@@ -420,7 +422,7 @@ async function writeTypecheckProject(
420
422
  }
421
423
 
422
424
  function preactTypeShimPath(config: ResolvedConfig): string {
423
- return path.join(config.outPath, 'typecheck', 'preact-compat.d.ts')
425
+ return path.join(config.outRootPath, 'typecheck', 'preact-compat.d.ts')
424
426
  }
425
427
 
426
428
  function preactTypeShim(preact: string, react: string): string {
@@ -10,8 +10,9 @@
10
10
  // undefined/non-component default export; and the output:'export' extras (exportPathMap + app, a
11
11
  // dynamic route without generateStaticParams, route handlers without static opt-ins, force-dynamic).
12
12
 
13
- import { existsSync, readdirSync, readFileSync } from 'node:fs'
13
+ import { existsSync, readdirSync } from 'node:fs'
14
14
  import path from 'node:path'
15
+ import { readSourceSync } from '../../resolve/source-text'
15
16
  import type { ResolvedConfig } from '../../config'
16
17
  import { extraPageExtensions } from '../../extensions'
17
18
  import type { RouteManifestEntry } from '../../types'
@@ -366,7 +367,7 @@ function readSource(file: string): string | undefined {
366
367
  if (sourceCache.has(file)) return sourceCache.get(file)
367
368
  let source: string | undefined
368
369
  try {
369
- source = readFileSync(file, 'utf8')
370
+ source = readSourceSync(file)
370
371
  } catch {
371
372
  source = undefined
372
373
  }
@@ -379,7 +380,12 @@ function routeFiles(route: RouteManifestEntry): string[] {
379
380
  }
380
381
 
381
382
  function isAppSourceFile(config: ResolvedConfig, file: string): boolean {
382
- if (isInside(config.outPath, file)) return false
383
+ const materialized =
384
+ file.includes(`${path.sep}source-app${path.sep}`) ||
385
+ file.includes(`${path.sep}source-pages${path.sep}`)
386
+ // A build materializes pages sources into its output (`<out>/pnext-pages-compat`).
387
+ if (isInside(config.outPath, file))
388
+ return materialized && file.includes(`${path.sep}pnext-pages-compat${path.sep}`)
383
389
  const relative = path.relative(config.root, file)
384
390
  if (relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative)) {
385
391
  // A standalone npm layout keeps node_modules INSIDE the root; framework-supplied route
@@ -387,10 +393,7 @@ function isAppSourceFile(config: ResolvedConfig, file: string): boolean {
387
393
  // (a JSDoc example in pnext's extensions.ts failed a real app's build as './pic.png').
388
394
  return !relative.split(path.sep).includes('node_modules')
389
395
  }
390
- return (
391
- file.includes(`${path.sep}source-app${path.sep}`) ||
392
- file.includes(`${path.sep}source-pages${path.sep}`)
393
- )
396
+ return materialized
394
397
  }
395
398
 
396
399
  function isInside(parent: string, file: string): boolean {
package/src/config.ts CHANGED
@@ -1,11 +1,21 @@
1
1
  import { existsSync, realpathSync } from 'node:fs'
2
2
  import { fileURLToPath, pathToFileURL } from 'node:url'
3
3
  import path from 'node:path'
4
+ import { outCachePath, standaloneOutSegment } from './out-paths'
4
5
  import { loadEnv } from './env'
5
- import { findWorkspaceRoot } from './resolve/imports'
6
+ import { findWorkspaceRoot } from './resolve/package-json'
6
7
  import { PREFETCH_MODES, type PNextConfig } from './types'
7
8
  import { setCacheComponents } from './render/ppr'
8
9
  import { setLegacyRequestAPIs } from './request/context'
10
+ import {
11
+ attachRelease,
12
+ buildIndexFile,
13
+ linkStandaloneOrigins,
14
+ readBuildIndex,
15
+ releaseConfig,
16
+ releaseSourceRoot,
17
+ servingReleaseDir,
18
+ } from './runtime/production'
9
19
 
10
20
  export type ResolvedConfig = Required<Pick<PNextConfig, 'outDir' | 'basePath'>> &
11
21
  Omit<PNextConfig, 'outDir' | 'basePath'> & {
@@ -15,7 +25,7 @@ export type ResolvedConfig = Required<Pick<PNextConfig, 'outDir' | 'basePath'>>
15
25
  publicDir: 'public'
16
26
  appPath: string
17
27
  publicPath: string
18
- /** Where this process writes and serves from: the out root, or `<outRoot>/dev` in dev. */
28
+ /** Where this process writes and serves from: `<outRoot>/standalone`, or `<outRoot>/dev` in dev. */
19
29
  outPath: string
20
30
  /** The out root itself (`.pnext`), identical in dev and build. */
21
31
  outRootPath: string
@@ -61,7 +71,13 @@ export interface ConfigSourceOverrides {
61
71
  }
62
72
  export type ConfigSource = (
63
73
  root: string,
64
- options: { dev?: boolean; serve?: boolean; warnings?: boolean },
74
+ options: {
75
+ dev?: boolean
76
+ serve?: boolean
77
+ warnings?: boolean
78
+ bundle?: string | null
79
+ handlers?: Record<string, string>
80
+ },
65
81
  ) => Promise<ConfigSourceOverrides>
66
82
 
67
83
  let configSource: ConfigSource | undefined
@@ -73,7 +89,7 @@ export function registerConfigSource(source: ConfigSource): void {
73
89
  async function resolveConfigSource(
74
90
  config: PNextConfig,
75
91
  root: string,
76
- options: { dev?: boolean; serve?: boolean; warnings?: boolean },
92
+ options: Parameters<ConfigSource>[1],
77
93
  ): Promise<ConfigSourceOverrides> {
78
94
  if (!config.compat?.next) return {}
79
95
  if (!configSource) {
@@ -90,6 +106,8 @@ export async function loadConfig(
90
106
  options: { dev?: boolean; serve?: boolean; warnings?: boolean } = {},
91
107
  ): Promise<ResolvedConfig> {
92
108
  const root = canonicalRoot(path.resolve(rootInput))
109
+ const released = options.serve ? servingReleaseDir() : undefined
110
+ if (released) return loadReleasedConfig(root, released, options)
93
111
  await loadEnv(root, options)
94
112
  const configPath = path.join(root, 'pnext.config.ts')
95
113
  const loadedConfig = existsSync(configPath)
@@ -119,8 +137,19 @@ export async function loadConfig(
119
137
  setCacheComponents(false)
120
138
  const sourceOverrides = stripUndefined(await resolveConfigSource(config, root, options))
121
139
  const merged = { ...defaultConfig, ...config, ...sourceOverrides }
122
- const appPath = await resolveAppPath(root, config)
123
140
  const outRootPath = path.resolve(root, merged.outDir)
141
+ // `pnext start` reads outDir from the config, as `next start` reads distDir, then serves the release.
142
+ const release = path.join(outRootPath, standaloneOutSegment)
143
+ if (options.serve && existsSync(buildIndexFile(release))) {
144
+ // The source config above already warned; its compiled copy must not warn again.
145
+ return loadReleasedConfig(root, release, { ...options, warnings: false })
146
+ }
147
+ // A build materializes pages into its output, so the release names them portably.
148
+ const appPath = await resolveAppPath(
149
+ root,
150
+ config,
151
+ options.dev ? undefined : path.join(outRootPath, 'pnext-pages-compat'),
152
+ )
124
153
  const workspaceRoot = canonicalRoot(
125
154
  config.workspaceRoot
126
155
  ? path.resolve(root, config.workspaceRoot)
@@ -149,13 +178,45 @@ export async function loadConfig(
149
178
  // Dev owns `<outRoot>/dev` exclusively so a concurrent `pnext build` — which
150
179
  // wipes its own outputs under the out root — can never pull a running dev
151
180
  // server's cache, manifest or assets out from under it.
152
- outPath: options.dev ? path.join(outRootPath, devOutSegment) : outRootPath,
181
+ outPath: path.join(outRootPath, options.dev ? devOutSegment : standaloneOutSegment),
153
182
  outRootPath,
154
183
  typesPath: path.join(outRootPath, 'types'),
155
184
  checksPath: path.join(outRootPath, 'typecheck', 'checks'),
156
185
  }
157
186
  }
158
187
 
188
+ /**
189
+ * A production server's config: what the build resolved, read from its release instead of re-importing
190
+ * `pnext.config.ts`/`next.config.*`. Per-app process state is re-derived exactly as a source load does.
191
+ */
192
+ async function loadReleasedConfig(
193
+ root: string,
194
+ outPath: string,
195
+ options: { dev?: boolean; serve?: boolean; warnings?: boolean },
196
+ ): Promise<ResolvedConfig> {
197
+ const index = readBuildIndex(outPath)
198
+ linkStandaloneOrigins(outPath)
199
+ const sourceRoot = releaseSourceRoot(outPath, index.config.outPath as string)
200
+ const config = releaseConfig(index.config, sourceRoot, outPath)
201
+ await loadEnv(root, options)
202
+ setLegacyRequestAPIs(
203
+ typeof config.compat?.next === 'object' && config.compat.next.legacyRequestAPIs === true,
204
+ )
205
+ setCacheComponents(false)
206
+ await resolveConfigSource(config, root, {
207
+ ...options,
208
+ bundle: index.nextConfig ? path.resolve(outPath, index.nextConfig) : null,
209
+ handlers: Object.fromEntries(
210
+ Object.entries(index.compat?.cacheHandlers ?? {}).map(([name, file]) => [
211
+ name,
212
+ path.resolve(outPath, file),
213
+ ]),
214
+ ),
215
+ })
216
+ attachRelease(config, index)
217
+ return config
218
+ }
219
+
159
220
  // One row per enum-valued config field; validateConfig checks them all.
160
221
  const enumFields: readonly [keyof PNextConfig, readonly unknown[]][] = [
161
222
  ['prefetch', PREFETCH_MODES],
@@ -175,6 +236,8 @@ function validateConfig(config: PNextConfig) {
175
236
  /** Dev's private subtree under the out root. Build never touches it. */
176
237
  export const devOutSegment = 'dev'
177
238
 
239
+ export { outCachePath, standaloneOutSegment }
240
+
178
241
  // Relative-import resolution realpaths the importing file (so a materialized shim resolves `./sibling`
179
242
  // against real source), so every containment check against root/workspaceRoot compares realpaths.
180
243
  // Canonicalize the roots to match - otherwise an app served through a symlinked directory (a macOS
@@ -253,6 +316,10 @@ export function frameworkRuntimeAliasEntries(): Record<FrameworkRuntimeAlias, st
253
316
  return frameworkRuntimeAliases
254
317
  }
255
318
 
319
+ /** Packages the framework resolves at runtime by name, not by import. */
320
+ export const frameworkRuntimeSpecifiers = (): string[] =>
321
+ Object.keys(frameworkRuntimeAliasEntries())
322
+
256
323
  function resolveRuntimeModule(specifier: string) {
257
324
  return fileURLToPath(import.meta.resolve(specifier))
258
325
  }
@@ -264,7 +331,7 @@ export function pnextAliases(target: CompatAliasTarget): Record<string, string>
264
331
  }
265
332
  }
266
333
 
267
- async function resolveAppPath(root: string, config: PNextConfig) {
334
+ async function resolveAppPath(root: string, config: PNextConfig, pagesBase?: string) {
268
335
  const rootApp = path.join(root, 'app')
269
336
  if (config.compat?.next) {
270
337
  const pagesPath = path.join(root, 'pages')
@@ -272,13 +339,13 @@ async function resolveAppPath(root: string, config: PNextConfig) {
272
339
  if (existsSync(rootApp)) {
273
340
  if (hasPages) {
274
341
  const { materializePagesApp } = await import('./compat/pages/router')
275
- const hybrid = await materializePagesApp(root)
342
+ const hybrid = await materializePagesApp(root, pagesBase)
276
343
  if (hybrid) return hybrid
277
344
  }
278
345
  return rootApp
279
346
  }
280
347
  const { materializePagesApp } = await import('./compat/pages/router')
281
- const pagesApp = await materializePagesApp(root)
348
+ const pagesApp = await materializePagesApp(root, pagesBase)
282
349
  if (pagesApp) return pagesApp
283
350
  } else if (existsSync(rootApp)) return rootApp
284
351
  return path.join(root, 'src', 'app')