@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
@@ -20,6 +20,7 @@ import { setRouteFactsStore, type RouteFacts, type RouteFactsStore } from '../..
20
20
  import { traceEnabled } from '../../utils/trace-flags'
21
21
  import { restartCacheEnabled } from './enabled'
22
22
  import { hash, lazyRecordCacheKey, revalidate, sourceEntry, type SourceEntry } from './record'
23
+ import { outCachePath } from '../../out-paths'
23
24
 
24
25
  interface RecordFile {
25
26
  version: number
@@ -45,7 +46,7 @@ export function installRouteFactsCache(config: ResolvedConfig) {
45
46
  // eslint-disable-next-line turbo/no-undeclared-env-vars
46
47
  if (process.env.PNEXT_DEV_FACTS_CACHE === '0') return
47
48
  const cache: FactsCache = {
48
- dir: path.join(config.outPath, 'cache', 'route-facts'),
49
+ dir: path.join(outCachePath(config.outPath), 'route-facts'),
49
50
  key: lazyRecordCacheKey(config, RECORD_VERSION),
50
51
  written: new Set(),
51
52
  }
package/src/dev/server.ts CHANGED
@@ -52,7 +52,7 @@ import {
52
52
  staticMetadataCacheControl,
53
53
  } from '../routing/metadata-files'
54
54
  import { handleRouteModule, type RouteHandlerModule } from '../routing/handler'
55
- import { malformedUrlResponse, trailingSlashRedirect } from '../routing/href'
55
+ import { malformedUrlResponse, repeatedSlashRedirect, trailingSlashRedirect } from '../routing/href'
56
56
  import { type PeerAddressSource, withForwardedHeaders } from '../routing/forwarded'
57
57
  import {
58
58
  assertNoServerActionsWithoutCompat,
@@ -87,7 +87,14 @@ import {
87
87
  setWorkUnitRoute,
88
88
  } from '../request/context'
89
89
  import { setRequestRuntime } from '../routing/request-environment'
90
- import { ensureDir, listFiles, writeText } from '../utils/fs'
90
+ import {
91
+ ensureDir,
92
+ exactNameIndex,
93
+ type ExactNames,
94
+ hasExactName,
95
+ listFiles,
96
+ writeText,
97
+ } from '../utils/fs'
91
98
  import { contentType } from '../utils/content-type'
92
99
  import { traceEnabled } from '../utils/trace-flags'
93
100
  import {
@@ -114,6 +121,7 @@ import { cacheRoot, setDevWatcherFreshness } from '../runtime/module-cache'
114
121
  import { abortUpstreamFetchOnDisconnect } from '../runtime/fetch-host'
115
122
  import { markBoot } from '../cli/boot/trace'
116
123
  import { formatDuration } from '../utils/verbose'
124
+ import { outCachePath } from '../out-paths'
117
125
 
118
126
  interface DevServerOptions {
119
127
  config: ResolvedConfig
@@ -210,7 +218,7 @@ async function evictStaleClientCaches(outPath: string) {
210
218
  // eslint-disable-next-line turbo/no-undeclared-env-vars
211
219
  const keep = Number(process.env.PNEXT_DEV_CLIENT_CACHE_KEEP || 32)
212
220
  if (!Number.isFinite(keep) || keep <= 0) return
213
- const clientRoot = path.join(outPath, 'cache', 'client')
221
+ const clientRoot = path.join(outCachePath(outPath), 'client')
214
222
  try {
215
223
  const entries = await readdir(clientRoot)
216
224
  if (entries.length <= keep) return
@@ -290,6 +298,7 @@ export async function startDevServer(options: DevServerOptions) {
290
298
  markBoot('boot:scanRoutes')
291
299
  await validateProxyFiles(config)
292
300
  let proxyRunner = createProxyRunner(config)
301
+ const exactNames = exactNameIndex(config.root)
293
302
  markBoot('boot:proxy')
294
303
  let devImportVersion = String(Date.now())
295
304
  // The proxy runs serially in front of every request, so its compile+import is
@@ -436,6 +445,8 @@ export async function startDevServer(options: DevServerOptions) {
436
445
  if (badRequest) return badRequest
437
446
  request = withForwardedHeaders(request, server)
438
447
  let url = new URL(request.url)
448
+ const slashRedirect = repeatedSlashRedirect(url)
449
+ if (slashRedirect) return slashRedirect
439
450
  const profile = devRequestProfile(request, url)
440
451
  if (profile) logDevProfile(profile, 'prologue (bootstrap/headers/url)', prologueStart)
441
452
  let pageLog = startDevPageStallTimer(pendingDevPageLoadLog(routes, request, url))
@@ -475,6 +486,10 @@ export async function startDevServer(options: DevServerOptions) {
475
486
  }
476
487
 
477
488
  try {
489
+ // Before the proxy, for every method, as in Next: the proxy only ever sees canonical paths.
490
+ const canonicalRedirect = trailingSlashRedirect(config, url)
491
+ if (canonicalRedirect) return finish(canonicalRedirect)
492
+
478
493
  const initialPrefetchResponse = maybeDevPagePrefetchResponse(routes, url.pathname, request)
479
494
  if (initialPrefetchResponse) return finish(initialPrefetchResponse)
480
495
 
@@ -570,7 +585,7 @@ export async function startDevServer(options: DevServerOptions) {
570
585
  }
571
586
 
572
587
  const staticResponse = await profileDevStep(profile, 'static lookup', () =>
573
- maybeStaticFile(config.publicPath, url.pathname),
588
+ maybeStaticFile(config.publicPath, url.pathname, exactNames),
574
589
  )
575
590
  if (staticResponse) {
576
591
  setWorkUnitRoute('static-asset', 'static')
@@ -582,7 +597,11 @@ export async function startDevServer(options: DevServerOptions) {
582
597
  .staticAssetPublicPrefixes()
583
598
  .some(prefix => url.pathname.startsWith(prefix))
584
599
  ) {
585
- const emitted = await maybeStaticFile(path.join(config.outPath, 'public'), url.pathname)
600
+ const emitted = await maybeStaticFile(
601
+ path.join(config.outPath, 'static'),
602
+ url.pathname,
603
+ exactNames,
604
+ )
586
605
  if (emitted) {
587
606
  setWorkUnitRoute('static-asset', 'static')
588
607
  return finish(applyProxyResponse(emitted, proxyResponse))
@@ -595,8 +614,9 @@ export async function startDevServer(options: DevServerOptions) {
595
614
  )
596
615
  if (staticAssetPathname) {
597
616
  const outPublicStatic = await maybeStaticFile(
598
- path.join(config.outPath, 'public'),
617
+ path.join(config.outPath, 'static'),
599
618
  staticAssetPathname,
619
+ exactNames,
600
620
  )
601
621
  if (outPublicStatic) {
602
622
  setWorkUnitRoute('static-asset', 'static')
@@ -647,9 +667,6 @@ export async function startDevServer(options: DevServerOptions) {
647
667
  }
648
668
  }
649
669
 
650
- const canonicalRedirect = trailingSlashRedirect(config, url, request.method.toUpperCase())
651
- if (canonicalRedirect) return finish(applyProxyResponse(canonicalRedirect, proxyResponse))
652
-
653
670
  const nav = parseNavState(request)
654
671
  const matched = profileDevSyncStep(profile, 'match route', () =>
655
672
  selectRouteForRequest(routes, url.pathname, nav),
@@ -1258,7 +1275,7 @@ async function buildDevClient(config: ResolvedConfig, route: RouteManifestEntry)
1258
1275
  batchRoutes.length === 1
1259
1276
  ? cacheKeys[0]!
1260
1277
  : `batch-${clientSourceHash([...cacheKeys].sort().join('\n'))}`
1261
- const outDir = path.join(config.outPath, 'cache', 'client', cacheKey)
1278
+ const outDir = path.join(outCachePath(config.outPath), 'client', cacheKey)
1262
1279
  const outFile = path.join(outDir, `${clientEntryName(route)}.js`)
1263
1280
 
1264
1281
  // Dedup in-flight builds first. A single page view fires up to three requests
@@ -1345,7 +1362,7 @@ async function resolveDevClientChunk(config: ResolvedConfig, name: string) {
1345
1362
  const settled = clientChunks.get(name)
1346
1363
  if (settled && existsSync(settled)) return settled
1347
1364
 
1348
- const clientRoot = path.join(config.outPath, 'cache', 'client')
1365
+ const clientRoot = path.join(outCachePath(config.outPath), 'client')
1349
1366
  for (const generation of await readdir(clientRoot).catch(() => [])) {
1350
1367
  const chunksDir = path.join(clientRoot, generation, 'chunks')
1351
1368
  const file = path.join(chunksDir, name)
@@ -1402,9 +1419,12 @@ async function maybeStaticMetadataFile(config: ResolvedConfig, url: URL) {
1402
1419
  })
1403
1420
  }
1404
1421
 
1405
- async function maybeStaticFile(publicPath: string, pathname: string) {
1422
+ async function maybeStaticFile(publicPath: string, pathname: string, exactNames: ExactNames) {
1406
1423
  const filePath = path.join(publicPath, pathname.replace(/^\/+/, ''))
1407
1424
  if (!isInside(publicPath, filePath) || !existsSync(filePath)) return null
1425
+ if (exactNames && !hasExactName(exactNames, publicPath, path.relative(publicPath, filePath))) {
1426
+ return null
1427
+ }
1408
1428
  const fileStat = await stat(filePath)
1409
1429
  if (!fileStat.isFile()) return null
1410
1430
  return devResponse(await readFile(filePath), contentType(filePath))
@@ -1481,8 +1501,8 @@ async function maybeBuiltAsset(
1481
1501
  }
1482
1502
 
1483
1503
  const outPath = config.outPath
1484
- const filePath = path.join(outPath, 'cache', pathname.replace(/^\/+/, ''))
1485
- if (!isInside(path.join(outPath, 'cache'), filePath) || !existsSync(filePath)) return null
1504
+ const filePath = path.join(outCachePath(outPath), pathname.replace(/^\/+/, ''))
1505
+ if (!isInside(path.join(outCachePath(outPath)), filePath) || !existsSync(filePath)) return null
1486
1506
  const fileStat = await stat(filePath)
1487
1507
  if (!fileStat.isFile()) return null
1488
1508
  return new Response(await readFile(filePath), {
package/src/extensions.ts CHANGED
@@ -80,6 +80,9 @@ export function createExtensionHost(): ExtensionHost {
80
80
  }
81
81
 
82
82
  let activeHost = createExtensionHost()
83
+ // Anchored for modules that must not import this one (api/server, shared by embedded hosts).
84
+ const ANCHOR = Symbol.for('pnext.extensionHost')
85
+ ;(globalThis as Record<symbol, unknown>)[ANCHOR] ??= activeHost
83
86
 
84
87
  /** The host every accessor in this module reads. */
85
88
  export function getExtensionHost(): ExtensionHost {
@@ -148,6 +151,7 @@ export function withExtensionHost<T>(host: ExtensionHost, callback: () => T): T
148
151
  }
149
152
 
150
153
  function announceHostChange() {
154
+ ;(globalThis as Record<symbol, unknown>)[ANCHOR] = activeHost
151
155
  for (const handler of resetHandlers) handler()
152
156
  // Consumers latch the loadable-extension set (the server load plugin's filter
153
157
  // regex); a swap changes it, so invalidate exactly as a registration would.
@@ -0,0 +1,10 @@
1
+ import path from 'node:path'
2
+
3
+ /** The release a build writes under the out root: everything a production server reads. */
4
+ export const standaloneOutSegment = 'standalone'
5
+
6
+ /** The build cache beside a release (`<outRoot>/cache`); dev keeps its own. */
7
+ export function outCachePath(outPath: string) {
8
+ const owner = path.basename(outPath) === standaloneOutSegment ? path.dirname(outPath) : outPath
9
+ return path.join(owner, 'cache')
10
+ }
@@ -66,12 +66,25 @@ export interface FontExtensions {
66
66
  * the same memo the render reads. Default: none.
67
67
  */
68
68
  prewarmFontAssets: (config: ResolvedConfig, options: { dev?: boolean }) => Promise<void>
69
+
70
+ /** Resolve every declared font for a release, keyed like the render looks them up. Default: none. */
71
+ releaseFonts: (config: ResolvedConfig) => Promise<Record<string, ReleasedFont>>
72
+ }
73
+
74
+ /** A font resolution as a release stores it; production serves it instead of resolving. */
75
+ export interface ReleasedFont {
76
+ css: string
77
+ sizeAdjust: boolean
78
+ preloads: import('../types').MetadataLink[]
79
+ usesFonts: boolean
80
+ files: string[]
69
81
  }
70
82
 
71
83
  const fontExtensions: FontExtensions = {
72
84
  runWithFontScope: callback => callback(),
73
85
  collectFontAssets: () => Promise.resolve({ css: '', preloads: [] }),
74
86
  prewarmFontAssets: () => Promise.resolve(),
87
+ releaseFonts: () => Promise.resolve({}),
75
88
  }
76
89
 
77
90
  export function getFontExtensions(): FontExtensions {
@@ -3,7 +3,6 @@ import { stat, unlink, writeFile } from 'node:fs/promises'
3
3
  import { basename, dirname, join, relative, sep } from 'node:path'
4
4
  import { createHash } from 'node:crypto'
5
5
  import { AsyncLocalStorage } from 'node:async_hooks'
6
- import { drainPreplanBuilds } from '../runtime/vendor'
7
6
  import {
8
7
  Component,
9
8
  Fragment,
@@ -29,10 +28,13 @@ import { markErrorLogged } from '../utils/error-log'
29
28
  import {
30
29
  devClientModuleHref,
31
30
  devServerModuleHref,
31
+ drainPreplanBuilds,
32
32
  importDevModule,
33
33
  importModuleOnce,
34
- } from '../runtime/modules'
35
- import { serverBundleTargetForRuntime } from '../runtime/loader'
34
+ registerServerRuntime,
35
+ serverBundleTargetForRuntime,
36
+ } from '../runtime/load'
37
+ import { conventionFileExists, productionRelease, servedConventions } from '../runtime/production'
36
38
  import {
37
39
  getActionModuleExtensions,
38
40
  getActiveMetadataExtensions,
@@ -63,7 +65,7 @@ import {
63
65
  routeCssAssetNames,
64
66
  routeCssHref,
65
67
  withAssetPrefix,
66
- } from '../css/build'
68
+ } from '../css/assets'
67
69
  import { dynamicReferenceSymbol, type DynamicReference } from '../api/dynamic'
68
70
  import {
69
71
  isForbiddenError,
@@ -98,13 +100,11 @@ import {
98
100
  trackPrerenderDynamic,
99
101
  } from './ppr'
100
102
  import {
101
- collectFileCss,
102
- collectNotFoundCss,
103
103
  findConventionFiles,
104
104
  findGlobalError,
105
105
  findLayouts,
106
106
  serverActionsUnsupportedMessage,
107
- } from '../routing/routes'
107
+ } from '../routing/match'
108
108
  import { matchSegments, pathnameSegments, slotDirectoriesIn } from '../routing/slots'
109
109
  import { getRequestRuntime } from '../routing/request-environment'
110
110
  import { toNextRequest } from '../api/server'
@@ -121,7 +121,6 @@ import {
121
121
  ISLAND_BOUNDARY_ERROR_MESSAGE_ATTRIBUTE,
122
122
  } from './boundary-error'
123
123
  import { captureCacheScope, runWithCacheScope } from '../request/cache'
124
- import { registerServerRuntime } from '../runtime/loader'
125
124
  import { readText } from '../utils/fs'
126
125
  import { traceEnabled } from '../utils/trace-flags'
127
126
  import {
@@ -804,7 +803,7 @@ function moduleHref(file: string, options: ModuleLoadOptions) {
804
803
  ? devServerModuleHref(options.config, file, options.devImportVersion ?? String(Date.now()), {
805
804
  conditionTarget,
806
805
  })
807
- : reactCompatEnabled(options.config)
806
+ : reactCompatEnabled(options.config) || productionRelease(options.config)
808
807
  ? devServerModuleHref(options.config, file, 'build', { conditionTarget })
809
808
  : pathToFileHref(file)
810
809
  }
@@ -851,6 +850,17 @@ async function importClientModule(file: string, options: ModuleLoadOptions) {
851
850
  return importModuleOnce<Record<string, unknown>>(href)
852
851
  }
853
852
 
853
+ /** A synthetic route's CSS: in a release, the stylesheet its build emitted; else a source scan. */
854
+ async function syntheticRouteCss(
855
+ config: ResolvedConfig,
856
+ id: string,
857
+ scan: (routes: typeof import('../routing/routes')) => Promise<string[]>,
858
+ ) {
859
+ if (!productionRelease(config)) return scan(await import('../routing/routes'))
860
+ const name = `${id}.css`
861
+ return emittedAssetName(config, name) === name ? [] : [name]
862
+ }
863
+
854
864
  function clientScriptPath(route: RouteManifestEntry, dev?: boolean) {
855
865
  return dev
856
866
  ? `/__pnext/client/${route.id}.js`
@@ -1530,7 +1540,7 @@ async function serializablePageProps(props: PageProps, omitSearchParams = false)
1530
1540
 
1531
1541
  export async function staticParamsFor(config: ResolvedConfig, route: RouteManifestEntry) {
1532
1542
  const moduleFiles = [
1533
- ...findLayouts(config.appPath, route.file).filter(file => existsSync(file)),
1543
+ ...findLayouts(config.appPath, route.file).filter(conventionFileExists),
1534
1544
  route.file,
1535
1545
  ]
1536
1546
  let paramSets: Record<string, RouteParamValue>[] = [{}]
@@ -1615,7 +1625,7 @@ export async function renderPage(options: RenderOptions) {
1615
1625
  export async function renderPageWithStatus(
1616
1626
  options: RenderOptions,
1617
1627
  ): Promise<{ html: string; status: number; location?: string }> {
1618
- registerServerRuntime(options.config, options.route.sourceFiles)
1628
+ await registerServerRuntime(options.config, options.route.sourceFiles)
1619
1629
  await seedPrerenderSidecar(options)
1620
1630
  const request = renderScopeRequest(options)
1621
1631
  return runWithRequest(
@@ -1648,7 +1658,7 @@ export async function renderPageWithStatus(
1648
1658
  }
1649
1659
 
1650
1660
  export async function renderPageStream(options: RenderOptions) {
1651
- registerServerRuntime(options.config, options.route.sourceFiles)
1661
+ await registerServerRuntime(options.config, options.route.sourceFiles)
1652
1662
  await seedPrerenderSidecar(options)
1653
1663
 
1654
1664
  const request = renderScopeRequest(options)
@@ -1666,7 +1676,7 @@ export async function renderPageStream(options: RenderOptions) {
1666
1676
  }
1667
1677
 
1668
1678
  export async function renderPageResponse(options: RenderOptions) {
1669
- registerServerRuntime(options.config, options.route.sourceFiles)
1679
+ await registerServerRuntime(options.config, options.route.sourceFiles)
1670
1680
  const method = options.request?.method.toUpperCase() ?? 'GET'
1671
1681
  if (method !== 'GET' && method !== 'HEAD') {
1672
1682
  return new Response('Method Not Allowed', {
@@ -1871,7 +1881,7 @@ const rejectedPprShellUpgrades = new Set<string>()
1871
1881
  * when dynamic data escapes every boundary - the route must then be served fully dynamically.
1872
1882
  */
1873
1883
  export async function renderPartialShell(options: RenderOptions): Promise<PrebuiltShell | null> {
1874
- registerServerRuntime(options.config, options.route.sourceFiles)
1884
+ await registerServerRuntime(options.config, options.route.sourceFiles)
1875
1885
  const sidecar = getRenderExtensions().prerenderSidecar
1876
1886
  const sidecarContext = prerenderSidecarContext(options)
1877
1887
  await sidecar.begin(sidecarContext)
@@ -1960,7 +1970,7 @@ export async function renderPartialShell(options: RenderOptions): Promise<Prebui
1960
1970
  export async function renderRuntimePrefetchDocument(
1961
1971
  options: RenderOptions,
1962
1972
  ): Promise<{ html: string; postponed: boolean } | null> {
1963
- registerServerRuntime(options.config, options.route.sourceFiles)
1973
+ await registerServerRuntime(options.config, options.route.sourceFiles)
1964
1974
  // Seed (read-only) the resume-data sidecar so 'use cache' scopes resolve
1965
1975
  // their build-time fills; unlike renderPartialShell nothing is collected or
1966
1976
  // persisted back.
@@ -2512,7 +2522,7 @@ function hasParamFreeRootLayout(config: ResolvedConfig): boolean {
2512
2522
  const cached = paramFreeRootLayoutCache.get(config.appPath)
2513
2523
  if (cached !== undefined) return cached
2514
2524
  const found = ['tsx', 'jsx', 'ts', 'js'].some(ext =>
2515
- existsSync(join(config.appPath, `layout.${ext}`)),
2525
+ conventionFileExists(join(config.appPath, `layout.${ext}`)),
2516
2526
  )
2517
2527
  paramFreeRootLayoutCache.set(config.appPath, found)
2518
2528
  return found
@@ -2636,7 +2646,7 @@ export async function renderGlobalNotFoundResponse(
2636
2646
  ) {
2637
2647
  const globalFile = ['tsx', 'ts', 'jsx', 'js']
2638
2648
  .map(ext => `${options.config.appPath}/global-not-found.${ext}`)
2639
- .find(candidate => existsSync(candidate))
2649
+ .find(conventionFileExists)
2640
2650
  // Render as GET regardless of the original method: the 404 page is what a
2641
2651
  // browser should see for a stray POST too (Next's MPA fallback), not a 405.
2642
2652
  const notFoundRequest = options.request
@@ -2664,9 +2674,9 @@ export async function renderGlobalNotFoundResponse(
2664
2674
  // Include CSS shared with the root layout: this document replaces the
2665
2675
  // whole page (no root layout render), so the global sheet isn't linked
2666
2676
  // and anything it covered must ship in this route's own chunk.
2667
- cssImports: await collectFileCss(options.config.appPath, [globalFile], {
2668
- includeGlobalCss: true,
2669
- }),
2677
+ cssImports: await syntheticRouteCss(options.config, 'global-not-found', routes =>
2678
+ routes.collectFileCss(options.config.appPath, [globalFile], { includeGlobalCss: true }),
2679
+ ),
2670
2680
  sourceFiles: [globalFile],
2671
2681
  synthetic: true,
2672
2682
  }
@@ -2706,7 +2716,9 @@ export async function renderGlobalNotFoundResponse(
2706
2716
  clientReferences: rootRoute?.clientReferences ?? [],
2707
2717
  // Root not-found ships its own CSS (`/assets/not-found.css`) alongside the
2708
2718
  // root-layout global sheet (`/assets/global.css`, emitted via globalCssHref).
2709
- cssImports: await collectNotFoundCss(options.config.appPath),
2719
+ cssImports: await syntheticRouteCss(options.config, 'not-found', routes =>
2720
+ routes.collectNotFoundCss(options.config.appPath),
2721
+ ),
2710
2722
  sourceFiles: rootRoute?.sourceFiles ?? [anchor],
2711
2723
  ...(rootRoute?.needsRouterEntry ? { needsRouterEntry: true } : {}),
2712
2724
  // Serve the ROOT route's built client entry: the synthetic id has no
@@ -2863,6 +2875,8 @@ const documentLayoutDefaults = new Map<string, { mtimeMs: number; hasDefault: bo
2863
2875
  async function routeHasDocumentLayout(layoutFiles: string[]) {
2864
2876
  const rootLayout = layoutFiles[0]
2865
2877
  if (!rootLayout) return false
2878
+ const served = servedConventions()
2879
+ if (served) return served.documentLayouts.has(rootLayout)
2866
2880
  let mtimeMs: number
2867
2881
  try {
2868
2882
  mtimeMs = statSync(rootLayout).mtimeMs
@@ -2923,7 +2937,7 @@ async function pageRender(options: RenderOptions, stream: boolean): Promise<Page
2923
2937
  // Drop the non-existent anchor file so we don't try to import it; the
2924
2938
  // layout's `children` is supplied by the `@children` slot instead (see
2925
2939
  // childrenFromSlots).
2926
- if (pageFile && !existsSync(pageFile)) pageFile = undefined
2940
+ if (pageFile && !conventionFileExists(pageFile)) pageFile = undefined
2927
2941
  }
2928
2942
  const props = profileRenderSyncStep(profile, 'create page props', () =>
2929
2943
  createPageProps(options),
@@ -3254,7 +3268,7 @@ async function renderAuthInterruptPage(
3254
3268
  const fallbackTree = httpAccessFallbackTree(status, message)
3255
3269
  const file = nearestConventionFile(options, `${kind}.tsx`)
3256
3270
  const boundaryOptions = optionsForNotFoundBoundary(options, file)
3257
- if (!file || !existsSync(file)) {
3271
+ if (!file || !conventionFileExists(file)) {
3258
3272
  return renderTreeOrFallback(
3259
3273
  boundaryOptions,
3260
3274
  fallbackTree,
@@ -3322,7 +3336,7 @@ async function renderGlobalErrorPage(
3322
3336
  // global-error.js is a client component ('use client'): import it through
3323
3337
  // the client module loader and SSR it to a full document. Server components
3324
3338
  // (no directive) load through the server loader.
3325
- const clientDirective = hasUseClientDirective(await readText(file))
3339
+ const clientDirective = await conventionUsesClient(file)
3326
3340
  const module = (
3327
3341
  clientDirective ? await importClientModule(file, options) : await importModule(file, options)
3328
3342
  ) as ErrorModule
@@ -3389,7 +3403,7 @@ export function withDevReloadScript(html: string, request?: Request): string {
3389
3403
  }
3390
3404
 
3391
3405
  function withGlobalErrorStylesheet(html: string, config: ResolvedConfig): string {
3392
- const asset = `${config.outPath}/public/assets/${emittedAssetName(config, 'global-error.css')}`
3406
+ const asset = `${config.outPath}/static/assets/${emittedAssetName(config, 'global-error.css')}`
3393
3407
  if (!existsSync(asset)) return html
3394
3408
  const link = `<link rel="stylesheet" href="${assetHref(config, 'global-error.css')}">`
3395
3409
  if (html.includes('</head>')) return html.replace('</head>', `${link}</head>`)
@@ -3956,7 +3970,7 @@ async function renderTreeInFrame(
3956
3970
  // global-not-found replaces the document without the root layout, so the
3957
3971
  // root layout's global sheet must not load (its CSS ships in the route's
3958
3972
  // own chunk, built with includeGlobalCss).
3959
- options.route.id === 'global-not-found' ? undefined : globalCssHref(options.config)
3973
+ options.route.id === 'global-not-found' ? undefined : await globalCssHref(options.config)
3960
3974
  let routeStylesheet = routeCssHref(options.route, options.config)
3961
3975
  // File names, not logical ones: the inline path READS these off disk, so it has
3962
3976
  // to spell them the way the build emitted them (content-hashed in production).
@@ -4378,7 +4392,7 @@ async function buildLoadingBoundaries(
4378
4392
  options.route.file,
4379
4393
  'loading.tsx',
4380
4394
  )) {
4381
- if (!existsSync(loadingFile)) continue
4395
+ if (!conventionFileExists(loadingFile)) continue
4382
4396
  const fallback = await buildLoadingFallback(options, loadingFile)
4383
4397
  if (fallback) fallbackByDir.set(dirname(loadingFile), fallback)
4384
4398
  }
@@ -4485,7 +4499,7 @@ async function renderNotFoundPage(
4485
4499
  // A `'use client'` not-found.js with a discovered client reference renders as an island (same as
4486
4500
  // error.js) so it HYDRATES - its hooks must work on the 404 document. Server-rendering it would
4487
4501
  // serialize child event-handler props as dead action stubs.
4488
- const clientDirective = hasUseClientDirective(await readText(file))
4502
+ const clientDirective = await conventionUsesClient(file)
4489
4503
  const clientReference = clientDirective
4490
4504
  ? conventionDefaultReference(notFoundOptions, file)
4491
4505
  : undefined
@@ -4598,7 +4612,7 @@ async function renderErrorPage(options: RenderOptions, error: unknown, stream: b
4598
4612
  if (!file) return renderGenericErrorPage(options, resolvedError, stream)
4599
4613
 
4600
4614
  try {
4601
- const clientDirective = hasUseClientDirective(await readText(file))
4615
+ const clientDirective = await conventionUsesClient(file)
4602
4616
  const module = (
4603
4617
  clientDirective ? await importClientModule(file, options) : await importModule(file, options)
4604
4618
  ) as ErrorModule
@@ -4867,10 +4881,16 @@ function generatedFallbackPage(
4867
4881
  }
4868
4882
  }
4869
4883
 
4884
+ /** Whether a convention file is `'use client'`: the frozen fact in production, its source otherwise. */
4885
+ async function conventionUsesClient(file: string) {
4886
+ const served = servedConventions()
4887
+ return served ? served.client.has(file) : hasUseClientDirective(await readText(file))
4888
+ }
4889
+
4870
4890
  function nearestConventionFile(options: RenderOptions, name: string) {
4871
4891
  return [...findConventionFiles(options.config.appPath, options.route.file, name)]
4872
4892
  .reverse()
4873
- .find(file => existsSync(file))
4893
+ .find(conventionFileExists)
4874
4894
  }
4875
4895
 
4876
4896
  /**
@@ -6541,7 +6561,7 @@ function dynamicTargetReference(
6541
6561
  const target = dynamicReference.target
6542
6562
  if (!target) return undefined
6543
6563
  const stored = state.clientReferences.get(
6544
- `c-${clientReferenceId(target.file, target.exportName)}`,
6564
+ target.id ?? `c-${clientReferenceId(target.file, target.exportName)}`,
6545
6565
  )
6546
6566
  if (stored) return stored
6547
6567
  for (const reference of state.clientReferences.values()) {
@@ -7616,7 +7636,7 @@ async function applyLayouts(
7616
7636
  await profileRenderStep(profile, 'import layouts', async () => {
7617
7637
  for (const layoutPath of options.layoutFiles ??
7618
7638
  findLayouts(options.config.appPath, options.route.file)) {
7619
- if (!options.moduleLoader && !existsSync(layoutPath)) continue
7639
+ if (!options.moduleLoader && !conventionFileExists(layoutPath)) continue
7620
7640
  const layoutModule = (await importModule(layoutPath, options)) as LayoutModule
7621
7641
  markServerReference(layoutModule.default)
7622
7642
  // Cheap leaf span for the segment-module resolution (nested under the
@@ -8025,7 +8045,7 @@ function fullSegmentPrefetchSkipsSharedLayout(options: RenderOptions): boolean {
8025
8045
  options.config.appPath,
8026
8046
  options.route.file,
8027
8047
  'layout.tsx',
8028
- ).filter(file => existsSync(file))
8048
+ ).filter(conventionFileExists)
8029
8049
  const documentDir = layoutFiles[0] ? dirname(layoutFiles[0]) : undefined
8030
8050
  return layoutFiles
8031
8051
  .slice(1)
@@ -8,6 +8,7 @@ import {
8
8
  PostponeError,
9
9
  } from '../render/ppr'
10
10
  import type { NextRequest, RouteParamValue } from '../types'
11
+ import type { Release } from '../runtime/production'
11
12
 
12
13
  // Request work-unit store (CORE seam for compat after()/onRequestError).
13
14
  //
@@ -46,6 +47,8 @@ export interface WorkUnit {
46
47
  * grows no dependency on compat.
47
48
  */
48
49
  compat?: Record<symbol, unknown>
50
+ /** The production release this request serves from; dev and build leave it unset. */
51
+ release?: Release
49
52
  }
50
53
 
51
54
  // Anchored on globalThis (like requestStorage below) so compiled compat
@@ -67,6 +70,12 @@ export function getWorkUnit(): WorkUnit | undefined {
67
70
  return workUnitStorage.getStore()
68
71
  }
69
72
 
73
+ /** Mark the current work unit as serving `release` (production reads its app facts from it). */
74
+ export function setWorkUnitRelease(release: Release): void {
75
+ const unit = workUnitStorage.getStore()
76
+ if (unit) unit.release = release
77
+ }
78
+
70
79
  /** Set the current work-unit phase (compat toggles render/action/handler/...). */
71
80
  export function setPhase(phase: WorkUnitPhase): void {
72
81
  const unit = workUnitStorage.getStore()
@@ -1,3 +1,4 @@
1
+ import { clientReferenceId } from '../client/reference'
1
2
  import { dynamicCallFacts, rewriteFacts, type DynamicCallFact } from './scan-facts'
2
3
 
3
4
  export interface DynamicCall {
@@ -132,7 +133,8 @@ export function rewriteDynamicCallTargets(
132
133
  const args = source.slice(call.open + 1, call.close)
133
134
  const argCount = topLevelArgCount(args)
134
135
  if (argCount < 1 || argCount > 2) continue
135
- const literal = JSON.stringify({ file, exportName: target.exportName })
136
+ const id = `c-${clientReferenceId(file, target.exportName)}`
137
+ const literal = JSON.stringify({ file, exportName: target.exportName, id })
136
138
  const separator = /,\s*$/.test(args) ? '' : ', '
137
139
  edits.push({
138
140
  at: call.close,
@@ -222,6 +224,7 @@ export function deferredDynamicImportSpecifiers(source: string, file?: string) {
222
224
  }
223
225
 
224
226
  const facts = dynamicCallFacts(source, file)
227
+ const rendered = new Set<string>()
225
228
  for (const call of dynamicCallsFromSource(source, dynamicNames)) {
226
229
  const target = loaderImportTargetForCall(call, facts)
227
230
  if (!target) continue
@@ -237,7 +240,10 @@ export function deferredDynamicImportSpecifiers(source: string, file?: string) {
237
240
  const visible =
238
241
  /\bload\s*:\s*['"]visible['"]/.test(options) && !/\bssr\s*:\s*true\b/.test(options)
239
242
  if (ssrFalse || visible) deferred.add(target.specifier)
243
+ else rendered.add(target.specifier)
240
244
  }
245
+ // A target another call server-renders must compile for the server.
246
+ for (const specifier of rendered) deferred.delete(specifier)
241
247
  return deferred
242
248
  }
243
249
 
@@ -2,6 +2,7 @@ import { existsSync } from 'node:fs'
2
2
  import path from 'node:path'
3
3
  import type { ResolverFactory, NapiResolveOptions } from 'oxc-resolver'
4
4
  import { loadNative } from '../utils/native-require'
5
+ import { splitResourceQuery } from '../utils/resource-query'
5
6
 
6
7
  // Lazy: the native binding costs ~1.3 MB RSS and a prod server may never resolve.
7
8
  function createResolverFactory(options: NapiResolveOptions): ResolverFactory {
@@ -95,18 +96,7 @@ export function tsConfigFileFor(root: string): string | undefined {
95
96
  return file
96
97
  }
97
98
 
98
- /**
99
- * Split a webpack resource specifier into its file request and its `?query#fragment` suffix - loader
100
- * plumbing (turbopack rules, `.wasm?module`) that takes no part in the on-disk lookup. A leading `#` is a
101
- * package-imports specifier, not a fragment.
102
- */
103
- export function splitResourceQuery(specifier: string): { path: string; query: string } {
104
- const offset = specifier.startsWith('#') ? 1 : 0
105
- const match = /[?#]/.exec(specifier.slice(offset))
106
- if (!match) return { path: specifier, query: '' }
107
- const index = offset + match.index
108
- return { path: specifier.slice(0, index), query: specifier.slice(index) }
109
- }
99
+ export { splitResourceQuery }
110
100
 
111
101
  /**
112
102
  * Resolve `specifier` from `dir`. `tsConfigRoot` opts the lookup into that