@wular/pnext 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/README.md +153 -0
  2. package/bin/pnext +67 -0
  3. package/config/lint/base.js +48 -0
  4. package/config/ts/base.json +26 -0
  5. package/config/ts/react.json +13 -0
  6. package/package.json +70 -0
  7. package/reference/compat.md +63 -0
  8. package/reference/config.md +120 -0
  9. package/reference/css.md +58 -0
  10. package/reference/dev.md +69 -0
  11. package/reference/env.md +40 -0
  12. package/reference/metadata.md +86 -0
  13. package/reference/navigation.md +149 -0
  14. package/reference/overview.md +35 -0
  15. package/reference/performance.md +97 -0
  16. package/reference/rendering.md +127 -0
  17. package/reference/routing.md +167 -0
  18. package/reference/typegen.md +64 -0
  19. package/src/api/cache.ts +80 -0
  20. package/src/api/client-cache.ts +9 -0
  21. package/src/api/client-navigation.ts +279 -0
  22. package/src/api/dynamic.tsx +102 -0
  23. package/src/api/link.tsx +119 -0
  24. package/src/api/navigation.ts +198 -0
  25. package/src/api/router/events.ts +53 -0
  26. package/src/api/router/history.ts +70 -0
  27. package/src/api/router/hub.ts +194 -0
  28. package/src/api/router/policies.ts +107 -0
  29. package/src/api/router/runtime.ts +5238 -0
  30. package/src/api/router/types.ts +299 -0
  31. package/src/api/router.ts +167 -0
  32. package/src/api/server.ts +323 -0
  33. package/src/api/suspense.ts +16 -0
  34. package/src/cache/context.ts +61 -0
  35. package/src/cli/adapters/vercel-warm.ts +375 -0
  36. package/src/cli/adapters/vercel.ts +1310 -0
  37. package/src/cli/analyze-print.ts +181 -0
  38. package/src/cli/analyze.ts +328 -0
  39. package/src/cli/boot-trace.ts +29 -0
  40. package/src/cli/build.ts +3114 -0
  41. package/src/cli/dev.ts +276 -0
  42. package/src/cli/index.ts +196 -0
  43. package/src/cli/named-bin.ts +119 -0
  44. package/src/cli/serve-ui.ts +160 -0
  45. package/src/cli/start.ts +1425 -0
  46. package/src/client/build.ts +2136 -0
  47. package/src/client/chunk-fold.ts +526 -0
  48. package/src/client/entry.ts +1525 -0
  49. package/src/client/paths.ts +22 -0
  50. package/src/client/prebuilt.ts +621 -0
  51. package/src/client/profile.ts +75 -0
  52. package/src/client/react-compiler.ts +94 -0
  53. package/src/client/reference-stub.ts +145 -0
  54. package/src/client/reference.ts +47 -0
  55. package/src/compat/actions/action-client.ts +531 -0
  56. package/src/compat/actions/action-dispatch.ts +676 -0
  57. package/src/compat/actions/action-router.ts +40 -0
  58. package/src/compat/actions/action-shared.ts +95 -0
  59. package/src/compat/actions/client-plugin.ts +135 -0
  60. package/src/compat/actions/client-stub.ts +65 -0
  61. package/src/compat/actions/config.ts +164 -0
  62. package/src/compat/actions/detect.ts +208 -0
  63. package/src/compat/actions/discovery.ts +244 -0
  64. package/src/compat/actions/early-submit.ts +40 -0
  65. package/src/compat/actions/endpoint.ts +602 -0
  66. package/src/compat/actions/flight.ts +52 -0
  67. package/src/compat/actions/form-state.ts +73 -0
  68. package/src/compat/actions/hoist.ts +485 -0
  69. package/src/compat/actions/ids.ts +39 -0
  70. package/src/compat/actions/index.ts +41 -0
  71. package/src/compat/actions/instances.ts +144 -0
  72. package/src/compat/actions/origin.ts +109 -0
  73. package/src/compat/actions/protocol.ts +125 -0
  74. package/src/compat/actions/registry.ts +74 -0
  75. package/src/compat/actions/rewrite.ts +282 -0
  76. package/src/compat/actions/serve.ts +414 -0
  77. package/src/compat/actions/server-tag.ts +21 -0
  78. package/src/compat/actions/unrecognized-error.ts +30 -0
  79. package/src/compat/adapter/build-complete.ts +257 -0
  80. package/src/compat/bundler/bun-externals.ts +53 -0
  81. package/src/compat/bundler/cjs-exports.ts +542 -0
  82. package/src/compat/bundler/config.ts +363 -0
  83. package/src/compat/bundler/externals.ts +34 -0
  84. package/src/compat/bundler/import-meta-url.ts +60 -0
  85. package/src/compat/bundler/modularize-imports.ts +119 -0
  86. package/src/compat/bundler/new-url-asset.ts +87 -0
  87. package/src/compat/bundler/optimize-package-imports.ts +273 -0
  88. package/src/compat/bundler/polyfill.ts +88 -0
  89. package/src/compat/bundler/react-compiler.ts +61 -0
  90. package/src/compat/bundler/react-profiler.tsx +25 -0
  91. package/src/compat/bundler/relay-transform.ts +116 -0
  92. package/src/compat/bundler/require-context.ts +281 -0
  93. package/src/compat/bundler/resolve-extensions.ts +75 -0
  94. package/src/compat/bundler/source-cache.ts +61 -0
  95. package/src/compat/bundler/static-imports.ts +25 -0
  96. package/src/compat/bundler/symlink-imports.ts +119 -0
  97. package/src/compat/bundler/tsconfig-paths.ts +50 -0
  98. package/src/compat/bundler/wasm.ts +153 -0
  99. package/src/compat/bundler/webpack-loaders.ts +685 -0
  100. package/src/compat/bundler/worker.ts +278 -0
  101. package/src/compat/cache/build-flags.ts +81 -0
  102. package/src/compat/cache/build-prerender-errors.ts +163 -0
  103. package/src/compat/cache/custom-handler.ts +159 -0
  104. package/src/compat/cache/fetch-patch.ts +745 -0
  105. package/src/compat/cache/handler.ts +100 -0
  106. package/src/compat/cache/modern-handler.ts +275 -0
  107. package/src/compat/cache/resume-data-cache.ts +143 -0
  108. package/src/compat/cache/revalidate.ts +759 -0
  109. package/src/compat/cache/runtime-error.ts +124 -0
  110. package/src/compat/cache/use-cache-transform.ts +961 -0
  111. package/src/compat/cache/use-cache.ts +1695 -0
  112. package/src/compat/cache-control.ts +269 -0
  113. package/src/compat/client/base-path.ts +64 -0
  114. package/src/compat/client/css-order.ts +36 -0
  115. package/src/compat/client/errors/control-flow.ts +92 -0
  116. package/src/compat/client/errors/error-boundary.ts +222 -0
  117. package/src/compat/client/errors/global-error.ts +238 -0
  118. package/src/compat/client/errors/install.ts +217 -0
  119. package/src/compat/client/errors/lazy.ts +53 -0
  120. package/src/compat/client/errors/primitive-throw.ts +126 -0
  121. package/src/compat/client/errors/soft-refresh.ts +14 -0
  122. package/src/compat/client/link-status.ts +86 -0
  123. package/src/compat/client/nav-compat-runtime.ts +57 -0
  124. package/src/compat/client/nav-compat.ts +42 -0
  125. package/src/compat/client/navigation-scroll.ts +154 -0
  126. package/src/compat/client/optimistic-routing.ts +206 -0
  127. package/src/compat/client/prefetch-cache.ts +111 -0
  128. package/src/compat/client/route-announcer.ts +72 -0
  129. package/src/compat/client/segment-cache-policy.ts +159 -0
  130. package/src/compat/client/segment-cache.ts +1077 -0
  131. package/src/compat/client/segment-prefetch.ts +375 -0
  132. package/src/compat/client/trailing-slash.ts +24 -0
  133. package/src/compat/css/chunking.ts +254 -0
  134. package/src/compat/css/inline-css.ts +73 -0
  135. package/src/compat/css/lightningcss.ts +90 -0
  136. package/src/compat/css/modules.ts +373 -0
  137. package/src/compat/css/nonce.ts +30 -0
  138. package/src/compat/css/sass-plugin.ts +65 -0
  139. package/src/compat/css/sass.ts +392 -0
  140. package/src/compat/css/styled-jsx-runtime.ts +80 -0
  141. package/src/compat/css/styled-jsx.ts +49 -0
  142. package/src/compat/edge-runtime.ts +71 -0
  143. package/src/compat/export/client.ts +112 -0
  144. package/src/compat/export/index.ts +272 -0
  145. package/src/compat/export/standalone.ts +207 -0
  146. package/src/compat/image-optimizer/cache.ts +119 -0
  147. package/src/compat/image-optimizer/detect.ts +143 -0
  148. package/src/compat/image-optimizer/index.ts +601 -0
  149. package/src/compat/image-optimizer/source.ts +243 -0
  150. package/src/compat/index.ts +458 -0
  151. package/src/compat/lifecycle/after-scope.ts +86 -0
  152. package/src/compat/lifecycle/after.ts +173 -0
  153. package/src/compat/lifecycle/error-funnel.ts +306 -0
  154. package/src/compat/lifecycle/error-serialize.ts +87 -0
  155. package/src/compat/lifecycle/error-ui.ts +167 -0
  156. package/src/compat/lifecycle/instrumentation-client.ts +138 -0
  157. package/src/compat/lifecycle/instrumentation.ts +277 -0
  158. package/src/compat/lifecycle/node-console.ts +19 -0
  159. package/src/compat/lifecycle/testmode.ts +263 -0
  160. package/src/compat/mdx/compile.ts +219 -0
  161. package/src/compat/mdx/next-mdx-stub.ts +46 -0
  162. package/src/compat/mdx/plugin.ts +37 -0
  163. package/src/compat/metadata-route-artifacts.ts +458 -0
  164. package/src/compat/metadata.ts +295 -0
  165. package/src/compat/middleware/manifest.ts +210 -0
  166. package/src/compat/misc/action-return.ts +173 -0
  167. package/src/compat/next/cache.ts +211 -0
  168. package/src/compat/next/canonical-url.ts +35 -0
  169. package/src/compat/next/client-cache.ts +57 -0
  170. package/src/compat/next/client-navigation.ts +313 -0
  171. package/src/compat/next/client-only.ts +3 -0
  172. package/src/compat/next/client-script.tsx +215 -0
  173. package/src/compat/next/client-server.ts +39 -0
  174. package/src/compat/next/config-loader.ts +569 -0
  175. package/src/compat/next/config.ts +29 -0
  176. package/src/compat/next/constants.cjs +6 -0
  177. package/src/compat/next/constants.ts +6 -0
  178. package/src/compat/next/custom-server.ts +236 -0
  179. package/src/compat/next/dist/client/components/app-router-headers.ts +32 -0
  180. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +38 -0
  181. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -0
  182. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -0
  183. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -0
  184. package/src/compat/next/dynamic.tsx +46 -0
  185. package/src/compat/next/error.tsx +148 -0
  186. package/src/compat/next/font/cache.ts +171 -0
  187. package/src/compat/next/font/google.ts +2 -0
  188. package/src/compat/next/font/index.ts +8 -0
  189. package/src/compat/next/font/local.ts +5 -0
  190. package/src/compat/next/font/runtime-client.ts +71 -0
  191. package/src/compat/next/font/runtime.ts +974 -0
  192. package/src/compat/next/font/shared.ts +281 -0
  193. package/src/compat/next/form.tsx +156 -0
  194. package/src/compat/next/head.tsx +10 -0
  195. package/src/compat/next/headers.ts +247 -0
  196. package/src/compat/next/image/config.ts +196 -0
  197. package/src/compat/next/image/optimizer.ts +96 -0
  198. package/src/compat/next/image/patterns.ts +103 -0
  199. package/src/compat/next/image/shared.ts +141 -0
  200. package/src/compat/next/image/static-metadata.ts +283 -0
  201. package/src/compat/next/image/validate.ts +269 -0
  202. package/src/compat/next/image-client.tsx +215 -0
  203. package/src/compat/next/image-props.ts +575 -0
  204. package/src/compat/next/image-usage.ts +102 -0
  205. package/src/compat/next/image.tsx +56 -0
  206. package/src/compat/next/index.ts +1 -0
  207. package/src/compat/next/legacy-image.tsx +97 -0
  208. package/src/compat/next/link-usage.ts +29 -0
  209. package/src/compat/next/link-validation-transform.ts +200 -0
  210. package/src/compat/next/link.tsx +466 -0
  211. package/src/compat/next/navigation.cjs +21 -0
  212. package/src/compat/next/navigation.ts +188 -0
  213. package/src/compat/next/offline.ts +51 -0
  214. package/src/compat/next/og.ts +324 -0
  215. package/src/compat/next/optimistic-route-state.ts +188 -0
  216. package/src/compat/next/preferred-region.ts +39 -0
  217. package/src/compat/next/redirects.ts +131 -0
  218. package/src/compat/next/resource-hints.ts +136 -0
  219. package/src/compat/next/rewrites.ts +350 -0
  220. package/src/compat/next/root-params.ts +142 -0
  221. package/src/compat/next/router.cjs +49 -0
  222. package/src/compat/next/router.ts +143 -0
  223. package/src/compat/next/script.tsx +355 -0
  224. package/src/compat/next/server-only.ts +3 -0
  225. package/src/compat/next/server.ts +28 -0
  226. package/src/compat/next/svgr.ts +58 -0
  227. package/src/compat/next/telemetry.ts +77 -0
  228. package/src/compat/next/user-agent.ts +100 -0
  229. package/src/compat/next/web-vitals.ts +56 -0
  230. package/src/compat/otel/api.ts +95 -0
  231. package/src/compat/otel/client-trace-metadata.ts +71 -0
  232. package/src/compat/otel/fetch-span.ts +77 -0
  233. package/src/compat/otel/tracer.ts +944 -0
  234. package/src/compat/pages/client-plugin.ts +108 -0
  235. package/src/compat/pages/index.ts +527 -0
  236. package/src/compat/pages/router-state.ts +94 -0
  237. package/src/compat/ppr/io.ts +38 -0
  238. package/src/compat/ppr/missing-root-params.ts +105 -0
  239. package/src/compat/ppr/root-params-scan.ts +164 -0
  240. package/src/compat/ppr/root-params-transform.ts +75 -0
  241. package/src/compat/ppr/root-params.ts +129 -0
  242. package/src/compat/ppr/segment-config-incompat.ts +34 -0
  243. package/src/compat/protocol.ts +202 -0
  244. package/src/compat/react/client.ts +59 -0
  245. package/src/compat/react/compiler-runtime.ts +60 -0
  246. package/src/compat/react/dom-client.ts +115 -0
  247. package/src/compat/react/dom-react-server.ts +20 -0
  248. package/src/compat/react/dom-server.ts +40 -0
  249. package/src/compat/react/dom.ts +154 -0
  250. package/src/compat/react/preact.ts +522 -0
  251. package/src/compat/react/react-server.ts +84 -0
  252. package/src/compat/react/router-shim.ts +26 -0
  253. package/src/compat/react/server-component-use.ts +48 -0
  254. package/src/compat/react/server-inserted-html.ts +87 -0
  255. package/src/compat/react/server.ts +156 -0
  256. package/src/compat/react/view-transition.ts +60 -0
  257. package/src/compat/register/actions.ts +875 -0
  258. package/src/compat/register/boot.ts +141 -0
  259. package/src/compat/register/build-tier.ts +11 -0
  260. package/src/compat/register/build.ts +182 -0
  261. package/src/compat/register/bundler.ts +587 -0
  262. package/src/compat/register/cache.ts +85 -0
  263. package/src/compat/register/client-errors.ts +18 -0
  264. package/src/compat/register/config.ts +17 -0
  265. package/src/compat/register/css-extras.ts +120 -0
  266. package/src/compat/register/edge-runtime.ts +6 -0
  267. package/src/compat/register/errors.ts +46 -0
  268. package/src/compat/register/export.ts +23 -0
  269. package/src/compat/register/font.ts +36 -0
  270. package/src/compat/register/hooks.ts +34 -0
  271. package/src/compat/register/image.ts +133 -0
  272. package/src/compat/register/index.ts +111 -0
  273. package/src/compat/register/instrumentation-client.ts +35 -0
  274. package/src/compat/register/lifecycle.ts +86 -0
  275. package/src/compat/register/mdx.ts +48 -0
  276. package/src/compat/register/middleware.ts +36 -0
  277. package/src/compat/register/misc.ts +44 -0
  278. package/src/compat/register/otel.ts +288 -0
  279. package/src/compat/register/pages-api.ts +473 -0
  280. package/src/compat/register/ppr.ts +56 -0
  281. package/src/compat/register/protocol.ts +57 -0
  282. package/src/compat/register/proxy.ts +127 -0
  283. package/src/compat/register/render.ts +268 -0
  284. package/src/compat/register/routing.ts +410 -0
  285. package/src/compat/register/segment.ts +1903 -0
  286. package/src/compat/register/static-image.ts +21 -0
  287. package/src/compat/register/typed-routes.ts +35 -0
  288. package/src/compat/register/usecache.ts +131 -0
  289. package/src/compat/register/validation.ts +56 -0
  290. package/src/compat/segment/loading-boundary.ts +113 -0
  291. package/src/compat/segment/page-slot.ts +200 -0
  292. package/src/compat/segment/tree.ts +481 -0
  293. package/src/compat/segment/vary-key.ts +102 -0
  294. package/src/compat/segment/vary-params.ts +551 -0
  295. package/src/compat/static-params.ts +33 -0
  296. package/src/compat/tsconfig-defaults.ts +301 -0
  297. package/src/compat/typecheck/index.ts +1481 -0
  298. package/src/compat/typecheck/worker.ts +26 -0
  299. package/src/compat/typed-routes/index.ts +92 -0
  300. package/src/compat/typed-routes/manifest.ts +356 -0
  301. package/src/compat/typed-routes/typegen.ts +566 -0
  302. package/src/compat/validation/errors.ts +159 -0
  303. package/src/compat/validation/index.ts +1770 -0
  304. package/src/compat/validation/prerender-diagnostics.ts +1508 -0
  305. package/src/compat-bootstrap.ts +67 -0
  306. package/src/config.ts +218 -0
  307. package/src/css/build.ts +697 -0
  308. package/src/css/index.ts +2 -0
  309. package/src/css/postcss.ts +236 -0
  310. package/src/css/worker.ts +34 -0
  311. package/src/dev/client-actions.ts +35 -0
  312. package/src/dev/client-chunk-store.ts +92 -0
  313. package/src/dev/client-key-cache.ts +178 -0
  314. package/src/dev/global-css-cache.ts +212 -0
  315. package/src/dev/imports.ts +2430 -0
  316. package/src/dev/module-cache.ts +721 -0
  317. package/src/dev/module-generations.ts +38 -0
  318. package/src/dev/module-transform.ts +188 -0
  319. package/src/dev/node-module-bundle-cache.ts +63 -0
  320. package/src/dev/restart-cache.ts +10 -0
  321. package/src/dev/route-bundle-key-cache.ts +154 -0
  322. package/src/dev/route-facts-cache.ts +223 -0
  323. package/src/dev/server.ts +1710 -0
  324. package/src/dynamic/source.ts +307 -0
  325. package/src/dynamic/tree-shake.ts +262 -0
  326. package/src/env.ts +92 -0
  327. package/src/extensions.ts +1898 -0
  328. package/src/index.ts +34 -0
  329. package/src/internal.ts +43 -0
  330. package/src/islands/boundary-error.ts +8 -0
  331. package/src/islands/static-children.ts +37 -0
  332. package/src/islands/static-slots.ts +106 -0
  333. package/src/ppr-postpone.ts +24 -0
  334. package/src/ppr.ts +784 -0
  335. package/src/proxy.ts +752 -0
  336. package/src/render/hooks.ts +384 -0
  337. package/src/render/index.ts +1 -0
  338. package/src/render/island-context.ts +47 -0
  339. package/src/render/metadata.ts +857 -0
  340. package/src/render/renderer.ts +7391 -0
  341. package/src/render/resource-hints.ts +44 -0
  342. package/src/render/slots.tsx +679 -0
  343. package/src/request/context.ts +396 -0
  344. package/src/resolve/engine.ts +219 -0
  345. package/src/resolve/imports.ts +1104 -0
  346. package/src/resolve/scan-facts.ts +474 -0
  347. package/src/resolve/source-text.ts +86 -0
  348. package/src/routing/forwarded.ts +41 -0
  349. package/src/routing/handler.ts +271 -0
  350. package/src/routing/href.ts +203 -0
  351. package/src/routing/metadata.ts +1018 -0
  352. package/src/routing/request-runtime.ts +43 -0
  353. package/src/routing/routes.ts +2560 -0
  354. package/src/routing/slots.ts +432 -0
  355. package/src/runtime/server.ts +3453 -0
  356. package/src/runtime/vendor.ts +1160 -0
  357. package/src/style-modules.d.ts +9 -0
  358. package/src/typegen.ts +151 -0
  359. package/src/types.ts +725 -0
  360. package/src/utils/ansi.ts +9 -0
  361. package/src/utils/content-type.ts +31 -0
  362. package/src/utils/decode.ts +7 -0
  363. package/src/utils/dev-profile.ts +31 -0
  364. package/src/utils/error-log.ts +29 -0
  365. package/src/utils/fs-cache.ts +31 -0
  366. package/src/utils/fs.ts +119 -0
  367. package/src/utils/html.ts +46 -0
  368. package/src/utils/serialize.ts +378 -0
  369. package/src/utils/source.ts +35 -0
  370. package/src/utils/verbose.ts +39 -0
  371. package/tsconfig.json +10 -0
