@wular/pnext 0.0.4 → 0.0.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 (395) hide show
  1. package/README.md +22 -16
  2. package/bin/pnext +6 -1
  3. package/config/lint/base.js +7 -7
  4. package/config/ts/base.json +2 -4
  5. package/config/ts/react.json +2 -6
  6. package/package.json +23 -2
  7. package/reference/compat.md +87 -22
  8. package/reference/config.md +40 -93
  9. package/reference/css.md +42 -33
  10. package/reference/dev.md +26 -33
  11. package/reference/env.md +18 -20
  12. package/reference/getting-started.md +132 -0
  13. package/reference/metadata.md +31 -46
  14. package/reference/navigation.md +65 -105
  15. package/reference/performance.md +21 -142
  16. package/reference/rendering.md +78 -75
  17. package/reference/routing.md +97 -107
  18. package/reference/typegen.md +27 -29
  19. package/src/api/cache.ts +36 -37
  20. package/src/api/client-cache.ts +2 -2
  21. package/src/api/client-navigation.ts +121 -114
  22. package/src/api/dynamic.tsx +58 -55
  23. package/src/api/link.tsx +52 -53
  24. package/src/api/navigation.ts +59 -58
  25. package/src/api/server.ts +152 -144
  26. package/src/api/suspense.ts +4 -4
  27. package/src/cli/adapters/vercel-warm.ts +126 -121
  28. package/src/cli/adapters/vercel.ts +437 -443
  29. package/src/cli/analyze.ts +333 -144
  30. package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
  31. package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
  32. package/src/cli/build.ts +976 -978
  33. package/src/cli/create.ts +72 -64
  34. package/src/cli/dev.ts +116 -116
  35. package/src/cli/index.ts +94 -85
  36. package/src/cli/migrate/package-json.ts +73 -71
  37. package/src/cli/migrate/report.ts +32 -33
  38. package/src/cli/migrate/{index.ts → run.ts} +48 -49
  39. package/src/cli/migrate/scan.ts +46 -46
  40. package/src/cli/migrate/spinner.ts +9 -9
  41. package/src/cli/migrate/tsconfig.ts +35 -35
  42. package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
  43. package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
  44. package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
  45. package/src/cli/start.ts +65 -66
  46. package/src/{typegen.ts → cli/typegen.ts} +59 -59
  47. package/src/client/build.ts +802 -768
  48. package/src/client/chunk-fold.ts +245 -240
  49. package/src/client/{paths.ts → chunk-name.ts} +6 -6
  50. package/src/client/entry.ts +162 -147
  51. package/src/client/prebuilt.ts +231 -223
  52. package/src/client/profile.ts +29 -29
  53. package/src/client/react-compiler.ts +20 -15
  54. package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
  55. package/src/client/reference-stub.ts +51 -51
  56. package/src/client/reference.ts +19 -19
  57. package/src/{api → client}/router/events.ts +32 -17
  58. package/src/{api → client}/router/history.ts +16 -16
  59. package/src/{api → client}/router/hub.ts +52 -53
  60. package/src/{api/router.ts → client/router/index.ts} +61 -60
  61. package/src/{api → client}/router/policies.ts +24 -24
  62. package/src/{api → client}/router/runtime.ts +2022 -1995
  63. package/src/{api → client}/router/types.ts +98 -98
  64. package/src/compat/actions/client-plugin.ts +42 -43
  65. package/src/compat/actions/client-stub.ts +14 -14
  66. package/src/compat/actions/{action-client.ts → client.ts} +194 -193
  67. package/src/compat/actions/config.ts +63 -64
  68. package/src/compat/actions/detect.ts +68 -68
  69. package/src/compat/actions/discovery.ts +105 -105
  70. package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
  71. package/src/compat/actions/early-submit.ts +1 -1
  72. package/src/compat/actions/endpoint.ts +198 -198
  73. package/src/compat/actions/extensions.ts +811 -0
  74. package/src/compat/actions/flight.ts +23 -23
  75. package/src/compat/actions/form-state.ts +34 -34
  76. package/src/compat/actions/hoist.ts +382 -236
  77. package/src/compat/actions/ids.ts +7 -12
  78. package/src/compat/actions/index.ts +8 -8
  79. package/src/compat/actions/instances.ts +46 -46
  80. package/src/compat/actions/origin.ts +47 -48
  81. package/src/compat/actions/protocol.ts +22 -22
  82. package/src/compat/actions/registry.ts +19 -19
  83. package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
  84. package/src/compat/actions/rewrite.ts +125 -125
  85. package/src/compat/actions/{action-router.ts → router.ts} +10 -10
  86. package/src/compat/actions/serve.ts +132 -136
  87. package/src/compat/actions/server-tag.ts +4 -4
  88. package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
  89. package/src/compat/actions/unrecognized-error.ts +4 -4
  90. package/src/compat/{index.ts → aliases.ts} +112 -109
  91. package/src/compat/bundler/bun-externals.ts +18 -18
  92. package/src/compat/bundler/cjs-exports.ts +271 -223
  93. package/src/compat/bundler/config.ts +116 -111
  94. package/src/compat/bundler/externals.ts +12 -12
  95. package/src/compat/bundler/import-meta-url.ts +19 -19
  96. package/src/compat/bundler/modularize-imports.ts +36 -33
  97. package/src/compat/bundler/new-url-asset.ts +22 -24
  98. package/src/compat/bundler/optimize-package-imports.ts +111 -107
  99. package/src/compat/bundler/polyfill.ts +28 -28
  100. package/src/compat/bundler/react-compiler.ts +35 -29
  101. package/src/compat/bundler/react-profiler.tsx +11 -11
  102. package/src/compat/bundler/relay-transform.ts +48 -47
  103. package/src/compat/bundler/require-context.ts +119 -113
  104. package/src/compat/bundler/resolve-extensions.ts +19 -19
  105. package/src/compat/bundler/source-cache.ts +25 -25
  106. package/src/compat/bundler/static-imports.ts +7 -7
  107. package/src/compat/bundler/symlink-imports.ts +46 -46
  108. package/src/compat/bundler/tsconfig-paths.ts +13 -15
  109. package/src/compat/bundler/wasm.ts +58 -60
  110. package/src/compat/bundler/webpack-loaders.ts +254 -241
  111. package/src/compat/bundler/worker.ts +101 -104
  112. package/src/compat/cache/build-flags.ts +29 -29
  113. package/src/compat/cache/build-prerender-errors.ts +40 -44
  114. package/src/compat/cache/custom-handler.ts +60 -53
  115. package/src/compat/cache/fetch-patch.ts +240 -240
  116. package/src/compat/cache/handler.ts +27 -31
  117. package/src/compat/cache/modern-handler.ts +148 -126
  118. package/src/compat/cache/resume-data-cache.ts +47 -45
  119. package/src/compat/cache/revalidate.ts +233 -232
  120. package/src/compat/cache/runtime-error.ts +35 -35
  121. package/src/compat/cache/use-cache-transform.ts +442 -417
  122. package/src/compat/cache/use-cache.ts +650 -614
  123. package/src/compat/cache-control.ts +140 -142
  124. package/src/compat/client/base-path.ts +21 -20
  125. package/src/compat/client/css-order.ts +18 -18
  126. package/src/compat/client/errors/bare-boundary.ts +11 -11
  127. package/src/compat/client/errors/control-flow.ts +23 -23
  128. package/src/compat/client/errors/error-boundary.ts +61 -61
  129. package/src/compat/client/errors/global-error.ts +101 -91
  130. package/src/compat/client/errors/install.ts +71 -72
  131. package/src/compat/client/errors/lazy.ts +23 -23
  132. package/src/compat/client/errors/primitive-throw.ts +47 -45
  133. package/src/compat/client/errors/soft-refresh.ts +4 -4
  134. package/src/compat/client/link-status.ts +33 -33
  135. package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
  136. package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
  137. package/src/compat/client/navigation-scroll.ts +63 -63
  138. package/src/compat/client/optimistic-routing.ts +93 -88
  139. package/src/compat/client/prefetch-cache.ts +23 -24
  140. package/src/compat/client/route-announcer.ts +35 -35
  141. package/src/compat/client/segment-cache-policy.ts +20 -20
  142. package/src/compat/client/segment-cache.ts +309 -315
  143. package/src/compat/client/segment-prefetch.ts +127 -132
  144. package/src/compat/client/trailing-slash.ts +5 -4
  145. package/src/compat/css/chunking.ts +113 -116
  146. package/src/compat/css/inline-css.ts +21 -21
  147. package/src/compat/css/lightningcss.ts +37 -38
  148. package/src/compat/css/modules.ts +161 -175
  149. package/src/compat/css/nonce.ts +7 -7
  150. package/src/compat/css/sass-plugin.ts +18 -21
  151. package/src/compat/css/sass.ts +150 -152
  152. package/src/compat/css/styled-jsx-runtime.ts +27 -27
  153. package/src/compat/css/styled-jsx.ts +21 -21
  154. package/src/compat/edge-runtime.ts +27 -27
  155. package/src/compat/{adapter → export}/build-complete.ts +72 -75
  156. package/src/compat/export/client.ts +29 -31
  157. package/src/compat/export/{index.ts → emit.ts} +111 -110
  158. package/src/compat/export/standalone.ts +62 -54
  159. package/src/compat/image-optimizer/cache.ts +51 -49
  160. package/src/compat/image-optimizer/detect.ts +52 -52
  161. package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
  162. package/src/compat/image-optimizer/source.ts +77 -80
  163. package/src/compat/lifecycle/after-scope.ts +26 -26
  164. package/src/compat/lifecycle/after.ts +48 -45
  165. package/src/compat/lifecycle/error-funnel.ts +98 -102
  166. package/src/compat/lifecycle/error-serialize.ts +30 -26
  167. package/src/compat/lifecycle/error-ui.ts +67 -32
  168. package/src/compat/lifecycle/instrumentation-client.ts +36 -38
  169. package/src/compat/lifecycle/instrumentation.ts +85 -85
  170. package/src/compat/lifecycle/node-console.ts +11 -11
  171. package/src/compat/lifecycle/testmode.ts +160 -132
  172. package/src/compat/mdx/compile.ts +60 -57
  173. package/src/compat/mdx/plugin.ts +11 -11
  174. package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
  175. package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
  176. package/src/compat/metadata.ts +75 -81
  177. package/src/compat/next/cache.ts +68 -69
  178. package/src/compat/next/canonical-url.ts +9 -9
  179. package/src/compat/next/client-cache.ts +19 -19
  180. package/src/compat/next/client-navigation.ts +112 -116
  181. package/src/compat/next/client-only.ts +1 -1
  182. package/src/compat/next/client-script.tsx +99 -93
  183. package/src/compat/next/client-server.ts +10 -10
  184. package/src/compat/next/config-loader.ts +176 -173
  185. package/src/compat/next/config.ts +7 -7
  186. package/src/compat/next/constants.cjs +6 -6
  187. package/src/compat/next/constants.ts +6 -6
  188. package/src/compat/next/custom-server.ts +26 -24
  189. package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
  190. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
  191. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
  192. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
  193. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
  194. package/src/compat/next/dynamic.tsx +21 -18
  195. package/src/compat/next/error.tsx +52 -52
  196. package/src/compat/next/font/cache.ts +74 -74
  197. package/src/compat/next/font/google.ts +2 -2
  198. package/src/compat/next/font/index.ts +1 -1
  199. package/src/compat/next/font/local.ts +3 -3
  200. package/src/compat/next/font/runtime-client.ts +17 -15
  201. package/src/compat/next/font/runtime.ts +443 -408
  202. package/src/compat/next/font/shared.ts +120 -108
  203. package/src/compat/next/form.tsx +63 -63
  204. package/src/compat/next/head.tsx +2 -2
  205. package/src/compat/next/headers.ts +103 -95
  206. package/src/compat/next/image/client.tsx +220 -0
  207. package/src/compat/next/image/config.ts +56 -58
  208. package/src/compat/next/image/optimizer.ts +40 -36
  209. package/src/compat/next/image/patterns.ts +37 -40
  210. package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
  211. package/src/compat/next/image/shared.ts +65 -57
  212. package/src/compat/next/image/static-metadata.ts +98 -107
  213. package/src/compat/next/image/validate.ts +79 -88
  214. package/src/compat/next/image.tsx +19 -23
  215. package/src/compat/next/index.ts +1 -1
  216. package/src/compat/next/legacy-image.tsx +59 -60
  217. package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
  218. package/src/compat/next/link.tsx +158 -158
  219. package/src/compat/next/navigation.cjs +12 -3
  220. package/src/compat/next/navigation.ts +48 -50
  221. package/src/compat/next/offline.ts +27 -27
  222. package/src/compat/next/og.ts +121 -124
  223. package/src/compat/next/preferred-region.ts +13 -14
  224. package/src/compat/next/redirects.ts +58 -56
  225. package/src/compat/next/resource-hints.ts +73 -76
  226. package/src/compat/next/rewrites.ts +130 -133
  227. package/src/compat/next/root-params.ts +45 -45
  228. package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
  229. package/src/compat/next/router.cjs +4 -2
  230. package/src/compat/next/router.ts +58 -61
  231. package/src/compat/next/script.tsx +108 -108
  232. package/src/compat/next/server-only.ts +1 -1
  233. package/src/compat/next/server.ts +19 -19
  234. package/src/compat/next/svgr.ts +18 -17
  235. package/src/compat/next/telemetry.ts +24 -24
  236. package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
  237. package/src/compat/next/user-agent.ts +53 -49
  238. package/src/compat/next/web-vitals.ts +22 -24
  239. package/src/compat/otel/api.ts +41 -41
  240. package/src/compat/otel/client-trace-metadata.ts +25 -27
  241. package/src/compat/otel/fetch-span.ts +29 -29
  242. package/src/compat/otel/tracer.ts +331 -331
  243. package/src/compat/pages/api.ts +456 -0
  244. package/src/compat/pages/client-plugin.ts +36 -36
  245. package/src/compat/pages/router-state.ts +34 -34
  246. package/src/compat/pages/{index.ts → router.ts} +130 -135
  247. package/src/compat/ppr/io.ts +12 -12
  248. package/src/compat/ppr/missing-root-params.ts +34 -38
  249. package/src/compat/ppr/root-params-scan.ts +66 -66
  250. package/src/compat/ppr/root-params-transform.ts +24 -26
  251. package/src/compat/ppr/root-params.ts +30 -30
  252. package/src/compat/ppr/segment-config-incompat.ts +6 -7
  253. package/src/compat/protocol.ts +71 -70
  254. package/src/compat/react/action-state.ts +47 -48
  255. package/src/compat/react/client-lite.ts +15 -15
  256. package/src/compat/react/client.ts +4 -4
  257. package/src/compat/react/compiler-runtime.ts +11 -11
  258. package/src/compat/react/dom-client.ts +44 -44
  259. package/src/compat/react/dom-react-server.ts +10 -16
  260. package/src/compat/react/dom-server.ts +10 -10
  261. package/src/compat/react/dom.ts +52 -52
  262. package/src/compat/react/hooks-extra.ts +34 -35
  263. package/src/compat/react/parity.ts +64 -61
  264. package/src/compat/react/preact.ts +81 -82
  265. package/src/compat/react/react-server.ts +28 -28
  266. package/src/compat/react/router-shim.ts +1 -1
  267. package/src/compat/react/server-component-use.ts +8 -8
  268. package/src/compat/react/server-inserted-html.ts +30 -31
  269. package/src/compat/react/server.ts +53 -55
  270. package/src/compat/react/use.ts +32 -32
  271. package/src/compat/react/view-transition.ts +20 -20
  272. package/src/compat/register/actions.ts +35 -824
  273. package/src/compat/register/boot.ts +41 -41
  274. package/src/compat/register/build-tier.ts +5 -5
  275. package/src/compat/register/build.ts +74 -70
  276. package/src/compat/register/bundler.ts +161 -156
  277. package/src/compat/register/cache.ts +20 -20
  278. package/src/compat/register/client-errors.ts +3 -3
  279. package/src/compat/register/config.ts +6 -6
  280. package/src/compat/register/css-extras.ts +30 -34
  281. package/src/compat/register/edge-runtime.ts +3 -3
  282. package/src/compat/register/errors.ts +10 -12
  283. package/src/compat/register/export.ts +16 -16
  284. package/src/compat/register/font.ts +11 -11
  285. package/src/compat/register/hooks.ts +2 -2
  286. package/src/compat/register/image.ts +40 -40
  287. package/src/compat/register/index.ts +59 -62
  288. package/src/compat/register/instrumentation-client.ts +10 -10
  289. package/src/compat/register/lifecycle.ts +25 -28
  290. package/src/compat/register/mdx.ts +10 -10
  291. package/src/compat/register/middleware.ts +226 -16
  292. package/src/compat/register/otel.ts +80 -88
  293. package/src/compat/register/pages-api.ts +10 -463
  294. package/src/compat/register/ppr.ts +16 -16
  295. package/src/compat/register/protocol.ts +17 -18
  296. package/src/compat/register/proxy.ts +49 -51
  297. package/src/compat/register/render.ts +79 -76
  298. package/src/compat/register/routing.ts +162 -159
  299. package/src/compat/register/segment.ts +24 -1897
  300. package/src/compat/register/static-image.ts +3 -3
  301. package/src/compat/register/{misc.ts → taint.ts} +9 -9
  302. package/src/compat/register/typed-routes.ts +14 -14
  303. package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
  304. package/src/compat/register/validation.ts +18 -18
  305. package/src/compat/segment/loading-boundary.ts +43 -45
  306. package/src/compat/segment/page-slot.ts +69 -69
  307. package/src/compat/segment/serve.ts +1884 -0
  308. package/src/compat/segment/tree.ts +113 -112
  309. package/src/compat/segment/vary-key.ts +38 -38
  310. package/src/compat/segment/vary-params.ts +138 -142
  311. package/src/compat/static-params.ts +14 -12
  312. package/src/compat/tsconfig-defaults.ts +87 -91
  313. package/src/compat/typecheck/{index.ts → check.ts} +234 -212
  314. package/src/compat/typecheck/worker.ts +15 -12
  315. package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
  316. package/src/compat/typed-routes/manifest.ts +174 -170
  317. package/src/compat/typed-routes/typegen.ts +127 -110
  318. package/src/compat/validation/errors.ts +16 -19
  319. package/src/compat/validation/prerender-diagnostics.ts +521 -504
  320. package/src/compat/validation/{index.ts → validate.ts} +647 -648
  321. package/src/compat-bootstrap.ts +16 -16
  322. package/src/config.ts +86 -69
  323. package/src/css/build.ts +230 -227
  324. package/src/css/postcss.ts +79 -80
  325. package/src/css/worker.ts +14 -15
  326. package/src/dev/client-actions.ts +10 -10
  327. package/src/dev/client-chunk-store.ts +27 -27
  328. package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
  329. package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
  330. package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
  331. package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
  332. package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
  333. package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
  334. package/src/dev/server.ts +804 -820
  335. package/src/env.ts +46 -43
  336. package/src/extensions.ts +491 -478
  337. package/src/index.ts +8 -8
  338. package/src/internal.ts +20 -23
  339. package/src/{islands → render}/boundary-error.ts +3 -3
  340. package/src/render/hooks.ts +71 -71
  341. package/src/render/island-context.ts +14 -14
  342. package/src/render/metadata.ts +310 -310
  343. package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
  344. package/src/{ppr.ts → render/ppr.ts} +244 -245
  345. package/src/render/renderer.ts +2087 -2082
  346. package/src/render/resource-hints.ts +16 -17
  347. package/src/render/slots.tsx +224 -235
  348. package/src/{islands → render}/static-children.ts +9 -12
  349. package/src/{islands → render}/static-slots.ts +37 -37
  350. package/src/{cache/context.ts → request/cache.ts} +20 -20
  351. package/src/request/context.ts +107 -107
  352. package/src/{dynamic/source.ts → resolve/dynamic.ts} +139 -139
  353. package/src/resolve/engine.ts +90 -77
  354. package/src/resolve/imports.ts +475 -463
  355. package/src/resolve/scan-facts.ts +318 -296
  356. package/src/resolve/source-text.ts +37 -37
  357. package/src/{dynamic → resolve}/tree-shake.ts +132 -128
  358. package/src/routing/forwarded.ts +19 -19
  359. package/src/routing/handler.ts +84 -91
  360. package/src/routing/href.ts +87 -70
  361. package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
  362. package/src/{proxy.ts → routing/proxy.ts} +306 -312
  363. package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
  364. package/src/routing/routes.ts +827 -815
  365. package/src/routing/slots.ts +164 -160
  366. package/src/runtime/loader.ts +954 -0
  367. package/src/{dev → runtime}/module-cache.ts +309 -287
  368. package/src/{dev → runtime}/module-generations.ts +9 -9
  369. package/src/{dev → runtime}/module-transform.ts +81 -72
  370. package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
  371. package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
  372. package/src/runtime/vendor.ts +428 -406
  373. package/src/styles.d.ts +9 -0
  374. package/src/types.ts +324 -335
  375. package/src/utils/ansi.ts +5 -5
  376. package/src/utils/{source.ts → code.ts} +15 -12
  377. package/src/utils/content-type.ts +3 -3
  378. package/src/utils/decode.ts +2 -2
  379. package/src/utils/dev-profile.ts +13 -13
  380. package/src/utils/error-log.ts +6 -6
  381. package/src/utils/esbuild.ts +18 -18
  382. package/src/utils/fs-cache.ts +13 -13
  383. package/src/utils/fs.ts +57 -49
  384. package/src/utils/html.ts +20 -24
  385. package/src/utils/native-require.ts +8 -8
  386. package/src/utils/serialize.ts +139 -146
  387. package/src/utils/verbose.ts +18 -18
  388. package/reference/overview.md +0 -35
  389. package/src/cli/analyze-print.ts +0 -181
  390. package/src/compat/middleware/manifest.ts +0 -210
  391. package/src/compat/next/image-client.tsx +0 -215
  392. package/src/compat/next/link-usage.ts +0 -29
  393. package/src/css/index.ts +0 -2
  394. package/src/render/index.ts +0 -1
  395. package/src/style-modules.d.ts +0 -9
