@wular/pnext 0.1.5 → 0.1.7

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 (302) hide show
  1. package/bin/pnext +9 -2
  2. package/dist/server/_chunks/aliases-j47a10ce.js +44 -0
  3. package/dist/server/_chunks/analyze-arswqe6s.js +445 -0
  4. package/dist/server/_chunks/boot-af0mv3dy.js +36 -0
  5. package/dist/server/_chunks/build-spy3dxp4.js +84 -0
  6. package/dist/server/_chunks/build-t9h0hec3.js +3550 -0
  7. package/dist/server/_chunks/build-tier-m7yvqz61.js +2472 -0
  8. package/dist/server/_chunks/compile-53j02j27.js +22 -0
  9. package/dist/server/_chunks/config-1yzwexq6.js +27 -0
  10. package/dist/server/_chunks/config-n2z7yxps.js +22 -0
  11. package/dist/server/_chunks/create-meec9hs6.js +134 -0
  12. package/dist/server/_chunks/dev-fjmqg6en.js +220 -0
  13. package/dist/server/_chunks/esbuild-yyktfrtt.js +15 -0
  14. package/dist/server/_chunks/generate-n9sh6hzt.js +21 -0
  15. package/dist/server/_chunks/index-0374j2b9.js +66 -0
  16. package/dist/server/_chunks/index-06nyqntx.js +213 -0
  17. package/dist/server/_chunks/index-1011cscr.js +5263 -0
  18. package/dist/server/_chunks/index-11sfk4hb.js +170 -0
  19. package/dist/server/_chunks/index-20ppawv7.js +297 -0
  20. package/dist/server/_chunks/index-2dv4y4wd.js +1065 -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-357na4a8.js +109 -0
  24. package/dist/server/_chunks/index-3b3at80h.js +109 -0
  25. package/dist/server/_chunks/index-3tf1xjsp.js +6166 -0
  26. package/dist/server/_chunks/index-3xbr4eyv.js +105 -0
  27. package/dist/server/_chunks/index-4399bvd2.js +18 -0
  28. package/dist/server/_chunks/index-4hgjrqh7.js +851 -0
  29. package/dist/server/_chunks/index-4q2gbfwk.js +2530 -0
  30. package/dist/server/_chunks/index-4t92hdxd.js +172 -0
  31. package/dist/server/_chunks/index-4zzebszd.js +10 -0
  32. package/dist/server/_chunks/index-5gyr4emz.js +107 -0
  33. package/dist/server/_chunks/index-5hr4062s.js +70 -0
  34. package/dist/server/_chunks/index-68m3mwbd.js +84 -0
  35. package/dist/server/_chunks/index-6b009mt0.js +56 -0
  36. package/dist/server/_chunks/index-6e4fqv30.js +588 -0
  37. package/dist/server/_chunks/index-6gex3pfa.js +15 -0
  38. package/dist/server/_chunks/index-6kp9r0d9.js +11 -0
  39. package/dist/server/_chunks/index-6q7zvrap.js +134 -0
  40. package/dist/server/_chunks/index-73z4esab.js +38 -0
  41. package/dist/server/_chunks/index-79ngvam8.js +60 -0
  42. package/dist/server/_chunks/index-7wc9n8be.js +25 -0
  43. package/dist/server/_chunks/index-8k7pfejn.js +1599 -0
  44. package/dist/server/_chunks/index-8kcxdfya.js +206 -0
  45. package/dist/server/_chunks/index-91x12ykq.js +274 -0
  46. package/dist/server/_chunks/index-9ch50vtq.js +18 -0
  47. package/dist/server/_chunks/index-9hx2641q.js +28 -0
  48. package/dist/server/_chunks/index-9n2914ww.js +535 -0
  49. package/dist/server/_chunks/index-9ych9ma8.js +207 -0
  50. package/dist/server/_chunks/index-b6wtkdp4.js +24 -0
  51. package/dist/server/_chunks/index-bcws5eh2.js +35 -0
  52. package/dist/server/_chunks/index-c0g2wf3z.js +92 -0
  53. package/dist/server/_chunks/index-cf4rvxv6.js +95 -0
  54. package/dist/server/_chunks/index-cjgqs13g.js +65 -0
  55. package/dist/server/_chunks/index-ckkdpht5.js +41 -0
  56. package/dist/server/_chunks/index-cyfednwb.js +87 -0
  57. package/dist/server/_chunks/index-danncfhy.js +311 -0
  58. package/dist/server/_chunks/index-dg0f05zs.js +199 -0
  59. package/dist/server/_chunks/index-e3a6yx84.js +18 -0
  60. package/dist/server/_chunks/index-e88tyv35.js +45 -0
  61. package/dist/server/_chunks/index-eajaspjk.js +1253 -0
  62. package/dist/server/_chunks/index-eeaanp2e.js +483 -0
  63. package/dist/server/_chunks/index-egykcqg3.js +101 -0
  64. package/dist/server/_chunks/index-en7n9f7m.js +22 -0
  65. package/dist/server/_chunks/index-ezgj0tr1.js +88 -0
  66. package/dist/server/_chunks/index-fkp000p1.js +590 -0
  67. package/dist/server/_chunks/index-fn1chkkd.js +214 -0
  68. package/dist/server/_chunks/index-fp8r0sqs.js +64 -0
  69. package/dist/server/_chunks/index-ggk2j965.js +9432 -0
  70. package/dist/server/_chunks/index-grta8ap3.js +238 -0
  71. package/dist/server/_chunks/index-h2a70tg4.js +7 -0
  72. package/dist/server/_chunks/index-h4xy648y.js +51 -0
  73. package/dist/server/_chunks/index-hkdfj58q.js +92 -0
  74. package/dist/server/_chunks/index-j2zrcp1k.js +66 -0
  75. package/dist/server/_chunks/index-jcsjq3d1.js +20 -0
  76. package/dist/server/_chunks/index-jepjvcrf.js +198 -0
  77. package/dist/server/_chunks/index-jks0x0a1.js +14 -0
  78. package/dist/server/_chunks/index-jx7bqvw1.js +57 -0
  79. package/dist/server/_chunks/index-k1g3epw0.js +1299 -0
  80. package/dist/server/_chunks/index-knp0s912.js +7 -0
  81. package/dist/server/_chunks/index-kq8kpzye.js +472 -0
  82. package/dist/server/_chunks/index-kqyy1d4f.js +50 -0
  83. package/dist/server/_chunks/index-kresp2n8.js +445 -0
  84. package/dist/server/_chunks/index-ms9v9tnx.js +31 -0
  85. package/dist/server/_chunks/index-n4hxpng2.js +11 -0
  86. package/dist/server/_chunks/index-ncnes8c6.js +17 -0
  87. package/dist/server/_chunks/index-nqh3xhdc.js +57 -0
  88. package/dist/server/_chunks/index-nyrba2kr.js +4130 -0
  89. package/dist/server/_chunks/index-p77b4kcx.js +296 -0
  90. package/dist/server/_chunks/index-pym1cezq.js +39 -0
  91. package/dist/server/_chunks/index-q738kkhj.js +100 -0
  92. package/dist/server/_chunks/index-qjj6gddn.js +54 -0
  93. package/dist/server/_chunks/index-qm6kmhqs.js +110 -0
  94. package/dist/server/_chunks/index-qn4z5s3c.js +819 -0
  95. package/dist/server/_chunks/index-qpwsgt3a.js +158 -0
  96. package/dist/server/_chunks/index-r97ptz1q.js +97 -0
  97. package/dist/server/_chunks/index-ras0sdab.js +26 -0
  98. package/dist/server/_chunks/index-rcs322t1.js +19 -0
  99. package/dist/server/_chunks/index-rn8dyr1f.js +126 -0
  100. package/dist/server/_chunks/index-rt9qq1va.js +4 -0
  101. package/dist/server/_chunks/index-rthrsed5.js +356 -0
  102. package/dist/server/_chunks/index-sctcgyvv.js +185 -0
  103. package/dist/server/_chunks/index-sjpckxqn.js +213 -0
  104. package/dist/server/_chunks/index-sq3kvz3g.js +200 -0
  105. package/dist/server/_chunks/index-stbk3jhn.js +31 -0
  106. package/dist/server/_chunks/index-sv8m6789.js +266 -0
  107. package/dist/server/_chunks/index-tgcakdxc.js +125 -0
  108. package/dist/server/_chunks/index-tgsfjmep.js +74 -0
  109. package/dist/server/_chunks/index-tq0pc7eg.js +154 -0
  110. package/dist/server/_chunks/index-tq1f96v7.js +51 -0
  111. package/dist/server/_chunks/index-tqn2qnnn.js +241 -0
  112. package/dist/server/_chunks/index-tvqkq6e6.js +128 -0
  113. package/dist/server/_chunks/index-v1ags1na.js +132 -0
  114. package/dist/server/_chunks/index-vvmmknm1.js +1431 -0
  115. package/dist/server/_chunks/index-vvq7rqtn.js +329 -0
  116. package/dist/server/_chunks/index-vz0km5qk.js +33 -0
  117. package/dist/server/_chunks/index-w6h8m6vp.js +138 -0
  118. package/dist/server/_chunks/index-web361b8.js +138 -0
  119. package/dist/server/_chunks/index-wpfhfbs4.js +194 -0
  120. package/dist/server/_chunks/index-x0y005nh.js +283 -0
  121. package/dist/server/_chunks/index-x3yn4ywm.js +22 -0
  122. package/dist/server/_chunks/index-x93hv0sc.js +271 -0
  123. package/dist/server/_chunks/index-xvj80c3a.js +30 -0
  124. package/dist/server/_chunks/index-xywjg2vc.js +100 -0
  125. package/dist/server/_chunks/index-y2jr2n72.js +152 -0
  126. package/dist/server/_chunks/index-y36a0ybw.js +57 -0
  127. package/dist/server/_chunks/index-ye9hmsvf.js +47 -0
  128. package/dist/server/_chunks/index-yhxkxghe.js +56 -0
  129. package/dist/server/_chunks/index-ym67zqxp.js +130 -0
  130. package/dist/server/_chunks/index-ytjvpbm2.js +736 -0
  131. package/dist/server/_chunks/index-yz54pxrh.js +960 -0
  132. package/dist/server/_chunks/index-z4z9zv3b.js +733 -0
  133. package/dist/server/_chunks/index-z84ab1w0.js +1270 -0
  134. package/dist/server/_chunks/index-z88qv16t.js +11 -0
  135. package/dist/server/_chunks/index-zqpqyg8p.js +666 -0
  136. package/dist/server/_chunks/loader-7yw0j98z.js +78 -0
  137. package/dist/server/_chunks/modules-jn8bmbn8.js +74 -0
  138. package/dist/server/_chunks/pipeline-yy9jhydf.js +832 -0
  139. package/dist/server/_chunks/production-jar9ztm3.js +44 -0
  140. package/dist/server/_chunks/proxy-config-j84dgksn.js +21 -0
  141. package/dist/server/_chunks/proxy-w1d0wmsn.js +33 -0
  142. package/dist/server/_chunks/router-k1reahfn.js +498 -0
  143. package/dist/server/_chunks/routes-2s277fc6.js +64 -0
  144. package/dist/server/_chunks/run-pvd8bs1t.js +696 -0
  145. package/dist/server/_chunks/server-mt4a994c.js +1835 -0
  146. package/dist/server/_chunks/tsconfig-defaults-1nb3308g.js +20 -0
  147. package/dist/server/_chunks/typegen-0qr0459b.js +35 -0
  148. package/dist/server/api/client-cache.js +14 -0
  149. package/dist/server/cli/index.js +378 -0
  150. package/dist/server/cli/start.js +137 -0
  151. package/dist/server/client/reference.js +16 -0
  152. package/dist/server/compat/next/cache.js +56 -0
  153. package/dist/server/compat/next/canonical-url.js +13 -0
  154. package/dist/server/compat/next/client-cache.js +57 -0
  155. package/dist/server/compat/next/client-navigation.js +232 -0
  156. package/dist/server/compat/next/client-only.js +1 -0
  157. package/dist/server/compat/next/client-script.js +178 -0
  158. package/dist/server/compat/next/client-server.js +34 -0
  159. package/dist/server/compat/next/config-headers.js +32 -0
  160. package/dist/server/compat/next/config-loader.js +25 -0
  161. package/dist/server/compat/next/config.js +10 -0
  162. package/dist/server/compat/next/constants.js +18 -0
  163. package/dist/server/compat/next/custom-server.js +10 -0
  164. package/dist/server/compat/next/dist/client/components/app-router-headers.js +48 -0
  165. package/dist/server/compat/next/dist/server/web/spec-extension/revalidate.js +27 -0
  166. package/dist/server/compat/next/dist/server/web/spec-extension/unstable-cache.js +25 -0
  167. package/dist/server/compat/next/dist/server/web/spec-extension/unstable-no-store.js +25 -0
  168. package/dist/server/compat/next/dynamic.js +32 -0
  169. package/dist/server/compat/next/error.js +112 -0
  170. package/dist/server/compat/next/font/cache.js +14 -0
  171. package/dist/server/compat/next/font/google.js +3 -0
  172. package/dist/server/compat/next/font/index.js +1 -0
  173. package/dist/server/compat/next/font/local.js +22 -0
  174. package/dist/server/compat/next/font/runtime-client.js +33 -0
  175. package/dist/server/compat/next/font/runtime.js +41 -0
  176. package/dist/server/compat/next/font/shared.js +22 -0
  177. package/dist/server/compat/next/form.js +129 -0
  178. package/dist/server/compat/next/head.js +10 -0
  179. package/dist/server/compat/next/headers.js +30 -0
  180. package/dist/server/compat/next/image/client.js +11 -0
  181. package/dist/server/compat/next/image/config.js +18 -0
  182. package/dist/server/compat/next/image/optimizer.js +25 -0
  183. package/dist/server/compat/next/image/patterns.js +12 -0
  184. package/dist/server/compat/next/image/props.js +16 -0
  185. package/dist/server/compat/next/image/shared.js +86 -0
  186. package/dist/server/compat/next/image/static-metadata.js +32 -0
  187. package/dist/server/compat/next/image/validate.js +8 -0
  188. package/dist/server/compat/next/image.js +59 -0
  189. package/dist/server/compat/next/index.js +1 -0
  190. package/dist/server/compat/next/legacy-image.js +78 -0
  191. package/dist/server/compat/next/legacy-request-apis.js +24 -0
  192. package/dist/server/compat/next/link-transform.js +14 -0
  193. package/dist/server/compat/next/link.js +14 -0
  194. package/dist/server/compat/next/navigation.js +190 -0
  195. package/dist/server/compat/next/offline.js +45 -0
  196. package/dist/server/compat/next/og.js +8 -0
  197. package/dist/server/compat/next/preferred-region.js +8 -0
  198. package/dist/server/compat/next/redirects.js +30 -0
  199. package/dist/server/compat/next/resource-hints.js +30 -0
  200. package/dist/server/compat/next/rewrites.js +41 -0
  201. package/dist/server/compat/next/root-params.js +121 -0
  202. package/dist/server/compat/next/route-state.js +23 -0
  203. package/dist/server/compat/next/router.js +103 -0
  204. package/dist/server/compat/next/script.js +37 -0
  205. package/dist/server/compat/next/server-only.js +1 -0
  206. package/dist/server/compat/next/server.js +131 -0
  207. package/dist/server/compat/next/svgr.js +14 -0
  208. package/dist/server/compat/next/telemetry.js +10 -0
  209. package/dist/server/compat/next/usage.js +13 -0
  210. package/dist/server/compat/next/user-agent.js +12 -0
  211. package/dist/server/compat/next/web-vitals.js +35 -0
  212. package/dist/server/compat/react/action-state.js +11 -0
  213. package/dist/server/compat/react/client-lite.js +83 -0
  214. package/dist/server/compat/react/client.js +131 -0
  215. package/dist/server/compat/react/compiler-runtime.js +42 -0
  216. package/dist/server/compat/react/dom-client.js +106 -0
  217. package/dist/server/compat/react/dom-react-server.js +37 -0
  218. package/dist/server/compat/react/dom-server.js +40 -0
  219. package/dist/server/compat/react/dom.js +52 -0
  220. package/dist/server/compat/react/hooks-extra.js +12 -0
  221. package/dist/server/compat/react/internals.js +14 -0
  222. package/dist/server/compat/react/parity.js +11 -0
  223. package/dist/server/compat/react/preact.js +113 -0
  224. package/dist/server/compat/react/react-server.js +133 -0
  225. package/dist/server/compat/react/router-shim.js +8 -0
  226. package/dist/server/compat/react/server-component-use.js +14 -0
  227. package/dist/server/compat/react/server-inserted-html.js +14 -0
  228. package/dist/server/compat/react/server.js +194 -0
  229. package/dist/server/compat/react/style-values.js +10 -0
  230. package/dist/server/compat/react/use.js +10 -0
  231. package/dist/server/compat/react/view-transition.js +12 -0
  232. package/dist/server/generation.json +1 -0
  233. package/package.json +4 -1
  234. package/src/api/dynamic.tsx +2 -0
  235. package/src/api/server.ts +24 -6
  236. package/src/cli/adapters/vercel-warm.ts +315 -61
  237. package/src/cli/adapters/vercel.ts +348 -71
  238. package/src/cli/build.ts +58 -33
  239. package/src/cli/index.ts +23 -10
  240. package/src/cli/serve/entry.ts +21 -159
  241. package/src/cli/serve/immutable.ts +31 -0
  242. package/src/cli/serve/pipeline.ts +82 -84
  243. package/src/cli/start.ts +3 -1
  244. package/src/client/chunk-fold.ts +19 -6
  245. package/src/client/react-tier.ts +3 -2
  246. package/src/client/reference-stub.ts +21 -2
  247. package/src/compat/actions/client-plugin.ts +4 -2
  248. package/src/compat/actions/config.ts +6 -5
  249. package/src/compat/actions/extensions.ts +2 -3
  250. package/src/compat/aliases.ts +8 -1
  251. package/src/compat/bundler/config.ts +88 -0
  252. package/src/compat/bundler/optimize-package-imports.ts +86 -26
  253. package/src/compat/bundler/relay-transform.ts +15 -3
  254. package/src/compat/cache-control.ts +16 -8
  255. package/src/compat/css/modules.ts +18 -5
  256. package/src/compat/lifecycle/error-funnel.ts +1 -1
  257. package/src/compat/lifecycle/instrumentation.ts +117 -24
  258. package/src/compat/lifecycle/testmode.ts +3 -2
  259. package/src/compat/next/config-loader.ts +137 -13
  260. package/src/compat/next/font/runtime.ts +22 -2
  261. package/src/compat/next/og.ts +3 -3
  262. package/src/compat/next/usage.ts +3 -2
  263. package/src/compat/pages/api.ts +83 -14
  264. package/src/compat/pages/router.ts +30 -14
  265. package/src/compat/register/bundler.ts +3 -1
  266. package/src/compat/register/middleware.ts +1 -1
  267. package/src/compat/register/otel.ts +5 -9
  268. package/src/compat/register/render.ts +3 -1
  269. package/src/compat/register/use-cache.ts +3 -1
  270. package/src/compat/release.ts +46 -0
  271. package/src/compat/segment/serve.ts +1 -1
  272. package/src/compat/validation/validate.ts +10 -7
  273. package/src/config.ts +51 -7
  274. package/src/css/assets.ts +213 -0
  275. package/src/css/build.ts +174 -238
  276. package/src/dev/server.ts +27 -8
  277. package/src/extensions.ts +4 -0
  278. package/src/render/hooks.ts +13 -0
  279. package/src/render/renderer.ts +52 -32
  280. package/src/request/context.ts +9 -0
  281. package/src/resolve/dynamic.ts +3 -1
  282. package/src/resolve/imports.ts +12 -52
  283. package/src/resolve/package-json.ts +56 -0
  284. package/src/resolve/scan-facts.ts +65 -0
  285. package/src/routing/href.ts +10 -3
  286. package/src/routing/match.ts +287 -0
  287. package/src/routing/metadata-files.ts +19 -5
  288. package/src/routing/proxy-config.ts +137 -0
  289. package/src/routing/proxy.ts +35 -134
  290. package/src/routing/routes.ts +22 -281
  291. package/src/runtime/fingerprint.ts +23 -8
  292. package/src/runtime/load.ts +170 -0
  293. package/src/runtime/loader.ts +215 -43
  294. package/src/runtime/module-cache.ts +13 -0
  295. package/src/runtime/modules.ts +370 -81
  296. package/src/runtime/prebundle.ts +81 -0
  297. package/src/runtime/production.ts +391 -0
  298. package/src/runtime/release-graph.ts +334 -0
  299. package/src/runtime/vendor-build.ts +724 -13
  300. package/src/runtime/vendor.ts +16 -3
  301. package/src/types.ts +2 -0
  302. package/src/utils/fs.ts +89 -3