@@ -0,0 +1,263 @@
1
+ // testmode / experimental.testProxy (COMPAT).
2
+ //
3
+ // When a request carries `Next-Test-Proxy-Port` + `Next-Test-Data` headers, all outgoing fetch during
4
+ // that request is proxied to the named localhost port following Next's testmode wire protocol. A
5
+ // request interceptor stores the proxy info for the duration of the request; global.fetch is patched
6
+ // exactly once at init and consults it - a no-op when no proxy info is active. Gated on
7
+ // experimental.testProxy in next.config.
8
+
9
+ import { PassThrough } from 'node:stream';
10
+ import type * as http from 'node:http';
11
+ import type * as https from 'node:https';
12
+ import type { IncomingMessage } from 'node:http';
13
+ import { createRequire } from 'node:module';
14
+ import { currentRequest, getWorkUnit } from '../../request/context';
15
+
16
+ // `import * as http` yields a frozen ES module namespace object under strict
17
+ // runtimes (Bun); assigning to `.get` on it throws "Attempted to assign to
18
+ // readonly property." `require()` returns the real, mutable CJS exports
19
+ // object that node/http/https resolve `.get` calls through at runtime, so
20
+ // patch that instead of the namespace binding.
21
+ const requireNode = createRequire(import.meta.url);
22
+ const httpModule = requireNode('node:http') as typeof http;
23
+ const httpsModule = requireNode('node:https') as typeof https;
24
+
25
+ interface TestProxyInfo {
26
+ proxyPort: number;
27
+ testData: string;
28
+ }
29
+
30
+ /**
31
+ * The proxy info rides on the core work unit (which spans the whole request lifecycle) rather than a
32
+ * private ALS, so the patched global.fetch - invoked deep inside a render or handler - can recover it
33
+ * via getWorkUnit(). The interceptor sets it before matching; the fetch patch reads it.
34
+ */
35
+ const TEST_PROXY_KEY = Symbol.for('pnext.testProxy');
36
+
37
+ /** Extract proxy info from a request's Next-Test-* headers, if present. */
38
+ export function readTestProxyInfo(headers: Headers): TestProxyInfo | undefined {
39
+ const portHeader = headers.get('next-test-proxy-port');
40
+ if (!portHeader) return undefined;
41
+ return { proxyPort: Number(portHeader), testData: headers.get('next-test-data') ?? '' };
42
+ }
43
+
44
+ /** Record the request's test-proxy info onto the active work unit (if any). */
45
+ export function activateTestProxy(headers: Headers): void {
46
+ const info = readTestProxyInfo(headers);
47
+ if (!info) return;
48
+ const unit = getWorkUnit();
49
+ if (!unit) return;
50
+ (unit.compat ??= {})[TEST_PROXY_KEY] = info;
51
+ }
52
+
53
+ function currentTestProxyInfo(): TestProxyInfo | undefined {
54
+ const workUnitInfo = getWorkUnit()?.compat?.[TEST_PROXY_KEY] as TestProxyInfo | undefined;
55
+ if (workUnitInfo) return workUnitInfo;
56
+ const request = currentRequest();
57
+ if (!request) return undefined;
58
+ return readTestProxyInfo(request.headers);
59
+ }
60
+
61
+ // pnext marks its own proxy round-trips via `next.internal` so the patched
62
+ // fetch (below) doesn't re-intercept them. The `next` field is typed as
63
+ // NextFetchRequestConfig globally, so extend it via intersection rather than
64
+ // interface-extends (which errors on the conflicting `next` property).
65
+ type InternalInit = RequestInit & { next?: { internal?: boolean } };
66
+
67
+ interface ProxyFetchRequest {
68
+ testData: string;
69
+ api: 'fetch';
70
+ request: {
71
+ url: string;
72
+ method: string;
73
+ headers: [string, string][];
74
+ body: string | null;
75
+ cache: RequestCache;
76
+ credentials: RequestCredentials;
77
+ integrity: string;
78
+ mode: RequestMode;
79
+ redirect: RequestRedirect;
80
+ referrer: string;
81
+ referrerPolicy: ReferrerPolicy;
82
+ };
83
+ }
84
+
85
+ async function buildProxyRequest(testData: string, request: Request): Promise<ProxyFetchRequest> {
86
+ return {
87
+ testData,
88
+ api: 'fetch',
89
+ request: {
90
+ url: request.url,
91
+ method: request.method,
92
+ headers: [
93
+ ...Array.from(request.headers),
94
+ ['next-test-stack', getTestStack()],
95
+ ],
96
+ body: request.body ? Buffer.from(await request.arrayBuffer()).toString('base64') : null,
97
+ cache: request.cache,
98
+ credentials: request.credentials,
99
+ integrity: request.integrity,
100
+ // Bun defaults a Request built from a URL string to mode 'navigate'
101
+ // (undici/browsers use 'cors'). 'navigate' cannot be reconstructed via
102
+ // the Request constructor, so the test proxy's buildRequest() would throw
103
+ // "invalid request mode navigate". Normalize it to the browser default so
104
+ // the proxy can rebuild the forwarded request.
105
+ mode: request.mode === 'navigate' ? 'cors' : request.mode,
106
+ redirect: request.redirect,
107
+ referrer: request.referrer,
108
+ referrerPolicy: request.referrerPolicy,
109
+ },
110
+ };
111
+ }
112
+
113
+ function getTestStack(): string {
114
+ let stack = (new Error().stack ?? '').split('\n');
115
+ for (let i = 1; i < stack.length; i++) {
116
+ const line = stack[i];
117
+ if (line && line.length > 0) {
118
+ stack = stack.slice(i);
119
+ break;
120
+ }
121
+ }
122
+ stack = stack.filter(item => !item.includes('/next/dist/'));
123
+ stack = stack.slice(0, 5);
124
+ stack = stack.map(line => line.replace('webpack-internal:///(rsc)/', '').trim());
125
+ return stack.join(' ');
126
+ }
127
+
128
+ interface ProxyResponse {
129
+ api: 'continue' | 'abort' | 'unhandled' | 'fetch';
130
+ response?: { status: number; headers: [string, string][]; body: string | null };
131
+ }
132
+
133
+ async function handleProxiedFetch(
134
+ originalFetch: typeof fetch,
135
+ info: TestProxyInfo,
136
+ request: Request,
137
+ ): Promise<Response> {
138
+ const proxyRequest = await buildProxyRequest(info.testData, request);
139
+ const resp = await originalFetch(`http://localhost:${info.proxyPort}`, {
140
+ method: 'POST',
141
+ body: JSON.stringify(proxyRequest),
142
+ next: { internal: true },
143
+ } as InternalInit);
144
+ if (!resp.ok) throw new Error(`Proxy request failed: ${resp.status}`);
145
+ const proxyResponse = (await resp.json()) as ProxyResponse;
146
+ switch (proxyResponse.api) {
147
+ case 'continue':
148
+ return originalFetch(request);
149
+ case 'fetch': {
150
+ const r = proxyResponse.response!;
151
+ return new Response(r.body ? Buffer.from(r.body, 'base64') : null, {
152
+ status: r.status,
153
+ headers: new Headers(r.headers),
154
+ });
155
+ }
156
+ default:
157
+ throw new Error(`Proxy request aborted [${request.method} ${request.url}]`);
158
+ }
159
+ }
160
+
161
+ type TestGetCallback = (res: IncomingMessage) => void;
162
+ type HttpGetInput = Parameters<typeof http.get>[0];
163
+
164
+ function buildResponseMessage(responsePromise: Promise<Response>, callback?: TestGetCallback): IncomingMessage {
165
+ const stream = new PassThrough();
166
+ const message = stream as unknown as IncomingMessage;
167
+ void responsePromise.then(async response => {
168
+ const headers: Record<string, string> = {};
169
+ const rawHeaders: string[] = [];
170
+ for (const [name, value] of response.headers) {
171
+ headers[name.toLowerCase()] = value;
172
+ rawHeaders.push(name, value);
173
+ }
174
+ message.statusCode = response.status;
175
+ message.statusMessage = response.statusText;
176
+ message.headers = headers;
177
+ message.rawHeaders = rawHeaders;
178
+ message.complete = true;
179
+ callback?.(message);
180
+ const body = await response.arrayBuffer();
181
+ stream.end(Buffer.from(body));
182
+ }).catch(error => {
183
+ stream.emit('error', error);
184
+ stream.end();
185
+ });
186
+ return message;
187
+ }
188
+
189
+ function getStringForGetInput(input: HttpGetInput): string | undefined {
190
+ if (typeof input === 'string' || input instanceof URL) return input.toString();
191
+ const candidate = input as { href?: string; protocol?: string; hostname?: string; host?: string; path?: string; pathname?: string; search?: string; port?: number | string };
192
+ if (typeof candidate.href === 'string' && /^[a-z][a-z\d+.-]*:\/\//i.test(candidate.href)) return candidate.href;
193
+ const protocol = typeof candidate.protocol === 'string' ? candidate.protocol : 'http:';
194
+ const host = typeof candidate.host === 'string' ? candidate.host : typeof candidate.hostname === 'string' ? candidate.hostname : undefined;
195
+ if (!host) return undefined;
196
+ const port =
197
+ typeof candidate.port === 'number' ? `:${candidate.port}` : typeof candidate.port === 'string' ? `:${candidate.port}` : '';
198
+ const path = typeof candidate.path === 'string' ? candidate.path : typeof candidate.pathname === 'string' ? candidate.pathname : '/';
199
+ const search = typeof candidate.search === 'string' ? candidate.search : '';
200
+ return `${protocol}//${host}${port}${path}${search}`;
201
+ }
202
+
203
+ function patchableGet(
204
+ originalGet: typeof http.get,
205
+ originalFetch: typeof fetch,
206
+ ): typeof http.get {
207
+ return ((input: HttpGetInput, options?: Parameters<typeof http.get>[1]) => {
208
+ const callback = typeof options === 'function' ? options : undefined;
209
+ const url = getStringForGetInput(input);
210
+ const info = currentTestProxyInfo();
211
+ if (!url || !info) {
212
+ if (typeof options === 'function') {
213
+ return originalGet(input, options);
214
+ }
215
+ if (options === undefined) {
216
+ return originalGet(input);
217
+ }
218
+ return originalGet(input, options);
219
+ }
220
+ const request = new Request(url, {
221
+ method: typeof options === 'object' && options && 'method' in options && typeof options.method === 'string'
222
+ ? options.method
223
+ : 'GET',
224
+ headers:
225
+ typeof options === 'object' && options && 'headers' in options
226
+ ? (options.headers as HeadersInit)
227
+ : undefined,
228
+ });
229
+ return buildResponseMessage(handleProxiedFetch(originalFetch, info, request), callback) as unknown as ReturnType<
230
+ typeof http.get
231
+ >;
232
+ }) as typeof http.get;
233
+ }
234
+
235
+ let patched = false;
236
+
237
+ /** Patch global.fetch and node http/https.get once for testmode outgoing requests. */
238
+ export function installTestProxyFetch(): void {
239
+ if (patched) return;
240
+ patched = true;
241
+ const originalFetch = globalThis.fetch.bind(globalThis);
242
+
243
+ const testFetch = function testFetch(
244
+ input: Parameters<typeof fetch>[0],
245
+ init?: Parameters<typeof fetch>[1],
246
+ ): Promise<Response> {
247
+ const nextInit = (init as { next?: { internal?: boolean } } | undefined)?.next;
248
+ if (nextInit?.internal) {
249
+ return originalFetch(input, init);
250
+ }
251
+ const info = currentTestProxyInfo();
252
+ if (!info) return originalFetch(input, init);
253
+ return handleProxiedFetch(originalFetch, info, new Request(input, init));
254
+ };
255
+ // Preserve fetch.preconnect (typeof fetch requires it).
256
+ (testFetch as typeof fetch).preconnect = originalFetch.preconnect?.bind(originalFetch);
257
+ globalThis.fetch = testFetch as typeof fetch;
258
+
259
+ const originalHttpGet = httpModule.get;
260
+ const originalHttpsGet = httpsModule.get;
261
+ httpModule.get = patchableGet(originalHttpGet, originalFetch);
262
+ httpsModule.get = patchableGet(originalHttpsGet, originalFetch);
263
+ }
@@ -0,0 +1,219 @@
1
+ // MDX compile core (COMPAT).
2
+ //
3
+ // Thin wrapper over `@mdx-js/mdx`'s `compile`. Kept dependency-light and lazy: `@mdx-js/mdx` is an
4
+ // optionalDependency loaded on first `.mdx` compile, so a pure app that never touches MDX pays nothing
5
+ // and builds without it installed.
6
+ //
7
+ // The user's remark/rehype/recma plugin arrays come from the wrapped next.config, read via
8
+ // `getNextConfig()` so no wiring is required beyond registering the loader.
9
+ //
10
+ // Output is JSX targeting the preact automatic runtime; the esbuild loader that calls this returns
11
+ // `loader: 'jsx'` so esbuild performs the final JSX-to-calls transform with the same jsxImportSource
12
+ // every other compile site uses, keeping a single JSX runtime end to end.
13
+ //
14
+ // `providerImportSource` is set to our components provider only when the app ships an `mdx-components`
15
+ // file; without one, MDX emits bare host elements, which is exactly the mdx-no-mdx-components contract.
16
+
17
+ import { existsSync } from 'node:fs';
18
+ import { createRequire } from 'node:module';
19
+ import path from 'node:path';
20
+ import { pathToFileURL } from 'node:url';
21
+ import { getNextConfig } from '../next/config-loader';
22
+
23
+ export interface MdxCompileResult {
24
+ code: string;
25
+ }
26
+
27
+ interface MdxModule {
28
+ compile: (
29
+ source: string | { value: string; path?: string },
30
+ options?: Record<string, unknown>,
31
+ ) => Promise<{ toString: () => string; value: string }>;
32
+ }
33
+
34
+ interface MdxOptions {
35
+ remarkPlugins?: unknown[];
36
+ rehypePlugins?: unknown[];
37
+ recmaPlugins?: unknown[];
38
+ remarkRehypeOptions?: Record<string, unknown>;
39
+ providerImportSource?: string;
40
+ }
41
+
42
+ let cachedModule: MdxModule | null | undefined;
43
+
44
+ /**
45
+ * Load `@mdx-js/mdx` lazily. Returns `null` (not throwing) when the optional
46
+ * dependency is absent so the loader can surface a single actionable error only
47
+ * when an app actually imports `.mdx`.
48
+ */
49
+ async function loadMdx(): Promise<MdxModule | null> {
50
+ if (cachedModule !== undefined) return cachedModule;
51
+ try {
52
+ const specifier = '@mdx-js/mdx';
53
+ cachedModule = (await import(/* @vite-ignore */ specifier)) as MdxModule;
54
+ } catch {
55
+ cachedModule = null;
56
+ }
57
+ return cachedModule;
58
+ }
59
+
60
+ /** The user's mdxOptions from the wrapped next.config, normalized. */
61
+ export function mdxOptionsFromConfig(): MdxOptions {
62
+ const config = getNextConfig() as {
63
+ mdxOptions?: MdxOptions;
64
+ experimental?: { mdxRs?: unknown };
65
+ };
66
+ // @next/mdx stores the merged plugin options under `mdxOptions` on the config
67
+ // object the wrapper produces. `experimental.mdxRs` selects the Rust pipeline
68
+ // upstream; we always use the JS pipeline, so it is intentionally ignored.
69
+ return config.mdxOptions ?? {};
70
+ }
71
+
72
+ /**
73
+ * Resolve the app's `mdx-components.{tsx,ts,jsx,js,mjs}` provider file if present.
74
+ * Next looks for it at the project root (or `src/`). Returns the absolute path
75
+ * used as `providerImportSource`, or `undefined` when absent (bare host elements).
76
+ */
77
+ export function resolveMdxComponents(root: string): string | undefined {
78
+ const bases = [root, path.join(root, 'src')];
79
+ const names = ['mdx-components.tsx', 'mdx-components.ts', 'mdx-components.jsx', 'mdx-components.js', 'mdx-components.mjs'];
80
+ for (const base of bases) {
81
+ for (const name of names) {
82
+ const candidate = path.join(base, name);
83
+ if (existsSync(candidate)) return candidate;
84
+ }
85
+ }
86
+ return undefined;
87
+ }
88
+
89
+ function interopDefault(module: unknown): unknown {
90
+ return (module as { default?: unknown })?.default ?? module;
91
+ }
92
+
93
+ // Sentinel returned when a configured plugin package can't be resolved from the
94
+ // app, so `resolvePlugins` can filter it out.
95
+ const UNRESOLVED_PLUGIN = Symbol('pnext.mdx.unresolved-plugin');
96
+
97
+ /**
98
+ * Resolve a single plugin entry to the shape `@mdx-js/mdx` expects. `@next/mdx` stores plugins as
99
+ * serializable STRINGS (a bare package name) or `[name, opts]` tuples, because webpack loader options
100
+ * must serialize. Mirroring its mdx-js-loader: resolve the name from the project root and
101
+ * dynamic-import it, taking the default export. Entries that are already functions or tuples of
102
+ * functions pass through untouched.
103
+ *
104
+ * A configured plugin that cannot be resolved OR fails to load returns UNRESOLVED_PLUGIN (a warn, not a
105
+ * throw): neither a missing optional transform nor a broken transitive dependency may take down the
106
+ * whole `.mdx` render. This also matches `mdxRs: true`, where Next never JS-resolves these packages at
107
+ * all. Load failures are common when the toolchain resolves into a flat auto-install cache rather than
108
+ * a real node_modules tree, so the real error is surfaced to the log instead of silently 500ing pages.
109
+ */
110
+ async function resolvePlugin(
111
+ entry: unknown,
112
+ resolveFrom: NodeRequire,
113
+ ): Promise<unknown> {
114
+ if (typeof entry === 'function') return entry;
115
+ if (typeof entry === 'string') {
116
+ return loadPlugin(entry, resolveFrom);
117
+ }
118
+ if (Array.isArray(entry) && typeof entry[0] === 'string') {
119
+ const tuple = entry as unknown[];
120
+ const plugin = await loadPlugin(tuple[0] as string, resolveFrom);
121
+ if (plugin === UNRESOLVED_PLUGIN) return UNRESOLVED_PLUGIN;
122
+ return [plugin, ...tuple.slice(1)];
123
+ }
124
+ return entry;
125
+ }
126
+
127
+ /**
128
+ * Resolve a plugin from the app root and import it. Returns the plugin's default
129
+ * export, or UNRESOLVED_PLUGIN (with a warning) when it is not installed or
130
+ * cannot be loaded. Resolution is rooted at the app (`createRequire(root/...)`)
131
+ * so the toolchain stays inside the app's node_modules.
132
+ */
133
+ async function loadPlugin(name: string, resolveFrom: NodeRequire): Promise<unknown> {
134
+ let resolved: string;
135
+ try {
136
+ resolved = resolveFrom.resolve(name);
137
+ } catch {
138
+ console.warn(
139
+ `pnext: MDX plugin '${name}' is configured but not installed; skipping it. ` +
140
+ 'Install it to apply this remark/rehype/recma plugin.',
141
+ );
142
+ return UNRESOLVED_PLUGIN;
143
+ }
144
+ try {
145
+ return interopDefault(await import(pathToFileURL(resolved).href));
146
+ } catch (error) {
147
+ console.warn(
148
+ `pnext: MDX plugin '${name}' failed to load and was skipped: ` +
149
+ `${error instanceof Error ? error.message : String(error)}. ` +
150
+ 'Ensure it is installed in the app’s node_modules (not only Bun’s auto-install cache).',
151
+ );
152
+ return UNRESOLVED_PLUGIN;
153
+ }
154
+ }
155
+
156
+ /** Resolve every entry of a remark/rehype/recma plugin array (skipping absent packages). */
157
+ async function resolvePlugins(plugins: unknown[], resolveFrom: NodeRequire): Promise<unknown[]> {
158
+ const resolved = await Promise.all(plugins.map(entry => resolvePlugin(entry, resolveFrom)));
159
+ return resolved.filter(entry => entry !== UNRESOLVED_PLUGIN);
160
+ }
161
+
162
+ /**
163
+ * Compile MDX source to JSX targeting the preact automatic runtime. `root` locates
164
+ * the optional mdx-components provider; `filePath` feeds source positions.
165
+ */
166
+ export async function compileMdx(
167
+ source: string,
168
+ filePath: string,
169
+ root: string,
170
+ ): Promise<MdxCompileResult> {
171
+ const mdx = await loadMdx();
172
+ if (!mdx) {
173
+ throw new Error(
174
+ "MDX support requires the '@mdx-js/mdx' package. Install it to render .mdx files.",
175
+ );
176
+ }
177
+
178
+ const userOptions = mdxOptionsFromConfig();
179
+ const provider = resolveMdxComponents(root);
180
+
181
+ // Resolve string/tuple plugin names (@next/mdx's serialized form) to actual
182
+ // plugin functions from the app's node_modules before handing them to MDX.
183
+ const resolveFrom = createRequire(path.join(root, 'noop.js'));
184
+
185
+ const [remarkPlugins, rehypePlugins, recmaPlugins] = await Promise.all([
186
+ resolvePlugins(userOptions.remarkPlugins ?? [], resolveFrom),
187
+ resolvePlugins(userOptions.rehypePlugins ?? [], resolveFrom),
188
+ resolvePlugins(userOptions.recmaPlugins ?? [], resolveFrom),
189
+ ]);
190
+
191
+ const compiled = await mdx.compile(
192
+ { value: source, path: filePath },
193
+ {
194
+ // Emit JSX; esbuild's `jsx` loader performs the final transform with
195
+ // jsxImportSource: 'preact', matching every other compile site.
196
+ jsx: true,
197
+ jsxImportSource: 'preact',
198
+ // Full MDX (not just markdown) so `import`/`export` and JSX in `.mdx` work.
199
+ format: 'mdx',
200
+ // Development positions off — matches production build output.
201
+ development: false,
202
+ remarkPlugins,
203
+ rehypePlugins,
204
+ recmaPlugins,
205
+ ...(userOptions.remarkRehypeOptions
206
+ ? { remarkRehypeOptions: userOptions.remarkRehypeOptions }
207
+ : {}),
208
+ // Only wire a components provider when the app supplies one; otherwise MDX
209
+ // emits plain host elements (the mdx-no-mdx-components contract).
210
+ ...(userOptions.providerImportSource
211
+ ? { providerImportSource: userOptions.providerImportSource }
212
+ : provider
213
+ ? { providerImportSource: provider }
214
+ : {}),
215
+ },
216
+ );
217
+
218
+ return { code: compiled.toString() };
219
+ }
@@ -0,0 +1,46 @@
1
+ // `@next/mdx` config-time stub (COMPAT).
2
+ //
3
+ // `createMDX(pluginOptions)(nextConfig)` embeds the user's remark/rehype/recma plugin options ONLY
4
+ // inside a webpack/turbopack loader config it attaches to the returned next.config - it never exposes
5
+ // them on a readable top-level field. pnext has no webpack, so those options would be lost and every
6
+ // configured MDX plugin would silently not run.
7
+ //
8
+ // During config bundling, `@next/mdx` resolves to this stub instead of the real package. The stub
9
+ // reproduces the wrapper's signature but stashes the plugin options on `config.mdxOptions`, exactly
10
+ // where pnext's MDX compile pipeline reads them. It also removes the need for `@next/mdx` itself to be
11
+ // installed for the config to load. The wrapped config's own webpack/turbopack/pageExtensions are
12
+ // irrelevant to pnext, so the stub only needs to merge `mdxOptions`.
13
+
14
+ import type { Plugin } from 'esbuild';
15
+
16
+ // CJS so esbuild's default-import interop yields the wrapper factory for both
17
+ // `import nextMDX from '@next/mdx'` and `require('@next/mdx')` config authors.
18
+ const NEXT_MDX_STUB_CONTENTS = `
19
+ const createMDX = (pluginOptions = {}) => (nextConfig = {}) => ({
20
+ ...nextConfig,
21
+ mdxOptions: (pluginOptions && pluginOptions.options) || {},
22
+ });
23
+ module.exports = createMDX;
24
+ module.exports.default = createMDX;
25
+ `;
26
+
27
+ /**
28
+ * esbuild plugin (config-load only) that replaces `@next/mdx` (and its
29
+ * `@next/mdx/*` deep specifiers) with a stub capturing the plugin options onto
30
+ * `config.mdxOptions`.
31
+ */
32
+ export function nextMdxConfigStubPlugin(): Plugin {
33
+ return {
34
+ name: 'pnext-next-mdx-config-stub',
35
+ setup(build) {
36
+ build.onResolve({ filter: /^@next\/mdx(?:\/|$)/ }, args => ({
37
+ path: args.path,
38
+ namespace: 'pnext-next-mdx-stub',
39
+ }));
40
+ build.onLoad({ filter: /.*/, namespace: 'pnext-next-mdx-stub' }, () => ({
41
+ contents: NEXT_MDX_STUB_CONTENTS,
42
+ loader: 'js',
43
+ }));
44
+ },
45
+ };
46
+ }
@@ -0,0 +1,37 @@
1
+ // MDX esbuild plugin (COMPAT).
2
+ //
3
+ // Intercepts `.mdx` (and `.md`) loads across every build graph - client bundle, dev RSC route bundle,
4
+ // and prod server compile - and replaces the file contents with the JSX compiled from MDX. Returned as
5
+ // `loader: 'jsx'` so esbuild does the final JSX-to-preact-runtime transform.
6
+ //
7
+ // The same factory is registered on both the client and server esbuild plugin lists. It resolves the
8
+ // project root from the build's `absWorkingDir` (all pnext esbuild builds set it to the project root)
9
+ // so the optional mdx-components provider is located correctly.
10
+
11
+ import { readFile } from 'node:fs/promises';
12
+ import type { Plugin } from 'esbuild';
13
+ import { applyClientSourceTransforms } from '../../extensions';
14
+ import { compileMdx } from './compile';
15
+
16
+ const MDX_FILTER = /\.mdx?$/;
17
+
18
+ /**
19
+ * esbuild plugin that compiles `.md`/`.mdx` modules through the JS MDX pipeline.
20
+ * `rootOverride` pins the project root; when omitted the plugin uses the build's
21
+ * `absWorkingDir` (falling back to `process.cwd()`).
22
+ */
23
+ export function mdxEsbuildPlugin(rootOverride?: string): Plugin {
24
+ return {
25
+ name: 'pnext-mdx',
26
+ setup(build) {
27
+ const root = rootOverride ?? build.initialOptions.absWorkingDir ?? process.cwd();
28
+ build.onLoad({ filter: MDX_FILTER }, async args => {
29
+ const source = await readFile(args.path, 'utf8');
30
+ const { code } = await compileMdx(source, args.path, root);
31
+ // Compiled MDX must still pass through registered source transforms
32
+ // (e.g. modularizeImports) — raw compileMdx output bypasses them.
33
+ return { contents: applyClientSourceTransforms(code, args.path, root), loader: 'jsx' };
34
+ });
35
+ },
36
+ };
37
+ }