@@ -1,30 +1,29 @@
1
- import { builtinModules } from 'node:module';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
- import { existsSync, readFileSync, type Dirent } from 'node:fs';
5
- import { fileURLToPath } from 'node:url';
6
- import { copyFile, mkdir, readdir, symlink, writeFile } from 'node:fs/promises';
7
- import type { OnResolveResult, Plugin } from 'esbuild';
8
- import { build } from '../utils/esbuild';
9
- import { drainPreplanBuilds, vendorTraceEnabled, vendorTraceRow } from '../runtime/vendor';
1
+ import { builtinModules } from 'node:module'
2
+ import os from 'node:os'
3
+ import path from 'node:path'
4
+ import { existsSync, readFileSync, type Dirent } from 'node:fs'
5
+ import { fileURLToPath } from 'node:url'
6
+ import { copyFile, mkdir, readdir, symlink, writeFile } from 'node:fs/promises'
7
+ import type { OnResolveResult, Plugin } from 'esbuild'
8
+ import { build } from '../utils/esbuild'
9
+ import { drainPreplanBuilds, vendorTraceEnabled, vendorTraceRow } from '../runtime/vendor'
10
10
  import {
11
11
  clientReferenceExportNames,
12
12
  clientReferenceModuleSource,
13
13
  hasUseClientDirective,
14
- } from '../client/reference-stub';
15
- import { getClientActionBundler } from './client-actions';
16
- import { deferredDynamicImportSpecifiers, devDynamicSplitEnabled } from '../dynamic/source';
17
- import { noteModuleGeneration, noteModuleImported } from './module-generations';
14
+ } from '../client/reference-stub'
15
+ import { getClientActionBundler } from '../dev/client-actions'
16
+ import { deferredDynamicImportSpecifiers, devDynamicSplitEnabled } from '../resolve/dynamic'
17
+ import { noteModuleGeneration, noteModuleImported } from './module-generations'
18
18
  import {
19
- externalServerPackageHref,
20
19
  rewriteServerSource,
21
- runtimeServerImportTarget,
22
20
  serverBundleTargetForRuntime,
23
21
  serverBundleRequireConditions,
24
22
  type ServerBundleTarget,
25
- } from '../runtime/server';
26
- import { writeFileAtomic } from '../utils/fs';
27
- import { createHash } from 'node:crypto';
23
+ } from './loader'
24
+ import { externalServerPackageHref, runtimeServerImportTarget } from '../runtime/vendor-build'
25
+ import { writeFileAtomic } from '../utils/fs'
26
+ import { createHash } from 'node:crypto'
28
27
  import {
29
28
  extraLoadableExtensions,
30
29
  getAssetExtensions,
@@ -32,15 +31,15 @@ import {
32
31
  getCssExtensions,
33
32
  getImportAliasExtensions,
34
33
  serverDefineOptions,
35
- } from '../extensions';
34
+ } from '../extensions'
36
35
  import {
37
36
  frameworkRuntimeAliasEntries,
38
37
  pathToFileHref,
39
38
  pnextAliases,
40
39
  type CompatAliasTarget,
41
40
  type ResolvedConfig,
42
- } from '../config';
43
- import { readText, toPosixPath } from '../utils/fs';
41
+ } from '../config'
42
+ import { readText, toPosixPath } from '../utils/fs'
44
43
  import {
45
44
  type ExternalLoadTarget,
46
45
  externalPackageImportTarget,
@@ -50,8 +49,8 @@ import {
50
49
  resolveImport,
51
50
  resolveExternalLoadTarget,
52
51
  resolvePackageSpecifier,
53
- } from '../resolve/imports';
54
- import { importSpecifiers, rewriteSpecifierLiterals } from '../resolve/scan-facts';
52
+ } from '../resolve/imports'
53
+ import { importSpecifiers, rewriteSpecifierLiterals } from '../resolve/scan-facts'
55
54
  import {
56
55
  cacheRoot,
57
56
  devArtifactUsable,
@@ -60,27 +59,28 @@ import {
60
59
  devSourceIdentity,
61
60
  noteDevArtifactWritten,
62
61
  type DevModuleCache,
63
- } from './module-cache';
64
- import { cachedRouteBundlePath, saveRouteBundlePath } from './route-bundle-key-cache';
62
+ } from './module-cache'
63
+ import { cachedRouteBundlePath, saveRouteBundlePath } from '../dev/restart/route-bundle-key'
65
64
  import {
66
65
  outputSpecifiers,
67
66
  spliceSource,
68
67
  transformBailReason,
69
68
  transformServerModule,
70
69
  type SpecifierKind,
71
- } from './module-transform';
72
- import { findShakeableDynamicImports } from '../dynamic/tree-shake';
73
- import type { RouteManifestEntry } from '../types';
74
- import { uniqueIdentifier } from '../utils/source';
70
+ } from './module-transform'
71
+ import { findShakeableDynamicImports } from '../resolve/tree-shake'
72
+ import type { RouteManifestEntry } from '../types'
73
+ import { uniqueIdentifier } from '../utils/code'
74
+ import { formatDuration } from '../utils/verbose'
75
75
 
76
- type AliasMap = Record<string, string>;
76
+ type AliasMap = Record<string, string>
77
77
 
78
78
  function nextCompatEnabled(config: ResolvedConfig) {
79
- return Boolean(config.compat?.next);
79
+ return Boolean(config.compat?.next)
80
80
  }
81
81
 
82
82
  function reactCompatEnabled(config: ResolvedConfig) {
83
- return Boolean(config.compat?.next || config.compat?.react || config.compat?.reactCompiler);
83
+ return Boolean(config.compat?.next || config.compat?.react || config.compat?.reactCompiler)
84
84
  }
85
85
 
86
86
  function coreAliases(config: ResolvedConfig, target: CompatAliasTarget): AliasMap {
@@ -88,67 +88,73 @@ function coreAliases(config: ResolvedConfig, target: CompatAliasTarget): AliasMa
88
88
  ...frameworkRuntimeAliasEntries(),
89
89
  ...pnextAliases(target),
90
90
  ...getImportAliasExtensions().aliases(config, target),
91
- };
91
+ }
92
92
  }
93
93
 
94
94
  // Cap concurrent esbuild builds: the recursive module graph can otherwise fire
95
95
  // hundreds of builds at once and saturate the host.
96
- const BUILD_CONCURRENCY = Math.max(2, (os.availableParallelism?.() ?? os.cpus().length) - 1);
97
- let buildActive = 0;
98
- const buildQueue: (() => void)[] = [];
99
- const moduleBuilds = new Map<string, Promise<string>>();
100
- const routeBundleBuilds = new Map<string, Promise<string>>();
96
+ const BUILD_CONCURRENCY = Math.max(2, (os.availableParallelism?.() ?? os.cpus().length) - 1)
97
+ let buildActive = 0
98
+ const buildQueue: (() => void)[] = []
99
+ const moduleBuilds = new Map<string, Promise<string>>()
100
+ const routeBundleBuilds = new Map<string, Promise<string>>()
101
101
 
102
102
  // PNEXT_DEV_MODULE_STATS=1: attribute every per-module compile to its profile,
103
103
  // so cross-profile duplicate work is countable rather than inferred. Counts are
104
104
  // load-insensitive, which is what makes them usable on a contended box.
105
- interface ModuleStat { profile: string; ms: number; esbuild: boolean }
105
+ interface ModuleStat {
106
+ profile: string
107
+ ms: number
108
+ esbuild: boolean
109
+ }
106
110
  // `${profile}:${file}` of modules that fell off the oxc fast path onto esbuild.
107
- const esbuildFallbacks = new Set<string>();
111
+ const esbuildFallbacks = new Set<string>()
108
112
  const moduleStats: Map<string, ModuleStat[]> | undefined =
109
113
  // eslint-disable-next-line turbo/no-undeclared-env-vars
110
- process.env.PNEXT_DEV_MODULE_STATS === '1' ? new Map() : undefined;
111
- let moduleStatsFlush: Timer | undefined;
114
+ process.env.PNEXT_DEV_MODULE_STATS === '1' ? new Map() : undefined
115
+ let moduleStatsFlush: Timer | undefined
112
116
 
113
117
  function noteModuleCompile(file: string, profile: string, ms: number, esbuild: boolean) {
114
- if (!moduleStats) return;
115
- const entries = moduleStats.get(file) ?? [];
116
- entries.push({ profile, ms, esbuild });
117
- moduleStats.set(file, entries);
118
+ if (!moduleStats) return
119
+ const entries = moduleStats.get(file) ?? []
120
+ entries.push({ profile, ms, esbuild })
121
+ moduleStats.set(file, entries)
118
122
  // Debounced: stages settle after the response, so a fixed point in the
119
123
  // request would miss the preloads this is meant to attribute.
120
- clearTimeout(moduleStatsFlush);
121
- moduleStatsFlush = setTimeout(reportModuleStats, 500);
122
- moduleStatsFlush.unref?.();
124
+ clearTimeout(moduleStatsFlush)
125
+ moduleStatsFlush = setTimeout(reportModuleStats, 500)
126
+ moduleStatsFlush.unref?.()
123
127
  }
124
128
 
125
129
  function reportModuleStats() {
126
- if (!moduleStats) return;
127
- const byProfile = new Map<string, { files: number; ms: number; esbuild: number }>();
128
- let total = 0;
129
- let duplicated = 0;
130
- let duplicateCompiles = 0;
131
- let duplicateMs = 0;
130
+ if (!moduleStats) return
131
+ const byProfile = new Map<string, { files: number; ms: number; esbuild: number }>()
132
+ let total = 0
133
+ let duplicated = 0
134
+ let duplicateCompiles = 0
135
+ let duplicateMs = 0
132
136
  for (const entries of moduleStats.values()) {
133
- total += entries.length;
134
- const profiles = new Set(entries.map(entry => entry.profile));
137
+ total += entries.length
138
+ const profiles = new Set(entries.map(entry => entry.profile))
135
139
  if (profiles.size > 1) {
136
- duplicated += 1;
137
- duplicateCompiles += entries.length - 1;
138
- duplicateMs += entries.slice(1).reduce((sum, entry) => sum + entry.ms, 0);
140
+ duplicated += 1
141
+ duplicateCompiles += entries.length - 1
142
+ duplicateMs += entries.slice(1).reduce((sum, entry) => sum + entry.ms, 0)
139
143
  }
140
144
  for (const entry of entries) {
141
- const bucket = byProfile.get(entry.profile) ?? { files: 0, ms: 0, esbuild: 0 };
142
- bucket.files += 1;
143
- bucket.ms += entry.ms;
144
- bucket.esbuild += entry.esbuild ? 1 : 0;
145
- byProfile.set(entry.profile, bucket);
145
+ const bucket = byProfile.get(entry.profile) ?? { files: 0, ms: 0, esbuild: 0 }
146
+ bucket.files += 1
147
+ bucket.ms += entry.ms
148
+ bucket.esbuild += entry.esbuild ? 1 : 0
149
+ byProfile.set(entry.profile, bucket)
146
150
  }
147
151
  }
148
152
  const rows = [...byProfile.entries()]
149
153
  .sort((a, b) => b[1].ms - a[1].ms)
150
- .map(([profile, b]) =>
151
- ` ${profile}: ${b.files} modules, ${b.ms.toFixed(0)} ms nested-wall, ${b.esbuild} via esbuild`);
154
+ .map(
155
+ ([profile, b]) =>
156
+ ` ${profile}: ${b.files} modules, ${b.ms.toFixed(0)} ms nested-wall, ${b.esbuild} via esbuild`,
157
+ )
152
158
  console.error(
153
159
  [
154
160
  `dev-module-stats ${moduleStats.size} distinct files, ${total} compiles`,
@@ -156,63 +162,64 @@ function reportModuleStats() {
156
162
  ` cross-profile: ${duplicated} files compiled in >1 profile, ` +
157
163
  `${duplicateCompiles} redundant compiles, ${duplicateMs.toFixed(0)} ms nested-wall`,
158
164
  ].join('\n'),
159
- );
165
+ )
160
166
  // PNEXT_DEV_MODULE_STATS_OUT=<path>: also dump the per-file list, so a floor
161
167
  // bench can replay the exact module set a route compiles in each profile.
162
168
  // eslint-disable-next-line turbo/no-undeclared-env-vars
163
- const out = process.env.PNEXT_DEV_MODULE_STATS_OUT;
164
- if (!out) return;
169
+ const out = process.env.PNEXT_DEV_MODULE_STATS_OUT
170
+ if (!out) return
165
171
  const files = [...moduleStats.entries()].flatMap(([file, entries]) =>
166
- entries.map(entry => ({ file, profile: entry.profile, ms: entry.ms, esbuild: entry.esbuild })));
167
- void writeFile(out, JSON.stringify(files, null, 0)).catch(() => undefined);
172
+ entries.map(entry => ({ file, profile: entry.profile, ms: entry.ms, esbuild: entry.esbuild })),
173
+ )
174
+ void writeFile(out, JSON.stringify(files, null, 0)).catch(() => undefined)
168
175
  }
169
- const routeModuleLoaders = new Map<string, Promise<DevRouteModuleLoaders>>();
176
+ const routeModuleLoaders = new Map<string, Promise<DevRouteModuleLoaders>>()
170
177
 
171
178
  // A module that throws while evaluating stays failed in Bun's registry for the process lifetime, and
172
179
  // re-importing it surfaces downstream symptoms (a missing bundle entry, TDZ on an export) instead of
173
180
  // the original error. Record the first failure per compiled href and re-throw THAT on every later
174
181
  // request. Compiled hrefs are content-addressed, so a save that fixes the module yields a new href
175
182
  // and the entry is never consulted again.
176
- const moduleEvalErrors = new Map<string, unknown>();
183
+ const moduleEvalErrors = new Map<string, unknown>()
177
184
 
178
185
  /** Import a compiled dev module, re-throwing its first evaluation error on later imports. */
179
186
  export async function importDevModule<T>(href: string): Promise<T> {
180
- if (moduleEvalErrors.has(href)) throw moduleEvalErrors.get(href);
187
+ if (moduleEvalErrors.has(href)) throw moduleEvalErrors.get(href)
181
188
  // Pre-planned vendor builds are enqueued without an awaiter; nothing
182
189
  // evaluates until they all settle (no-op when the pipeline is empty).
183
- await drainPreplanBuilds();
190
+ await drainPreplanBuilds()
184
191
  try {
185
- const module = (await import(href)) as T;
186
- noteModuleImported(href);
187
- return module;
192
+ const module = (await import(href)) as T
193
+ noteModuleImported(href)
194
+ return module
188
195
  } catch (error) {
189
- moduleEvalErrors.set(href, error);
190
- throw error;
196
+ moduleEvalErrors.set(href, error)
197
+ throw error
191
198
  }
192
199
  }
193
- const extensionlessDynamicImportExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts']);
200
+ const extensionlessDynamicImportExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts'])
194
201
  const externalLoadNamespaces = {
195
202
  server: 'pnext-route-external-server',
196
203
  client: 'pnext-route-external-client',
197
204
  module: 'pnext-module-external',
198
- } as const;
205
+ } as const
199
206
 
200
- const moduleCaches = new WeakMap<ResolvedConfig, DevModuleCache>();
207
+ const moduleCaches = new WeakMap<ResolvedConfig, DevModuleCache>()
201
208
 
202
209
  /**
203
- * The content-addressing cache behind every compiled path (dev/module-cache.ts).
210
+ * The content-addressing cache behind every compiled path (runtime/module-cache.ts).
204
211
  * Artifacts are named by a hash of their whole source graph, so this is also the
205
212
  * thing that decides what a save invalidates.
206
213
  */
207
214
  export function devModuleGraph(config: ResolvedConfig): DevModuleCache {
208
- const existing = moduleCaches.get(config);
209
- if (existing) return existing;
215
+ const existing = moduleCaches.get(config)
216
+ if (existing) return existing
210
217
  const cache = devModuleCache(config, {
211
218
  compileKey: devCompileKey(config),
212
219
  edges: (file, source) => graphEdges(config, file, source),
213
- });
214
- moduleCaches.set(config, cache);
215
- return cache;
220
+ })
221
+ moduleCaches.set(config, cache)
222
+ return cache
216
223
  }
217
224
 
218
225
  /**
@@ -225,18 +232,22 @@ export function devModuleGraph(config: ResolvedConfig): DevModuleCache {
225
232
  * registers, and it is memoized from the first read.
226
233
  */
227
234
  export async function warmDevModulePipeline(config: ResolvedConfig) {
228
- const source = 'import "./pnext-warm-import";\nexport const warm: number = 1;\n';
229
- const file = path.join(config.root, 'pnext-warm.ts');
235
+ const source = 'import "./pnext-warm-import";\nexport const warm: number = 1;\n'
236
+ const file = path.join(config.root, 'pnext-warm.ts')
230
237
  await Promise.allSettled([
231
238
  // esbuild spawns its Go service child and handshakes on the first build.
232
- build({ stdin: { contents: '0', loader: 'ts', resolveDir: config.root }, write: false, logLevel: 'silent' }),
239
+ build({
240
+ stdin: { contents: '0', loader: 'ts', resolveDir: config.root },
241
+ write: false,
242
+ logLevel: 'silent',
243
+ }),
233
244
  Promise.resolve().then(() => {
234
- importSpecifiers(source, file); // oxc-parser
235
- transformServerModule(source, file, serverDefineOptions().define); // oxc-transform
236
- resolveLocalImport(config, file, './pnext-warm-import'); // oxc-resolver factory
237
- devModuleGraph(config); // cache marker + graph.json + framework fingerprint
245
+ importSpecifiers(source, file) // oxc-parser
246
+ transformServerModule(source, file, serverDefineOptions().define) // oxc-transform
247
+ resolveLocalImport(config, file, './pnext-warm-import') // oxc-resolver factory
248
+ devModuleGraph(config) // cache marker + graph.json + framework fingerprint
238
249
  }),
239
- ]);
250
+ ])
240
251
  }
241
252
 
242
253
  /** Compile inputs that are not sources: a change to any of them renames everything. */
@@ -249,7 +260,7 @@ function devCompileKey(config: ResolvedConfig) {
249
260
  nextConfig: nextConfigFingerprint(config),
250
261
  define: serverDefineOptions().define ?? null,
251
262
  aliases: [...devAliasKeys(config)].sort(),
252
- });
263
+ })
253
264
  }
254
265
 
255
266
  // next.config knobs (modularizeImports, resolve aliases, compiler options) shape compiled output, and only
@@ -263,35 +274,35 @@ const NEXT_CONFIG_BASENAMES = [
263
274
  'next.config.js',
264
275
  'next.config.mjs',
265
276
  'next.config.cjs',
266
- ];
277
+ ]
267
278
 
268
279
  function nextConfigFingerprint(config: ResolvedConfig) {
269
- if (!config.compat?.next) return '';
280
+ if (!config.compat?.next) return ''
270
281
  for (const name of NEXT_CONFIG_BASENAMES) {
271
- const candidate = path.join(config.root, name);
272
- if (!existsSync(candidate)) continue;
282
+ const candidate = path.join(config.root, name)
283
+ if (!existsSync(candidate)) continue
273
284
  try {
274
- return `${name}:${Bun.hash(readFileSync(candidate)).toString(36)}`;
285
+ return `${name}:${Bun.hash(readFileSync(candidate)).toString(36)}`
275
286
  } catch {
276
- return name;
287
+ return name
277
288
  }
278
289
  }
279
- return '';
290
+ return ''
280
291
  }
281
292
 
282
- const aliasKeys = new WeakMap<ResolvedConfig, Set<string>>();
293
+ const aliasKeys = new WeakMap<ResolvedConfig, Set<string>>()
283
294
 
284
295
  /** Specifiers every profile rewrites at build time — never source edges. */
285
296
  function devAliasKeys(config: ResolvedConfig) {
286
- const existing = aliasKeys.get(config);
287
- if (existing) return existing;
297
+ const existing = aliasKeys.get(config)
298
+ if (existing) return existing
288
299
  const keys = new Set([
289
300
  ...Object.keys(coreAliases(config, 'server')),
290
301
  ...Object.keys(getImportAliasExtensions().reactServerLayerAliases(config)),
291
302
  ...Object.keys(clientSsrAliases(config)),
292
- ]);
293
- aliasKeys.set(config, keys);
294
- return keys;
303
+ ])
304
+ aliasKeys.set(config, keys)
305
+ return keys
295
306
  }
296
307
 
297
308
  /**
@@ -300,45 +311,47 @@ function devAliasKeys(config: ResolvedConfig) {
300
311
  * its content, so assets and client components are edges here even where the compile walk skips them.
301
312
  */
302
313
  function graphEdges(config: ResolvedConfig, file: string, source: string) {
303
- const aliases = devAliasKeys(config);
304
- const imports: string[] = [];
305
- const packages: string[] = [];
314
+ const aliases = devAliasKeys(config)
315
+ const imports: string[] = []
316
+ const packages: string[] = []
306
317
  for (const specifier of importSpecifiers(source, file)) {
307
- const { sourcePath } = splitHash(specifier);
308
- if (aliases.has(specifier) || aliases.has(sourcePath)) continue;
309
- const resolved = resolveLocalImport(config, file, sourcePath.replace(/\?.*$/, ''));
310
- const isLocalSource = sourcePath.startsWith('.') || path.isAbsolute(sourcePath);
318
+ const { sourcePath } = splitHash(specifier)
319
+ if (aliases.has(specifier) || aliases.has(sourcePath)) continue
320
+ const resolved = resolveLocalImport(config, file, sourcePath.replace(/\?.*$/, ''))
321
+ const isLocalSource = sourcePath.startsWith('.') || path.isAbsolute(sourcePath)
311
322
  // Compiled output is never a source of the graph that produced it.
312
- if (resolved && isInside(config.outPath, resolved)) continue;
323
+ if (resolved && isInside(config.outPath, resolved)) continue
313
324
  if (resolved && (isLocalSource || isInside(config.workspaceRoot, resolved))) {
314
- imports.push(path.resolve(resolved));
315
- continue;
325
+ imports.push(path.resolve(resolved))
326
+ continue
316
327
  }
317
328
  // Registry package demand: recorded here so a route's vendor bundles can be
318
329
  // started as their own stage instead of blocking the module pass at first
319
330
  // use.
320
- if (!isLocalSource && isPackageSpecifier(sourcePath)) packages.push(sourcePath);
331
+ if (!isLocalSource && isPackageSpecifier(sourcePath)) packages.push(sourcePath)
321
332
  }
322
- return { imports, packages, client: hasUseClientDirective(source) };
333
+ return { imports, packages, client: hasUseClientDirective(source) }
323
334
  }
324
335
 
325
336
  function withBuildSlot<T>(task: () => Promise<T>): Promise<T> {
326
337
  return new Promise<T>((resolve, reject) => {
327
338
  const run = () => {
328
- buildActive += 1;
329
- task().then(resolve, reject).finally(() => {
330
- buildActive -= 1;
331
- buildQueue.shift()?.();
332
- });
333
- };
334
- if (buildActive < BUILD_CONCURRENCY) run();
335
- else buildQueue.push(run);
336
- });
339
+ buildActive += 1
340
+ task()
341
+ .then(resolve, reject)
342
+ .finally(() => {
343
+ buildActive -= 1
344
+ buildQueue.shift()?.()
345
+ })
346
+ }
347
+ if (buildActive < BUILD_CONCURRENCY) run()
348
+ else buildQueue.push(run)
349
+ })
337
350
  }
338
351
 
339
352
  export interface DevServerModuleOptions {
340
- conditionTarget?: ServerBundleTarget;
341
- externalLoadTarget?: ExternalLoadTarget;
353
+ conditionTarget?: ServerBundleTarget
354
+ externalLoadTarget?: ExternalLoadTarget
342
355
  /**
343
356
  * True for modules compiled under the true `react-server` layer (App RSC,
344
357
  * route handlers, proxy/middleware): layers stricter React alias overrides
@@ -346,7 +359,7 @@ export interface DevServerModuleOptions {
346
359
  * Pages/api and other server modules keep the base aliases (full-hooks
347
360
  * `react`, kept for backward compatibility).
348
361
  */
349
- reactServerLayer?: boolean;
362
+ reactServerLayer?: boolean
350
363
  }
