@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,458 @@
1
+ import path from 'node:path';
2
+ import { createRequire } from 'node:module';
3
+ import type { Plugin } from 'esbuild';
4
+ import { frameworkRuntimeAliasEntries, pnextAliases, type CompatAliasTarget } from '../config';
5
+ import type { PNextConfig } from '../types';
6
+ import { escapeRegex, isIdentifier, uniqueIdentifier } from '../utils/source';
7
+ import { actionClientStubPlugin, hasClientActionModules } from './actions/client-plugin';
8
+ import { rewriteRootParamsSource } from './ppr/root-params-transform';
9
+
10
+ export type CompatConfig = Pick<PNextConfig, 'compat'>;
11
+
12
+ export function reactCompatEnabled(config: CompatConfig) {
13
+ return Boolean(config.compat?.next || config.compat?.react || reactCompilerOptions(config));
14
+ }
15
+
16
+ export function reactCompilerOptions(config: CompatConfig) {
17
+ if (!config.compat?.reactCompiler) return undefined;
18
+ return { target: '18' };
19
+ }
20
+
21
+ export function nextCompatEnabled(config: CompatConfig) {
22
+ return Boolean(config.compat?.next);
23
+ }
24
+
25
+ /** The only `next/dist/*` deep imports pnext shims (see reference/compat.md "Smaller surfaces"). */
26
+ const SHIMMED_NEXT_DIST_PATHS = [
27
+ 'next/dist/client/components/app-router-headers',
28
+ 'next/dist/server/web/spec-extension/unstable-cache',
29
+ 'next/dist/server/web/spec-extension/unstable-no-store',
30
+ 'next/dist/server/web/spec-extension/revalidate',
31
+ 'next/dist/server/app-render/work-unit-async-storage.external',
32
+ ];
33
+
34
+ /**
35
+ * Clear failure for compat-only imports in apps that never enabled the
36
+ * matching compat mode.
37
+ */
38
+ export function missingCompatImportError(
39
+ config: CompatConfig,
40
+ specifier: string,
41
+ ): string | undefined {
42
+ if (/^next(\/|$)/.test(specifier)) {
43
+ if (!nextCompatEnabled(config)) {
44
+ return `'${specifier}' requires Next compatibility. Enable compat.next in pnext.config.ts.`;
45
+ }
46
+ if (specifier.startsWith('next/dist/')) {
47
+ const bare = specifier.replace(/\.js$/, '');
48
+ if (!SHIMMED_NEXT_DIST_PATHS.includes(bare)) {
49
+ return (
50
+ `'${specifier}' is not a supported import: next/dist internals are not part of the ` +
51
+ `public surface pnext shims. Only these next/dist/* paths are shimmed:\n` +
52
+ SHIMMED_NEXT_DIST_PATHS.map(path => ` - ${path}`).join('\n') +
53
+ `\nSee reference/compat.md ("Smaller surfaces") and report the library that needs a new shim.`
54
+ );
55
+ }
56
+ }
57
+ return undefined;
58
+ }
59
+ if (/^react(-dom)?(\/|$)/.test(specifier) && !reactCompatEnabled(config)) {
60
+ return `'${specifier}' requires React compatibility. Enable compat.react (or compat.next) in pnext.config.ts.`;
61
+ }
62
+ return undefined;
63
+ }
64
+
65
+ export function compatAliases(
66
+ config: CompatConfig,
67
+ target: CompatAliasTarget,
68
+ ): Record<string, string> {
69
+ return {
70
+ ...frameworkRuntimeAliasEntries(),
71
+ ...pnextAliases(target),
72
+ ...reactCompatAliases(config, target),
73
+ ...nextCompatAliases(config, target),
74
+ };
75
+ }
76
+
77
+ /**
78
+ * B4 react-server layer override for App RSC, route handlers, and edge
79
+ * modules. The base server shims keep client hooks for Pages/API and
80
+ * instrumentation compatibility.
81
+ */
82
+ export function reactServerLayerAliases(config: CompatConfig): Record<string, string> {
83
+ if (!reactCompatEnabled(config)) return {};
84
+ const reactRoot = path.resolve(import.meta.dirname, 'react');
85
+ return {
86
+ react: path.join(reactRoot, 'react-server.ts'),
87
+ 'react-dom': path.join(reactRoot, 'dom-react-server.ts'),
88
+ };
89
+ }
90
+
91
+ export function reactCompatAliases(
92
+ config: CompatConfig,
93
+ target: CompatAliasTarget,
94
+ ): Record<string, string> {
95
+ if (!reactCompatEnabled(config)) return {};
96
+ const reactRoot = path.resolve(import.meta.dirname, 'react');
97
+ const runtime = frameworkRuntimeAliasEntries();
98
+ return {
99
+ react: path.join(reactRoot, target === 'client' ? 'client.ts' : 'server.ts'),
100
+ // dom.ts wraps preact/compat with the form-action hooks react-dom ships
101
+ // (useFormState/useFormStatus/requestFormReset).
102
+ 'react-dom': path.join(reactRoot, 'dom.ts'),
103
+ 'react-dom/client': runtime['preact/compat/client'],
104
+ // react-dom/server (+ .edge/.browser entrypoints) shimmed over
105
+ // preact-render-to-string for the synchronous string renderers.
106
+ 'react-dom/server': path.join(reactRoot, 'dom-server.ts'),
107
+ 'react-dom/server.edge': path.join(reactRoot, 'dom-server.ts'),
108
+ 'react-dom/server.browser': path.join(reactRoot, 'dom-server.ts'),
109
+ 'react/jsx-runtime': runtime['preact/jsx-runtime'],
110
+ 'react/jsx-dev-runtime': runtime['preact/jsx-dev-runtime'],
111
+ // React Compiler memo-cache runtime. Both the React 19 built-in specifier
112
+ // (`react/compiler-runtime`) and the standalone npm package name
113
+ // (`react-compiler-runtime`, emitted for a React 17/18 compiler target, and
114
+ // imported by pre-compiled libraries) map to a preact-backed shim. The npm
115
+ // package's own `require('react').useMemo` throws under preact SSR.
116
+ 'react/compiler-runtime': path.join(reactRoot, 'compiler-runtime.ts'),
117
+ 'react-compiler-runtime': path.join(reactRoot, 'compiler-runtime.ts'),
118
+ };
119
+ }
120
+
121
+ export function nextCompatAliases(
122
+ config: CompatConfig,
123
+ target: CompatAliasTarget,
124
+ ): Record<string, string> {
125
+ if (!nextCompatEnabled(config)) return {};
126
+ return nextCompatAliasEntries(target);
127
+ }
128
+
129
+ export function nextCompatAliasEntries(target: CompatAliasTarget): Record<string, string> {
130
+ const nextRoot = path.resolve(import.meta.dirname, 'next');
131
+ return withJsExtensionAliases({
132
+ next: path.join(nextRoot, 'index.ts'),
133
+ // Client variant avoids node:async_hooks (render cache-meta collector) in
134
+ // browser bundles; the APIs are server-only in Next anyway.
135
+ 'next/cache': path.join(nextRoot, target === 'client' ? 'client-cache.ts' : 'cache.ts'),
136
+ 'next/config': path.join(nextRoot, 'config.ts'),
137
+ // CJS so an app `require('next/constants')` loads a genuine CommonJS module: the server runtime's
138
+ // onLoad transform only intercepts `[jt]sx?`, so a `.cjs` external is left to Bun's native loader
139
+ // and stays synchronously requirable - an ESM `.ts` external forced through that transform becomes
140
+ // async and require() returns undefined for every named export.
141
+ 'next/constants': path.join(nextRoot, 'constants.cjs'),
142
+ 'next/dynamic': path.join(nextRoot, 'dynamic.tsx'),
143
+ 'next/error': path.join(nextRoot, 'error.tsx'),
144
+ 'next/font': path.join(nextRoot, 'font', 'index.ts'),
145
+ 'next/font/google': path.join(nextRoot, 'font', 'google.ts'),
146
+ 'next/font/local': path.join(nextRoot, 'font', 'local.ts'),
147
+ 'next/form': path.join(nextRoot, 'form.tsx'),
148
+ 'next/head': path.join(nextRoot, 'head.tsx'),
149
+ 'next/headers': path.join(nextRoot, 'headers.ts'),
150
+ 'next/image': path.join(nextRoot, 'image.tsx'),
151
+ // The legacy component blanks a lazy image's `src` until it is near the
152
+ // viewport — its own module, not an alias of the modern one.
153
+ 'next/legacy/image': path.join(nextRoot, 'legacy-image.tsx'),
154
+ 'next/link': path.join(nextRoot, 'link.tsx'),
155
+ 'next/navigation': path.join(
156
+ nextRoot,
157
+ target === 'client' ? 'client-navigation.ts' : 'navigation.ts',
158
+ ),
159
+ 'next/offline': path.join(nextRoot, 'offline.ts'),
160
+ 'next/og': path.join(nextRoot, 'og.ts'),
161
+ '@vercel/og': path.join(nextRoot, 'og.ts'),
162
+ 'next/root-params': path.join(nextRoot, 'root-params.ts'),
163
+ 'next/router': path.join(nextRoot, 'router.ts'),
164
+ 'next/script': path.join(nextRoot, target === 'client' ? 'client-script.tsx' : 'script.tsx'),
165
+ 'next/web-vitals': path.join(nextRoot, 'web-vitals.ts'),
166
+ // Client variant avoids node:async_hooks (request scope / ppr / revalidate)
167
+ // in browser bundles; the request-scope APIs are server-only in Next anyway.
168
+ 'next/server': path.join(nextRoot, target === 'client' ? 'client-server.ts' : 'server.ts'),
169
+ // Server compiles accept the marker (react-server condition equivalent);
170
+ // client bundles keep the package's own error behavior.
171
+ ...(target === 'server' ? { 'server-only': path.join(nextRoot, 'server-only.ts') } : {}),
172
+ // client-only must resolve on both targets: a 'use client' page importing it
173
+ // is legitimately SSR'd (server compile) as well as browser-bundled.
174
+ 'client-only': path.join(nextRoot, 'client-only.ts'),
175
+ // The styled-jsx source transform (css/styled-jsx.ts) rewrites <style jsx>
176
+ // blocks to import from this specifier; apps never install styled-jsx.
177
+ 'styled-jsx': path.join(nextRoot, '..', 'css', 'styled-jsx-runtime.ts'),
178
+ // Deep next/dist imports that show up in real Next apps and e2e fixtures.
179
+ // Exact-match aliases only; unknown next/dist paths keep failing through
180
+ // missingCompatImportError / normal resolution.
181
+ 'next/dist/client/components/app-router-headers': path.join(
182
+ nextRoot,
183
+ 'dist',
184
+ 'client',
185
+ 'components',
186
+ 'app-router-headers.ts',
187
+ ),
188
+ 'next/dist/server/web/spec-extension/unstable-cache': path.join(
189
+ nextRoot,
190
+ 'dist',
191
+ 'server',
192
+ 'web',
193
+ 'spec-extension',
194
+ 'unstable-cache.ts',
195
+ ),
196
+ 'next/dist/server/web/spec-extension/unstable-no-store': path.join(
197
+ nextRoot,
198
+ 'dist',
199
+ 'server',
200
+ 'web',
201
+ 'spec-extension',
202
+ 'unstable-no-store.ts',
203
+ ),
204
+ 'next/dist/server/web/spec-extension/revalidate': path.join(
205
+ nextRoot,
206
+ 'dist',
207
+ 'server',
208
+ 'web',
209
+ 'spec-extension',
210
+ 'revalidate.ts',
211
+ ),
212
+ // Direct import of the work-unit store (see the shim's header). Server-only;
213
+ // a browser bundle never reaches request context. CJS for the same reason as
214
+ // next/constants above: apps `require()` this deep internal, and a `.ts`
215
+ // external would become an async module through the onLoad transform, making
216
+ // the require() throw outright.
217
+ ...(target === 'server'
218
+ ? {
219
+ 'next/dist/server/app-render/work-unit-async-storage.external': path.join(
220
+ nextRoot,
221
+ 'dist',
222
+ 'server',
223
+ 'app-render',
224
+ 'work-unit-async-storage.external.cjs',
225
+ ),
226
+ }
227
+ : {}),
228
+ });
229
+ }
230
+
231
+ function withJsExtensionAliases(aliases: Record<string, string>) {
232
+ const entries = { ...aliases };
233
+ for (const [specifier, target] of Object.entries(aliases)) {
234
+ if (specifier !== 'next') entries[`${specifier}.js`] = target;
235
+ }
236
+ return entries;
237
+ }
238
+
239
+ /**
240
+ * esbuild client-bundle aliasing for compat specifiers, with clear failures
241
+ * for compat-only imports instead of resolving to whatever react/next package
242
+ * happens to be installed.
243
+ */
244
+ export function compatAliasPlugin(config: CompatConfig): Plugin {
245
+ const aliases = compatAliases(config, 'client');
246
+ const specifiers = Object.keys(aliases);
247
+ return {
248
+ name: 'pnext-compat-alias',
249
+ setup(build) {
250
+ // When compat.next discovered server-action modules, swap their client
251
+ // imports for the generated fetch stub. Registered here so the action
252
+ // stub participates in every client build without editing the client
253
+ // build pipeline. No-op when no action modules were discovered.
254
+ if (nextCompatEnabled(config) && hasClientActionModules()) {
255
+ void actionClientStubPlugin().setup(build);
256
+ }
257
+
258
+ build.onResolve({ filter: /^(?:react(?:-dom)?|next)(?:\/|$)/ }, args => {
259
+ if (aliases[args.path]) return undefined;
260
+ const message = missingCompatImportError(config, args.path);
261
+ return message ? { errors: [{ text: message }] } : undefined;
262
+ });
263
+ if (specifiers.length === 0) return;
264
+ build.onResolve(
265
+ { filter: new RegExp(`^(${specifiers.map(escapeRegex).join('|')})$`) },
266
+ async args => {
267
+ const target = aliases[args.path];
268
+ if (!target) return undefined;
269
+ if (path.isAbsolute(target)) return { path: target };
270
+ return build.resolve(target, {
271
+ kind: args.kind,
272
+ importer: args.importer,
273
+ namespace: args.namespace,
274
+ resolveDir: args.resolveDir,
275
+ });
276
+ },
277
+ );
278
+ },
279
+ };
280
+ }
281
+
282
+ // Compat apps render React's Suspense (the preact/compat class, via the react
283
+ // aliases or a direct import). That identity is loaded lazily and only when
284
+ // compat is enabled, so core apps never pull preact/compat into the server
285
+ // process — core streaming uses PNext's own Suspense marker instead.
286
+ let compatSuspense: unknown;
287
+
288
+ export async function ensureCompatSuspense(config: CompatConfig) {
289
+ if (compatSuspense !== undefined || !reactCompatEnabled(config)) return;
290
+ compatSuspense = ((await import('preact/compat')) as { Suspense: unknown }).Suspense;
291
+ }
292
+
293
+ export function compatSuspenseType() {
294
+ return compatSuspense;
295
+ }
296
+
297
+ /**
298
+ * next/font runtime targets. The SERVER runtime (runtime.ts) emits CSS + font
299
+ * files and imports `node:*` at module scope; the CLIENT runtime
300
+ * (runtime-client.ts) is `node:*`-free and only returns the pre-hashed
301
+ * className/variable/style a browser bundle needs (CORE GAP D). The rewrite
302
+ * binds font imports to the runtime matching the build target, mirroring the
303
+ * next/cache + next/navigation client/server alias split in nextCompatAliasEntries.
304
+ */
305
+ const fontRuntimePaths: Record<CompatAliasTarget, string> = {
306
+ server: path.join(import.meta.dirname, 'next', 'font', 'runtime.ts'),
307
+ client: path.join(import.meta.dirname, 'next', 'font', 'runtime-client.ts'),
308
+ };
309
+
310
+ /**
311
+ * Next-compat source rewrites for `next/font/google` and `next/font/local` imports. Only invoked when
312
+ * compat.next is enabled; without it the imports survive untouched and fail with the missing-compat
313
+ * resolve error.
314
+ *
315
+ * `target` selects the runtime the rewritten imports bind to: 'server' (default) for server compiles,
316
+ * 'client' for browser bundles, which avoids pulling node:async_hooks / node:crypto into the bundle.
317
+ */
318
+ export function rewriteNextFontSource(
319
+ source: string,
320
+ file: string,
321
+ target: CompatAliasTarget = 'server',
322
+ root?: string,
323
+ ) {
324
+ if (!source.includes('next/font') && !source.includes('next/root-params')) return source;
325
+ // The single next/root-params rewrite in the tree; every other call site
326
+ // reaches it through this pass rather than re-running its own copy.
327
+ source = rewriteRootParamsSource(source);
328
+ const runtimePath = fontRuntimePaths[target];
329
+ let next = rewriteNextGoogleFontImports(source, runtimePath);
330
+ const localFontNames = nextLocalFontImportNames(next);
331
+ if (localFontNames.size === 0) return next;
332
+ const callerFile = localFontCallerFile(file, root);
333
+ // Rewrite each `NAME(...)` call to `NAME.withFile(<file>)(...)` so the runtime
334
+ // gets the caller path (for `src` resolution + a stable per-caller hash). The
335
+ // client runtime only uses `callerFile` for the hash (which must match the
336
+ // server's exactly), so the SAME `withFile(<file>)` wrapping applies to both.
337
+ for (const name of localFontNames) {
338
+ next = next.replace(
339
+ new RegExp(`(?<![.$\\w])${escapeRegex(name)}\\s*\\(`, 'g'),
340
+ `${name}.withFile(${JSON.stringify(callerFile)})(`,
341
+ );
342
+ }
343
+ // Next names local-font families after the binding (`const myFont = …`),
344
+ // including when the loader lives in a package under node_modules.
345
+ for (const name of localFontNames) {
346
+ next = next.replace(
347
+ new RegExp(`((?:export\\s+)?(?:const|let|var)\\s+)([A-Za-z_$][\\w$]*)(\\s*=\\s*)${escapeRegex(name)}\\.withFile\\(([^)]*)\\)\\(`, 'g'),
348
+ (_statement, declaration: string, binding: string, equals: string, caller: string) =>
349
+ `${declaration}${binding}${equals}${name}.withFile(${caller}, ${JSON.stringify(binding)})(`,
350
+ );
351
+ }
352
+ // Replace the bare `import NAME from 'next/font/local'` with an import bound
353
+ // directly to the runtime's absolute path. This avoids relying on server-side
354
+ // `next/font/local` alias resolution, which does not reach modules imported
355
+ // from workspace dirs outside app/ (e.g. a `fonts/index.js` barrel).
356
+ next = next.replace(
357
+ /^[ \t]*import\s+([A-Za-z_$][\w$]*)\s+from\s+(['"])([^'"]+)\2\s*;?[ \t]*$/gm,
358
+ (statement, name: string, _quote: string, specifier: string) => {
359
+ if (!isNextLocalFontSpecifier(specifier)) return statement;
360
+ return `import { localFont as ${name} } from ${JSON.stringify(runtimePath)};`;
361
+ },
362
+ );
363
+ return next;
364
+ }
365
+
366
+ function rewriteNextGoogleFontImports(source: string, runtimePath: string) {
367
+ const statements: string[] = [];
368
+ const helperName = uniqueIdentifier(source, '__pnextGoogleFont');
369
+ const namespaceHelperName = uniqueIdentifier(source, '__pnextGoogleFonts', helperName);
370
+ const rewritten = source.replace(
371
+ /^[ \t]*import\s+(?!type\b)((?:[^\n;]|\n(?![ \t]*(?:import|export)\b))*?)\s+from\s+['"]next\/font\/google['"]\s*;?[ \t]*(?:\r?\n)?/gm,
372
+ (_statement: string, clause: string) => {
373
+ const bindings = nextGoogleFontBindings(clause);
374
+ if (bindings.length === 0) return '';
375
+ statements.push(
376
+ ...bindings.map(binding =>
377
+ googleFontBindingSource(binding, helperName, namespaceHelperName),
378
+ ),
379
+ );
380
+ return '';
381
+ },
382
+ );
383
+
384
+ if (statements.length === 0) return source;
385
+ return [
386
+ `import { googleFont as ${helperName} } from ${JSON.stringify(runtimePath)};`,
387
+ `const ${namespaceHelperName} = new Proxy({}, { get: (_target, key) => typeof key === 'string' ? ${helperName}(key.replace(/_/g, ' ')) : undefined });`,
388
+ ...statements,
389
+ rewritten,
390
+ ].join('\n');
391
+ }
392
+
393
+ interface GoogleFontBinding {
394
+ imported: string;
395
+ local: string;
396
+ namespace?: boolean;
397
+ }
398
+
399
+ function nextGoogleFontBindings(clause: string): GoogleFontBinding[] {
400
+ const trimmed = clause.trim();
401
+ const namespace = /^\*\s+as\s+([A-Za-z_$][\w$]*)$/.exec(trimmed)?.[1];
402
+ if (namespace) return [{ imported: namespace, local: namespace, namespace: true }];
403
+
404
+ const named = /\{([\s\S]*)\}/.exec(trimmed)?.[1];
405
+ if (!named) return [];
406
+ return named
407
+ .split(',')
408
+ .map(part => part.trim())
409
+ .filter(Boolean)
410
+ .flatMap(part => {
411
+ if (part.startsWith('type ')) return [];
412
+ const [importedRaw, localRaw] = part.split(/\s+as\s+/);
413
+ const imported = importedRaw?.trim();
414
+ const local = (localRaw ?? importedRaw)?.trim();
415
+ if (!imported || !local || !isIdentifier(imported) || !isIdentifier(local)) return [];
416
+ return [{ imported, local }];
417
+ });
418
+ }
419
+
420
+ function googleFontBindingSource(
421
+ binding: GoogleFontBinding,
422
+ helperName: string,
423
+ namespaceHelperName: string,
424
+ ) {
425
+ if (binding.namespace) return `const ${binding.local} = ${namespaceHelperName};`;
426
+ return `const ${binding.local} = ${helperName}(${JSON.stringify(googleFontFamily(binding.imported))});`;
427
+ }
428
+
429
+ function googleFontFamily(exportName: string) {
430
+ return exportName.replace(/_/g, ' ');
431
+ }
432
+
433
+ function nextLocalFontImportNames(source: string) {
434
+ const names = new Set<string>();
435
+ const pattern = /\bimport\s+([A-Za-z_$][\w$]*)\s+from\s+(['"])([^'"]+)\2/g;
436
+ let match: RegExpExecArray | null;
437
+ while ((match = pattern.exec(source))) {
438
+ if (match[1] && match[3] && isNextLocalFontSpecifier(match[3])) names.add(match[1]);
439
+ }
440
+ return names;
441
+ }
442
+
443
+ function isNextLocalFontSpecifier(specifier: string) {
444
+ if (specifier === 'next/font/local' || specifier === 'next/font/local.js') return true;
445
+ const normalized = specifier.replace(/^file:\/\//, '').replace(/\\/g, '/');
446
+ return /\/compat\/next\/font\/local\.[jt]s$/.test(normalized);
447
+ }
448
+
449
+ function localFontCallerFile(file: string, root: string | undefined) {
450
+ if (path.isAbsolute(file)) return file;
451
+ if (file.startsWith('.')) return path.resolve(root ?? process.cwd(), file);
452
+ if (!root) return file;
453
+ try {
454
+ return createRequire(path.join(root, 'pnext-resolve.cjs')).resolve(file);
455
+ } catch {
456
+ return file;
457
+ }
458
+ }
@@ -0,0 +1,86 @@
1
+ // after() request-API phase rules (COMPAT).
2
+ //
3
+ // When a page render schedules after() work, the callback runs detached: there is no request scope, so
4
+ // headers()/cookies()/connection() must throw Next's exact "Route <path> used `<api>()` inside
5
+ // `after()`. This is not supported." message. In a route handler or server action, the same APIs called
6
+ // inside after() work because the async context captured a live request scope, so those cases never
7
+ // reach this guard.
8
+ //
9
+ // The route pathname is stashed on the work unit at request start so the detached callback can name it.
10
+
11
+ import { AsyncLocalStorage } from 'node:async_hooks';
12
+ import { currentRequest, getWorkUnit } from '../../request/context';
13
+
14
+ const REQUEST_PATH_KEY = Symbol.for('pnext.requestPath');
15
+ const INSPECT_CUSTOM = Symbol.for('nodejs.util.inspect.custom');
16
+ const requestApiScope = new AsyncLocalStorage<boolean>();
17
+
18
+ /** Run one after() task with its request-API capability. */
19
+ export function runWithAfterRequestApiScope<T>(allowed: boolean, callback: () => T): T {
20
+ return requestApiScope.run(allowed, callback);
21
+ }
22
+
23
+ /** Whether the current after() task may use request APIs. */
24
+ export function afterRequestApisAllowed(): boolean {
25
+ return requestApiScope.getStore() === true;
26
+ }
27
+
28
+ /** Stash the request pathname on the active work unit for after() diagnostics. */
29
+ export function recordRequestPath(pathname: string): void {
30
+ const unit = getWorkUnit();
31
+ if (unit) (unit.compat ??= {})[REQUEST_PATH_KEY] = pathname;
32
+ }
33
+
34
+ function currentRoutePath(): string {
35
+ const recorded = getWorkUnit()?.compat?.[REQUEST_PATH_KEY] as string | undefined;
36
+ if (recorded) return recorded;
37
+ try {
38
+ const request = currentRequest();
39
+ return request ? new URL(request.url).pathname : '';
40
+ } catch {
41
+ return '';
42
+ }
43
+ }
44
+
45
+ // Returns an Error-shaped object rather than a real `new Error`: the app's
46
+ // `catch (err) { console.error(..., err) }` prints it as `Error: <message>`
47
+ // with no misleading pnext-internal stack frame (the suite matches the message
48
+ // text exactly). Typed as Error so throw sites satisfy `only-throw-error`.
49
+ export function lifecycleError(message: string): Error {
50
+ const formatted = `Error: ${message}`;
51
+ return {
52
+ name: 'Error',
53
+ message,
54
+ stack: formatted,
55
+ toString: () => formatted,
56
+ [INSPECT_CUSTOM]: () => formatted,
57
+ } as unknown as Error;
58
+ }
59
+
60
+ /**
61
+ * Throw Next's "used inside after()" error when a request API is accessed from a
62
+ * detached after() callback (render phase, no live request scope). Returns
63
+ * without throwing in every legal case so callers proceed to the normal path.
64
+ */
65
+ export function assertRequestApiAllowedInAfter(api: 'headers()' | 'cookies()' | 'connection()'): void {
66
+ const unit = getWorkUnit();
67
+ if (unit?.phase !== 'after') return;
68
+ // Rendering keeps a synthetic request context during prerendering, so the
69
+ // explicit after-task capability—not merely currentRequest()—defines this.
70
+ if (afterRequestApisAllowed() && currentRequest()) return;
71
+ const suffix = api === 'connection()' ? '' : ' This is not supported.';
72
+ throw lifecycleError(
73
+ `Route ${currentRoutePath()} used \`${api}\` inside \`after()\`.${suffix}`,
74
+ );
75
+ }
76
+
77
+ /**
78
+ * draftMode() stays readable inside after(), but .enable()/.disable() throw in
79
+ * every context (Next's exact message names the route + method).
80
+ */
81
+ export function assertDraftModeToggleAllowedInAfter(method: 'enable' | 'disable'): void {
82
+ if (getWorkUnit()?.phase !== 'after') return;
83
+ throw lifecycleError(
84
+ `Route ${currentRoutePath()} used "draftMode().${method}()" inside \`after()\``,
85
+ );
86
+ }