@@ -1,7 +1,7 @@
1
1
  import { builtinModules } from 'node:module'
2
2
  import os from 'node:os'
3
3
  import path from 'node:path'
4
- import { existsSync, readFileSync, type Dirent } from 'node:fs'
4
+ import { existsSync, readFileSync, readdirSync, realpathSync, statSync, type Dirent } from 'node:fs'
5
5
  import { fileURLToPath } from 'node:url'
6
6
  import { copyFile, mkdir, readdir, readlink, rm, symlink, writeFile } from 'node:fs/promises'
7
7
  import type { OnResolveResult, Plugin } from 'esbuild'
@@ -23,7 +23,13 @@ import {
23
23
  serverBundleRequireConditions,
24
24
  type ServerBundleTarget,
25
25
  } from './loader'
26
- import { externalServerPackageHref, runtimeServerImportTarget } from '../runtime/vendor-build'
26
+ import {
27
+ clearReleaseDependencyLog,
28
+ externalServerPackageHref,
29
+ noteReleaseModuleLayer,
30
+ runtimeServerImportTarget,
31
+ type ReleaseDependencyLayer,
32
+ } from '../runtime/vendor-build'
27
33
  import { writeFileAtomic } from '../utils/fs'
28
34
  import { traceEnabled, traceValue } from '../utils/trace-flags'