351
364
 
352
365
  export async function devServerModuleHref(
@@ -361,29 +374,25 @@ export async function devServerModuleHref(
361
374
  config,
362
375
  file,
363
376
  moduleOptions.conditionTarget ?? 'server',
364
- );
377
+ )
365
378
  const reactServerLayer =
366
- moduleOptions.reactServerLayer ??
367
- (conditionTarget === 'server' || conditionTarget === 'edge');
368
- const release = devModuleGraph(config).hold();
379
+ moduleOptions.reactServerLayer ?? (conditionTarget === 'server' || conditionTarget === 'edge')
380
+ const release = devModuleGraph(config).hold()
369
381
  const compiled = await writeDevModule(config, file, new Set(), {
370
382
  aliases: {
371
383
  ...coreAliases(config, 'server'),
372
- ...(reactServerLayer
373
- ? getImportAliasExtensions().reactServerLayerAliases(config)
374
- : {}),
384
+ ...(reactServerLayer ? getImportAliasExtensions().reactServerLayerAliases(config) : {}),
375
385
  },
376
386
  profile: reactCompatEnabled(config) ? 'compat' : 'server',
377
387
  conditionTarget,
378
388
  reactServerLayer,
379
389
  externalLoadTarget:
380
- moduleOptions.externalLoadTarget ??
381
- externalLoadTargetForConditionTarget(conditionTarget),
390
+ moduleOptions.externalLoadTarget ?? externalLoadTargetForConditionTarget(conditionTarget),
382
391
  stubClientImports: reactCompatEnabled(config),
383
392
  rewriteExternalServerImports: true,
384
393
  bundleExternalPackages: reactCompatEnabled(config),
385
- }).finally(release);
386
- return pathToFileHref(compiled);
394
+ }).finally(release)
395
+ return pathToFileHref(compiled)
387
396
  }
388
397
 
389
398
  export async function devClientModuleHref(
@@ -392,17 +401,17 @@ export async function devClientModuleHref(
392
401
  _version?: string,
393
402
  serverTarget?: ServerBundleTarget,
394
403
  ) {
395
- const conditionTarget = clientLayerConditionTarget(config, file, serverTarget ?? 'server');
396
- const throughPackage = await packageClientModuleHref(config, file, conditionTarget);
397
- if (throughPackage) return throughPackage;
398
- const release = devModuleGraph(config).hold();
404
+ const conditionTarget = clientLayerConditionTarget(config, file, serverTarget ?? 'server')
405
+ const throughPackage = await packageClientModuleHref(config, file, conditionTarget)
406
+ if (throughPackage) return throughPackage
407
+ const release = devModuleGraph(config).hold()
399
408
  const compiled = await writeDevModule(
400
409
  config,
401
410
  file,
402
411
  new Set(),
403
412
  clientLayerOptions(config, conditionTarget),
404
- ).finally(release);
405
- return pathToFileHref(compiled);
413
+ ).finally(release)
414
+ return pathToFileHref(compiled)
406
415
  }
407
416
 
408
417
  /**
@@ -422,18 +431,18 @@ async function packageClientModuleHref(
422
431
  file: string,
423
432
  conditionTarget: ServerBundleTarget,
424
433
  ) {
425
- if (!reactCompatEnabled(config)) return undefined;
426
- const packageDir = nodeModulesPackageDir(file);
427
- if (!packageDir) return undefined;
428
- const specifier = await packageSpecifierPublishing(config, packageDir, file);
429
- if (!specifier) return undefined;
434
+ if (!reactCompatEnabled(config)) return undefined
435
+ const packageDir = nodeModulesPackageDir(file)
436
+ if (!packageDir) return undefined
437
+ const specifier = await packageSpecifierPublishing(config, packageDir, file)
438
+ if (!specifier) return undefined
430
439
  return externalServerPackageHref(
431
440
  config,
432
441
  specifier,
433
442
  'client',
434
443
  path.dirname(file),
435
444
  conditionTarget,
436
- ).catch(() => undefined);
445
+ ).catch(() => undefined)
437
446
  }
438
447
 
439
448
  /**
@@ -446,70 +455,70 @@ async function packageSpecifierPublishing(
446
455
  packageDir: string,
447
456
  file: string,
448
457
  ) {
449
- const packageName = packageNameOfDirectory(packageDir);
450
- if (!packageName) return undefined;
458
+ const packageName = packageNameOfDirectory(packageDir)
459
+ if (!packageName) return undefined
451
460
  for (const subpath of packageExportSubpaths(packageDir)) {
452
- const specifier = subpath === '.' ? packageName : `${packageName}/${subpath.slice(2)}`;
453
- if (resolvePackageFile(config, file, specifier) === file) return specifier;
461
+ const specifier = subpath === '.' ? packageName : `${packageName}/${subpath.slice(2)}`
462
+ if (resolvePackageFile(config, file, specifier) === file) return specifier
454
463
  }
455
- const entry = resolvePackageFile(config, file, packageName);
456
- if (!entry || entry === file) return entry ? packageName : undefined;
457
- const published = new Set(await clientReferenceExportNames(config, entry));
458
- const names = await clientReferenceExportNames(config, file);
459
- return names.length > 0 && names.every(name => published.has(name)) ? packageName : undefined;
464
+ const entry = resolvePackageFile(config, file, packageName)
465
+ if (!entry || entry === file) return entry ? packageName : undefined
466
+ const published = new Set(await clientReferenceExportNames(config, entry))
467
+ const names = await clientReferenceExportNames(config, file)
468
+ return names.length > 0 && names.every(name => published.has(name)) ? packageName : undefined
460
469
  }
461
470
 
462
471
  /** The client layer's own conditions, so the file matches the bundle's entry. */
463
472
  function resolvePackageFile(config: ResolvedConfig, fromFile: string, specifier: string) {
464
- return resolvePackageSpecifier(config.root, fromFile, specifier, ['module', 'import']);
473
+ return resolvePackageSpecifier(config.root, fromFile, specifier, ['module', 'import'])
465
474
  }
466
475
 
467
476
  /** Every `exports` key of a package manifest; `{}` and sugar forms included. */
468
477
  function packageExportSubpaths(packageDir: string) {
469
478
  try {
470
479
  const manifest = JSON.parse(readFileSync(path.join(packageDir, 'package.json'), 'utf8')) as {
471
- exports?: unknown;
472
- };
473
- if (typeof manifest.exports !== 'object' || manifest.exports === null) return ['.'];
474
- const keys = Object.keys(manifest.exports).filter(key => key.startsWith('.'));
480
+ exports?: unknown
481
+ }
482
+ if (typeof manifest.exports !== 'object' || manifest.exports === null) return ['.']
483
+ const keys = Object.keys(manifest.exports).filter(key => key.startsWith('.'))
475
484
  // A conditions-only map (`{ import: ... }`) has no subpath keys at all.
476
- return keys.length > 0 ? keys.filter(key => !key.includes('*')) : ['.'];
485
+ return keys.length > 0 ? keys.filter(key => !key.includes('*')) : ['.']
477
486
  } catch {
478
- return ['.'];
487
+ return ['.']
479
488
  }
480
489
  }
481
490
 
482
491
  /** `…/node_modules/@scope/name` -> `@scope/name`. */
483
492
  function packageNameOfDirectory(packageDir: string) {
484
- const parts = packageDir.split(path.sep);
485
- const index = parts.lastIndexOf('node_modules');
486
- if (index === -1) return undefined;
487
- return parts.slice(index + 1).join('/');
493
+ const parts = packageDir.split(path.sep)
494
+ const index = parts.lastIndexOf('node_modules')
495
+ if (index === -1) return undefined
496
+ return parts.slice(index + 1).join('/')
488
497
  }
489
498
 
490
499
  /** `…/node_modules/@scope/name/build/x.js` -> `…/node_modules/@scope/name`. */
491
500
  function nodeModulesPackageDir(file: string) {
492
- const parts = file.split(path.sep);
493
- const index = parts.lastIndexOf('node_modules');
494
- if (index === -1) return undefined;
495
- const end = index + (parts[index + 1]?.startsWith('@') ? 3 : 2);
496
- return end <= parts.length ? parts.slice(0, end).join(path.sep) : undefined;
501
+ const parts = file.split(path.sep)
502
+ const index = parts.lastIndexOf('node_modules')
503
+ if (index === -1) return undefined
504
+ const end = index + (parts[index + 1]?.startsWith('@') ? 3 : 2)
505
+ return end <= parts.length ? parts.slice(0, end).join(path.sep) : undefined
497
506
  }
498
507
 
499
508
  export interface DevRouteModuleLoaders {
500
- moduleLoader: (file: string) => Promise<Record<string, unknown>>;
501
- clientModuleLoader: (file: string) => Promise<Record<string, unknown>>;
509
+ moduleLoader: (file: string) => Promise<Record<string, unknown>>
510
+ clientModuleLoader: (file: string) => Promise<Record<string, unknown>>
502
511
  }
503
512
 
504
513
  interface DevModuleOptions {
505
- aliases: AliasMap;
506
- profile: 'server' | 'compat' | 'client';
507
- conditionTarget: ServerBundleTarget;
508
- reactServerLayer?: boolean;
509
- externalLoadTarget: ExternalLoadTarget;
510
- stubClientImports: boolean;
511
- rewriteExternalServerImports: boolean;
512
- bundleExternalPackages: boolean;
514
+ aliases: AliasMap
515
+ profile: 'server' | 'compat' | 'client'
516
+ conditionTarget: ServerBundleTarget
517
+ reactServerLayer?: boolean
518
+ externalLoadTarget: ExternalLoadTarget
519
+ stubClientImports: boolean
520
+ rewriteExternalServerImports: boolean
521
+ bundleExternalPackages: boolean
513
522
  }
514
523
 
515
524
  /**
@@ -522,9 +531,9 @@ interface DevModuleOptions {
522
531
  function startRouteVendorStage(config: ResolvedConfig, route: RouteManifestEntry, files: string[]) {
523
532
  // Kill switch for bisecting a suspected stage-overlap difference.
524
533
  // eslint-disable-next-line turbo/no-undeclared-env-vars
525
- if (process.env.PNEXT_DISABLE_VENDOR_STAGE) return;
526
- if (!reactCompatEnabled(config)) return;
527
- const conditionTarget = serverBundleTargetForRuntime(route.segmentConfig?.runtime);
534
+ if (process.env.PNEXT_DISABLE_VENDOR_STAGE) return
535
+ if (!reactCompatEnabled(config)) return
536
+ const conditionTarget = serverBundleTargetForRuntime(route.segmentConfig?.runtime)
528
537
  const options: DevModuleOptions = {
529
538
  aliases: coreAliases(config, 'server'),
530
539
  profile: 'compat',
@@ -533,40 +542,40 @@ function startRouteVendorStage(config: ResolvedConfig, route: RouteManifestEntry
533
542
  stubClientImports: true,
534
543
  rewriteExternalServerImports: true,
535
544
  bundleExternalPackages: true,
536
- };
545
+ }
537
546
  // Only the SERVER vendor layer is seeded here: the client-reference walk already fans the
538
547
  // client-SSR demand out, so seeding that layer too only competes for the JS thread.
539
548
  //
540
549
  // Layered seeding: a separate walk pruned at 'use client' boundaries, so client-only packages get
541
550
  // no server-target builds. On-demand fallback covers anything needed sooner.
542
551
  // eslint-disable-next-line turbo/no-undeclared-env-vars
543
- const layered = process.env.PNEXT_VENDOR_SEED_LAYERED !== '0';
544
- const seedT0 = performance.now();
552
+ const layered = process.env.PNEXT_VENDOR_SEED_LAYERED !== '0'
553
+ const seedT0 = performance.now()
545
554
  void (async () => {
546
- const graph = devModuleGraph(config);
547
- const demand = new Set<string>();
548
- const dropped = new Set<string>();
555
+ const graph = devModuleGraph(config)
556
+ const demand = new Set<string>()
557
+ const dropped = new Set<string>()
549
558
  // The layer of a source is part of the record the naming walk just scanned,
550
559
  // so the seed reads it instead of re-reading the whole route graph.
551
560
  const prune = (file: string) =>
552
561
  (devHeadTrimEnabled() ? graph.isClientSource(file) : fileHasUseClientDirective(file)).catch(
553
562
  () => false,
554
- );
563
+ )
555
564
  for (const file of files) {
556
- const full = await graph.packageDemand(file);
557
- const serverReachable = layered ? new Set(await graph.packageDemand(file, prune)) : undefined;
565
+ const full = await graph.packageDemand(file)
566
+ const serverReachable = layered ? new Set(await graph.packageDemand(file, prune)) : undefined
558
567
  for (const specifier of full) {
559
- if (!shouldBundleExternalPackage(specifier, config, file, options)) continue;
560
- if (serverReachable && !serverReachable.has(specifier)) dropped.add(specifier);
561
- else demand.add(specifier);
568
+ if (!shouldBundleExternalPackage(specifier, config, file, options)) continue
569
+ if (serverReachable && !serverReachable.has(specifier)) dropped.add(specifier)
570
+ else demand.add(specifier)
562
571
  }
563
572
  }
564
- for (const specifier of demand) dropped.delete(specifier);
573
+ for (const specifier of demand) dropped.delete(specifier)
565
574
  // eslint-disable-next-line turbo/no-undeclared-env-vars
566
575
  if (process.env.PNEXT_DEV_PROFILE) {
567
576
  console.log(
568
577
  `dev-import vendor seed walk ${route.route} in ${formatDuration(performance.now() - seedT0)} (kept ${demand.size}, dropped ${dropped.size})`,
569
- );
578
+ )
570
579
  }
571
580
  if (vendorTraceEnabled()) {
572
581
  vendorTraceRow({
@@ -576,18 +585,22 @@ function startRouteVendorStage(config: ResolvedConfig, route: RouteManifestEntry
576
585
  kept: [...demand].sort(),
577
586
  dropped: [...dropped].sort(),
578
587
  atMs: performance.now(),
579
- });
588
+ })
580
589
  }
581
590
  const warm = (specifiers: Iterable<string>) =>
582
591
  Promise.all(
583
592
  [...specifiers].map(specifier =>
584
- externalServerPackageHref(config, specifier, 'server', config.root, conditionTarget).catch(
585
- () => undefined,
586
- ),
593
+ externalServerPackageHref(
594
+ config,
595
+ specifier,
596
+ 'server',
597
+ config.root,
598
+ conditionTarget,
599
+ ).catch(() => undefined),
587
600
  ),
588
- );
589
- await warm(demand);
590
- })().catch(() => undefined);
601
+ )
602
+ await warm(demand)
603
+ })().catch(() => undefined)
591
604
  }
592
605
 
593
606
  export async function devRouteModuleLoaders(
@@ -599,35 +612,35 @@ export async function devRouteModuleLoaders(
599
612
  // The bundle's own content-addressed path is the cache key: a save that
600
613
  // changes nothing this route imports keeps it, one that does gives a new
601
614
  // path — no version bump, no wholesale eviction.
602
- const release = devModuleGraph(config).hold();
615
+ const release = devModuleGraph(config).hold()
603
616
  const key = await profileDevImport(`route bundle key ${route.route}`, () =>
604
617
  devRouteBundlePath(config, route, layoutFiles),
605
618
  ).catch(error => {
606
- release();
607
- throw error;
608
- });
609
- noteModuleGeneration(`bundle:${route.route}`, key);
610
- const existing = routeModuleLoaders.get(key);
619
+ release()
620
+ throw error
621
+ })
622
+ noteModuleGeneration(`bundle:${route.route}`, key)
623
+ const existing = routeModuleLoaders.get(key)
611
624
  if (existing) {
612
- release();
613
- return existing;
625
+ release()
626
+ return existing
614
627
  }
615
628
  // A bundle already on disk was built against vendor artifacts the previous process left next to it, so
616
629
  // seeding the stage would walk the whole graph again to demand what is already there - and it would do it
617
630
  // on the JS thread the restart's first response is waiting on. Anything genuinely missing is still built
618
631
  // on demand by the resolve path.
619
632
  if (!devArtifactUsable(key)) {
620
- startRouteVendorStage(config, route, uniqueFiles([route.file, ...layoutFiles]));
633
+ startRouteVendorStage(config, route, uniqueFiles([route.file, ...layoutFiles]))
621
634
  }
622
635
 
623
636
  const next = createDevRouteModuleLoaders(config, route, layoutFiles, key)
624
637
  .catch(error => {
625
- routeModuleLoaders.delete(key);
626
- throw error;
638
+ routeModuleLoaders.delete(key)
639
+ throw error
627
640
  })
628
- .finally(release);
629
- routeModuleLoaders.set(key, next);
630
- return next;
641
+ .finally(release)
642
+ routeModuleLoaders.set(key, next)
643
+ return next
631
644
  }
632
645
 
633
646
  async function createDevRouteModuleLoaders(
@@ -636,21 +649,22 @@ async function createDevRouteModuleLoaders(
636
649
  layoutFiles: string[],
637
650
  outFile: string,
638
651
  ): Promise<DevRouteModuleLoaders> {
639
- const bundleFile = await writeDevRouteBundle(config, route, layoutFiles, outFile);
652
+ const bundleFile = await writeDevRouteBundle(config, route, layoutFiles, outFile)
640
653
  const bundle = await profileDevImport(`import route ${route.route}`, () =>
641
- importDevModule<DevRouteBundle>(pathToFileHref(bundleFile)));
654
+ importDevModule<DevRouteBundle>(pathToFileHref(bundleFile)),
655
+ )
642
656
  const moduleLoader = async (file: string) => {
643
- const module = bundle.modules[file];
644
- if (module) return module;
657
+ const module = bundle.modules[file]
658
+ if (module) return module
645
659
  return importDevModule<Record<string, unknown>>(
646
660
  await devServerModuleHref(config, file, undefined, {
647
661
  conditionTarget: serverBundleTargetForRuntime(route.segmentConfig?.runtime),
648
662
  }),
649
- );
650
- };
663
+ )
664
+ }
651
665
  const clientModuleLoader = async (file: string) => {
652
- const module = bundle.clientModules[file];
653
- if (module) return module;
666
+ const module = bundle.clientModules[file]
667
+ if (module) return module
654
668
  return importDevModule<Record<string, unknown>>(
655
669
  await devClientModuleHref(
656
670
  config,
@@ -658,14 +672,14 @@ async function createDevRouteModuleLoaders(
658
672
  undefined,
659
673
  serverBundleTargetForRuntime(route.segmentConfig?.runtime),
660
674
  ),
661
- );
662
- };
663
- return { moduleLoader, clientModuleLoader };
675
+ )
676
+ }
677
+ return { moduleLoader, clientModuleLoader }
664
678
  }
665
679
 
666
680
  interface DevRouteBundle {
667
- modules: Record<string, Record<string, unknown>>;
668
- clientModules: Record<string, Record<string, unknown>>;
681
+ modules: Record<string, Record<string, unknown>>
682
+ clientModules: Record<string, Record<string, unknown>>
669
683
  }
670
684
 
671
685
  async function writeDevRouteBundle(
@@ -674,80 +688,79 @@ async function writeDevRouteBundle(
674
688
  layoutFiles: string[],
675
689
  outFile: string,
676
690
  ) {
677
- if (devArtifactUsable(outFile)) return outFile;
691
+ if (devArtifactUsable(outFile)) return outFile
678
692
 
679
- const existing = routeBundleBuilds.get(outFile);
680
- if (existing) return existing;
693
+ const existing = routeBundleBuilds.get(outFile)
694
+ if (existing) return existing
681
695
 
682
696
  const next = profileDevImport(`build route ${route.route}`, async () => {
683
- await mkdir(path.dirname(outFile), { recursive: true });
684
- const serverFiles = uniqueFiles([
685
- ...(route.client ? [] : [route.file]),
686
- ...layoutFiles,
687
- ]);
688
- const result = await withBuildSlot(() => build({
689
- stdin: {
690
- contents: routeBundleEntrySource(serverFiles, []),
691
- loader: 'ts',
692
- resolveDir: config.root,
693
- sourcefile: `${route.id}.route-bundle.ts`,
694
- },
695
- bundle: true,
696
- write: false,
697
- format: 'esm',
698
- platform: 'neutral',
699
- target: 'es2022',
700
- // Workspace .js/.mjs app modules may contain JSX (packages stay
701
- // external, so node_modules never reach these loaders).
702
- loader: { '.js': 'jsx', '.mjs': 'jsx' },
703
- jsx: 'automatic',
704
- jsxImportSource: 'preact',
705
- packages: 'external',
706
- logLevel: 'silent',
707
- ...serverDefineOptions(),
708
- plugins: [
709
- serverAssetPlugin(config),
710
- // Ahead of the extension plugins: they resolve without a namespace, so a tsconfig-`paths`
711
- // import from a route-bundle module would land in esbuild's default namespace and take its
712
- // whole subtree with it - out of this build's hooks, resolving bare specifiers like
713
- // `next/dynamic` against real node_modules instead of the compat aliases.
714
- devRouteBundlePlugin(config, route),
715
- ...getBundlerExtensions().serverEsbuildPlugins(config),
716
- ],
717
- }));
718
- const output = result.outputFiles[0];
719
- if (!output) throw new Error(`Failed to build dev route bundle for ${route.route}`);
720
- await writeCompiledFile(outFile, output.text);
721
- return outFile;
697
+ await mkdir(path.dirname(outFile), { recursive: true })
698
+ const serverFiles = uniqueFiles([...(route.client ? [] : [route.file]), ...layoutFiles])
699
+ const result = await withBuildSlot(() =>
700
+ build({
701
+ stdin: {
702
+ contents: routeBundleEntrySource(serverFiles, []),
703
+ loader: 'ts',
704
+ resolveDir: config.root,
705
+ sourcefile: `${route.id}.route-bundle.ts`,
706
+ },
707
+ bundle: true,
708
+ write: false,
709
+ format: 'esm',
710
+ platform: 'neutral',
711
+ target: 'es2022',
712
+ // Workspace .js/.mjs app modules may contain JSX (packages stay
713
+ // external, so node_modules never reach these loaders).
714
+ loader: { '.js': 'jsx', '.mjs': 'jsx' },
715
+ jsx: 'automatic',
716
+ jsxImportSource: 'preact',
717
+ packages: 'external',
718
+ logLevel: 'silent',
719
+ ...serverDefineOptions(),
720
+ plugins: [
721
+ serverAssetPlugin(config),
722
+ // Ahead of the extension plugins: they resolve without a namespace, so a tsconfig-`paths`
723
+ // import from a route-bundle module would land in esbuild's default namespace and take its
724
+ // whole subtree with it - out of this build's hooks, resolving bare specifiers like
725
+ // `next/dynamic` against real node_modules instead of the compat aliases.
726
+ devRouteBundlePlugin(config, route),
727
+ ...getBundlerExtensions().serverEsbuildPlugins(config),
728
+ ],
729
+ }),
730
+ )
731
+ const output = result.outputFiles[0]
732
+ if (!output) throw new Error(`Failed to build dev route bundle for ${route.route}`)
733
+ await writeCompiledFile(outFile, output.text)
734
+ return outFile
722
735
  }).catch(error => {
723
- routeBundleBuilds.delete(outFile);
724
- throw error;
725
- });
736
+ routeBundleBuilds.delete(outFile)
737
+ throw error
738
+ })
726
739
 
727
- routeBundleBuilds.set(outFile, next);
728
- return next;
740
+ routeBundleBuilds.set(outFile, next)
741
+ return next
729
742
  }
730
743
 
731
744
  function routeBundleEntrySource(serverFiles: string[], clientFiles: string[]) {
732
- const serverImports = serverFiles.map((file, index) =>
733
- `import * as server${index} from ${JSON.stringify(`pnext-server:${file}`)};`);
734
- const clientImports = clientFiles.map((file, index) =>
735
- `import * as client${index} from ${JSON.stringify(`pnext-client:${file}`)};`);
736
- const serverEntries = serverFiles.map((file, index) =>
737
- `${JSON.stringify(file)}: server${index},`);
738
- const clientEntries = clientFiles.map((file, index) =>
739
- `${JSON.stringify(file)}: client${index},`);
745
+ const serverImports = serverFiles.map(
746
+ (file, index) => `import * as server${index} from ${JSON.stringify(`pnext-server:${file}`)};`,
747
+ )
748
+ const clientImports = clientFiles.map(
749
+ (file, index) => `import * as client${index} from ${JSON.stringify(`pnext-client:${file}`)};`,
750
+ )
751
+ const serverEntries = serverFiles.map((file, index) => `${JSON.stringify(file)}: server${index},`)
752
+ const clientEntries = clientFiles.map((file, index) => `${JSON.stringify(file)}: client${index},`)
740
753
 
741
754
  return [
742
755
  ...serverImports,
743
756
  ...clientImports,
744
757
  `export const modules = {${serverEntries.join('')}};`,
745
758
  `export const clientModules = {${clientEntries.join('')}};`,
746
- ].join('\n');
759
+ ].join('\n')
747
760
  }
748
761
 
749
762
  function devRouteBundlePlugin(config: ResolvedConfig, route: RouteManifestEntry): Plugin {
750
- const conditionTarget = serverBundleTargetForRuntime(route.segmentConfig?.runtime);
763
+ const conditionTarget = serverBundleTargetForRuntime(route.segmentConfig?.runtime)
751
764
  const serverOptions: DevModuleOptions = {
752
765
  aliases: {
753
766
  ...coreAliases(config, 'server'),
@@ -763,7 +776,7 @@ function devRouteBundlePlugin(config: ResolvedConfig, route: RouteManifestEntry)
763
776
  stubClientImports: true,
764
777
  rewriteExternalServerImports: true,
765
778
  bundleExternalPackages: reactCompatEnabled(config),
766
- };
779
+ }
767
780
  const clientOptions: DevModuleOptions = {
768
781
  aliases: clientSsrAliases(config),
769
782
  profile: 'client',
@@ -772,7 +785,7 @@ function devRouteBundlePlugin(config: ResolvedConfig, route: RouteManifestEntry)
772
785
  stubClientImports: false,
773
786
  rewriteExternalServerImports: false,
774
787
  bundleExternalPackages: reactCompatEnabled(config),
775
- };
788
+ }
776
789
 
777
790
  return {
778
791
  name: 'pnext-dev-route-bundle',
@@ -780,25 +793,37 @@ function devRouteBundlePlugin(config: ResolvedConfig, route: RouteManifestEntry)
780
793
  // Ensure the compat client-action module set is discovered for the active
781
794
  // dev version before any route-client module loads (dev startup/reload used
782
795
  // to run this inline). No-op for pure-core / non-next apps.
783
- if (nextCompatEnabled(config)) await getClientActionBundler()?.ensureArmed();
784
- build.onResolve({ filter: /^file:\/\// }, args => ({ path: args.path, external: true }));
796
+ if (nextCompatEnabled(config)) await getClientActionBundler()?.ensureArmed()
797
+ build.onResolve({ filter: /^file:\/\// }, args => ({ path: args.path, external: true }))
785
798
  build.onResolve({ filter: /^pnext-server:/ }, args => ({
786
799
  path: args.path.slice('pnext-server:'.length),
787
800
  namespace: 'pnext-route-server',
788
- }));
801
+ }))
789
802
  build.onResolve({ filter: /^pnext-client:/ }, args => ({
790
803
  path: args.path.slice('pnext-client:'.length),
791
804
  namespace: 'pnext-route-client',
792
- }));
805
+ }))
793
806
 
794
807
  build.onResolve({ filter: /.*/, namespace: 'pnext-route-server' }, args =>
795
- resolveRouteBundleSpecifier(config, args.path, args.importer, 'pnext-route-server', serverOptions),
796
- );
808
+ resolveRouteBundleSpecifier(
809
+ config,
810
+ args.path,
811
+ args.importer,
812
+ 'pnext-route-server',
813
+ serverOptions,
814
+ ),
815
+ )
797
816
  build.onResolve({ filter: /.*/, namespace: 'pnext-route-client' }, args =>
798
- resolveRouteBundleSpecifier(config, args.path, args.importer, 'pnext-route-client', clientOptions),
799
- );
800
- registerExternalLoadHandlers(build, config, serverOptions, externalLoadNamespaces.server);
801
- registerExternalLoadHandlers(build, config, clientOptions, externalLoadNamespaces.client);
817
+ resolveRouteBundleSpecifier(
818
+ config,
819
+ args.path,
820
+ args.importer,
821
+ 'pnext-route-client',
822
+ clientOptions,
823
+ ),
824
+ )
825
+ registerExternalLoadHandlers(build, config, serverOptions, externalLoadNamespaces.server)
826
+ registerExternalLoadHandlers(build, config, clientOptions, externalLoadNamespaces.client)
802
827
 
803
828
  build.onLoad({ filter: /.*/, namespace: 'pnext-route-server' }, async args => ({
804
829
  contents: rewriteServerSource(await readText(args.path), args.path, {
@@ -806,15 +831,15 @@ function devRouteBundlePlugin(config: ResolvedConfig, route: RouteManifestEntry)
806
831
  root: config.root,
807
832
  }),
808
833
  loader: esbuildLoader(args.path),
809
- }));
834
+ }))
810
835
  build.onLoad({ filter: /.*/, namespace: 'pnext-route-client' }, async args => {
811
836
  // A client-side import of a 'use server' module becomes the RPC stub.
812
- const clientActions = nextCompatEnabled(config) ? getClientActionBundler() : undefined;
837
+ const clientActions = nextCompatEnabled(config) ? getClientActionBundler() : undefined
813
838
  if (clientActions?.isClientActionModule(args.path)) {
814
839
  return {
815
840
  contents: await clientActions.stubSource(args.path),
816
841
  loader: 'js',
817
- };
842
+ }
818
843
  }
819
844
  return {
820
845
  contents: rewriteServerSource(await readText(args.path), args.path, {
@@ -822,14 +847,17 @@ function devRouteBundlePlugin(config: ResolvedConfig, route: RouteManifestEntry)
822
847
  root: config.root,
823
848
  }),
824
849
  loader: esbuildLoader(args.path),
825
- };
826
- });
850
+ }
851
+ })
827
852
  build.onLoad({ filter: /.*/, namespace: 'pnext-client-reference' }, async args => ({
828
- contents: clientReferenceModuleSource(args.path, await clientReferenceExportNames(config, args.path)),
853
+ contents: clientReferenceModuleSource(
854
+ args.path,
855
+ await clientReferenceExportNames(config, args.path),
856
+ ),
829
857
  loader: 'js',
830
- }));
858
+ }))
831
859
  },
832
- };
860
+ }
833
861
  }
834
862
 
835
863
  /**
@@ -845,13 +873,12 @@ function pagesCompatSourceAlias(
845
873
  importer: string | undefined,
846
874
  options: DevModuleOptions,
847
875
  ): string | undefined {
848
- if (!options.reactServerLayer) return undefined;
849
- if (specifier !== 'react' && specifier !== 'react-dom') return undefined;
850
- if (!importer) return undefined;
851
- const posix = importer.replace(/\\/g, '/');
852
- if (!posix.includes('/pnext-pages-compat/') || !posix.includes('/source-pages/'))
853
- return undefined;
854
- return coreAliases(config, 'server')[specifier];
876
+ if (!options.reactServerLayer) return undefined
877
+ if (specifier !== 'react' && specifier !== 'react-dom') return undefined
878
+ if (!importer) return undefined
879
+ const posix = importer.replace(/\\/g, '/')
880
+ if (!posix.includes('/pnext-pages-compat/') || !posix.includes('/source-pages/')) return undefined
881
+ return coreAliases(config, 'server')[specifier]
855
882
  }
856
883
 
857
884
  async function resolveRouteBundleSpecifier(
@@ -862,24 +889,24 @@ async function resolveRouteBundleSpecifier(
862
889
  options: DevModuleOptions,
863
890
  ): Promise<OnResolveResult | undefined> {
864
891
  const compatAlias =
865
- pagesCompatSourceAlias(config, specifier, importer, options) ?? options.aliases[specifier];
892
+ pagesCompatSourceAlias(config, specifier, importer, options) ?? options.aliases[specifier]
866
893
  if (compatAlias) {
867
894
  return {
868
895
  path: path.isAbsolute(compatAlias) ? pathToFileHref(compatAlias) : compatAlias,
869
896
  external: true,
870
- };
897
+ }
871
898
  }
872
899
 
873
900
  // A `turbopack.rules` loader chain owns this specifier (see the same check in
874
901
  // resolveDevModuleSpecifier): import its materialized output.
875
- const pendingRuleModule = getAssetExtensions().loaderRuleModule(specifier, importer);
876
- const ruleModule = pendingRuleModule === undefined ? undefined : await pendingRuleModule;
877
- if (ruleModule) return { path: pathToFileHref(ruleModule), external: true };
902
+ const pendingRuleModule = getAssetExtensions().loaderRuleModule(specifier, importer)
903
+ const ruleModule = pendingRuleModule === undefined ? undefined : await pendingRuleModule
904
+ if (ruleModule) return { path: pathToFileHref(ruleModule), external: true }
878
905
 
879
- if (isServerIgnoredAssetSpecifier(specifier)) return undefined;
906
+ if (isServerIgnoredAssetSpecifier(specifier)) return undefined
880
907
 
881
- const { sourcePath, hash } = splitHash(specifier);
882
- const resolved = resolveLocalImport(config, importer, sourcePath);
908
+ const { sourcePath, hash } = splitHash(specifier)
909
+ const resolved = resolveLocalImport(config, importer, sourcePath)
883
910
  // `resolveExtensions` can land an extensionless specifier on an asset
884
911
  // (`import img from './image'` -> image.png), which the specifier-keyed check
885
912
  // above and serverAssetPlugin's own filter both miss. Hand it to the asset
@@ -889,17 +916,17 @@ async function resolveRouteBundleSpecifier(
889
916
  isServerIgnoredAssetSpecifier(resolved) &&
890
917
  !getAssetExtensions().hasLoaderRuleFor(resolved)
891
918
  ) {
892
- return { path: resolved, namespace: serverAssetNamespace };
919
+ return { path: resolved, namespace: serverAssetNamespace }
893
920
  }
894
921
  if (resolved && isInside(config.workspaceRoot, resolved)) {
895
922
  if (
896
923
  options.stubClientImports &&
897
924
  !isCssFile(resolved) &&
898
- await fileHasUseClientDirective(resolved)
925
+ (await fileHasUseClientDirective(resolved))
899
926
  ) {
900
- return { path: resolved, namespace: 'pnext-client-reference' };
927
+ return { path: resolved, namespace: 'pnext-client-reference' }
901
928
  }
902
- return { path: resolved, namespace };
929
+ return { path: resolved, namespace }
903
930
  }
904
931
  // An absolute path that lands outside the workspace is framework runtime a rewrite injected by path
905
932
  // rather than by alias - pnext's own source is not part of the app's module graph, so it loads
@@ -907,10 +934,13 @@ async function resolveRouteBundleSpecifier(
907
934
  // esbuild does not fall back to the filesystem for an import raised from a plugin namespace, so the
908
935
  // whole route bundle fails and the route silently drops to per-module loading.
909
936
  if (resolved && path.isAbsolute(sourcePath)) {
910
- return { path: `${pathToFileHref(resolved)}${hash}`, external: true };
937
+ return { path: `${pathToFileHref(resolved)}${hash}`, external: true }
911
938
  }
912
939
 
913
- if (options.bundleExternalPackages && shouldBundleExternalPackage(sourcePath, config, importer, options)) {
940
+ if (
941
+ options.bundleExternalPackages &&
942
+ shouldBundleExternalPackage(sourcePath, config, importer, options)
943
+ ) {
914
944
  return {
915
945
  path: await externalServerPackageHref(
916
946
  config,
@@ -920,7 +950,7 @@ async function resolveRouteBundleSpecifier(
920
950
  options.conditionTarget,
921
951
  ),
922
952
  external: true,
923
- };
953
+ }
924
954
  }
925
955
 
926
956
  if (isPackageSpecifier(sourcePath) || isBuiltinSpecifier(sourcePath)) {
@@ -929,17 +959,18 @@ async function resolveRouteBundleSpecifier(
929
959
  fromFile: importer,
930
960
  specifier: sourcePath,
931
961
  target: options.externalLoadTarget,
932
- });
933
- if (resolvedExternalLoad && await externalLoadNeedsFacade(resolvedExternalLoad)) {
962
+ })
963
+ if (resolvedExternalLoad && (await externalLoadNeedsFacade(resolvedExternalLoad))) {
934
964
  return {
935
965
  path: resolvedExternalLoad,
936
- namespace: options.profile === 'client'
937
- ? externalLoadNamespaces.client
938
- : externalLoadNamespaces.server,
939
- };
966
+ namespace:
967
+ options.profile === 'client'
968
+ ? externalLoadNamespaces.client
969
+ : externalLoadNamespaces.server,
970
+ }
940
971
  }
941
972
  const externalTarget =
942
- resolvedExternalLoad ?? externalPackageImportTarget(config.root, importer, sourcePath);
973
+ resolvedExternalLoad ?? externalPackageImportTarget(config.root, importer, sourcePath)
943
974
  return {
944
975
  path: externalTarget
945
976
  ? `${pathToFileHref(externalTarget)}${hash}`
@@ -947,48 +978,48 @@ async function resolveRouteBundleSpecifier(
947
978
  ? runtimeServerImportTarget(specifier)
948
979
  : specifier,
949
980
  external: true,
950
- };
981
+ }
951
982
  }
952
983
 
953
- return undefined;
984
+ return undefined
954
985
  }
955
986
 
956
987
  // Sidecar of each artifact's raw import specifiers so the vercel trace reads
957
988
  // them back instead of re-parsing artifacts. Off outside vercel prod builds.
958
- let emitCompiledSpecifiersManifest = false;
989
+ let emitCompiledSpecifiersManifest = false
959
990
 
960
991
  /** @internal Test-only. Returns a restore function. */
961
992
  export function setEmitCompiledSpecifiersManifest(enabled: boolean) {
962
- const previous = emitCompiledSpecifiersManifest;
963
- emitCompiledSpecifiersManifest = enabled;
993
+ const previous = emitCompiledSpecifiersManifest
994
+ emitCompiledSpecifiersManifest = enabled
964
995
  return () => {
965
- emitCompiledSpecifiersManifest = previous;
966
- };
996
+ emitCompiledSpecifiersManifest = previous
997
+ }
967
998
  }
968
999
 
969
1000
  /** Sidecar suffix for a compiled artifact's recorded specifiers. */
970
- export const compiledSpecifiersManifestSuffix = '.pnext-specifiers.json';
1001
+ export const compiledSpecifiersManifestSuffix = '.pnext-specifiers.json'
971
1002
 
972
- const compiledScriptFilePattern = /\.(?:m?js|cjs|jsx|tsx?)$/;
1003
+ const compiledScriptFilePattern = /\.(?:m?js|cjs|jsx|tsx?)$/
973
1004
 
974
1005
  async function writeCompiledFile(file: string, contents: string) {
975
- await writeFileAtomic(file, contents);
976
- noteDevArtifactWritten(file);
1006
+ await writeFileAtomic(file, contents)
1007
+ noteDevArtifactWritten(file)
977
1008
  if (emitCompiledSpecifiersManifest && compiledScriptFilePattern.test(file)) {
978
1009
  await writeFileAtomic(
979
1010
  `${file}${compiledSpecifiersManifestSuffix}`,
980
1011
  JSON.stringify(importSpecifiers(contents, file)),
981
- );
1012
+ )
982
1013
  }
983
1014
  }
984
1015
 
985
1016
  /** Where `file` compiles to under `profile`, named by its current source graph. */
986
1017
  async function devModulePathFor(config: ResolvedConfig, file: string, profile: string) {
987
- return devServerPath(config, file, profile, await devModuleGraph(config).graphHash(file));
1018
+ return devServerPath(config, file, profile, await devModuleGraph(config).graphHash(file))
988
1019
  }
989
1020
 
990
1021
  function devModulePath(config: ResolvedConfig, file: string, options: DevModuleOptions) {
991
- return devModulePathFor(config, file, devModuleProfile(options));
1022
+ return devModulePathFor(config, file, devModuleProfile(options))
992
1023
  }
993
1024
 
994
1025
  async function writeDevModule(
@@ -997,39 +1028,44 @@ async function writeDevModule(
997
1028
  visited: Set<string>,
998
1029
  options: DevModuleOptions,
999
1030
  ) {
1000
- const profile = devModuleProfile(options);
1001
- const outFile = await devModulePath(config, file, options);
1002
- const visitedKey = `${profile}:${file}`;
1003
- noteModuleGeneration(visitedKey, outFile);
1004
- if (visited.has(visitedKey)) return outFile;
1031
+ const profile = devModuleProfile(options)
1032
+ const outFile = await devModulePath(config, file, options)
1033
+ const visitedKey = `${profile}:${file}`
1034
+ noteModuleGeneration(visitedKey, outFile)
1035
+ if (visited.has(visitedKey)) return outFile
1005
1036
 
1006
1037
  // The name carries the hash of this module's whole source graph, so an existing artifact is by
1007
1038
  // construction current. An unusable one (the cache folder was wiped from outside) falls through to
1008
1039
  // recompile, which overwrites it in place - never delete first, since Bun caches a failed resolution for
1009
1040
  // the life of the process.
1010
- if (devArtifactUsable(outFile)) return outFile;
1041
+ if (devArtifactUsable(outFile)) return outFile
1011
1042
 
1012
- const existing = moduleBuilds.get(outFile);
1013
- if (existing) return existing;
1043
+ const existing = moduleBuilds.get(outFile)
1044
+ if (existing) return existing
1014
1045
 
1015
- visited.add(visitedKey);
1016
- const started = moduleStats ? performance.now() : 0;
1046
+ visited.add(visitedKey)
1047
+ const started = moduleStats ? performance.now() : 0
1017
1048
  const next = writeDevModuleUncached(config, file, visited, options, outFile)
1018
1049
  .then(result => {
1019
1050
  if (moduleStats) {
1020
- noteModuleCompile(file, profile, performance.now() - started, esbuildFallbacks.delete(visitedKey));
1051
+ noteModuleCompile(
1052
+ file,
1053
+ profile,
1054
+ performance.now() - started,
1055
+ esbuildFallbacks.delete(visitedKey),
1056
+ )
1021
1057
  }
1022
- return result;
1058
+ return result
1023
1059
  })
1024
1060
  .catch(error => {
1025
- moduleBuilds.delete(outFile);
1026
- throw error;
1061
+ moduleBuilds.delete(outFile)
1062
+ throw error
1027
1063
  })
1028
1064
  .finally(() => {
1029
- moduleBuilds.delete(outFile);
1030
- });
1031
- moduleBuilds.set(outFile, next);
1032
- return next;
1065
+ moduleBuilds.delete(outFile)
1066
+ })
1067
+ moduleBuilds.set(outFile, next)
1068
+ return next
1033
1069
  }
1034
1070
 
1035
1071
  async function writeDevModuleUncached(
@@ -1039,44 +1075,49 @@ async function writeDevModuleUncached(
1039
1075
  options: DevModuleOptions,
1040
1076
  outFile: string,
1041
1077
  ) {
1042
- await mkdir(path.dirname(outFile), { recursive: true });
1078
+ await mkdir(path.dirname(outFile), { recursive: true })
1043
1079
  if (isCssFile(file)) {
1044
- await copyFile(file, outFile);
1045
- return outFile;
1080
+ await copyFile(file, outFile)
1081
+ return outFile
1046
1082
  }
1047
1083
 
1048
1084
  // A client-profile compile of a 'use server' module emits the RPC stub, so
1049
1085
  // the browser bundle POSTs to the endpoint instead of shipping server code.
1050
1086
  if (options.profile === 'client' && nextCompatEnabled(config)) {
1051
- const clientActions = getClientActionBundler();
1087
+ const clientActions = getClientActionBundler()
1052
1088
  if (clientActions) {
1053
- await clientActions.ensureArmed();
1089
+ await clientActions.ensureArmed()
1054
1090
  if (clientActions.isClientActionModule(file)) {
1055
- await writeCompiledFile(outFile, await clientActions.stubSource(file));
1056
- return outFile;
1091
+ await writeCompiledFile(outFile, await clientActions.stubSource(file))
1092
+ return outFile
1057
1093
  }
1058
1094
  }
1059
1095
  }
1060
1096
 
1061
- let source = await readText(file);
1097
+ let source = await readText(file)
1062
1098
  if (/\.mdx?$/.test(file) && nextCompatEnabled(config)) {
1063
1099
  // Markdown modules must be compiled to JS before import analysis and the
1064
1100
  // stdin-based bundle pass (which bypasses file-resolved onLoad plugins).
1065
- const { compileMdx } = await import('../compat/mdx/compile');
1066
- source = (await compileMdx(source, file, config.root)).code;
1101
+ const { compileMdx } = await import('../compat/mdx/compile')
1102
+ source = (await compileMdx(source, file, config.root)).code
1067
1103
  }
1068
1104
  const transformedSource = rewriteServerSource(source, file, {
1069
1105
  nextFonts: nextCompatEnabled(config),
1070
1106
  root: config.root,
1071
- });
1107
+ })
1072
1108
  const effectiveOptions =
1073
- reactCompatEnabled(config) && options.profile !== 'client' && hasUseClientDirective(transformedSource)
1074
- ? clientLayerOptions(config, clientLayerConditionTarget(config, file, options.conditionTarget))
1075
- : options;
1109
+ reactCompatEnabled(config) &&
1110
+ options.profile !== 'client' &&
1111
+ hasUseClientDirective(transformedSource)
1112
+ ? clientLayerOptions(
1113
+ config,
1114
+ clientLayerConditionTarget(config, file, options.conditionTarget),
1115
+ )
1116
+ : options
1076
1117
  // The asset-context link walk is pure I/O against directories nothing below