29
35
  import { createHash } from 'node:crypto'
@@ -58,6 +64,7 @@ import {
58
64
  importSpecifiers,
59
65
  moduleSpecifierEdges,
60
66
  rewriteSpecifierLiterals,
67
+ stripJsonImportAttributes,
61
68
  } from '../resolve/scan-facts'
62
69
  import {
63
70
  cacheRoot,
@@ -76,7 +83,20 @@ import {
76
83
  transformServerModule,
77
84
  type SpecifierKind,
78
85
  } from './module-transform'
86
+ import { splitResourceQuery } from '../resolve/engine'
79
87
  import { findShakeableDynamicImports } from '../resolve/tree-shake'
88
+ import { productionRelease, releaseModuleHref } from './production'
89
+ import {
90
+ clearModuleGraphFailure,
91
+ forgetModuleEvalError,
92
+ importModuleOnce,
93
+ isReleaseCompile,
94
+ noteModuleGraphFailure,
95
+ setReleaseCompileFlag,
96
+ throwIfModuleEvalFailed,
97
+ } from './load'
98
+
99
+ export { importModuleOnce, isReleaseCompile, throwIfModuleGraphFailed } from './load'
80
100
  import type { RouteManifestEntry } from '../types'
81
101
  import { uniqueIdentifier } from '../utils/code'
82
102
  import { formatDuration } from '../utils/verbose'
@@ -114,37 +134,13 @@ const routeBundleBuilds = new Map<string, Promise<string>>()
114
134
  // present during an earlier check. This avoids repeated reads/parses without
115
135
  // turning one observation into a permanent claim that the closure still exists.
116
136
  const completeArtifactClosures = new Map<string, readonly string[]>()
117
- let moduleGraphFailure: Error | undefined
118
137
 
119
138
  /** Reset build-only graph state before starting another build in this process. */
120
139
  export function resetModuleGraphFailure(): void {
121
- moduleGraphFailure = undefined
140
+ clearModuleGraphFailure()
122
141
  completeArtifactClosures.clear()
123
142
  }
124
143
 
125
- /**
126
- * Fail a build even when its renderer converted a module-resolution exception
127
- * into a 500 response. Rendering may recover from user-code errors, but a
128
- * missing content-addressed artifact means the build output itself is broken.
129
- */
130
- export function throwIfModuleGraphFailed(): void {
131
- if (moduleGraphFailure !== undefined) throw moduleGraphFailure
132
- }
133
-
134
- function noteModuleGraphFailure(error: unknown): void {
135
- moduleGraphFailure ??= error instanceof Error ? error : new Error(String(error))
136
- }
137
-
138
- function isCompiledModuleResolutionError(error: unknown, href: string): boolean {
139
- const message = error instanceof Error ? error.message : String(error)
140
- if (!message.includes('Cannot find module')) return false
141
- return (
142
- href.includes('/cache/server/') ||
143
- /[/\\]cache[/\\]server[/\\]/.test(message) ||
144
- message.includes('/cache/server/')
145
- )
146
- }
147
-
148
144
  // PNEXT_TRACE=server: attribute every per-module compile to its profile,
149
145
  // so cross-profile duplicate work is countable rather than inferred. Counts are
150
146
  // load-insensitive, which is what makes them usable on a contended box.
@@ -222,35 +218,10 @@ function reportModuleStats() {
222
218
  }
223
219
  const routeModuleLoaders = new Map<string, Promise<DevRouteModuleLoaders>>()
224
220
 
225
- // A module that throws while evaluating stays failed in Bun's registry for the process lifetime, and
226
- // re-importing it surfaces downstream symptoms (a missing bundle entry, TDZ on an export) instead of
227
- // the original error. Record the first failure per compiled href and re-throw THAT on every later
228
- // request. Compiled hrefs are content-addressed, so a save that fixes the module yields a new href
229
- // and the entry is never consulted again.
230
- const moduleEvalErrors = new Map<string, unknown>()
231
221
  const warmRetryHrefs = new Map<string, Promise<string>>()
232
222
  const warmImports = new Map<string, Promise<void>>()
233
223
  let warmRetryGeneration = 0
234
224
 
235
- /**
236
- * Import a compiled module, re-throwing its first evaluation error on later imports.
237
- *
238
- * Production needs this as much as dev: a re-import resolves with a half-evaluated namespace whose
239
- * `export const` bindings are still in TDZ, so the render's next read (`module.metadata`) throws
240
- * `Cannot access 'metadata' before initialization` and that replaces the real error in the response.
241
- * Prod hrefs are content-addressed too, so the entry keys the same way dev's does.
242
- */
243
- export async function importModuleOnce<T>(href: string): Promise<T> {
244
- if (moduleEvalErrors.has(href)) throw moduleEvalErrors.get(href)
245
- try {
246
- return (await import(href)) as T
247
- } catch (error) {
248
- if (isCompiledModuleResolutionError(error, href)) noteModuleGraphFailure(error)
249
- moduleEvalErrors.set(href, error)
250
- throw error
251
- }
252
- }
253
-
254
225
  interface ImportDevModuleOptions {
255
226
  /** A speculative import whose evaluation failure must not become render state. */
256
227
  warm?: boolean
@@ -298,7 +269,7 @@ export async function importDevModule<T>(
298
269
  let attemptedEvaluation = false
299
270
  try {
300
271
  importHref = (await warmRetryHrefs.get(href)) ?? href
301
- if (moduleEvalErrors.has(importHref)) throw moduleEvalErrors.get(importHref)
272
+ throwIfModuleEvalFailed(importHref)
302
273
  // Pre-planned vendor builds are enqueued without an awaiter; nothing
303
274
  // evaluates until they all settle (no-op when the pipeline is empty).
304
275
  await drainPreplanBuilds()
@@ -312,7 +283,7 @@ export async function importDevModule<T>(
312
283
  // the real render a fresh module identity, so speculative work can never poison the request.
313
284
  const retryHref = nextWarmRetryHref(href)
314
285
  warmRetryHrefs.set(href, retryHref)
315
- if (moduleEvalErrors.get(importHref) === error) moduleEvalErrors.delete(importHref)
286
+ forgetModuleEvalError(importHref, error)
316
287
  await retryHref
317
288
  }
318
289
  throw error
@@ -338,6 +309,7 @@ const moduleCaches = new WeakMap<ResolvedConfig, DevModuleCache>()
338
309
  export function devModuleGraph(config: ResolvedConfig): DevModuleCache {
339
310
  const existing = moduleCaches.get(config)
340
311
  if (existing) return existing
312
+ if (isReleaseCompile()) releaseAnchors.set(cacheRoot(config.outPath), portableAnchors(config))
341
313
  const cache = devModuleCache(config, {
342
314
  compileKey: devCompileKey(config),
343
315
  edges: (file, source) => graphEdges(config, file, source),
@@ -429,6 +401,14 @@ function devAliasKeys(config: ResolvedConfig) {
429
401
  return keys
430
402
  }
431
403
 
404
+ /** The out dir is compiled output, save the pages sources a build materializes into it. */
405
+ function isCompiledOutput(config: ResolvedConfig, file: string) {
406
+ return (
407
+ isInside(config.outPath, file) &&
408
+ !isInside(path.join(config.outRootPath, 'pnext-pages-compat'), file)
409
+ )
410
+ }
411
+
432
412
  /**
433
413
  * Source edges the graph hash follows. A superset of the compiled graph is safe - it only widens what
434
414
  * a save invalidates - but a subset would leave a dependent holding a name that no longer describes
@@ -444,7 +424,7 @@ function graphEdges(config: ResolvedConfig, file: string, source: string) {
444
424
  const resolved = resolveLocalImport(config, file, sourcePath.replace(/\?.*$/, ''))
445
425
  const isLocalSource = sourcePath.startsWith('.') || path.isAbsolute(sourcePath)
446
426
  // Compiled output is never a source of the graph that produced it.
447
- if (resolved && isInside(config.outPath, resolved)) continue
427
+ if (resolved && isCompiledOutput(config, resolved)) continue
448
428
  if (resolved && (isLocalSource || isInside(config.workspaceRoot, resolved))) {
449
429
  imports.push(path.resolve(resolved))
450
430
  continue
@@ -547,6 +527,9 @@ export async function devServerModuleHref(
547
527
  _version?: string,
548
528
  moduleOptions: DevServerModuleOptions = {},
549
529
  ) {
530
+ const served = productionRelease(config)
531
+ if (served)
532
+ return releaseModuleHref(served, 'server', moduleOptions.conditionTarget ?? 'server', file)
550
533
  const conditionTarget = pagesLayerConditionTarget(
551
534
  config,
552
535
  file,
@@ -585,6 +568,8 @@ export async function devClientModuleHref(
585
568
  _version?: string,
586
569
  serverTarget?: ServerBundleTarget,
587
570
  ) {
571
+ const served = productionRelease(config)
572
+ if (served) return releaseModuleHref(served, 'client', serverTarget ?? 'server', file)
588
573
  const conditionTarget = clientLayerConditionTarget(config, file, serverTarget ?? 'server')
589
574
  const throughPackage = await packageClientModuleHref(config, file, conditionTarget)
590
575
  if (throughPackage) return throughPackage
@@ -1414,30 +1399,252 @@ export function setEmitCompiledSpecifiersManifest(enabled: boolean) {
1414
1399
  }
1415
1400
  }
1416
1401
 
1402
+ // A production build compiles for its release: `.js` artifact names, one asset-context link per
1403
+ // directory, core CSS module class maps. Dev keeps its own compile.
1404
+ // A build's sources do not change under it, so each directory's asset context links once; dev relinks.
1405
+ const linkedAssetContexts = new Map<string, Promise<void>>()
1406
+
1407
+ /** @internal The build sets this for its own duration. Returns a restore function. */
1408
+ export function setReleaseCompile(enabled: boolean) {
1409
+ const previous = isReleaseCompile()
1410
+ setReleaseCompileFlag(enabled)
1411
+ // Each build starts from an emptied out dir, so nothing linked or stubbed before still stands.
1412
+ linkedAssetContexts.clear()
1413
+ assetStubs.clear()
1414
+ releaseAnchors.clear()
1415
+ packageCandidates.clear()
1416
+ packageSpecifiers.clear()
1417
+ clearReleaseDependencyLog()
1418
+ return () => setReleaseCompileFlag(previous)
1419
+ }
1420
+
1417
1421
  /** Sidecar suffix for a compiled artifact's recorded specifiers. */
1418
1422
  export const compiledSpecifiersManifestSuffix = '.pnext-specifiers.json'
1419
1423
 
1420
1424
  const compiledScriptFilePattern = /\.(?:m?js|cjs|jsx|tsx?)$/
1421
1425
 
1426
+ /** Where a release may move with its app: the root, its node_modules links, the workspace. */
1427
+ interface PortableAnchors {
1428
+ roots: string[]
1429
+ /** Real directory -> the logical path the app's node_modules names it by. */
1430
+ links: Map<string, string>
1431
+ }
1432
+
1433
+ const releaseAnchors = new Map<string, PortableAnchors>()
1434
+
1435
+ function portableAnchors(config: ResolvedConfig): PortableAnchors {
1436
+ const links = new Map<string, string>()
1437
+ const link = (logical: string) => {
1438
+ try {
1439
+ const real = realpathSync(logical)
1440
+ if (real !== logical && !links.has(real)) links.set(real, logical)
1441
+ } catch {
1442
+ // A dangling entry links nothing.
1443
+ }
1444
+ }
1445
+ const nodeModules = path.join(config.root, 'node_modules')
1446
+ link(nodeModules)
1447
+ for (const name of listDirectory(nodeModules)) {
1448
+ const entry = path.join(nodeModules, name)
1449
+ if (name.startsWith('@'))
1450
+ for (const scoped of listDirectory(entry)) link(path.join(entry, scoped))
1451
+ else link(entry)
1452
+ }
1453
+ return { roots: [config.root, config.workspaceRoot], links }
1454
+ }
1455
+
1456
+ function listDirectory(dir: string) {
1457
+ try {
1458
+ return readdirSync(dir)
1459
+ } catch {
1460
+ return []
1461
+ }
1462
+ }
1463
+
1464
+ /**
1465
+ * Rewrite `file://` URLs in compiled output relative to `file`: import specifiers become relative
1466
+ * specifiers, and (for a release) inlined URL literals become `new URL(<relative>, import.meta.url)`.
1467
+ * `locate` maps an absolute target to the path to link, or undefined to leave it. A release names an
1468
+ * installed package file by the bare specifier that resolves back to it, so the serving install wins.
1469
+ */
1470
+ function relocateSource(
1471
+ file: string,
1472
+ contents: string,
1473
+ locate: (absolute: string) => string | undefined,
1474
+ installed: (absolute: string) => boolean = () => false,
1475
+ ) {
1476
+ const relativeTo = (target: string) => {
1477
+ const relative = toPosixPath(path.relative(path.dirname(file), target))
1478
+ return relative.startsWith('.') ? relative : `./${relative}`
1479
+ }
1480
+ const edits: { start: number; end: number; value: string }[] = []
1481
+ for (const found of outputSpecifiers(contents)) {
1482
+ const { sourcePath, hash } = splitHash(found.value)
1483
+ const url = sourcePath.startsWith('file://')
1484
+ // A release also re-links plain absolute specifiers (a bundled `require('/abs/path')`).
1485
+ if (!url && !(isReleaseCompile() && path.isAbsolute(sourcePath))) continue
1486
+ const absolute = url ? fileURLToPath(sourcePath) : sourcePath
1487
+ const target = locate(absolute)
1488
+ const bare = isReleaseCompile() && installed(absolute) && packageSpecifier(file, absolute)
1489
+ const value = bare || (target && relativeTo(target))
1490
+ if (!value) continue
1491
+ const query = url ? new URL(sourcePath).search : ''
1492
+ edits.push({
1493
+ start: found.start,
1494
+ end: found.end,
1495
+ value: JSON.stringify(`${value}${query}${hash}`),
1496
+ })
1497
+ }
1498
+ const specifiers = spliceSource(contents, edits)
1499
+ if (!isReleaseCompile()) return specifiers
1500
+ // Path literals (client-reference targets, module URLs) resolve beside the artifact at runtime.
1501
+ return specifiers.replace(
1502
+ /(?<!\\)"((?:file:\/\/)?\/[^"\\\n]+)"(\s*:)?/g,
1503
+ (literal, value: string, key: string | undefined) => {
1504
+ const url = value.startsWith('file:')
1505
+ const target = locate(url ? fileURLToPath(value) : value)
1506
+ if (!target) return literal
1507
+ const href = `new URL(${JSON.stringify(relativeTo(target))}, import.meta.url)`
1508
+ const expression = url ? `${href}.href` : `decodeURIComponent(${href}.pathname)`
1509
+ return key ? `[${expression}]${key}` : expression
1510
+ },
1511
+ )
1512
+ }
1513
+
1514
+ const isInstalled = (file: string) => file.includes(`${path.sep}node_modules${path.sep}`)
1515
+ const packageCandidates = new Map<string, string | null>()
1516
+ const packageSpecifiers = new Map<string, boolean>()
1517
+
1518
+ /** The bare specifier that resolves from `from` back to installed `file`, if one does. */
1519
+ function packageSpecifier(from: string, file: string) {
1520
+ const candidate = packageCandidate(file)
1521
+ // One miss settles it: the fallback path is always correct, only less portable.
1522
+ if (!candidate || packageSpecifiers.get(candidate) === false) return undefined
1523
+ const key = `${path.dirname(from)}\0${candidate}`
1524
+ let resolves = packageSpecifiers.get(key)
1525
+ if (resolves === undefined) {
1526
+ try {
1527
+ resolves = realpathSync(Bun.resolveSync(candidate, path.dirname(from))) === realpathSync(file)
1528
+ } catch {
1529
+ resolves = false
1530
+ }
1531
+ packageSpecifiers.set(key, resolves)
1532
+ if (!resolves) packageSpecifiers.set(candidate, false)
1533
+ }
1534
+ return resolves ? candidate : undefined
1535
+ }
1536
+
1537
+ /** How installed `file` is named bare: the exports subpath that targets it, else its deep path. */
1538
+ function packageCandidate(file: string) {
1539
+ let candidate = packageCandidates.get(file)
1540
+ if (candidate !== undefined) return candidate
1541
+ candidate = null
1542
+ const packageDir = nodeModulesPackageDir(file)
1543
+ const name = packageDir && packageNameOfDirectory(packageDir)
1544
+ if (packageDir && name) {
1545
+ const subpath = `./${toPosixPath(path.relative(packageDir, file))}`
1546
+ const exports = packageExportsMap(packageDir)
1547
+ if (!exports) candidate = `${name}/${subpath.slice(2)}`
1548
+ for (const [key, value] of Object.entries(exports ?? {})) {
1549
+ if (key.includes('*') || !exportTargets(value).includes(subpath)) continue
1550
+ candidate = key === '.' ? name : `${name}/${key.slice(2)}`
1551
+ break
1552
+ }
1553
+ }
1554
+ packageCandidates.set(file, candidate)
1555
+ return candidate
1556
+ }
1557
+
1558
+ /** A package's exports as a subpath map (`"."` for a bare entry), or undefined when it has none. */
1559
+ function packageExportsMap(packageDir: string): Record<string, unknown> | undefined {
1560
+ try {
1561
+ const { exports } = JSON.parse(readFileSync(path.join(packageDir, 'package.json'), 'utf8')) as {
1562
+ exports?: unknown
1563
+ }
1564
+ if (exports === undefined || exports === null) return undefined
1565
+ if (typeof exports !== 'object' || Array.isArray(exports)) return { '.': exports }
1566
+ return Object.keys(exports).some(key => key.startsWith('.'))
1567
+ ? (exports as Record<string, unknown>)
1568
+ : { '.': exports }
1569
+ } catch {
1570
+ return undefined
1571
+ }
1572
+ }
1573
+
1574
+ function exportTargets(value: unknown): string[] {
1575
+ if (typeof value === 'string') return [value]
1576
+ if (Array.isArray(value)) return value.flatMap(exportTargets)
1577
+ return value && typeof value === 'object' ? Object.values(value).flatMap(exportTargets) : []
1578
+ }
1579
+
1580
+ /** A release bundle emitted outside the module graph, rewritten to reach its targets relatively. */
1581
+ export function relocateReleaseBundle(config: ResolvedConfig, file: string, contents: string) {
1582
+ const anchors = portableAnchors(config)
1583
+ return relocateSource(file, contents, absolute => portableTarget(anchors, absolute), isInstalled)
1584
+ }
1585
+
1586
+ /** A release artifact written outside writeCompiledFile (vendor bundles), re-linked like one. */
1587
+ export function releaseArtifactSource(file: string, contents: string) {
1588
+ if (!isReleaseCompile()) return contents
1589
+ const cacheRoot = [...releaseAnchors.keys()].find(root => isInside(root, file))
1590
+ const anchors = cacheRoot && releaseAnchors.get(cacheRoot)
1591
+ if (!cacheRoot || !anchors) return contents
1592
+ return relocateSource(
1593
+ file,
1594
+ releaseInputComments(contents),
1595
+ absolute => (isInside(cacheRoot, absolute) ? absolute : portableTarget(anchors, absolute)),
1596
+ absolute => !isInside(cacheRoot, absolute) && isInstalled(absolute),
1597
+ )
1598
+ }
1599
+
1600
+ /**
1601
+ * esbuild's per-input `// <path>` comments, cut to the file name (or its `node_modules/` path, which
1602
+ * names a vendored font caller): their paths walk the build machine.
1603
+ */
1604
+ function releaseInputComments(contents: string) {
1605
+ return contents.replace(/^\/\/ (?:[\w-]+:)?\.{0,2}\/[^\s]*$/gm, comment => {
1606
+ const installed = comment.indexOf('node_modules/')
1607
+ return `// ${installed === -1 ? path.basename(comment) : comment.slice(installed)}`
1608
+ })
1609
+ }
1610
+
1611
+ /** `target` as a path a moved release still reaches: through the app's own tree, never the machine's. */
1612
+ function portableTarget(anchors: PortableAnchors, target: string) {
1613
+ if (anchors.roots.some(root => isInside(root, target))) return target
1614
+ // Nearest linked ancestor first, so a package link wins over the node_modules link that holds it.
1615
+ for (let dir = path.dirname(target); dir !== path.dirname(dir); dir = path.dirname(dir)) {
1616
+ const logical = anchors.links.get(dir)
1617
+ if (logical) return path.join(logical, path.relative(dir, target))
1618
+ }
1619
+ return undefined
1620
+ }
1621
+
1622
+ const compiledJsonModule = /\.json\.js(?:[?#]|$)/
1623
+
1422
1624
  async function writeCompiledFile(file: string, contents: string) {
1423
1625
  const profileRoot = compiledArtifactProfileRoot(file)
1424
- if (profileRoot && contents.includes('file://')) {
1626
+ const anchors =
1627
+ profileRoot && isReleaseCompile() ? releaseAnchors.get(path.dirname(profileRoot)) : undefined
1628
+ if (
1629
+ profileRoot &&
1630
+ (anchors
1631
+ ? /["'](?:file:\/\/)?\/|^\/\/ (?:[\w-]+:)?\.{0,2}\//m.test(contents)
1632
+ : contents.includes('file://'))
1633
+ ) {
1425
1634
  const cacheRoot = path.dirname(profileRoot)
1426
- const edits: { start: number; end: number; value: string }[] = []
1427
- for (const found of outputSpecifiers(contents)) {
1428
- const { sourcePath, hash } = splitHash(found.value)
1429
- if (!sourcePath.startsWith('file://')) continue
1430
- const target = fileURLToPath(sourcePath)
1431
- if (!isInside(cacheRoot, target)) continue
1432
- const relative = toPosixPath(path.relative(path.dirname(file), target))
1433
- edits.push({
1434
- start: found.start,
1435
- end: found.end,
1436
- value: JSON.stringify(`${relative.startsWith('.') ? relative : `./${relative}`}${hash}`),
1437
- })
1438
- }
1439
- contents = spliceSource(contents, edits)
1635
+ contents = relocateSource(
1636
+ file,
1637
+ anchors ? releaseInputComments(contents) : contents,
1638
+ absolute =>
1639
+ isInside(cacheRoot, absolute) ? absolute : anchors && portableTarget(anchors, absolute),
1640
+ absolute => !isInside(cacheRoot, absolute) && isInstalled(absolute),
1641
+ )
1440
1642
  }
1643
+ // A compiled `.json.js` is JS: a release loads it natively, where `type: "json"` would parse it as JSON.
1644
+ if (anchors && contents.includes('.json.js'))
1645
+ contents = stripJsonImportAttributes(contents, file, specifier =>
1646
+ compiledJsonModule.test(specifier),
1647
+ )
1441
1648
  await writeFileAtomic(file, contents)
1442
1649
  noteDevArtifactWritten(file)
1443
1650
  if (emitCompiledSpecifiersManifest && compiledScriptFilePattern.test(file)) {
@@ -1497,7 +1704,7 @@ function missingCompiledArtifact(entry: string): string | undefined {
1497
1704
  try {
1498
1705
  target = sourcePath.startsWith('file://')
1499
1706
  ? fileURLToPath(sourcePath)
1500
- : path.resolve(path.dirname(file), sourcePath)
1707
+ : path.resolve(path.dirname(file), splitResourceQuery(sourcePath).path)
1501
1708
  } catch {
1502
1709
  return sourcePath
1503
1710
  }
@@ -1668,6 +1875,9 @@ async function writeDevModuleUncached(
1668
1875
  clientLayerConditionTarget(config, file, options.conditionTarget),
1669
1876
  )
1670
1877
  : options
1878
+ if (effectiveOptions !== options) {
1879
+ noteReleaseModuleLayer(config, outFile, 'client', effectiveOptions.conditionTarget)
1880
+ }
1671
1881
  // The asset-context link walk is pure I/O against directories nothing below
1672
1882
  // reads, so it overlaps the shaking/scan/child-compile work and is only
1673
1883
  // awaited before the write.
@@ -2212,7 +2422,7 @@ async function assetStubModule(config: ResolvedConfig, resolved: string, profile
2212
2422
  if (existing) return existing
2213
2423
  const next = (async () => {
2214
2424
  const contents =
2215
- getCssExtensions().loadCssModuleForClient(resolved) ??
2425
+ (await serverCssModuleSource(config, resolved)) ??
2216
2426
  (isCssFile(base)
2217
2427
  ? ''
2218
2428
  : isStaticImageFile(base)
@@ -2309,7 +2519,7 @@ function serverAssetPlugin(config: ResolvedConfig): Plugin {
2309
2519
  build.onLoad({ filter: /.*/, namespace: serverAssetNamespace }, async args => {
2310
2520
  // Registry-provided CSS modules (e.g. *.module.scss) export their class
2311
2521
  // map on the server instead of the empty-asset stub.
2312
- const cssModule = getCssExtensions().loadCssModuleForClient(args.path)
2522
+ const cssModule = await serverCssModuleSource(config, args.path)
2313
2523
  return {
2314
2524
  contents:
2315
2525
  cssModule ??
@@ -2347,7 +2557,7 @@ async function resolveDevModuleSpecifier(
2347
2557
  if (
2348
2558
  options.stubClientImports &&
2349
2559
  !isCssFile(target) &&
2350
- !isInside(config.outPath, target) &&
2560
+ !isCompiledOutput(config, target) &&
2351
2561
  existsSync(target) &&
2352
2562
  (await fileHasUseClientDirective(target))
2353
2563
  ) {
@@ -2374,6 +2584,23 @@ async function resolveDevModuleSpecifier(
2374
2584
 
2375
2585
  const { sourcePath, hash } = splitHash(specifier)
2376
2586
 
2587
+ // A release compiles an unclaimed `?query` import's base module; the query keeps its own instance.
2588
+ const queried = splitResourceQuery(sourcePath)
2589
+ if (
2590
+ isReleaseCompile() &&
2591
+ queried.query &&
2592
+ sourcePath.startsWith('.') &&
2593
+ compiledScriptFilePattern.test(queried.path)
2594
+ ) {
2595
+ const from = importer && !importer.startsWith('<') ? importer : file
2596
+ const base = resolveImport(config.root, from, queried.path, config.workspaceRoot)
2597
+ if (base && isInside(config.workspaceRoot, base)) {
2598
+ await writeDevModule(config, base, visited, options)
2599
+ const href = pathToFileHref(await devModulePath(config, base, options))
2600
+ return { path: `${href}${queried.query}${hash}`, external: true }
2601
+ }
2602
+ }
2603
+
2377
2604
  const compatAlias =
2378
2605
  pagesCompatSourceAlias(config, specifier, importer, options) ?? options.aliases[specifier]
2379
2606
  if (compatAlias) {
@@ -2893,7 +3120,7 @@ function localImportTargets(
2893
3120
  // vendor bundle) are already compiled modules Bun imports directly. Feeding one back through the
2894
3121
  // pipeline compiles it a SECOND time, splitting module identity and pulling artifact names into the
2895
3122
  // source graph, where every boot's fresh names look like edits.
2896
- if (resolved && !isInside(config.outPath, resolved)) {
3123
+ if (resolved && !isCompiledOutput(config, resolved)) {
2897
3124
  targets.push({
2898
3125
  target: resolved,
2899
3126
  isLocalSource: sourcePath.startsWith('.') || path.isAbsolute(sourcePath),
@@ -2941,9 +3168,20 @@ async function writeClientReferenceModules(
2941
3168
  return { specifiers, files }
2942
3169
  }
2943
3170
 
3171
+ /** How a release's stub names its source portably (see ClientReferenceModuleOptions.portable). */
3172
+ function portableReference(file: string, sourceFile: string) {
3173
+ const profileRoot = compiledArtifactProfileRoot(file)
3174
+ const anchors = isReleaseCompile() && profileRoot && releaseAnchors.get(path.dirname(profileRoot))
3175
+ const target = anchors && portableTarget(anchors, sourceFile)
3176
+ return target ? { portable: { from: file, target, real: target !== sourceFile } } : {}
3177
+ }
3178
+
2944
3179
  async function writeClientReferenceModule(file: string, sourceFile: string, exportNames: string[]) {
2945
3180
  await mkdir(path.dirname(file), { recursive: true })
2946
- await writeCompiledFile(file, clientReferenceModuleSource(sourceFile, exportNames))
3181
+ await writeCompiledFile(
3182
+ file,
3183
+ clientReferenceModuleSource(sourceFile, exportNames, portableReference(file, sourceFile)),
3184
+ )
2947
3185
  }
2948
3186
 
2949
3187
  async function fileHasUseClientDirective(file: string) {
@@ -2964,6 +3202,20 @@ function isInside(root: string, file: string) {
2964
3202
  return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))
2965
3203
  }
2966
3204
 
3205
+ /** Where an App Router layer's release modules compile to. */
3206
+ export function releaseLayerArtifactDir(config: ResolvedConfig, layer: ReleaseDependencyLayer) {
3207
+ const profile =
3208
+ layer === 'client:client'
3209
+ ? devModuleProfile(clientLayerOptions(config))
3210
+ : devModuleProfile({
3211
+ profile: 'compat',
3212
+ conditionTarget: 'server',
3213
+ externalLoadTarget: externalLoadTargetForConditionTarget('server'),
3214
+ reactServerLayer: true,
3215
+ } as DevModuleOptions)
3216
+ return path.join(cacheRoot(config.outPath), `modules-${profile}`)
3217
+ }
3218
+
2967
3219
  function devModuleProfile(options: DevModuleOptions) {
2968
3220
  if (options.profile === 'client') {
2969
3221
  // Pages-router SSR layers ('pages'/'pages-edge') resolve package exports
@@ -2989,7 +3241,13 @@ function devServerPath(config: ResolvedConfig, file: string, profile: string, ha
2989
3241
  // A compiled `.json` module is JS (esbuild's json loader wraps it in exports),
2990
3242
  // so it must not keep the `.json` name — the runtime would parse the artifact
2991
3243
  // as JSON. Keeping the source name in front of `.js` keeps it collision-free.
2992
- const outExt = ext === '.json' ? '.json.js' : ext
3244
+ // Build output names compiled scripts `.js`, so production imports them without a TS/MDX loader.
3245
+ const outExt =
3246
+ ext === '.json'
3247
+ ? '.json.js'
3248
+ : isReleaseCompile() && /\.(?:[jt]sx?|mdx?)$/.test(ext)
3249
+ ? '.js'
3250
+ : ext
2993
3251
  return path.join(
2994
3252
  cacheRoot(config.outPath),
2995
3253
  profile === 'server' ? 'modules' : `modules-${profile}`,
@@ -3082,6 +3340,15 @@ function isCssFile(file: string) {
3082
3340
  return file.endsWith('.css')
3083
3341
  }
3084
3342
 
3343
+ /** A CSS module's class map as a build's server output imports it; core's plain CSS modules map here. */
3344
+ async function serverCssModuleSource(config: ResolvedConfig, file: string) {
3345
+ const registered = getCssExtensions().loadCssModuleForClient(file)
3346
+ const coreBuild = isReleaseCompile() && !reactCompatEnabled(config)
3347
+ if (registered !== undefined || !coreBuild || !file.endsWith('.module.css')) return registered
3348
+ const { cssModuleMapping } = await import('../css/build')
3349
+ return `export default ${JSON.stringify(await cssModuleMapping(file))};`
3350
+ }
3351
+
3085
3352
  function isStaticImageFile(file: string) {
3086
3353
  return /\.(?:png|jpe?g|gif|webp|avif|svg|ico|bmp)(?:$|[?#])/.test(file)
3087
3354
  }
@@ -3133,9 +3400,20 @@ function resolveLocalImport(config: ResolvedConfig, fromFile: string, specifier:
3133
3400
 
3134
3401
  async function linkAssetContext(config: ResolvedConfig, file: string, profile: string) {
3135
3402
  if (!isInside(config.root, file)) return
3136
- const sourceDirs = ancestorDirs(config.root, path.dirname(file))
3403
+ const sourceDirs = ancestorDirs(config.root, path.dirname(file)).filter(
3404
+ dir => !isCompiledOutput(config, dir),
3405
+ )
3137
3406
  await Promise.all(
3138
- sourceDirs.map(sourceDir => linkDirectoryAssetContext(config, sourceDir, profile)),
3407
+ sourceDirs.map(sourceDir => {
3408
+ if (!isReleaseCompile()) return linkDirectoryAssetContext(config, sourceDir, profile)
3409
+ const key = `${config.outPath}\0${profile}\0${sourceDir}`
3410
+ let linked = linkedAssetContexts.get(key)
3411
+ if (!linked) {
3412
+ linked = linkDirectoryAssetContext(config, sourceDir, profile)
3413
+ linkedAssetContexts.set(key, linked)
3414
+ }
3415
+ return linked
3416
+ }),
3139
3417
  )
3140
3418
  }
3141
3419
 
@@ -3168,6 +3446,14 @@ async function linkDirectoryAssetContext(
3168
3446
  if (!assetContextEntry(config, sourceDir, entry.name, entry.isDirectory())) return
3169
3447
  const source = path.join(sourceDir, entry.name)
3170
3448
  const target = path.join(targetDir, entry.name)
3449
+ // A linked directory below the root (pages-compat's `source-pages`) compiles on its own path:
3450
+ // staging the link would write its artifacts through it, into the source tree.
3451
+ if (
3452
+ entry.isSymbolicLink() &&
3453
+ sourceDir !== config.root &&
3454
+ statSync(source, { throwIfNoEntry: false })?.isDirectory()
3455
+ )
3456
+ return
3171
3457
  // Root-level sibling asset directories are mirrored, not symlinked wholesale: a `dir` symlink
3172
3458
  // would make every path beneath it - including code modules that must be compiled through the
3173
3459
  // alias transform - resolve straight back to raw source, shadowing the staged compile.
@@ -3208,9 +3494,11 @@ async function stagedManifestSource(file: string) {
3208
3494
  async function linkOrCopyAsset(source: string, target: string) {
3209
3495
  const resolvedSource = path.resolve(source)
3210
3496
  // Concurrent walkers keep a repaired link and remove only the stale target.
3497
+ // A release links relatively: moved with its app, it names no build-machine path.
3498
+ const linkTarget = isReleaseCompile() ? path.relative(path.dirname(target), source) : source
3211
3499
  for (let attempt = 0; attempt < 4; attempt += 1) {
3212
3500
  try {
3213
- await symlink(source, target, 'file')
3501
+ await symlink(linkTarget, target, 'file')
3214
3502
  return
3215
3503
  } catch (error) {
3216
3504
  if ((error as NodeJS.ErrnoException).code !== 'EEXIST') break
@@ -3266,7 +3554,8 @@ function assetContextEntry(
3266
3554
  'pages',
3267
3555
  'public',
3268
3556
  'node_modules',
3269
- path.basename(config.outRootPath),
3557
+ // The out dir's top segment (`dist` for `dist/build`): mirroring it would recurse into its own output.
3558
+ path.relative(config.root, config.outRootPath).split(path.sep)[0],
3270
3559
  ]).has(name)
3271
3560
  }
3272
3561