1077
1118
  // reads, so it overlaps the shaking/scan/child-compile work and is only
1078
1119
  // awaited before the write.
1079
- const assetContext = linkAssetContext(config, file, devModuleProfile(effectiveOptions));
1120
+ const assetContext = linkAssetContext(config, file, devModuleProfile(effectiveOptions))
1080
1121
  // Rewrite destructured dynamic imports (`const { used } = await import('x')`)
1081
1122
  // to point at a tree-shaken facade so the target's unused exports are dropped
1082
1123
  // (compat: mirrors webpack's dynamic-import export usage analysis). Done
@@ -1088,7 +1129,7 @@ async function writeDevModuleUncached(
1088
1129
  const deferredDynamic =
1089
1130
  effectiveOptions.profile === 'client' && devDynamicSplitEnabled()
1090
1131
  ? deferredDynamicTargetFiles(transformedSource, config, file)
1091
- : undefined;
1132
+ : undefined
1092
1133
  const shakenSource = await applyDynamicImportTreeShaking(
1093
1134
  transformedSource,
1094
1135
  config,
@@ -1096,10 +1137,13 @@ async function writeDevModuleUncached(
1096
1137
  visited,
1097
1138
  effectiveOptions,
1098
1139
  deferredDynamic,
1099
- );
1100
- const clientReferences = reactCompatEnabled(config) && effectiveOptions.stubClientImports && !hasUseClientDirective(shakenSource)
1101
- ? await writeClientReferenceModules(shakenSource, config, file, effectiveOptions.aliases)
1102
- : { specifiers: new Map<string, string>(), files: new Set<string>() };
1140
+ )
1141
+ const clientReferences =
1142
+ reactCompatEnabled(config) &&
1143
+ effectiveOptions.stubClientImports &&
1144
+ !hasUseClientDirective(shakenSource)
1145
+ ? await writeClientReferenceModules(shakenSource, config, file, effectiveOptions.aliases)
1146
+ : { specifiers: new Map<string, string>(), files: new Set<string>() }
1103
1147
  // Compiling this module only needs its imports' *paths*, so it runs while the
1104
1148
  // targets themselves compile. The write still waits for them: a compiled
1105
1149
  // module must never become loadable before its edges exist on disk.
@@ -1109,10 +1153,10 @@ async function writeDevModuleUncached(
1109
1153
  file,
1110
1154
  clientReferences.specifiers,
1111
1155
  effectiveOptions,
1112
- );
1156
+ )
1113
1157
  // The real rejection is reported below; this only keeps it from surfacing as
1114
1158
  // an unhandled rejection while the imports compile.
1115
- compiled.catch(() => undefined);
1159
+ compiled.catch(() => undefined)
1116
1160
  await Promise.all([
1117
1161
  assetContext,
1118
1162
  ...localImportTargets(shakenSource, config, file, effectiveOptions.aliases)
@@ -1127,17 +1171,17 @@ async function writeDevModuleUncached(
1127
1171
  !deferredDynamic?.has(path.resolve(target)),
1128
1172
  )
1129
1173
  .map(({ target }) => writeDevModule(config, target, visited, effectiveOptions)),
1130
- ]);
1174
+ ])
1131
1175
 
1132
- await writeCompiledFile(outFile, await compiled);
1133
- return outFile;
1176
+ await writeCompiledFile(outFile, await compiled)
1177
+ return outFile
1134
1178
  }
1135
1179
 
1136
- const shakeEntrySpecifier = 'pnext-shake-entry';
1137
- const shakeEntryNamespace = 'pnext-shake-entry';
1180
+ const shakeEntrySpecifier = 'pnext-shake-entry'
1181
+ const shakeEntryNamespace = 'pnext-shake-entry'
1138
1182
 
1139
1183
  // Rewrite each shakeable destructured dynamic import in `source` so its
1140
- // specifier points at a tree-shaken facade module (see dynamic/tree-shake.ts).
1184
+ // specifier points at a tree-shaken facade module (see resolve/tree-shake.ts).
1141
1185
  // Only local workspace modules are shaken; 'use client' targets keep their
1142
1186
  // client-reference stubbing path. Returns the source unchanged when nothing
1143
1187
  // qualifies.
@@ -1149,45 +1193,45 @@ async function applyDynamicImportTreeShaking(
1149
1193
  options: DevModuleOptions,
1150
1194
  deferredDynamic?: Set<string>,
1151
1195
  ): Promise<string> {
1152
- if (!nextCompatEnabled(config)) return source;
1153
- const candidates = findShakeableDynamicImports(source);
1154
- if (candidates.length === 0) return source;
1196
+ if (!nextCompatEnabled(config)) return source
1197
+ const candidates = findShakeableDynamicImports(source)
1198
+ if (candidates.length === 0) return source
1155
1199
 
1156
- const edits: { start: number; end: number; value: string }[] = [];
1200
+ const edits: { start: number; end: number; value: string }[] = []
1157
1201
  for (const candidate of candidates) {
1158
- const { sourcePath } = splitHash(candidate.specifier);
1159
- if (isServerIgnoredAssetSpecifier(sourcePath) && !isCssFile(sourcePath)) continue;
1160
- const resolved = resolveLocalImport(config, file, sourcePath);
1161
- if (!resolved || !isInside(config.workspaceRoot, resolved) || isCssFile(resolved)) continue;
1202
+ const { sourcePath } = splitHash(candidate.specifier)
1203
+ if (isServerIgnoredAssetSpecifier(sourcePath) && !isCssFile(sourcePath)) continue
1204
+ const resolved = resolveLocalImport(config, file, sourcePath)
1205
+ if (!resolved || !isInside(config.workspaceRoot, resolved) || isCssFile(resolved)) continue
1162
1206
  // A deferred dynamic() target must not compile eagerly through the facade.
1163
- if (deferredDynamic?.has(path.resolve(resolved))) continue;
1164
- if (options.stubClientImports && (await fileHasUseClientDirective(resolved))) continue;
1207
+ if (deferredDynamic?.has(path.resolve(resolved))) continue
1208
+ if (options.stubClientImports && (await fileHasUseClientDirective(resolved))) continue
1165
1209
  const facade = await writeShakenDynamicModule(
1166
1210
  config,
1167
1211
  resolved,
1168
1212
  candidate.usedExports,
1169
1213
  visited,
1170
1214
  options,
1171
- );
1215
+ )
1172
1216
  edits.push({
1173
1217
  start: candidate.literalStart,
1174
1218
  end: candidate.literalEnd,
1175
1219
  value: JSON.stringify(pathToFileHref(facade)),
1176
- });
1220
+ })
1177
1221
  }
1178
1222
 
1179
- if (edits.length === 0) return source;
1180
- let next = source;
1223
+ if (edits.length === 0) return source
1224
+ let next = source
1181
1225
  for (const edit of edits.sort((a, b) => b.start - a.start)) {
1182
- next = `${next.slice(0, edit.start)}${edit.value}${next.slice(edit.end)}`;
1226
+ next = `${next.slice(0, edit.start)}${edit.value}${next.slice(edit.end)}`
1183
1227
  }
1184
- return next;
1228
+ return next
1185
1229
  }
1186
1230
 
1187
1231
  function shakeExportsKey(usedExports: string[]): string {
1188
- const sorted = [...new Set(usedExports)].sort();
1189
- if (sorted.length === 0) return 'none';
1190
- return createHash('sha256').update(sorted.join(',')).digest('hex').slice(0, 8);
1232
+ const sorted = [...new Set(usedExports)].sort()
1233
+ if (sorted.length === 0) return 'none'
1234
+ return createHash('sha256').update(sorted.join(',')).digest('hex').slice(0, 8)
1191
1235
  }
1192
1236
 
1193
1237
  async function shakenModulePath(
@@ -1196,26 +1240,26 @@ async function shakenModulePath(
1196
1240
  profile: string,
1197
1241
  key: string,
1198
1242
  ): Promise<string> {
1199
- const base = await devModulePathFor(config, targetFile, profile);
1200
- const ext = path.extname(base) || '.js';
1201
- return `${base.slice(0, base.length - ext.length)}.pnext-shake-${key}${ext}`;
1243
+ const base = await devModulePathFor(config, targetFile, profile)
1244
+ const ext = path.extname(base) || '.js'
1245
+ return `${base.slice(0, base.length - ext.length)}.pnext-shake-${key}${ext}`
1202
1246
  }
1203
1247
 
1204
1248
  // Facade entry that re-exports only the destructured names from the target
1205
1249
  // (loaded inline via the shake-entry namespace), so esbuild tree-shakes the
1206
1250
  // rest. An empty set keeps side effects only.
1207
1251
  function shakenEntrySource(usedExports: string[]): string {
1208
- const names = [...new Set(usedExports)];
1209
- if (names.length === 0) return `import ${JSON.stringify(shakeEntrySpecifier)};\n`;
1210
- const named = names.filter(name => name !== 'default');
1211
- const lines: string[] = [];
1252
+ const names = [...new Set(usedExports)]
1253
+ if (names.length === 0) return `import ${JSON.stringify(shakeEntrySpecifier)};\n`
1254
+ const named = names.filter(name => name !== 'default')
1255
+ const lines: string[] = []
1212
1256
  if (named.length > 0) {
1213
- lines.push(`export { ${named.join(', ')} } from ${JSON.stringify(shakeEntrySpecifier)};`);
1257
+ lines.push(`export { ${named.join(', ')} } from ${JSON.stringify(shakeEntrySpecifier)};`)
1214
1258
  }
1215
1259
  if (names.includes('default')) {
1216
- lines.push(`export { default } from ${JSON.stringify(shakeEntrySpecifier)};`);
1260
+ lines.push(`export { default } from ${JSON.stringify(shakeEntrySpecifier)};`)
1217
1261
  }
1218
- return `${lines.join('\n')}\n`;
1262
+ return `${lines.join('\n')}\n`
1219
1263
  }
1220
1264
 
1221
1265
  async function writeShakenDynamicModule(
@@ -1225,24 +1269,31 @@ async function writeShakenDynamicModule(
1225
1269
  visited: Set<string>,
1226
1270
  options: DevModuleOptions,
1227
1271
  ): Promise<string> {
1228
- const profile = devModuleProfile(options);
1229
- const outFile = await shakenModulePath(config, targetFile, profile, shakeExportsKey(usedExports));
1230
- if (devArtifactUsable(outFile)) return outFile;
1272
+ const profile = devModuleProfile(options)
1273
+ const outFile = await shakenModulePath(config, targetFile, profile, shakeExportsKey(usedExports))
1274
+ if (devArtifactUsable(outFile)) return outFile
1231
1275
 
1232
- const key = `shake\0${outFile}`;
1233
- const existing = moduleBuilds.get(key);
1234
- if (existing) return existing;
1276
+ const key = `shake\0${outFile}`
1277
+ const existing = moduleBuilds.get(key)
1278
+ if (existing) return existing
1235
1279
 
1236
- const next = writeShakenDynamicModuleUncached(config, targetFile, usedExports, visited, options, outFile)
1280
+ const next = writeShakenDynamicModuleUncached(
1281
+ config,
1282
+ targetFile,
1283
+ usedExports,
1284
+ visited,
1285
+ options,
1286
+ outFile,
1287
+ )
1237
1288
  .catch(error => {
1238
- moduleBuilds.delete(key);
1239
- throw error;
1289
+ moduleBuilds.delete(key)
1290
+ throw error
1240
1291
  })
1241
1292
  .finally(() => {
1242
- moduleBuilds.delete(key);
1243
- });
1244
- moduleBuilds.set(key, next);
1245
- return next;
1293
+ moduleBuilds.delete(key)
1294
+ })
1295
+ moduleBuilds.set(key, next)
1296
+ return next
1246
1297
  }
1247
1298
 
1248
1299
  async function writeShakenDynamicModuleUncached(
@@ -1253,20 +1304,22 @@ async function writeShakenDynamicModuleUncached(
1253
1304
  options: DevModuleOptions,
1254
1305
  outFile: string,
1255
1306
  ): Promise<string> {
1256
- await mkdir(path.dirname(outFile), { recursive: true });
1307
+ await mkdir(path.dirname(outFile), { recursive: true })
1257
1308
  const targetSource = rewriteServerSource(await readText(targetFile), targetFile, {
1258
1309
  nextFonts: nextCompatEnabled(config),
1259
1310
  root: config.root,
1260
- });
1311
+ })
1261
1312
  // The target is inlined into the facade, but its own transitive imports stay
1262
1313
  // external file:// modules — compile them the same way the normal graph walk
1263
1314
  // would so those hrefs resolve on disk.
1264
1315
  await Promise.all(
1265
1316
  localImportTargets(targetSource, config, targetFile, options.aliases)
1266
- .filter(({ target, isLocalSource }) => isLocalSource || isInside(config.workspaceRoot, target))
1317
+ .filter(
1318
+ ({ target, isLocalSource }) => isLocalSource || isInside(config.workspaceRoot, target),
1319
+ )
1267
1320
  .map(({ target }) => writeDevModule(config, target, visited, options)),
1268
- );
1269
- const externalBundles = await externalBundleSpecifiers(targetSource, config, targetFile, options);
1321
+ )
1322
+ const externalBundles = await externalBundleSpecifiers(targetSource, config, targetFile, options)
1270
1323
  const result = await withBuildSlot(() =>
1271
1324
  build({
1272
1325
  stdin: {
@@ -1292,11 +1345,11 @@ async function writeShakenDynamicModuleUncached(
1292
1345
  devModuleResolvePlugin(config, targetFile, new Map(), externalBundles, options),
1293
1346
  ],
1294
1347
  }),
1295
- );
1296
- const output = result.outputFiles[0];
1297
- if (!output) throw new Error(`Failed to tree-shake ${targetFile}`);
1298
- await writeCompiledFile(outFile, addExtensionlessDynamicImportGlobAliases(output.text));
1299
- return outFile;
1348
+ )
1349
+ const output = result.outputFiles[0]
1350
+ if (!output) throw new Error(`Failed to tree-shake ${targetFile}`)
1351
+ await writeCompiledFile(outFile, addExtensionlessDynamicImportGlobAliases(output.text))
1352
+ return outFile
1300
1353
  }
1301
1354
 
1302
1355
  // Resolve the facade's re-export entry to the target's already-transformed
@@ -1308,18 +1361,18 @@ function shakeEntryPlugin(targetFile: string, targetSource: string): Plugin {
1308
1361
  build.onResolve({ filter: new RegExp(`^${shakeEntrySpecifier}$`) }, () => ({
1309
1362
  path: targetFile,
1310
1363
  namespace: shakeEntryNamespace,
1311
- }));
1364
+ }))
1312
1365
  build.onLoad({ filter: /.*/, namespace: shakeEntryNamespace }, () => ({
1313
1366
  contents: targetSource,
1314
1367
  loader: esbuildLoader(targetFile),
1315
1368
  resolveDir: path.dirname(targetFile),
1316
- }));
1369
+ }))
1317
1370
  },
1318
- };
1371
+ }
1319
1372
  }
1320
1373
 
1321
1374
  function externalLoadTargetForConditionTarget(target: ServerBundleTarget): ExternalLoadTarget {
1322
- return target === 'edge' ? 'edge' : 'server';
1375
+ return target === 'edge' ? 'edge' : 'server'
1323
1376
  }
1324
1377
 
1325
1378
  function clientLayerOptions(
@@ -1334,7 +1387,7 @@ function clientLayerOptions(
1334
1387
  stubClientImports: false,
1335
1388
  rewriteExternalServerImports: false,
1336
1389
  bundleExternalPackages: reactCompatEnabled(config),
1337
- };
1390
+ }
1338
1391
  }
1339
1392
 
1340
1393
  /**
@@ -1355,9 +1408,9 @@ function pagesLayerConditionTarget(
1355
1408
  file: string,
1356
1409
  incoming: ServerBundleTarget,
1357
1410
  ): ServerBundleTarget {
1358
- if (incoming === 'server') return isPagesRouterSourceFile(config, file) ? 'pages' : 'server';
1359
- if (incoming === 'edge') return isPagesRouterSourceFile(config, file) ? 'pages-edge' : 'edge';
1360
- return incoming;
1411
+ if (incoming === 'server') return isPagesRouterSourceFile(config, file) ? 'pages' : 'server'
1412
+ if (incoming === 'edge') return isPagesRouterSourceFile(config, file) ? 'pages-edge' : 'edge'
1413
+ return incoming
1361
1414
  }
1362
1415
 
1363
1416
  function clientLayerConditionTarget(
@@ -1365,24 +1418,24 @@ function clientLayerConditionTarget(
1365
1418
  file: string,
1366
1419
  incoming: ServerBundleTarget,
1367
1420
  ): ServerBundleTarget {
1368
- if (!isPagesRouterSourceFile(config, file)) return 'client';
1369
- return incoming === 'edge' || incoming === 'pages-edge' ? 'pages-edge' : 'pages';
1421
+ if (!isPagesRouterSourceFile(config, file)) return 'client'
1422
+ return incoming === 'edge' || incoming === 'pages-edge' ? 'pages-edge' : 'pages'
1370
1423
  }
1371
1424
 
1372
1425
  function isPagesRouterSourceFile(config: ResolvedConfig, file: string): boolean {
1373
- const posix = file.split(path.sep).join('/');
1374
- if (posix.includes('/source-pages/')) return true;
1426
+ const posix = file.split(path.sep).join('/')
1427
+ if (posix.includes('/source-pages/')) return true
1375
1428
  // Materialized hybrid wrappers live under pnext-pages-compat/<hash>/app/ for
1376
1429
  // BOTH routers; only pages wrappers re-export from source-pages/, so sniff
1377
1430
  // the (tiny, generated) wrapper body to tell them apart.
1378
1431
  if (posix.includes('pnext-pages-compat/')) {
1379
1432
  try {
1380
- return readFileSync(file, 'utf8').includes('source-pages/');
1433
+ return readFileSync(file, 'utf8').includes('source-pages/')
1381
1434
  } catch {
1382
- return false;
1435
+ return false
1383
1436
  }
1384
1437
  }
1385
- return isInside(path.join(config.root, 'pages'), file);
1438
+ return isInside(path.join(config.root, 'pages'), file)
1386
1439
  }
1387
1440
 
1388
1441
  async function buildDevModuleSource(
@@ -1392,7 +1445,7 @@ async function buildDevModuleSource(
1392
1445
  clientReferenceSpecifiers: Map<string, string>,
1393
1446
  options: DevModuleOptions,
1394
1447
  ) {
1395
- const externalBundles = await externalBundleSpecifiers(source, config, file, options);
1448
+ const externalBundles = await externalBundleSpecifiers(source, config, file, options)
1396
1449
  const transformed = await transformDevModuleSource(
1397
1450
  source,
1398
1451
  config,
@@ -1400,42 +1453,44 @@ async function buildDevModuleSource(
1400
1453
  clientReferenceSpecifiers,
1401
1454
  externalBundles,
1402
1455
  options,
1403
- );
1404
- if (transformed !== undefined) return transformed;
1405
- if (moduleStats) esbuildFallbacks.add(`${devModuleProfile(options)}:${file}`);
1406
- const result = await withBuildSlot(() => build({
1407
- stdin: {
1408
- contents: source,
1409
- loader: esbuildLoader(file),
1410
- resolveDir: path.dirname(file),
1411
- sourcefile: file,
1412
- },
1413
- bundle: true,
1414
- write: false,
1415
- format: 'esm',
1416
- platform: 'neutral',
1417
- target: 'es2022',
1418
- // Workspace .js/.mjs app modules may contain JSX (packages stay external,
1419
- // so node_modules never reach these loaders).
1420
- loader: { '.js': 'jsx', '.mjs': 'jsx' },
1421
- jsx: 'automatic',
1422
- jsxImportSource: 'preact',
1423
- packages: 'external',
1424
- logLevel: 'silent',
1425
- ...serverDefineOptions(),
1426
- plugins: [
1427
- serverAssetPlugin(config),
1428
- ...getBundlerExtensions().serverEsbuildPlugins(config),
1429
- serverClientReferencePlugin(config, options),
1430
- devModuleResolvePlugin(config, file, clientReferenceSpecifiers, externalBundles, options),
1431
- ],
1432
- }));
1433
- const output = result.outputFiles[0];
1434
- if (!output) throw new Error(`Failed to build ${file}`);
1435
- const compiled = addExtensionlessDynamicImportGlobAliases(output.text);
1456
+ )
1457
+ if (transformed !== undefined) return transformed
1458
+ if (moduleStats) esbuildFallbacks.add(`${devModuleProfile(options)}:${file}`)
1459
+ const result = await withBuildSlot(() =>
1460
+ build({
1461
+ stdin: {
1462
+ contents: source,
1463
+ loader: esbuildLoader(file),
1464
+ resolveDir: path.dirname(file),
1465
+ sourcefile: file,
1466
+ },
1467
+ bundle: true,
1468
+ write: false,
1469
+ format: 'esm',
1470
+ platform: 'neutral',
1471
+ target: 'es2022',
1472
+ // Workspace .js/.mjs app modules may contain JSX (packages stay external,
1473
+ // so node_modules never reach these loaders).
1474
+ loader: { '.js': 'jsx', '.mjs': 'jsx' },
1475
+ jsx: 'automatic',
1476
+ jsxImportSource: 'preact',
1477
+ packages: 'external',
1478
+ logLevel: 'silent',
1479
+ ...serverDefineOptions(),
1480
+ plugins: [
1481
+ serverAssetPlugin(config),
1482
+ ...getBundlerExtensions().serverEsbuildPlugins(config),
1483
+ serverClientReferencePlugin(config, options),
1484
+ devModuleResolvePlugin(config, file, clientReferenceSpecifiers, externalBundles, options),
1485
+ ],
1486
+ }),
1487
+ )
1488
+ const output = result.outputFiles[0]
1489
+ if (!output) throw new Error(`Failed to build ${file}`)
1490
+ const compiled = addExtensionlessDynamicImportGlobAliases(output.text)
1436
1491
  return options.profile === 'client' && isCommonJsModuleSource(source, file)
1437
1492
  ? unwrapCommonJsDefaultExport(compiled)
1438
- : compiled;
1493
+ : compiled
1439
1494
  }
1440
1495
 
1441
1496
  /**
@@ -1455,13 +1510,13 @@ async function transformDevModuleSource(
1455
1510
  // Kill switch: puts every module back on the esbuild build below, for
1456
1511
  // bisecting a suspected transform-path difference.
1457
1512
  // eslint-disable-next-line turbo/no-undeclared-env-vars
1458
- if (process.env.PNEXT_DISABLE_BATCHED_TRANSFORM) return undefined;
1459
- if (transformBailReason(source, file)) return undefined;
1460
- const transformed = transformServerModule(source, file, serverDefineOptions().define);
1461
- if ('bail' in transformed) return undefined;
1513
+ if (process.env.PNEXT_DISABLE_BATCHED_TRANSFORM) return undefined
1514
+ if (transformBailReason(source, file)) return undefined
1515
+ const transformed = transformServerModule(source, file, serverDefineOptions().define)
1516
+ if ('bail' in transformed) return undefined
1462
1517
 
1463
- const { code } = transformed;
1464
- const edits: { start: number; end: number; value: string }[] = [];
1518
+ const { code } = transformed
1519
+ const edits: { start: number; end: number; value: string }[] = []
1465
1520
  for (const found of outputSpecifiers(code)) {
1466
1521
  const target = await devModuleSpecifierTarget(
1467
1522
  found.value,
@@ -1471,11 +1526,12 @@ async function transformDevModuleSource(
1471
1526
  clientReferenceSpecifiers,
1472
1527
  externalBundles,
1473
1528
  options,
1474
- );
1475
- if (target === undefined) return undefined;
1476
- if (target !== found.value) edits.push({ start: found.start, end: found.end, value: JSON.stringify(target) });
1529
+ )
1530
+ if (target === undefined) return undefined
1531
+ if (target !== found.value)
1532
+ edits.push({ start: found.start, end: found.end, value: JSON.stringify(target) })
1477
1533
  }
1478
- return spliceSource(code, edits);
1534
+ return spliceSource(code, edits)
1479
1535
  }
1480
1536
 
1481
1537
  /**
@@ -1495,11 +1551,11 @@ async function devModuleSpecifierTarget(
1495
1551
  // Same claim `serverAssetPlugin` makes, and in the same order: it is the first
1496
1552
  // onResolve in the build below.
1497
1553
  if (isServerIgnoredAssetSpecifier(specifier)) {
1498
- const resolved = resolveAssetPath(specifier, path.dirname(file));
1554
+ const resolved = resolveAssetPath(specifier, path.dirname(file))
1499
1555
  // A configured loader-rule chain (turbopack.rules) preempts the generic
1500
1556
  // asset stub — fall through to the rule-aware resolver below.
1501
1557
  if (!getAssetExtensions().hasLoaderRuleFor(resolved)) {
1502
- return pathToFileHref(await assetStubModule(config, resolved, devModuleProfile(options)));
1558
+ return pathToFileHref(await assetStubModule(config, resolved, devModuleProfile(options)))
1503
1559
  }
1504
1560
  }
1505
1561
  const resolved = await resolveDevModuleSpecifier(
@@ -1511,20 +1567,20 @@ async function devModuleSpecifierTarget(
1511
1567
  options,
1512
1568
  kind,
1513
1569
  file,
1514
- );
1570
+ )
1515
1571
  // A non-external result names a namespace the bundler loads inline
1516
1572
  // (`pnext-module-external` facades) or a bare require-alias path.
1517
- return resolved.external && typeof resolved.path === 'string' ? resolved.path : undefined;
1573
+ return resolved.external && typeof resolved.path === 'string' ? resolved.path : undefined
1518
1574
  }
1519
1575
 
1520
- const assetStubs = new Map<string, Promise<string>>();
1576
+ const assetStubs = new Map<string, Promise<string>>()
1521
1577
 
1522
1578
  /** The empty/static-asset/css-module stub the bundler used to inline, as a file. */
1523
1579
  async function assetStubModule(config: ResolvedConfig, resolved: string, profile: string) {
1524
- const base = splitHash(resolved).sourcePath.replace(/\?.*$/, '');
1525
- const outFile = `${await devModulePathFor(config, base, profile)}.pnext-asset.js`;
1526
- const existing = assetStubs.get(outFile);
1527
- if (existing) return existing;
1580
+ const base = splitHash(resolved).sourcePath.replace(/\?.*$/, '')
1581
+ const outFile = `${await devModulePathFor(config, base, profile)}.pnext-asset.js`
1582
+ const existing = assetStubs.get(outFile)
1583
+ if (existing) return existing
1528
1584
  const next = (async () => {
1529
1585
  const contents =
1530
1586
  getCssExtensions().loadCssModuleForClient(resolved) ??
@@ -1532,27 +1588,27 @@ async function assetStubModule(config: ResolvedConfig, resolved: string, profile
1532
1588
  ? ''
1533
1589
  : isStaticImageFile(base)
1534
1590
  ? await staticImageModuleSource(config, resolved)
1535
- : 'export default "";');
1536
- await mkdir(path.dirname(outFile), { recursive: true });
1537
- await writeCompiledFile(outFile, contents);
1538
- return outFile;
1591
+ : 'export default "";')
1592
+ await mkdir(path.dirname(outFile), { recursive: true })
1593
+ await writeCompiledFile(outFile, contents)
1594
+ return outFile
1539
1595
  })().catch(error => {
1540
- assetStubs.delete(outFile);
1541
- throw error;
1542
- });
1543
- assetStubs.set(outFile, next);
1544
- return next;
1596
+ assetStubs.delete(outFile)
1597
+ throw error
1598
+ })
1599
+ assetStubs.set(outFile, next)
1600
+ return next
1545
1601
  }
1546
1602
 
1547
1603
  function unwrapCommonJsDefaultExport(code: string) {
1548
- const match = /(^|\n)export default ([^;\n]+);/.exec(code);
1549
- if (!match?.[2]) return code;
1550
- const moduleName = uniqueIdentifier(code, '__pnext_cjs_module');
1551
- const defaultName = uniqueIdentifier(code, '__pnext_cjs_default', moduleName);
1604
+ const match = /(^|\n)export default ([^;\n]+);/.exec(code)
1605
+ if (!match?.[2]) return code
1606
+ const moduleName = uniqueIdentifier(code, '__pnext_cjs_module')
1607
+ const defaultName = uniqueIdentifier(code, '__pnext_cjs_default', moduleName)
1552
1608
  return code.replace(
1553
1609
  match[0],
1554
1610
  `${match[1]}var ${moduleName} = ${match[2]};\nvar ${defaultName} = ${moduleName} != null && "default" in Object(${moduleName}) ? ${moduleName}.default : ${moduleName};\nexport { ${defaultName} as default };`,
1555
- );
1611
+ )
1556
1612
  }
1557
1613
 
1558
1614
  function devModuleResolvePlugin(
@@ -1565,7 +1621,7 @@ function devModuleResolvePlugin(
1565
1621
  return {
1566
1622
  name: 'pnext-dev-module-resolve',
1567
1623
  setup(build) {
1568
- registerExternalLoadHandlers(build, config, options, externalLoadNamespaces.module);
1624
+ registerExternalLoadHandlers(build, config, options, externalLoadNamespaces.module)
1569
1625
  build.onResolve({ filter: /.*/ }, args =>
1570
1626
  resolveDevModuleSpecifier(
1571
1627
  args.path,
@@ -1577,9 +1633,9 @@ function devModuleResolvePlugin(
1577
1633
  args.kind,
1578
1634
  args.importer,
1579
1635
  ),
1580
- );
1636
+ )
1581
1637
  },
1582
- };
1638
+ }
1583
1639
  }
1584
1640
 
1585
1641
  function serverClientReferencePlugin(config: ResolvedConfig, options: DevModuleOptions): Plugin {
@@ -1587,9 +1643,9 @@ function serverClientReferencePlugin(config: ResolvedConfig, options: DevModuleO
1587
1643
  name: 'pnext-server-client-references',
1588
1644
  setup(build) {
1589
1645
  build.onLoad({ filter: /\.[cm]?[jt]sx?$/, namespace: 'file' }, async args => {
1590
- if (!options.stubClientImports) return undefined;
1591
- const source = await readText(args.path);
1592
- if (!hasUseClientDirective(source)) return undefined;
1646
+ if (!options.stubClientImports) return undefined
1647
+ const source = await readText(args.path)
1648
+ if (!hasUseClientDirective(source)) return undefined
1593
1649
  return {
1594
1650
  contents: clientReferenceModuleSource(
1595
1651
  args.path,
@@ -1597,43 +1653,45 @@ function serverClientReferencePlugin(config: ResolvedConfig, options: DevModuleO
1597
1653
  ),
1598
1654
  loader: 'js',
1599
1655
  resolveDir: path.dirname(args.path),
1600
- };
1601
- });
1656
+ }
1657
+ })
1602
1658
  },
1603
- };
1659
+ }
1604
1660
  }
1605
1661
 
1606
- const serverAssetNamespace = 'pnext-empty-server-asset';
1662
+ const serverAssetNamespace = 'pnext-empty-server-asset'
1607
1663
 
1608
1664
  function serverAssetPlugin(config: ResolvedConfig): Plugin {
1609
1665
  return {
1610
1666
  name: 'pnext-empty-server-assets',
1611
1667
  setup(build) {
1612
1668
  build.onResolve({ filter: serverIgnoredAssetFilter }, args => {
1613
- const resolved = resolveAssetPath(args.path, args.resolveDir);
1669
+ const resolved = resolveAssetPath(args.path, args.resolveDir)
1614
1670
  // A configured `turbopack.rules` loader chain for this extension (e.g.
1615
1671
  // `*.svg`) preempts the generic asset pipeline: defer so the compat
1616
1672
  // loader-rule plugin's onLoad runs the chain against the real file.
1617
1673
  // This is the plugin that builds app-page SERVER bundles, so without
1618
1674
  // this escape hatch a configured `*.svg` rule never runs here.
1619
- if (getAssetExtensions().hasLoaderRuleFor(resolved)) return undefined;
1620
- return { path: resolved, namespace: serverAssetNamespace };
1621
- });
1675
+ if (getAssetExtensions().hasLoaderRuleFor(resolved)) return undefined
1676
+ return { path: resolved, namespace: serverAssetNamespace }
1677
+ })
1622
1678
  build.onLoad({ filter: /.*/, namespace: serverAssetNamespace }, async args => {
1623
1679
  // Registry-provided CSS modules (e.g. *.module.scss) export their class
1624
1680
  // map on the server instead of the empty-asset stub.
1625
- const cssModule = getCssExtensions().loadCssModuleForClient(args.path);
1681
+ const cssModule = getCssExtensions().loadCssModuleForClient(args.path)
1626
1682
  return {
1627
- contents: cssModule ?? (isCssFile(args.path)
1628
- ? ''
1629
- : isStaticImageFile(args.path)
1630
- ? await staticImageModuleSource(config, args.path)
1631
- : 'export default "";'),
1683
+ contents:
1684
+ cssModule ??
1685
+ (isCssFile(args.path)
1686
+ ? ''
1687
+ : isStaticImageFile(args.path)
1688
+ ? await staticImageModuleSource(config, args.path)
1689
+ : 'export default "";'),
1632
1690
  loader: 'js',
1633
- };
1634
- });
1691
+ }
1692
+ })
1635
1693
  },
1636
- };
1694
+ }
1637
1695
  }
1638
1696
 
1639
1697
  async function resolveDevModuleSpecifier(
@@ -1652,8 +1710,8 @@ async function resolveDevModuleSpecifier(
1652
1710
  // client-reference stubbing as a bare specifier - importing the raw file yields an untagged third
1653
1711
  // module instance the render walk never recognizes as a client boundary.
1654
1712
  if (specifier.startsWith('file://')) {
1655
- const { sourcePath: hrefPath, hash: hrefHash } = splitHash(specifier);
1656
- const target = fileURLToPath(hrefPath);
1713
+ const { sourcePath: hrefPath, hash: hrefHash } = splitHash(specifier)
1714
+ const target = fileURLToPath(hrefPath)
1657
1715
  if (
1658
1716
  options.stubClientImports &&
1659
1717
  !isCssFile(target) &&
@@ -1661,11 +1719,15 @@ async function resolveDevModuleSpecifier(
1661
1719
  existsSync(target) &&
1662
1720
  (await fileHasUseClientDirective(target))
1663
1721
  ) {
1664
- const stub = await clientReferencePath(config, target);
1665
- await writeClientReferenceModule(stub, target, await clientReferenceExportNames(config, target));
1666
- return { path: `${pathToFileHref(stub)}${hrefHash}`, external: true };
1722
+ const stub = await clientReferencePath(config, target)
1723
+ await writeClientReferenceModule(
1724
+ stub,
1725
+ target,
1726
+ await clientReferenceExportNames(config, target),
1727
+ )
1728
+ return { path: `${pathToFileHref(stub)}${hrefHash}`, external: true }
1667
1729
  }
1668
- return { path: specifier, external: true };
1730
+ return { path: specifier, external: true }
1669
1731
  }
1670
1732
 
1671
1733
  // A `turbopack.rules` loader chain owns this specifier (`?query` and all):
@@ -1674,17 +1736,17 @@ async function resolveDevModuleSpecifier(
1674
1736
  const pendingRuleModule = getAssetExtensions().loaderRuleModule(
1675
1737
  specifier,
1676
1738
  importer && !importer.startsWith('<') ? importer : file,
1677
- );
1678
- const ruleModule = pendingRuleModule === undefined ? undefined : await pendingRuleModule;
1679
- if (ruleModule) return { path: pathToFileHref(ruleModule), external: true };
1739
+ )
1740
+ const ruleModule = pendingRuleModule === undefined ? undefined : await pendingRuleModule
1741
+ if (ruleModule) return { path: pathToFileHref(ruleModule), external: true }
1680
1742
 
1681
- const { sourcePath, hash } = splitHash(specifier);
1743
+ const { sourcePath, hash } = splitHash(specifier)
1682
1744
 
1683
1745
  const compatAlias =
1684
- pagesCompatSourceAlias(config, specifier, importer, options) ?? options.aliases[specifier];
1746
+ pagesCompatSourceAlias(config, specifier, importer, options) ?? options.aliases[specifier]
1685
1747
  if (compatAlias) {
1686
1748
  if (kind === 'require-call' && path.isAbsolute(compatAlias)) {
1687
- return { path: serverRequireAlias(specifier, compatAlias, options) };
1749
+ return { path: serverRequireAlias(specifier, compatAlias, options) }
1688
1750
  }
1689
1751
  // A compat alias pointing at a 'use client' file needs the SAME stub-and-match treatment as any
1690
1752
  // other local client component: the render tree walk recognizes a client boundary by a
@@ -1697,36 +1759,44 @@ async function resolveDevModuleSpecifier(
1697
1759
  !isCssFile(compatAlias) &&
1698
1760
  (await fileHasUseClientDirective(compatAlias))
1699
1761
  ) {
1700
- const stub = await clientReferencePath(config, compatAlias);
1701
- await writeClientReferenceModule(stub, compatAlias, await clientReferenceExportNames(config, compatAlias));
1702
- return { path: pathToFileHref(stub), external: true };
1762
+ const stub = await clientReferencePath(config, compatAlias)
1763
+ await writeClientReferenceModule(
1764
+ stub,
1765
+ compatAlias,
1766
+ await clientReferenceExportNames(config, compatAlias),
1767
+ )
1768
+ return { path: pathToFileHref(stub), external: true }
1703
1769
  }
1704
1770
  return {
1705
1771
  path: path.isAbsolute(compatAlias) ? pathToFileHref(compatAlias) : compatAlias,
1706
1772
  external: true,
1707
- };
1773
+ }
1708
1774
  }
1709
1775
 
1710
- const externalBundle = externalBundles.get(sourcePath);
1776
+ const externalBundle = externalBundles.get(sourcePath)
1711
1777
  if (externalBundle && kind === 'require-call') {
1712
1778
  const required = resolvePackageSpecifier(
1713
1779
  config.root,
1714
1780
  file,
1715
1781
  sourcePath,
1716
1782
  serverBundleRequireConditions(options.conditionTarget),
1717
- );
1718
- if (required) return { path: `${required}${hash}`, external: true };
1783
+ )
1784
+ if (required) return { path: `${required}${hash}`, external: true }
1719
1785
  }
1720
- if (externalBundle) return { path: `${externalBundle}${hash}`, external: true };
1786
+ if (externalBundle) return { path: `${externalBundle}${hash}`, external: true }
1721
1787
 
1722
- const clientReferenceStubPath = clientReferenceSpecifiers.get(sourcePath);
1723
- if (clientReferenceStubPath) return { path: `${pathToFileHref(clientReferenceStubPath)}${hash}`, external: true };
1788
+ const clientReferenceStubPath = clientReferenceSpecifiers.get(sourcePath)
1789
+ if (clientReferenceStubPath)
1790
+ return { path: `${pathToFileHref(clientReferenceStubPath)}${hash}`, external: true }
1724
1791
 
1725
1792
  if (
1726
1793
  sourcePath === './preact' &&
1727
1794
  path.resolve(importer) === new URL('../compat/react/client.ts', import.meta.url).pathname
1728
1795
  ) {
1729
- return { path: pathToFileHref(new URL('../compat/react/preact.ts', import.meta.url).pathname), external: true };
1796
+ return {
1797
+ path: pathToFileHref(new URL('../compat/react/preact.ts', import.meta.url).pathname),
1798
+ external: true,
1799
+ }
1730
1800
  }
1731
1801
 
1732
1802
  if (
@@ -1734,36 +1804,38 @@ async function resolveDevModuleSpecifier(
1734
1804
  path.resolve(importer) === new URL('../compat/react/preact.ts', import.meta.url).pathname
1735
1805
  ) {
1736
1806
  return {
1737
- path: pathToFileHref(new URL('../compat/client/errors/primitive-throw.ts', import.meta.url).pathname),
1807
+ path: pathToFileHref(
1808
+ new URL('../compat/client/errors/primitive-throw.ts', import.meta.url).pathname,
1809
+ ),
1738
1810
  external: true,
1739
- };
1811
+ }
1740
1812
  }
1741
1813
 
1742
1814
  const resolved = resolveImport(
1743
1815
  config.root,
1744
1816
  importer && !importer.startsWith('<') ? importer : file,
1745
1817
  sourcePath,
1746
- );
1818
+ )
1747
1819
  // A relative specifier that resolved names first-party source next to its importer, so compile it
1748
1820
  // through the normal pipeline even outside workspaceRoot (compat's own runtime files live inside
1749
1821
  // pnext's package, not the app's workspace). A bare specifier resolving outside workspaceRoot is a
1750
1822
  // package meant to stay external, untouched.
1751
- const isLocalSourceSpecifier = sourcePath.startsWith('.') || path.isAbsolute(sourcePath);
1823
+ const isLocalSourceSpecifier = sourcePath.startsWith('.') || path.isAbsolute(sourcePath)
1752
1824
  if (!resolved || (!isLocalSourceSpecifier && !isInside(config.workspaceRoot, resolved))) {
1753
1825
  const resolvedExternalLoad = resolveExternalLoadTarget({
1754
1826
  root: config.root,
1755
1827
  fromFile: file,
1756
1828
  specifier: sourcePath,
1757
1829
  target: options.externalLoadTarget,
1758
- });
1759
- if (resolvedExternalLoad && await externalLoadNeedsFacade(resolvedExternalLoad)) {
1830
+ })
1831
+ if (resolvedExternalLoad && (await externalLoadNeedsFacade(resolvedExternalLoad))) {
1760
1832
  return {
1761
1833
  path: resolvedExternalLoad,
1762
1834
  namespace: externalLoadNamespaces.module,
1763
- };
1835
+ }
1764
1836
  }
1765
1837
  const externalTarget =
1766
- resolvedExternalLoad ?? externalPackageImportTarget(config.root, file, sourcePath);
1838
+ resolvedExternalLoad ?? externalPackageImportTarget(config.root, file, sourcePath)
1767
1839
  return {
1768
1840
  path: externalTarget
1769
1841
  ? `${pathToFileHref(externalTarget)}${hash}`
@@ -1771,7 +1843,7 @@ async function resolveDevModuleSpecifier(
1771
1843
  ? runtimeServerImportTarget(specifier)
1772
1844
  : specifier,
1773
1845
  external: true,
1774
- };
1846
+ }
1775
1847
  }
1776
1848
  // A configured `resolveExtensions` order can make an EXTENSIONLESS specifier
1777
1849
  // land on an asset (`import img from './image'` -> image.png). The asset
@@ -1785,13 +1857,21 @@ async function resolveDevModuleSpecifier(
1785
1857
  !isCssFile(resolved) &&
1786
1858
  !getAssetExtensions().hasLoaderRuleFor(resolved)
1787
1859
  ) {
1788
- const stub = await assetStubModule(config, resolved, devModuleProfile(options));
1789
- return { path: pathToFileHref(stub), external: true };
1860
+ const stub = await assetStubModule(config, resolved, devModuleProfile(options))
1861
+ return { path: pathToFileHref(stub), external: true }
1790
1862
  }
1791
- if (options.stubClientImports && !isCssFile(resolved) && await fileHasUseClientDirective(resolved)) {
1792
- const stub = await clientReferencePath(config, resolved);
1793
- await writeClientReferenceModule(stub, resolved, await clientReferenceExportNames(config, resolved));
1794
- return { path: `${pathToFileHref(stub)}${hash}`, external: true };
1863
+ if (
1864
+ options.stubClientImports &&
1865
+ !isCssFile(resolved) &&
1866
+ (await fileHasUseClientDirective(resolved))
1867
+ ) {
1868
+ const stub = await clientReferencePath(config, resolved)
1869
+ await writeClientReferenceModule(
1870
+ stub,
1871
+ resolved,
1872
+ await clientReferenceExportNames(config, resolved),
1873
+ )
1874
+ return { path: `${pathToFileHref(stub)}${hash}`, external: true }
1795
1875
  }
1796
1876
  // The compiled-path rewrite below is only safe for imports the WRITE pass walks:
1797
1877
  // writeDevModuleUncached compiles the entry source's own local import targets, nothing deeper. A
@@ -1799,13 +1879,13 @@ async function resolveDevModuleSpecifier(
1799
1879
  // relative imports through this hook, and nothing ever writes those into the profile cache - the
1800
1880
  // import would resolve to a file that does not exist. Point such a transitive import at its own
1801
1881
  // source instead; Bun compiles the `.ts` on import.
1802
- const importerFile = importer && !importer.startsWith('<') ? path.resolve(importer) : '';
1882
+ const importerFile = importer && !importer.startsWith('<') ? path.resolve(importer) : ''
1803
1883
  const bundledCompatImporter =
1804
1884
  isLocalSourceSpecifier &&
1805
1885
  importerFile !== '' &&
1806
1886
  importerFile !== path.resolve(file) &&
1807
1887
  !isInside(config.workspaceRoot, importerFile) &&
1808
- !isInside(config.workspaceRoot, resolved);
1888
+ !isInside(config.workspaceRoot, resolved)
1809
1889
  // The `importerFile !== file` term above keeps this to imports the write pass misses: the entry's
1810
1890
  // OWN local imports are already walked by writeDevModuleUncached. Everything deeper - a relative
1811
1891
  // sibling of a compat file esbuild inlined into the entry - is nobody's job but this one, whether
@@ -1818,23 +1898,23 @@ async function resolveDevModuleSpecifier(
1818
1898
  // pointing at the raw source instead would load a SECOND copy of the compat runtime graph and split its
1819
1899
  // module state. writeDevModule dedupes in-flight builds per artifact path, so a module reached from
1820
1900
  // several bundled importers is compiled once.
1821
- await writeDevModule(config, resolved, new Set(), options);
1901
+ await writeDevModule(config, resolved, new Set(), options)
1822
1902
  }
1823
1903
  return {
1824
1904
  path: `${pathToFileHref(await devModulePath(config, resolved, options))}${hash}`,
1825
1905
  external: true,
1826
- };
1906
+ }
1827
1907
  }
1828
1908
 
1829
1909
  function serverRequireAlias(specifier: string, alias: string, options: DevModuleOptions) {
1830
- if (options.conditionTarget === 'client') return alias;
1910
+ if (options.conditionTarget === 'client') return alias
1831
1911
  if (specifier === 'next/navigation' && alias.endsWith(`${path.sep}navigation.ts`)) {
1832
- return path.join(path.dirname(alias), 'navigation.cjs');
1912
+ return path.join(path.dirname(alias), 'navigation.cjs')
1833
1913
  }
1834
1914
  if (specifier === 'next/router' && alias.endsWith(`${path.sep}router.ts`)) {
1835
- return path.join(path.dirname(alias), 'router.cjs');
1915
+ return path.join(path.dirname(alias), 'router.cjs')
1836
1916
  }
1837
- return alias;
1917
+ return alias
1838
1918
  }
1839
1919
 
1840
1920
  function registerExternalLoadHandlers(
@@ -1844,34 +1924,34 @@ function registerExternalLoadHandlers(
1844
1924
  namespace: string,
1845
1925
  ) {
1846
1926
  build.onResolve({ filter: /.*/, namespace }, args => {
1847
- const { sourcePath } = splitHash(args.path);
1927
+ const { sourcePath } = splitHash(args.path)
1848
1928
  const importer = args.importer.startsWith(`${namespace}:`)
1849
1929
  ? args.importer.slice(namespace.length + 1)
1850
- : args.importer;
1851
- const compatAlias = options.aliases[sourcePath];
1930
+ : args.importer
1931
+ const compatAlias = options.aliases[sourcePath]
1852
1932
  if (compatAlias) {
1853
1933
  return {
1854
1934
  path: path.isAbsolute(compatAlias) ? pathToFileHref(compatAlias) : compatAlias,
1855
1935
  external: true,
1856
- };
1936
+ }
1857
1937
  }
1858
1938
  const target = resolveExternalLoadTarget({
1859
1939
  root: config.root,
1860
1940
  fromFile: importer,
1861
1941
  specifier: sourcePath,
1862
1942
  target: options.externalLoadTarget,
1863
- });
1864
- if (target) return { path: target, namespace };
1943
+ })
1944
+ if (target) return { path: target, namespace }
1865
1945
  if (sourcePath.startsWith('.') || path.isAbsolute(sourcePath)) {
1866
- return { path: path.resolve(path.dirname(importer), sourcePath), namespace };
1946
+ return { path: path.resolve(path.dirname(importer), sourcePath), namespace }
1867
1947
  }
1868
- return { path: args.path, external: true };
1869
- });
1948
+ return { path: args.path, external: true }
1949
+ })
1870
1950
  build.onLoad({ filter: /.*/, namespace }, async args => ({
1871
1951
  contents: rewriteExternalLoadSpecifiers(await readText(args.path), args.path, config, options),
1872
1952
  loader: esbuildLoader(args.path),
1873
1953
  resolveDir: path.dirname(args.path),
1874
- }));
1954
+ }))
1875
1955
  }
1876
1956
 
1877
1957
  function rewriteExternalLoadSpecifiers(
@@ -1883,74 +1963,74 @@ function rewriteExternalLoadSpecifiers(
1883
1963
  // Folded onto the module record (PERF-REWRITES #28); side-effect imports keep
1884
1964
  // resolving through the host, as under the `from`-anchored regex.
1885
1965
  return rewriteSpecifierLiterals(source, file, (specifier, kind) => {
1886
- if (kind === 'side-effect' || !specifier.startsWith('#')) return undefined;
1966
+ if (kind === 'side-effect' || !specifier.startsWith('#')) return undefined
1887
1967
  const target = resolveExternalLoadTarget({
1888
1968
  root: config.root,
1889
1969
  fromFile: file,
1890
1970
  specifier,
1891
1971
  target: options.externalLoadTarget,
1892
- });
1893
- return target ? JSON.stringify(target) : undefined;
1894
- });
1972
+ })
1973
+ return target ? JSON.stringify(target) : undefined
1974
+ })
1895
1975
  }
1896
1976
 
1897
1977
  async function externalLoadNeedsFacade(file: string) {
1898
- const source = await readText(file).catch(() => '');
1899
- return /\bfrom\s*['"]#|\b(?:import|require)\s*\(\s*['"]#/.test(source);
1978
+ const source = await readText(file).catch(() => '')
1979
+ return /\bfrom\s*['"]#|\b(?:import|require)\s*\(\s*['"]#/.test(source)
1900
1980
  }
1901
1981
 
1902
1982
  function addExtensionlessDynamicImportGlobAliases(source: string) {
1903
- if (!source.includes('__glob({')) return source;
1904
- const lines = source.split('\n');
1905
- const next: string[] = [];
1906
- let inGlob = false;
1907
- const keys = new Set<string>();
1983
+ if (!source.includes('__glob({')) return source
1984
+ const lines = source.split('\n')
1985
+ const next: string[] = []
1986
+ let inGlob = false
1987
+ const keys = new Set<string>()
1908
1988
 
1909
1989
  for (const line of lines) {
1910
1990
  if (line.includes('__glob({')) {
1911
- inGlob = true;
1912
- keys.clear();
1991
+ inGlob = true
1992
+ keys.clear()
1913
1993
  }
1914
1994
  if (inGlob) {
1915
- const key = globKey(line);
1916
- if (key) keys.add(key);
1995
+ const key = globKey(line)
1996
+ if (key) keys.add(key)
1917
1997
  }
1918
- next.push(line);
1998
+ next.push(line)
1919
1999
  if (inGlob) {
1920
- const aliases = globAliases(line, keys);
2000
+ const aliases = globAliases(line, keys)
1921
2001
  if (aliases.length > 0) {
1922
- if (!line.endsWith(',')) next[next.length - 1] = `${line},`;
1923
- const value = line.replace(/^(\s*)"[^"]+":\s*/, '');
1924
- const normalizedValue = value.endsWith(',') ? value.slice(0, -1) : value;
2002
+ if (!line.endsWith(',')) next[next.length - 1] = `${line},`
2003
+ const value = line.replace(/^(\s*)"[^"]+":\s*/, '')
2004
+ const normalizedValue = value.endsWith(',') ? value.slice(0, -1) : value
1925
2005
  for (const alias of aliases) {
1926
- keys.add(alias);
1927
- next.push(`${line.match(/^\s*/)?.[0] ?? ''}${JSON.stringify(alias)}: ${normalizedValue},`);
2006
+ keys.add(alias)
2007
+ next.push(`${line.match(/^\s*/)?.[0] ?? ''}${JSON.stringify(alias)}: ${normalizedValue},`)
1928
2008
  }
1929
2009
  }
1930
2010
  }
1931
2011
  if (inGlob && line.trim() === '});') {
1932
- const previous = next[next.length - 2];
1933
- if (previous?.endsWith(',')) next[next.length - 2] = previous.slice(0, -1);
1934
- inGlob = false;
2012
+ const previous = next[next.length - 2]
2013
+ if (previous?.endsWith(',')) next[next.length - 2] = previous.slice(0, -1)
2014
+ inGlob = false
1935
2015
  }
1936
2016
  }
1937
2017
 
1938
- return next.join('\n');
2018
+ return next.join('\n')
1939
2019
  }
1940
2020
 
1941
2021
  function globKey(line: string) {
1942
- return /^\s*"([^"]+)":\s*/.exec(line)?.[1];
2022
+ return /^\s*"([^"]+)":\s*/.exec(line)?.[1]
1943
2023
  }
1944
2024
 
1945
2025
  function globAliases(line: string, existing: Set<string>) {
1946
- const key = globKey(line);
1947
- if (!key) return [];
1948
- const extension = path.extname(key);
1949
- if (!extensionlessDynamicImportExtensions.has(extension)) return [];
1950
- const aliases = [key.slice(0, -extension.length)];
1951
- const indexSuffix = `/index${extension}`;
1952
- if (key.endsWith(indexSuffix)) aliases.push(key.slice(0, -indexSuffix.length));
1953
- return aliases.filter(alias => alias && !existing.has(alias));
2026
+ const key = globKey(line)
2027
+ if (!key) return []
2028
+ const extension = path.extname(key)
2029
+ if (!extensionlessDynamicImportExtensions.has(extension)) return []
2030
+ const aliases = [key.slice(0, -extension.length)]
2031
+ const indexSuffix = `/index${extension}`
2032
+ if (key.endsWith(indexSuffix)) aliases.push(key.slice(0, -indexSuffix.length))
2033
+ return aliases.filter(alias => alias && !existing.has(alias))
1954
2034
  }
1955
2035
 
1956
2036
  async function externalBundleSpecifiers(
@@ -1959,12 +2039,14 @@ async function externalBundleSpecifiers(
1959
2039
  file: string,
1960
2040
  options: DevModuleOptions,
1961
2041
  ) {
1962
- const bundles = new Map<string, string>();
1963
- if (!options.bundleExternalPackages) return bundles;
2042
+ const bundles = new Map<string, string>()
2043
+ if (!options.bundleExternalPackages) return bundles
1964
2044
 
1965
- const candidates = [...new Set(importSpecifiers(source, file).map(specifier => splitHash(specifier).sourcePath))]
2045
+ const candidates = [
2046
+ ...new Set(importSpecifiers(source, file).map(specifier => splitHash(specifier).sourcePath)),
2047
+ ]
1966
2048
  .filter(specifier => !isServerIgnoredAssetSpecifier(specifier))
1967
- .filter(specifier => shouldBundleExternalPackage(specifier, config, file, options));
2049
+ .filter(specifier => shouldBundleExternalPackage(specifier, config, file, options))
1968
2050
  await Promise.all(
1969
2051
  candidates.map(async specifier => {
1970
2052
  bundles.set(
@@ -1976,10 +2058,10 @@ async function externalBundleSpecifiers(
1976
2058
  path.dirname(file),
1977
2059
  options.conditionTarget,
1978
2060
  ),
1979
- );
2061
+ )
1980
2062
  }),
1981
- );
1982
- return bundles;
2063
+ )
2064
+ return bundles
1983
2065
  }
1984
2066
 
1985
2067
  function shouldBundleExternalPackage(
@@ -1988,76 +2070,84 @@ function shouldBundleExternalPackage(
1988
2070
  file: string,
1989
2071
  options: DevModuleOptions,
1990
2072
  ) {
1991
- if (options.aliases[specifier]) return false;
1992
- if (specifier === 'server-only') return false;
1993
- if (!isPackageSpecifier(specifier)) return false;
1994
- const resolved = resolveImport(config.root, file, specifier);
1995
- const inWorkspace = resolved !== undefined && isInside(config.workspaceRoot, resolved);
2073
+ if (options.aliases[specifier]) return false
2074
+ if (specifier === 'server-only') return false
2075
+ if (!isPackageSpecifier(specifier)) return false
2076
+ const resolved = resolveImport(config.root, file, specifier)
2077
+ const inWorkspace = resolved !== undefined && isInside(config.workspaceRoot, resolved)
1996
2078
  // transpilePackages force-bundle; serverExternalPackages force-external (never bundled). A
1997
2079
  // transpiled package that resolves INSIDE the workspace is first-party source, not a registry
1998
2080
  // copy: it belongs to the batched module pipeline, which compiles it per file instead of
1999
2081
  // re-bundling its whole graph once per demanded subpath. Named imports survive that boundary
2000
2082
  // because CommonJS deps are now externalized or faceted rather than inlined. "Vendor" is registry
2001
2083
  // node_modules only.
2002
- const packageName = packageNameOfSpecifier(specifier);
2084
+ const packageName = packageNameOfSpecifier(specifier)
2003
2085
  if (packageName) {
2004
- const policy = getExternalPackagePolicy();
2005
- if (policy.transpile(packageName)) return !inWorkspace;
2006
- if (policy.external(packageName)) return false;
2086
+ const policy = getExternalPackagePolicy()
2087
+ if (policy.transpile(packageName)) return !inWorkspace
2088
+ if (policy.external(packageName)) return false
2007
2089
  }
2008
- return !inWorkspace;
2090
+ return !inWorkspace
2009
2091
  }
2010
2092
 
2011
2093
  function clientSsrAliases(config: ResolvedConfig) {
2012
2094
  return {
2013
2095
  ...coreAliases(config, 'client'),
2014
2096
  ...getImportAliasExtensions().clientSsrAliases(config),
2015
- };
2097
+ }
2016
2098
  }
2017
2099
 
2018
2100
  /** Absolute files of this module's dynamic(ssr:false) targets — the dev split points. */
2019
2101
  function deferredDynamicTargetFiles(source: string, config: ResolvedConfig, file: string) {
2020
- const specifiers = deferredDynamicImportSpecifiers(source, file);
2021
- if (specifiers.size === 0) return undefined;
2022
- const files = new Set<string>();
2102
+ const specifiers = deferredDynamicImportSpecifiers(source, file)
2103
+ if (specifiers.size === 0) return undefined
2104
+ const files = new Set<string>()
2023
2105
  for (const specifier of specifiers) {
2024
- const resolved = resolveLocalImport(config, file, specifier);
2025
- if (resolved) files.add(path.resolve(resolved));
2106
+ const resolved = resolveLocalImport(config, file, specifier)
2107
+ if (resolved) files.add(path.resolve(resolved))
2026
2108
  }
2027
- return files.size > 0 ? files : undefined;
2109
+ return files.size > 0 ? files : undefined
2028
2110
  }
2029
2111
 
2030
- function localImportTargets(source: string, config: ResolvedConfig, file: string, aliases: AliasMap) {
2031
- const targets: { target: string; isLocalSource: boolean }[] = [];
2112
+ function localImportTargets(
2113
+ source: string,
2114
+ config: ResolvedConfig,
2115
+ file: string,
2116
+ aliases: AliasMap,
2117
+ ) {
2118
+ const targets: { target: string; isLocalSource: boolean }[] = []
2032
2119
  for (const specifier of importSpecifiers(source, file)) {
2033
2120
  // Compat-aliased specifiers (next/server etc.) are rewritten at build
2034
2121
  // time; never resolve them locally — inside a workspace that ships a real
2035
2122
  // `next` package they would drag its whole module graph into the recursion.
2036
- const { sourcePath } = splitHash(specifier);
2037
- if (aliases[specifier] || aliases[sourcePath]) continue;
2123
+ const { sourcePath } = splitHash(specifier)
2124
+ if (aliases[specifier] || aliases[sourcePath]) continue
2038
2125
  // Non-css ignored assets (scss, fonts, images) are stubbed at build time;
2039
2126
  // compiling them as modules would fail the import scan. Css keeps its
2040
2127
  // copy-through so compiled css module paths stay importable.
2041
- if (isServerIgnoredAssetSpecifier(sourcePath) && !isCssFile(sourcePath)) continue;
2042
- const resolved = resolveImport(config.root, file, sourcePath);
2128
+ if (isServerIgnoredAssetSpecifier(sourcePath) && !isCssFile(sourcePath)) continue
2129
+ const resolved = resolveImport(config.root, file, sourcePath)
2043
2130
  // `resolveExtensions` can land an extensionless specifier on an asset
2044
2131
  // (`import img from './image'` -> image.png), which the specifier check
2045
2132
  // above misses — compiling the image bytes as a module fails the build.
2046
- if (resolved && isServerIgnoredAssetSpecifier(resolved) && !isCssFile(resolved)) continue;
2133
+ if (resolved && isServerIgnoredAssetSpecifier(resolved) && !isCssFile(resolved)) continue
2047
2134
  // A loader-rule source is not a module either: its chain output is
2048
2135
  // materialized by the resolvers, so compiling the raw file would only fail
2049
2136
  // the parse (`*.txt`, `*.test-file.ts`, …).
2050
- if (resolved && getAssetExtensions().hasLoaderRuleFor(resolved)) continue;
2137
+ if (resolved && getAssetExtensions().hasLoaderRuleFor(resolved)) continue
2051
2138
  // Our own emitted artifacts (a tree-shake facade the source above was just rewritten to point at, a
2052
2139
  // vendor bundle) are already compiled modules Bun imports directly. Feeding one back through the
2053
2140
  // pipeline compiles it a SECOND time, splitting module identity and pulling artifact names into the
2054
2141
  // source graph, where every boot's fresh names look like edits.
2055
2142
  if (resolved && !isInside(config.outPath, resolved)) {
2056
- targets.push({ target: resolved, isLocalSource: sourcePath.startsWith('.') || path.isAbsolute(sourcePath) });
2143
+ targets.push({
2144
+ target: resolved,
2145
+ isLocalSource: sourcePath.startsWith('.') || path.isAbsolute(sourcePath),
2146
+ })
2057
2147
  }
2058
2148
  }
2059
2149
 
2060
- return targets;
2150
+ return targets
2061
2151
  }
2062
2152
 
2063
2153
  async function writeClientReferenceModules(
@@ -2066,57 +2156,57 @@ async function writeClientReferenceModules(
2066
2156
  file: string,
2067
2157
  aliases: AliasMap,
2068
2158
  ) {
2069
- const references = new Map<string, { file: string; exports: Set<string> }>();
2159
+ const references = new Map<string, { file: string; exports: Set<string> }>()
2070
2160
 
2071
2161
  for (const specifier of importSpecifiers(source, file)) {
2072
- const { sourcePath } = splitHash(specifier);
2073
- if (aliases[specifier] || aliases[sourcePath]) continue;
2074
- const resolved = resolveImport(config.root, file, sourcePath);
2075
- if (!resolved || !isInside(config.workspaceRoot, resolved) || isCssFile(resolved)) continue;
2076
- if (!(await fileHasUseClientDirective(resolved))) continue;
2162
+ const { sourcePath } = splitHash(specifier)
2163
+ if (aliases[specifier] || aliases[sourcePath]) continue
2164
+ const resolved = resolveImport(config.root, file, sourcePath)
2165
+ if (!resolved || !isInside(config.workspaceRoot, resolved) || isCssFile(resolved)) continue
2166
+ if (!(await fileHasUseClientDirective(resolved))) continue
2077
2167
 
2078
- const entry = references.get(sourcePath) ?? { file: resolved, exports: new Set<string>() };
2168
+ const entry = references.get(sourcePath) ?? { file: resolved, exports: new Set<string>() }
2079
2169
  for (const exportName of await clientReferenceExportNames(config, resolved)) {
2080
- entry.exports.add(exportName);
2170
+ entry.exports.add(exportName)
2081
2171
  }
2082
- references.set(sourcePath, entry);
2172
+ references.set(sourcePath, entry)
2083
2173
  }
2084
2174
 
2085
- const specifiers = new Map<string, string>();
2086
- const files = new Set<string>();
2175
+ const specifiers = new Map<string, string>()
2176
+ const files = new Set<string>()
2087
2177
  await Promise.all(
2088
2178
  [...references.entries()].map(async ([specifier, reference]) => {
2089
- const stub = await clientReferencePath(config, reference.file);
2090
- await writeClientReferenceModule(stub, reference.file, [...reference.exports].sort());
2091
- specifiers.set(specifier, stub);
2092
- files.add(path.resolve(reference.file));
2179
+ const stub = await clientReferencePath(config, reference.file)
2180
+ await writeClientReferenceModule(stub, reference.file, [...reference.exports].sort())
2181
+ specifiers.set(specifier, stub)
2182
+ files.add(path.resolve(reference.file))
2093
2183
  }),
2094
- );
2184
+ )
2095
2185
 
2096
- return { specifiers, files };
2186
+ return { specifiers, files }
2097
2187
  }
2098
2188
 
2099
2189
  async function writeClientReferenceModule(file: string, sourceFile: string, exportNames: string[]) {
2100
- await mkdir(path.dirname(file), { recursive: true });
2101
- await writeCompiledFile(file, clientReferenceModuleSource(sourceFile, exportNames));
2190
+ await mkdir(path.dirname(file), { recursive: true })
2191
+ await writeCompiledFile(file, clientReferenceModuleSource(sourceFile, exportNames))
2102
2192
  }
2103
2193
 
2104
2194
  async function fileHasUseClientDirective(file: string) {
2105
- return hasUseClientDirective(await readText(file));
2195
+ return hasUseClientDirective(await readText(file))
2106
2196
  }
2107
2197
 
2108
2198
  function splitHash(specifier: string) {
2109
2199
  // A leading '#' is a subpath-import/tsconfig-paths specifier (e.g. '#/lib/x'),
2110
2200
  // not a fragment separator.
2111
- const index = specifier.startsWith('#') ? specifier.indexOf('#', 1) : specifier.indexOf('#');
2201
+ const index = specifier.startsWith('#') ? specifier.indexOf('#', 1) : specifier.indexOf('#')
2112
2202
  return index === -1
2113
2203
  ? { sourcePath: specifier, hash: '' }
2114
- : { sourcePath: specifier.slice(0, index), hash: specifier.slice(index) };
2204
+ : { sourcePath: specifier.slice(0, index), hash: specifier.slice(index) }
2115
2205
  }
2116
2206
 
2117
2207
  function isInside(root: string, file: string) {
2118
- const relative = path.relative(root, file);
2119
- return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
2208
+ const relative = path.relative(root, file)
2209
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))
2120
2210
  }
2121
2211
 
2122
2212
  function devModuleProfile(options: DevModuleOptions) {
@@ -2126,35 +2216,35 @@ function devModuleProfile(options: DevModuleOptions) {
2126
2216
  // compiled modules (and baked-in vendor hrefs) in separate cache dirs.
2127
2217
  return options.conditionTarget === 'client'
2128
2218
  ? options.profile
2129
- : `client-${options.conditionTarget}`;
2219
+ : `client-${options.conditionTarget}`
2130
2220
  }
2131
2221
  const externalTarget =
2132
2222
  options.externalLoadTarget === externalLoadTargetForConditionTarget(options.conditionTarget)
2133
2223
  ? ''
2134
- : `-external-${options.externalLoadTarget}`;
2135
- return `${options.profile}-${options.conditionTarget}${externalTarget}${options.reactServerLayer ? '-react-server' : ''}`;
2224
+ : `-external-${options.externalLoadTarget}`
2225
+ return `${options.profile}-${options.conditionTarget}${externalTarget}${options.reactServerLayer ? '-react-server' : ''}`
2136
2226
  }
2137
2227
 
2138
2228
  // Compiled artifacts are named `<source name>.<graph hash>.<ext>` under a per-profile directory that
2139
2229
  // mirrors the source tree. The hash covers the module's whole source graph, so an edit renames the edited
2140
2230
  // file and its dependents and nothing else - a stale artifact is simply never asked for.
2141
2231
  function devServerPath(config: ResolvedConfig, file: string, profile: string, hash: string) {
2142
- const key = devServerCacheKey(config, file);
2143
- const ext = path.extname(key) || '.js';
2232
+ const key = devServerCacheKey(config, file)
2233
+ const ext = path.extname(key) || '.js'
2144
2234
  // A compiled `.json` module is JS (esbuild's json loader wraps it in exports),
2145
2235
  // so it must not keep the `.json` name — the runtime would parse the artifact
2146
2236
  // as JSON. Keeping the source name in front of `.js` keeps it collision-free.
2147
- const outExt = ext === '.json' ? '.json.js' : ext;
2237
+ const outExt = ext === '.json' ? '.json.js' : ext
2148
2238
  return path.join(
2149
2239
  cacheRoot(config.outPath),
2150
2240
  profile === 'server' ? 'modules' : `modules-${profile}`,
2151
2241
  `${key.slice(0, key.length - ext.length)}.${hash}${outExt}`,
2152
- );
2242
+ )
2153
2243
  }
2154
2244
 
2155
2245
  /** The staged directory mirroring `dir` for `profile` (asset links live there). */
2156
2246
  function devServerDir(config: ResolvedConfig, dir: string, profile: string) {
2157
- return path.dirname(devServerPath(config, path.join(dir, '__pnext_context__.js'), profile, 'dir'));
2247
+ return path.dirname(devServerPath(config, path.join(dir, '__pnext_context__.js'), profile, 'dir'))
2158
2248
  }
2159
2249
 
2160
2250
  // A module compiled standalone (not merely as a transitive import of an
@@ -2168,23 +2258,23 @@ function devServerDir(config: ResolvedConfig, dir: string, profile: string) {
2168
2258
  // location once written there). Key those out-of-workspace files by a stable
2169
2259
  // hash of their absolute path instead of a relative path.
2170
2260
  function devServerCacheKey(config: ResolvedConfig, file: string) {
2171
- if (isInside(config.workspaceRoot, file)) return path.relative(config.workspaceRoot, file);
2261
+ if (isInside(config.workspaceRoot, file)) return path.relative(config.workspaceRoot, file)
2172
2262
  // Hash the portable identity, not the absolute path: a deployed build runs
2173
2263
  // these same files from a different root and must find the same artifact.
2174
2264
  const hash = createHash('sha256')
2175
2265
  .update(devSourceIdentity(file, config.workspaceRoot))
2176
2266
  .digest('hex')
2177
- .slice(0, 16);
2178
- return path.join('external', `${hash}${path.extname(file) || '.js'}`);
2267
+ .slice(0, 16)
2268
+ return path.join('external', `${hash}${path.extname(file) || '.js'}`)
2179
2269
  }
2180
2270
 
2181
2271
  // Bundle names memoized per (route, entries) so a warm request re-derives
2182
2272
  // nothing; any save clears the whole map (see clearDevRouteBundleKeys).
2183
- const routeBundleKeys = new Map<string, Promise<string>>();
2273
+ const routeBundleKeys = new Map<string, Promise<string>>()
2184
2274
 
2185
2275
  /** A save may rename any route bundle: drop every memoized name. */
2186
2276
  export function clearDevRouteBundleKeys() {
2187
- routeBundleKeys.clear();
2277
+ routeBundleKeys.clear()
2188
2278
  }
2189
2279
 
2190
2280
  // The route bundle inlines the route file and its layouts, so its name carries
@@ -2194,16 +2284,16 @@ function devRouteBundlePath(
2194
2284
  route: RouteManifestEntry,
2195
2285
  layoutFiles: string[],
2196
2286
  ) {
2197
- const entries = uniqueFiles([route.file, ...layoutFiles]);
2198
- const memoKey = `${route.id}\0${entries.join('\0')}`;
2199
- const memoized = routeBundleKeys.get(memoKey);
2200
- if (memoized) return memoized;
2287
+ const entries = uniqueFiles([route.file, ...layoutFiles])
2288
+ const memoKey = `${route.id}\0${entries.join('\0')}`
2289
+ const memoized = routeBundleKeys.get(memoKey)
2290
+ if (memoized) return memoized
2201
2291
  const next = computeDevRouteBundlePath(config, route, entries).catch(error => {
2202
- routeBundleKeys.delete(memoKey);
2203
- throw error;
2204
- });
2205
- routeBundleKeys.set(memoKey, next);
2206
- return next;
2292
+ routeBundleKeys.delete(memoKey)
2293
+ throw error
2294
+ })
2295
+ routeBundleKeys.set(memoKey, next)
2296
+ return next
2207
2297
  }
2208
2298
 
2209
2299
  async function computeDevRouteBundlePath(
@@ -2211,12 +2301,12 @@ async function computeDevRouteBundlePath(
2211
2301
  route: RouteManifestEntry,
2212
2302
  entries: string[],
2213
2303
  ) {
2214
- const graph = devModuleGraph(config);
2215
- const cached = await cachedRouteBundlePath(config, route.id, graph.graphKey, entries);
2216
- if (cached) return cached;
2217
- const hashes = await Promise.all(entries.map(file => graph.graphHash(file)));
2218
- const hash = createHash('sha256').update(hashes.join('\0')).digest('hex').slice(0, 16);
2219
- const bundle = path.join(cacheRoot(config.outPath), 'routes', `${route.id}.${hash}.mjs`);
2304
+ const graph = devModuleGraph(config)
2305
+ const cached = await cachedRouteBundlePath(config, route.id, graph.graphKey, entries)
2306
+ if (cached) return cached
2307
+ const hashes = await Promise.all(entries.map(file => graph.graphHash(file)))
2308
+ const hash = createHash('sha256').update(hashes.join('\0')).digest('hex').slice(0, 16)
2309
+ const bundle = path.join(cacheRoot(config.outPath), 'routes', `${route.id}.${hash}.mjs`)
2220
2310
  saveRouteBundlePath(
2221
2311
  config,
2222
2312
  route.id,
@@ -2224,82 +2314,85 @@ async function computeDevRouteBundlePath(
2224
2314
  entries,
2225
2315
  bundle,
2226
2316
  await graph.graphSources(entries),
2227
- );
2228
- return bundle;
2317
+ )
2318
+ return bundle
2229
2319
  }
2230
2320
 
2231
2321
  async function clientReferencePath(config: ResolvedConfig, file: string) {
2232
- return `${await devModulePathFor(config, file, 'compat')}.client-reference.ts`;
2322
+ return `${await devModulePathFor(config, file, 'compat')}.client-reference.ts`
2233
2323
  }
2234
2324
 
2235
2325
  function isCssFile(file: string) {
2236
- return file.endsWith('.css');
2326
+ return file.endsWith('.css')
2237
2327
  }
2238
2328
 
2239
2329
  function isStaticImageFile(file: string) {
2240
- return /\.(?:png|jpe?g|gif|webp|avif|svg|ico|bmp)(?:$|[?#])/.test(file);
2330
+ return /\.(?:png|jpe?g|gif|webp|avif|svg|ico|bmp)(?:$|[?#])/.test(file)
2241
2331
  }
2242
2332
 
2243
2333
  const serverIgnoredAssetFilter =
2244
- /\.(?:css|scss|sass|woff2?|ttf|otf|eot|png|jpe?g|gif|webp|avif|svg|ico|bmp)(?:$|[?#])/;
2334
+ /\.(?:css|scss|sass|woff2?|ttf|otf|eot|png|jpe?g|gif|webp|avif|svg|ico|bmp)(?:$|[?#])/
2245
2335
 
2246
2336
  function isServerIgnoredAssetSpecifier(specifier: string) {
2247
- return serverIgnoredAssetFilter.test(specifier);
2337
+ return serverIgnoredAssetFilter.test(specifier)
2248
2338
  }
2249
2339
 
2250
2340
  function esbuildLoader(file: string) {
2251
- if (file.endsWith('.tsx')) return 'tsx';
2252
- if (file.endsWith('.ts')) return 'ts';
2341
+ if (file.endsWith('.tsx')) return 'tsx'
2342
+ if (file.endsWith('.ts')) return 'ts'
2253
2343
  // `import data from './x.json'` is an ordinary module edge, so the walk
2254
2344
  // reaches the data file itself; parsing it as JS fails on the first key.
2255
- if (file.endsWith('.json')) return 'json';
2345
+ if (file.endsWith('.json')) return 'json'
2256
2346
  // App-convention .js/.jsx/.mjs modules may contain JSX (jsxImportSource is
2257
2347
  // preact via the build config), so parse them with the jsx loader. .tsx/.ts
2258
2348
  // keep their dedicated loaders above for the fast path.
2259
- return 'jsx';
2349
+ return 'jsx'
2260
2350
  }
2261
2351
 
2262
2352
  function isPackageSpecifier(specifier: string) {
2263
- if (specifier.startsWith('.') || specifier.startsWith('/') || specifier.startsWith('file:')) return false;
2264
- if (specifier.startsWith('#')) return false;
2265
- if (specifier.startsWith('node:')) return false;
2266
- if (specifier.includes(':')) return false;
2267
- const packageName = packageNameFromSpecifier(specifier);
2268
- return Boolean(packageName && !builtinModules.includes(packageName));
2353
+ if (specifier.startsWith('.') || specifier.startsWith('/') || specifier.startsWith('file:'))
2354
+ return false
2355
+ if (specifier.startsWith('#')) return false
2356
+ if (specifier.startsWith('node:')) return false
2357
+ if (specifier.includes(':')) return false
2358
+ const packageName = packageNameFromSpecifier(specifier)
2359
+ return Boolean(packageName && !builtinModules.includes(packageName))
2269
2360
  }
2270
2361
 
2271
2362
  function isBuiltinSpecifier(specifier: string) {
2272
- if (specifier.startsWith('node:')) return true;
2273
- const packageName = packageNameFromSpecifier(specifier);
2274
- return Boolean(packageName && builtinModules.includes(packageName));
2363
+ if (specifier.startsWith('node:')) return true
2364
+ const packageName = packageNameFromSpecifier(specifier)
2365
+ return Boolean(packageName && builtinModules.includes(packageName))
2275
2366
  }
2276
2367
 
2277
2368
  function packageNameFromSpecifier(specifier: string) {
2278
- const parts = specifier.split('/');
2279
- return specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
2369
+ const parts = specifier.split('/')
2370
+ return specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0]
2280
2371
  }
2281
2372
 
2282
2373
  function resolveLocalImport(config: ResolvedConfig, fromFile: string, specifier: string) {
2283
- if (path.isAbsolute(specifier)) return existsSync(specifier) ? specifier : undefined;
2284
- return resolveImport(config.root, fromFile, specifier);
2374
+ if (path.isAbsolute(specifier)) return existsSync(specifier) ? specifier : undefined
2375
+ return resolveImport(config.root, fromFile, specifier)
2285
2376
  }
2286
2377
 
2287
2378
  async function linkAssetContext(config: ResolvedConfig, file: string, profile: string) {
2288
- if (!isInside(config.root, file)) return;
2289
- const sourceDirs = ancestorDirs(config.root, path.dirname(file));
2290
- await Promise.all(sourceDirs.map(sourceDir => linkDirectoryAssetContext(config, sourceDir, profile)));
2379
+ if (!isInside(config.root, file)) return
2380
+ const sourceDirs = ancestorDirs(config.root, path.dirname(file))
2381
+ await Promise.all(
2382
+ sourceDirs.map(sourceDir => linkDirectoryAssetContext(config, sourceDir, profile)),
2383
+ )
2291
2384
  }
2292
2385
 
2293
2386
  function ancestorDirs(root: string, dir: string) {
2294
- const dirs = [root];
2295
- const relative = path.relative(root, dir);
2296
- if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) return dirs;
2297
- let current = root;
2387
+ const dirs = [root]
2388
+ const relative = path.relative(root, dir)
2389
+ if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) return dirs
2390
+ let current = root
2298
2391
  for (const segment of relative.split(path.sep).filter(Boolean)) {
2299
- current = path.join(current, segment);
2300
- dirs.push(current);
2392
+ current = path.join(current, segment)
2393
+ dirs.push(current)
2301
2394
  }
2302
- return dirs;
2395
+ return dirs
2303
2396
  }
2304
2397
 
2305
2398
  async function linkDirectoryAssetContext(
@@ -2307,35 +2400,35 @@ async function linkDirectoryAssetContext(
2307
2400
  sourceDir: string,
2308
2401
  profile: string,
2309
2402
  ) {
2310
- let entries: Dirent<string>[];
2403
+ let entries: Dirent<string>[]
2311
2404
  try {
2312
- entries = await readdir(sourceDir, { withFileTypes: true, encoding: 'utf8' });
2405
+ entries = await readdir(sourceDir, { withFileTypes: true, encoding: 'utf8' })
2313
2406
  } catch {
2314
- return;
2407
+ return
2315
2408
  }
2316
- const targetDir = devServerDir(config, sourceDir, profile);
2409
+ const targetDir = devServerDir(config, sourceDir, profile)
2317
2410
  await Promise.all(
2318
2411
  entries.map(async entry => {
2319
- if (!assetContextEntry(config, sourceDir, entry.name, entry.isDirectory())) return;
2320
- const source = path.join(sourceDir, entry.name);
2321
- const target = path.join(targetDir, entry.name);
2412
+ if (!assetContextEntry(config, sourceDir, entry.name, entry.isDirectory())) return
2413
+ const source = path.join(sourceDir, entry.name)
2414
+ const target = path.join(targetDir, entry.name)
2322
2415
  // Root-level sibling asset directories are mirrored, not symlinked wholesale: a `dir` symlink
2323
2416
  // would make every path beneath it - including code modules that must be compiled through the
2324
2417
  // alias transform - resolve straight back to raw source, shadowing the staged compile.
2325
2418
  // Mirroring links only non-code leaf files.
2326
2419
  if (entry.isDirectory()) {
2327
- await mirrorAssetDirectory(source, target);
2328
- return;
2420
+ await mirrorAssetDirectory(source, target)
2421
+ return
2329
2422
  }
2330
- if (existsSync(target)) return;
2331
- await mkdir(path.dirname(target), { recursive: true });
2423
+ if (existsSync(target)) return
2424
+ await mkdir(path.dirname(target), { recursive: true })
2332
2425
  try {
2333
- await symlink(source, target, 'file');
2426
+ await symlink(source, target, 'file')
2334
2427
  } catch {
2335
- if (!existsSync(target)) await copyFile(source, target);
2428
+ if (!existsSync(target)) await copyFile(source, target)
2336
2429
  }
2337
2430
  }),
2338
- );
2431
+ )
2339
2432
  }
2340
2433
 
2341
2434
  // Recursively stage an asset directory: link non-code leaf files and recurse
@@ -2343,31 +2436,31 @@ async function linkDirectoryAssetContext(
2343
2436
  // module graph into their own staged output and must not be shadowed by a raw
2344
2437
  // symlink.
2345
2438
  async function mirrorAssetDirectory(sourceDir: string, targetDir: string) {
2346
- let entries: Dirent<string>[];
2439
+ let entries: Dirent<string>[]
2347
2440
  try {
2348
- entries = await readdir(sourceDir, { withFileTypes: true, encoding: 'utf8' });
2441
+ entries = await readdir(sourceDir, { withFileTypes: true, encoding: 'utf8' })
2349
2442
  } catch {
2350
- return;
2443
+ return
2351
2444
  }
2352
2445
  await Promise.all(
2353
2446
  entries.map(async entry => {
2354
- if (entry.name.startsWith('.')) return;
2355
- const source = path.join(sourceDir, entry.name);
2356
- const target = path.join(targetDir, entry.name);
2447
+ if (entry.name.startsWith('.')) return
2448
+ const source = path.join(sourceDir, entry.name)
2449
+ const target = path.join(targetDir, entry.name)
2357
2450
  if (entry.isDirectory()) {
2358
- await mirrorAssetDirectory(source, target);
2359
- return;
2451
+ await mirrorAssetDirectory(source, target)
2452
+ return
2360
2453
  }
2361
- if (isCodeFile(entry.name)) return;
2362
- if (existsSync(target)) return;
2363
- await mkdir(path.dirname(target), { recursive: true });
2454
+ if (isCodeFile(entry.name)) return
2455
+ if (existsSync(target)) return
2456
+ await mkdir(path.dirname(target), { recursive: true })
2364
2457
  try {
2365
- await symlink(source, target, 'file');
2458
+ await symlink(source, target, 'file')
2366
2459
  } catch {
2367
- if (!existsSync(target)) await copyFile(source, target);
2460
+ if (!existsSync(target)) await copyFile(source, target)
2368
2461
  }
2369
2462
  }),
2370
- );
2463
+ )
2371
2464
  }
2372
2465
 
2373
2466
  function assetContextEntry(
@@ -2376,9 +2469,9 @@ function assetContextEntry(
2376
2469
  name: string,
2377
2470
  directory: boolean,
2378
2471
  ) {
2379
- if (name.startsWith('.')) return false;
2380
- if (!directory) return !isCodeFile(name);
2381
- if (sourceDir !== config.root) return false;
2472
+ if (name.startsWith('.')) return false
2473
+ if (!directory) return !isCodeFile(name)
2474
+ if (sourceDir !== config.root) return false
2382
2475
  return !new Set([
2383
2476
  'app',
2384
2477
  'src',
@@ -2386,40 +2479,40 @@ function assetContextEntry(
2386
2479
  'public',
2387
2480
  'node_modules',
2388
2481
  path.basename(config.outRootPath),
2389
- ]).has(name);
2482
+ ]).has(name)
2390
2483
  }
2391
2484
 
2392
2485
  function isCodeFile(file: string) {
2393
- if (/\.(?:tsx?|jsx?|mjs|cjs)$/.test(file)) return true;
2486
+ if (/\.(?:tsx?|jsx?|mjs|cjs)$/.test(file)) return true
2394
2487
  // Extra loadable source extensions registered by compat (e.g. mdx/md) compile
2395
2488
  // through the module graph, so they must not be symlinked raw as asset
2396
2489
  // context — otherwise the raw markdown shadows the compiled staged module.
2397
- const dot = file.lastIndexOf('.');
2398
- if (dot < 0) return false;
2399
- return extraLoadableExtensions().includes(file.slice(dot + 1));
2490
+ const dot = file.lastIndexOf('.')
2491
+ if (dot < 0) return false
2492
+ return extraLoadableExtensions().includes(file.slice(dot + 1))
2400
2493
  }
2401
2494
 
2402
2495
  function resolveAssetPath(specifier: string, resolveDir: string) {
2403
- const { sourcePath, hash } = splitHash(specifier);
2404
- return `${path.isAbsolute(sourcePath) ? sourcePath : path.resolve(resolveDir, sourcePath)}${hash}`;
2496
+ const { sourcePath, hash } = splitHash(specifier)
2497
+ return `${path.isAbsolute(sourcePath) ? sourcePath : path.resolve(resolveDir, sourcePath)}${hash}`
2405
2498
  }
2406
2499
 
2407
2500
  async function staticImageModuleSource(config: ResolvedConfig, file: string) {
2408
- const { sourcePath } = splitHash(file);
2409
- const bytes = new Uint8Array(await Bun.file(sourcePath).arrayBuffer());
2410
- const emitted: string[] = [];
2501
+ const { sourcePath } = splitHash(file)
2502
+ const bytes = new Uint8Array(await Bun.file(sourcePath).arrayBuffer())
2503
+ const emitted: string[] = []
2411
2504
  const emit = (relative: string) => {
2412
- emitted.push(relative);
2413
- return `/${relative}`;
2414
- };
2415
- const compat = await getAssetExtensions().staticAssetModule({ sourcePath, bytes, emit });
2416
- const source = compat ?? coreStaticAssetModule(sourcePath, bytes, emit);
2505
+ emitted.push(relative)
2506
+ return `/${relative}`
2507
+ }
2508
+ const compat = await getAssetExtensions().staticAssetModule({ sourcePath, bytes, emit })
2509
+ const source = compat ?? coreStaticAssetModule(sourcePath, bytes, emit)
2417
2510
  for (const relative of emitted) {
2418
- const target = path.join(config.outPath, 'public', ...relative.split('/'));
2419
- await mkdir(path.dirname(target), { recursive: true });
2420
- if (!existsSync(target)) await copyFile(sourcePath, target);
2511
+ const target = path.join(config.outPath, 'public', ...relative.split('/'))
2512
+ await mkdir(path.dirname(target), { recursive: true })
2513
+ if (!existsSync(target)) await copyFile(sourcePath, target)
2421
2514
  }
2422
- return source;
2515
+ return source
2423
2516
  }
2424
2517
 
2425
2518
  // Core's generic static-asset module (no compat override): emit under a hashed
@@ -2429,31 +2522,25 @@ function coreStaticAssetModule(
2429
2522
  bytes: Uint8Array,
2430
2523
  emit: (relative: string, bytes: Uint8Array) => string,
2431
2524
  ): string {
2432
- const ext = path.extname(sourcePath).toLowerCase() || '.bin';
2433
- const hash = createHash('sha256').update(bytes).digest('hex').slice(0, 8);
2434
- const base = path.basename(sourcePath, path.extname(sourcePath)).replace(/[^A-Za-z0-9_-]+/g, '-');
2435
- const relative = getAssetExtensions().staticAssetRelativePath({ sourcePath, hash, base, ext });
2436
- const src = emit(relative, bytes);
2437
- return `const src = ${JSON.stringify(src)};\nexport default src;\nexport { src };\n`;
2525
+ const ext = path.extname(sourcePath).toLowerCase() || '.bin'
2526
+ const hash = createHash('sha256').update(bytes).digest('hex').slice(0, 8)
2527
+ const base = path.basename(sourcePath, path.extname(sourcePath)).replace(/[^A-Za-z0-9_-]+/g, '-')
2528
+ const relative = getAssetExtensions().staticAssetRelativePath({ sourcePath, hash, base, ext })
2529
+ const src = emit(relative, bytes)
2530
+ return `const src = ${JSON.stringify(src)};\nexport default src;\nexport { src };\n`
2438
2531
  }
2439
2532
 
2440
2533
  function uniqueFiles(files: string[]) {
2441
- return [...new Set(files.map(file => path.resolve(file)).filter(file => existsSync(file)))];
2534
+ return [...new Set(files.map(file => path.resolve(file)).filter(file => existsSync(file)))]
2442
2535
  }
2443
2536
 
2444
2537
  async function profileDevImport<T>(label: string, task: () => Promise<T>) {
2445
2538
  // eslint-disable-next-line turbo/no-undeclared-env-vars
2446
- if (!process.env.PNEXT_DEV_PROFILE) return task();
2447
- const start = performance.now();
2539
+ if (!process.env.PNEXT_DEV_PROFILE) return task()
2540
+ const start = performance.now()
2448
2541
  try {
2449
- return await task();
2542
+ return await task()
2450
2543
  } finally {
2451
- console.log(`dev-import ${label} in ${formatDuration(performance.now() - start)}`);
2544
+ console.log(`dev-import ${label} in ${formatDuration(performance.now() - start)}`)
2452
2545
  }
2453
2546
  }
2454
-
2455
- function formatDuration(durationMs: number) {
2456
- if (durationMs < 10) return `${durationMs.toFixed(1)}ms`;
2457
- if (durationMs < 1000) return `${Math.round(durationMs)}ms`;
2458
- return `${(durationMs / 1000).toFixed(durationMs < 10000 ? 2 : 1)}s`;
2459
- }