@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
@@ -11,29 +11,29 @@
11
11
  * Limitations: anonymous default exports, object methods, and function expressions passed inline as
12
12
  * arguments are not wrapped.
13
13
  */
14
- import path from 'node:path';
14
+ import path from 'node:path'
15
15
 
16
- const useCacheRuntimePath = path.join(import.meta.dirname, 'use-cache.ts');
16
+ const useCacheRuntimePath = path.join(import.meta.dirname, 'use-cache.ts')
17
17
 
18
- const quickTest = /(['"])use cache(?:\s*:\s*[\w-]+)?\1/;
19
- const directiveText = /(['"])use cache(?:\s*:\s*([\w-]+))?\1\s*;?/;
18
+ const quickTest = /(['"])use cache(?:\s*:\s*[\w-]+)?\1/
19
+ const directiveText = /(['"])use cache(?:\s*:\s*([\w-]+))?\1\s*;?/
20
20
  const rootParamsImportLine =
21
- /^[ \t]*import\s+\{[\s\S]*?\}\s+from\s+['"]next\/root-params['"]\s*;?[ \t]*(?:\r?\n)?/gm;
21
+ /^[ \t]*import\s+\{[\s\S]*?\}\s+from\s+['"]next\/root-params['"]\s*;?[ \t]*(?:\r?\n)?/gm
22
22
 
23
- const boundHelper = '__pnextUseCacheBound';
23
+ const boundHelper = '__pnextUseCacheBound'
24
24
 
25
25
  export function rewriteUseCacheSource(source: string, file: string, root?: string): string {
26
26
  // `includes` first: over a real module corpus the quoted-directive regex is
27
27
  // ~24x the cost of the token scan, and virtually every module fails the token.
28
- if (!source.includes('use cache') || !quickTest.test(source)) return source;
28
+ if (!source.includes('use cache') || !quickTest.test(source)) return source
29
29
 
30
- const idBase = root ? path.relative(root, file) : file;
31
- const wrapped = new Set<string>();
32
- const privateWrapped = new Set<string>();
33
- let out = source;
34
- let usedBoundHelper = false;
30
+ const idBase = root ? path.relative(root, file) : file
31
+ const wrapped = new Set<string>()
32
+ const privateWrapped = new Set<string>()
33
+ let out = source
34
+ let usedBoundHelper = false
35
35
 
36
- out = rewriteModuleLevelDirective(out, wrapped);
36
+ out = rewriteModuleLevelDirective(out, wrapped)
37
37
  // Nested cached closures capturing enclosing bindings (bound args) must be wrapped in place - a
38
38
  // module-level reassignment cannot reach a function-scoped binding, and the captured values
39
39
  // participate in the cache key. Runs before the top-level pass so it only claims genuinely-nested
@@ -41,83 +41,85 @@ export function rewriteUseCacheSource(source: string, file: string, root?: strin
41
41
  // including any non-serializable function, which is a build error - wrap them first so the directive
42
42
  // is claimed here before the nested-named-closure pass, whose brace-balance heuristic would otherwise
43
43
  // mis-attribute the arrow's directive to a sibling function declaration.
44
- const arrows = rewriteReturnedArrowClosures(out, idBase);
45
- out = arrows.source;
46
- usedBoundHelper = arrows.wrapped;
47
- const nested = rewriteNestedBoundClosures(out, idBase);
48
- out = nested.source;
49
- usedBoundHelper = usedBoundHelper || nested.wrapped;
44
+ const arrows = rewriteReturnedArrowClosures(out, idBase)
45
+ out = arrows.source
46
+ usedBoundHelper = arrows.wrapped
47
+ const nested = rewriteNestedBoundClosures(out, idBase)
48
+ out = nested.source
49
+ usedBoundHelper = usedBoundHelper || nested.wrapped
50
50
  // Function expressions in value position (JSX attributes / call arguments):
51
51
  // `<Form foo={async function () { 'use cache'; … }} />`.
52
- const expressions = rewriteExpressionClosures(out, idBase);
53
- out = expressions.source;
54
- usedBoundHelper = usedBoundHelper || expressions.wrapped;
55
- const objectMethods = rewriteObjectMethods(out, idBase);
56
- out = objectMethods.source;
57
- usedBoundHelper = usedBoundHelper || objectMethods.wrapped;
58
- const staticMethods = rewriteStaticClassMethods(out, idBase);
59
- out = staticMethods.source;
60
- const usedDirectHelper = staticMethods.wrapped;
61
- out = rewriteFunctionLevelDirectives(out, wrapped, privateWrapped);
62
-
63
- if (wrapped.size === 0 && !usedBoundHelper && !usedDirectHelper) return out;
64
-
65
- const imports: string[] = [];
52
+ const expressions = rewriteExpressionClosures(out, idBase)
53
+ out = expressions.source
54
+ usedBoundHelper = usedBoundHelper || expressions.wrapped
55
+ const objectMethods = rewriteObjectMethods(out, idBase)
56
+ out = objectMethods.source
57
+ usedBoundHelper = usedBoundHelper || objectMethods.wrapped
58
+ const staticMethods = rewriteStaticClassMethods(out, idBase)
59
+ out = staticMethods.source
60
+ const usedDirectHelper = staticMethods.wrapped
61
+ out = rewriteFunctionLevelDirectives(out, wrapped, privateWrapped)
62
+
63
+ if (wrapped.size === 0 && !usedBoundHelper && !usedDirectHelper) return out
64
+
65
+ const imports: string[] = []
66
66
  if (wrapped.size > 0 || usedDirectHelper) {
67
- imports.push(`import { pnextUseCache as __pnextUseCache } from ${JSON.stringify(useCacheRuntimePath)};`);
67
+ imports.push(
68
+ `import { pnextUseCache as __pnextUseCache } from ${JSON.stringify(useCacheRuntimePath)};`,
69
+ )
68
70
  }
69
71
  if (privateWrapped.size > 0) {
70
72
  imports.push(
71
73
  `import { pnextUseCachePrivate as __pnextUseCachePrivate } from ${JSON.stringify(useCacheRuntimePath)};`,
72
- );
74
+ )
73
75
  }
74
76
  if (usedBoundHelper) {
75
77
  imports.push(
76
78
  `import { pnextUseCacheBound as ${boundHelper} } from ${JSON.stringify(useCacheRuntimePath)};`,
77
- );
79
+ )
78
80
  }
79
81
  // `export default NAME;` snapshots the binding's value when the statement
80
82
  // evaluates (a default-exported identifier is not a live binding, unlike
81
83
  // `export default function NAME`), so the module-end reassignment would be
82
84
  // invisible to importers. Move the statement after the reassignments.
83
- let defaultExport = '';
85
+ let defaultExport = ''
84
86
  for (const name of wrapped) {
85
87
  const statement = new RegExp(
86
88
  `(^|[\\n;])[ \\t]*export\\s+default\\s+${escapeIdent(name)}(?![\\w$])\\s*;?`,
87
- ).exec(out);
88
- if (!statement) continue;
89
+ ).exec(out)
90
+ if (!statement) continue
89
91
  out =
90
92
  out.slice(0, statement.index) +
91
93
  (statement[1] ?? '') +
92
- out.slice(statement.index + statement[0].length);
93
- defaultExport = `\nexport default ${name};`;
94
- break;
94
+ out.slice(statement.index + statement[0].length)
95
+ defaultExport = `\nexport default ${name};`
96
+ break
95
97
  }
96
98
 
97
- const hoisted = hoistRootParamsImports(out);
99
+ const hoisted = hoistRootParamsImports(out)
98
100
  const assignments = [...wrapped]
99
101
  .map(name =>
100
102
  privateWrapped.has(name)
101
103
  ? `${name} = __pnextUseCachePrivate(${JSON.stringify(`${idBase}#${name}`)}, ${name});`
102
104
  : `${name} = __pnextUseCache(${JSON.stringify(`${idBase}#${name}`)}, ${name});`,
103
105
  )
104
- .join('\n');
105
- const suffix = assignments ? `\n;${assignments}${defaultExport}\n` : '\n';
106
- return `${hoisted.imports}${imports.join('\n')}\n${hoisted.source}${suffix}`;
106
+ .join('\n')
107
+ const suffix = assignments ? `\n;${assignments}${defaultExport}\n` : '\n'
108
+ return `${hoisted.imports}${imports.join('\n')}\n${hoisted.source}${suffix}`
107
109
  }
108
110
 
109
111
  function hoistRootParamsImports(source: string): { imports: string; source: string } {
110
- if (!source.includes('next/root-params')) return { imports: '', source };
111
- const imports: string[] = [];
112
+ if (!source.includes('next/root-params')) return { imports: '', source }
113
+ const imports: string[] = []
112
114
  const body = source.replace(rootParamsImportLine, statement => {
113
- imports.push(statement.trimEnd());
114
- return '';
115
- });
116
- if (imports.length === 0) return { imports: '', source };
115
+ imports.push(statement.trimEnd())
116
+ return ''
117
+ })
118
+ if (imports.length === 0) return { imports: '', source }
117
119
  return {
118
120
  imports: `${imports.join('\n')}\n`,
119
121
  source: body.replace(/^\s*\n/, ''),
120
- };
122
+ }
121
123
  }
122
124
 
123
125
  /**
@@ -138,42 +140,41 @@ function rewriteNestedBoundClosures(
138
140
  source: string,
139
141
  idBase: string,
140
142
  ): { source: string; wrapped: boolean } {
141
- let out = source;
142
- let wrapped = false;
143
- let searchFrom = 0;
143
+ let out = source
144
+ let wrapped = false
145
+ let searchFrom = 0
144
146
  for (;;) {
145
- const tail = out.slice(searchFrom);
146
- const directive = directiveText.exec(tail);
147
- if (!directive) break;
148
- const index = searchFrom + directive.index;
149
- const prefix = out.slice(0, index);
147
+ const tail = out.slice(searchFrom)
148
+ const directive = directiveText.exec(tail)
149
+ if (!directive) break
150
+ const index = searchFrom + directive.index
151
+ const prefix = out.slice(0, index)
150
152
  if (!/\{\s*$/.test(prefix)) {
151
- searchFrom = index + directive[0].length;
152
- continue;
153
+ searchFrom = index + directive[0].length
154
+ continue
153
155
  }
154
- const closure = findNestedClosure(out, index);
156
+ const closure = findNestedClosure(out, index)
155
157
  if (!closure) {
156
- searchFrom = index + directive[0].length;
157
- continue;
158
+ searchFrom = index + directive[0].length
159
+ continue
158
160
  }
159
- const id = `${idBase}#${closure.enclosingName}.${closure.name}`;
160
- const captureList = closure.captures.length ? closure.captures.join(', ') : '';
161
+ const id = `${idBase}#${closure.enclosingName}.${closure.name}`
162
+ const captureList = closure.captures.length ? closure.captures.join(', ') : ''
161
163
  // Replace `[async ]function NAME(params) {` with a wrapped const binding, and
162
164
  // strip the directive from the body.
163
- const wrappedHead =
164
- `const ${closure.name} = ${boundHelper}(${JSON.stringify(id)}, ${closure.asyncKeyword}function ${closure.name}(${closure.params}) {`;
165
- const bodyAfterDirective = out.slice(index + directive[0].length, closure.bodyEnd);
166
- const rest = out.slice(closure.bodyEnd);
165
+ const wrappedHead = `const ${closure.name} = ${boundHelper}(${JSON.stringify(id)}, ${closure.asyncKeyword}function ${closure.name}(${closure.params}) {`
166
+ const bodyAfterDirective = out.slice(index + directive[0].length, closure.bodyEnd)
167
+ const rest = out.slice(closure.bodyEnd)
167
168
  out =
168
169
  out.slice(0, closure.declStart) +
169
170
  wrappedHead +
170
171
  bodyAfterDirective +
171
172
  `}, () => [${captureList}])` +
172
- rest.replace(/^\}/, '');
173
- wrapped = true;
174
- searchFrom = closure.declStart + wrappedHead.length;
173
+ rest.replace(/^\}/, '')
174
+ wrapped = true
175
+ searchFrom = closure.declStart + wrappedHead.length
175
176
  }
176
- return { source: out, wrapped };
177
+ return { source: out, wrapped }
177
178
  }
178
179
 
179
180
  /**
@@ -193,41 +194,41 @@ function rewriteReturnedArrowClosures(
193
194
  source: string,
194
195
  idBase: string,
195
196
  ): { source: string; wrapped: boolean } {
196
- let out = source;
197
- let wrapped = false;
198
- let searchFrom = 0;
199
- let counter = 0;
197
+ let out = source
198
+ let wrapped = false
199
+ let searchFrom = 0
200
+ let counter = 0
200
201
  for (;;) {
201
- const tail = out.slice(searchFrom);
202
- const directive = directiveText.exec(tail);
203
- if (!directive) break;
204
- const index = searchFrom + directive.index;
205
- const prefix = out.slice(0, index);
202
+ const tail = out.slice(searchFrom)
203
+ const directive = directiveText.exec(tail)
204
+ if (!directive) break
205
+ const index = searchFrom + directive.index
206
+ const prefix = out.slice(0, index)
206
207
  if (!/\{\s*$/.test(prefix)) {
207
- searchFrom = index + directive[0].length;
208
- continue;
208
+ searchFrom = index + directive[0].length
209
+ continue
209
210
  }
210
- const closure = findReturnedArrowClosure(out, index);
211
+ const closure = findReturnedArrowClosure(out, index)
211
212
  if (!closure) {
212
- searchFrom = index + directive[0].length;
213
- continue;
213
+ searchFrom = index + directive[0].length
214
+ continue
214
215
  }
215
- const id = `${idBase}#${closure.enclosingName}.arrow${counter === 0 ? '' : counter}`;
216
- counter += 1;
217
- const captureList = closure.captures.join(', ');
218
- const wrappedHead = `${boundHelper}(${JSON.stringify(id)}, ${closure.head}`;
219
- const bodyAfterDirective = out.slice(index + directive[0].length, closure.bodyEnd);
220
- const rest = out.slice(closure.bodyEnd);
216
+ const id = `${idBase}#${closure.enclosingName}.arrow${counter === 0 ? '' : counter}`
217
+ counter += 1
218
+ const captureList = closure.captures.join(', ')
219
+ const wrappedHead = `${boundHelper}(${JSON.stringify(id)}, ${closure.head}`
220
+ const bodyAfterDirective = out.slice(index + directive[0].length, closure.bodyEnd)
221
+ const rest = out.slice(closure.bodyEnd)
221
222
  out =
222
223
  out.slice(0, closure.declStart) +
223
224
  wrappedHead +
224
225
  bodyAfterDirective +
225
226
  `}, () => [${captureList}])` +
226
- rest.replace(/^\}/, '');
227
- wrapped = true;
228
- searchFrom = closure.declStart + wrappedHead.length;
227
+ rest.replace(/^\}/, '')
228
+ wrapped = true
229
+ searchFrom = closure.declStart + wrappedHead.length
229
230
  }
230
- return { source: out, wrapped };
231
+ return { source: out, wrapped }
231
232
  }
232
233
 
233
234
  /**
@@ -241,90 +242,90 @@ function rewriteExpressionClosures(
241
242
  source: string,
242
243
  idBase: string,
243
244
  ): { source: string; wrapped: boolean } {
244
- let out = source;
245
- let wrapped = false;
246
- let searchFrom = 0;
247
- let counter = 0;
245
+ let out = source
246
+ let wrapped = false
247
+ let searchFrom = 0
248
+ let counter = 0
248
249
  for (;;) {
249
- const tail = out.slice(searchFrom);
250
- const directive = directiveText.exec(tail);
251
- if (!directive) break;
252
- const index = searchFrom + directive.index;
250
+ const tail = out.slice(searchFrom)
251
+ const directive = directiveText.exec(tail)
252
+ if (!directive) break
253
+ const index = searchFrom + directive.index
253
254
  if (directive[2] !== undefined || !/\{\s*$/.test(out.slice(0, index))) {
254
- searchFrom = index + directive[0].length;
255
- continue;
255
+ searchFrom = index + directive[0].length
256
+ continue
256
257
  }
257
- const closure = findExpressionClosure(out, index);
258
+ const closure = findExpressionClosure(out, index)
258
259
  if (!closure) {
259
- searchFrom = index + directive[0].length;
260
- continue;
260
+ searchFrom = index + directive[0].length
261
+ continue
261
262
  }
262
- const id = `${idBase}#${closure.enclosingName}.expr${counter === 0 ? '' : counter}`;
263
- counter += 1;
264
- const wrappedHead = `${boundHelper}(${JSON.stringify(id)}, ${closure.head}`;
265
- const bodyAfterDirective = out.slice(index + directive[0].length, closure.bodyEnd);
266
- const rest = out.slice(closure.bodyEnd);
263
+ const id = `${idBase}#${closure.enclosingName}.expr${counter === 0 ? '' : counter}`
264
+ counter += 1
265
+ const wrappedHead = `${boundHelper}(${JSON.stringify(id)}, ${closure.head}`
266
+ const bodyAfterDirective = out.slice(index + directive[0].length, closure.bodyEnd)
267
+ const rest = out.slice(closure.bodyEnd)
267
268
  out =
268
269
  out.slice(0, closure.declStart) +
269
270
  wrappedHead +
270
271
  bodyAfterDirective +
271
272
  `}, () => [${closure.captures.join(', ')}])` +
272
- rest.replace(/^\}/, '');
273
- wrapped = true;
274
- searchFrom = closure.declStart + wrappedHead.length;
273
+ rest.replace(/^\}/, '')
274
+ wrapped = true
275
+ searchFrom = closure.declStart + wrappedHead.length
275
276
  }
276
- return { source: out, wrapped };
277
+ return { source: out, wrapped }
277
278
  }
278
279
 
279
280
  function findExpressionClosure(
280
281
  source: string,
281
282
  directiveIndex: number,
282
283
  ): ReturnedArrowClosure | null {
283
- const prefix = source.slice(0, directiveIndex);
284
+ const prefix = source.slice(0, directiveIndex)
284
285
  // Value-position contexts: a JSX attribute (`={`), a call argument (`(` or
285
286
  // `,`), or an object-property value (`:`), followed by a function expression
286
287
  // or an arrow whose body opens with the directive.
287
288
  const exprRe =
288
- /(=\s*\{|[(,:])\s*((?:async\s+)?(?:function(?:\s+[A-Za-z_$][\w$]*)?\s*\(([^)]*)\)|(?:\(([^)]*)\)|[A-Za-z_$][\w$]*)\s*=>)\s*\{)/g;
289
- let match: RegExpExecArray | null;
290
- let best: { headStart: number; head: string; params: string; bodyOpen: number } | null = null;
289
+ /(=\s*\{|[(,:])\s*((?:async\s+)?(?:function(?:\s+[A-Za-z_$][\w$]*)?\s*\(([^)]*)\)|(?:\(([^)]*)\)|[A-Za-z_$][\w$]*)\s*=>)\s*\{)/g
290
+ let match: RegExpExecArray | null
291
+ let best: { headStart: number; head: string; params: string; bodyOpen: number } | null = null
291
292
  while ((match = exprRe.exec(prefix)) !== null) {
292
- const head = match[2]!;
293
- const headStart = match.index + match[0].length - head.length;
294
- const bodyOpen = match.index + match[0].length;
295
- const between = source.slice(bodyOpen, directiveIndex);
293
+ const head = match[2]!
294
+ const headStart = match.index + match[0].length - head.length
295
+ const bodyOpen = match.index + match[0].length
296
+ const between = source.slice(bodyOpen, directiveIndex)
296
297
  // The directive must be the first statement of THIS body: zero net braces
297
298
  // between, and the body must never close in between (a dip below zero means
298
299
  // this candidate's body ended before the directive).
299
- let balance = 0;
300
- let closed = false;
300
+ let balance = 0
301
+ let closed = false
301
302
  for (const ch of between) {
302
- if (ch === '{') balance += 1;
303
+ if (ch === '{') balance += 1
303
304
  else if (ch === '}') {
304
- balance -= 1;
305
+ balance -= 1
305
306
  if (balance < 0) {
306
- closed = true;
307
- break;
307
+ closed = true
308
+ break
308
309
  }
309
310
  }
310
311
  }
311
312
  if (!closed && balance === 0) {
312
- best = { headStart, head, params: match[3] ?? match[4] ?? '', bodyOpen };
313
+ best = { headStart, head, params: match[3] ?? match[4] ?? '', bodyOpen }
313
314
  }
314
315
  }
315
- if (!best) return null;
316
+ if (!best) return null
316
317
 
317
- const bodyEnd = matchClosingBrace(source, best.bodyOpen);
318
- if (bodyEnd < directiveIndex) return null;
319
- const body = source.slice(best.bodyOpen, bodyEnd);
318
+ const bodyEnd = matchClosingBrace(source, best.bodyOpen)
319
+ if (bodyEnd < directiveIndex) return null
320
+ const body = source.slice(best.bodyOpen, bodyEnd)
320
321
 
321
- const enclosing = findEnclosingFunctionFull(source, best.headStart);
322
- const ownParams = new Set(paramNames(best.params));
322
+ const enclosing = findEnclosingFunctionFull(source, best.headStart)
323
+ const ownParams = new Set(paramNames(best.params))
323
324
  const captures = enclosing
324
325
  ? enclosingCaptureCandidates(source, enclosing).filter(
325
326
  name => !ownParams.has(name) && new RegExp(`\\b${escapeIdent(name)}\\b`).test(body),
326
327
  )
327
- : [];
328
+ : []
328
329
 
329
330
  return {
330
331
  head: best.head,
@@ -332,61 +333,61 @@ function findExpressionClosure(
332
333
  bodyEnd,
333
334
  enclosingName: enclosing?.name ?? 'module',
334
335
  captures,
335
- };
336
+ }
336
337
  }
337
338
 
338
339
  interface ReturnedArrowClosure {
339
- head: string;
340
- declStart: number;
341
- bodyEnd: number;
342
- enclosingName: string;
343
- captures: string[];
340
+ head: string
341
+ declStart: number
342
+ bodyEnd: number
343
+ enclosingName: string
344
+ captures: string[]
344
345
  }
345
346
 
346
347
  function findReturnedArrowClosure(
347
348
  source: string,
348
349
  directiveIndex: number,
349
350
  ): ReturnedArrowClosure | null {
350
- const prefix = source.slice(0, directiveIndex);
351
+ const prefix = source.slice(0, directiveIndex)
351
352
  // The parameter list must stay on one line and carry no `<`: otherwise
352
353
  // `return (\n <Form getDate={async () => {` reads as one giant "arrow" whose
353
354
  // params run from the returned parenthesis to the first `)` inside the JSX,
354
355
  // and the wrap lands on the return expression instead of the attribute's
355
356
  // closure (use-cache-with-server-function-props). Value-position closures like
356
357
  // that are claimed by rewriteExpressionClosures instead.
357
- const arrowRe = /\breturn\s+(async\s+)?(\([^)<\n]*\)|[A-Za-z_$][\w$]*)\s*=>\s*\{/g;
358
- let match: RegExpExecArray | null;
359
- let best: { headStart: number; head: string; params: string; bodyOpen: number } | null = null;
358
+ const arrowRe = /\breturn\s+(async\s+)?(\([^)<\n]*\)|[A-Za-z_$][\w$]*)\s*=>\s*\{/g
359
+ let match: RegExpExecArray | null
360
+ let best: { headStart: number; head: string; params: string; bodyOpen: number } | null = null
360
361
  while ((match = arrowRe.exec(prefix)) !== null) {
361
- const retPrefix = /^return\s+/.exec(match[0])?.[0] ?? 'return ';
362
- const headStart = match.index + retPrefix.length;
363
- const bodyOpen = match.index + match[0].length;
364
- const between = source.slice(bodyOpen, directiveIndex);
365
- let balance = 0;
362
+ const retPrefix = /^return\s+/.exec(match[0])?.[0] ?? 'return '
363
+ const headStart = match.index + retPrefix.length
364
+ const bodyOpen = match.index + match[0].length
365
+ const between = source.slice(bodyOpen, directiveIndex)
366
+ let balance = 0
366
367
  for (const ch of between) {
367
- if (ch === '{') balance += 1;
368
- else if (ch === '}') balance -= 1;
368
+ if (ch === '{') balance += 1
369
+ else if (ch === '}') balance -= 1
369
370
  }
370
371
  if (balance === 0) {
371
- const paramSpec = match[2]!;
372
- const params = paramSpec.startsWith('(') ? paramSpec.slice(1, -1) : paramSpec;
373
- best = { headStart, head: match[0].slice(retPrefix.length), params, bodyOpen };
372
+ const paramSpec = match[2]!
373
+ const params = paramSpec.startsWith('(') ? paramSpec.slice(1, -1) : paramSpec
374
+ best = { headStart, head: match[0].slice(retPrefix.length), params, bodyOpen }
374
375
  }
375
376
  }
376
- if (!best) return null;
377
+ if (!best) return null
377
378
 
378
- const enclosing = findEnclosingFunctionFull(source, best.headStart);
379
- if (!enclosing) return null;
379
+ const enclosing = findEnclosingFunctionFull(source, best.headStart)
380
+ if (!enclosing) return null
380
381
 
381
- const bodyEnd = matchClosingBrace(source, best.bodyOpen);
382
- if (bodyEnd < 0) return null;
383
- const body = source.slice(best.bodyOpen, bodyEnd);
382
+ const bodyEnd = matchClosingBrace(source, best.bodyOpen)
383
+ if (bodyEnd < 0) return null
384
+ const body = source.slice(best.bodyOpen, bodyEnd)
384
385
 
385
- const arrowParams = new Set(paramNames(best.params));
386
- const candidates = enclosingCaptureCandidates(source, enclosing);
386
+ const arrowParams = new Set(paramNames(best.params))
387
+ const candidates = enclosingCaptureCandidates(source, enclosing)
387
388
  const captures = candidates.filter(
388
389
  name => !arrowParams.has(name) && new RegExp(`\\b${escapeIdent(name)}\\b`).test(body),
389
- );
390
+ )
390
391
 
391
392
  return {
392
393
  head: best.head,
@@ -394,7 +395,7 @@ function findReturnedArrowClosure(
394
395
  bodyEnd,
395
396
  enclosingName: enclosing.name,
396
397
  captures,
397
- };
398
+ }
398
399
  }
399
400
 
400
401
  /** Nearest enclosing named function wrapping `pos`, with its body span. */
@@ -402,26 +403,26 @@ function findEnclosingFunctionFull(
402
403
  source: string,
403
404
  pos: number,
404
405
  ): { name: string; params: string[]; bodyStart: number; bodyEnd: number } | null {
405
- const before = source.slice(0, pos);
406
- const re = /(?:^|[\s;{}])(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g;
407
- let match: RegExpExecArray | null;
406
+ const before = source.slice(0, pos)
407
+ const re = /(?:^|[\s;{}])(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
408
+ let match: RegExpExecArray | null
408
409
  let candidate: { name: string; params: string[]; bodyStart: number; bodyEnd: number } | null =
409
- null;
410
+ null
410
411
  while ((match = re.exec(before)) !== null) {
411
- const kwOffset = match[0].search(/async|function/);
412
- if (!isStatementFunctionHead(source, match.index + kwOffset)) continue;
413
- const bodyOpen = match.index + match[0].length;
414
- const close = matchClosingBrace(source, bodyOpen);
412
+ const kwOffset = match[0].search(/async|function/)
413
+ if (!isStatementFunctionHead(source, match.index + kwOffset)) continue
414
+ const bodyOpen = match.index + match[0].length
415
+ const close = matchClosingBrace(source, bodyOpen)
415
416
  if (close > pos) {
416
417
  candidate = {
417
418
  name: match[1]!,
418
419
  params: paramNames(match[2]!),
419
420
  bodyStart: bodyOpen,
420
421
  bodyEnd: close,
421
- };
422
+ }
422
423
  }
423
424
  }
424
- return candidate;
425
+ return candidate
425
426
  }
426
427
 
427
428
  /**
@@ -433,25 +434,25 @@ function enclosingCaptureCandidates(
433
434
  source: string,
434
435
  enclosing: { params: string[]; bodyStart: number; bodyEnd: number },
435
436
  ): string[] {
436
- const names = new Set(enclosing.params);
437
- const body = source.slice(enclosing.bodyStart, enclosing.bodyEnd);
437
+ const names = new Set(enclosing.params)
438
+ const body = source.slice(enclosing.bodyStart, enclosing.bodyEnd)
438
439
  for (const match of body.matchAll(/\bfunction\s*\*?\s*([A-Za-z_$][\w$]*)/g)) {
439
- if (match[1]) names.add(match[1]);
440
+ if (match[1]) names.add(match[1])
440
441
  }
441
442
  for (const match of body.matchAll(/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)/g)) {
442
- if (match[1]) names.add(match[1]);
443
+ if (match[1]) names.add(match[1])
443
444
  }
444
- return [...names];
445
+ return [...names]
445
446
  }
446
447
 
447
448
  interface NestedClosure {
448
- name: string;
449
- params: string;
450
- asyncKeyword: string;
451
- declStart: number;
452
- bodyEnd: number;
453
- enclosingName: string;
454
- captures: string[];
449
+ name: string
450
+ params: string
451
+ asyncKeyword: string
452
+ declStart: number
453
+ bodyEnd: number
454
+ enclosingName: string
455
+ captures: string[]
455
456
  }
456
457
 
457
458
  /**
@@ -461,57 +462,62 @@ interface NestedClosure {
461
462
  * statics and arrows are left inert, which is safe: they run uncached rather than breaking the build.
462
463
  */
463
464
  function findNestedClosure(source: string, directiveIndex: number): NestedClosure | null {
464
- const prefix = source.slice(0, directiveIndex);
465
+ const prefix = source.slice(0, directiveIndex)
465
466
  // Match the nearest `[async ]function NAME(params) {` whose brace opens the
466
467
  // directive body (exactly one unclosed brace between it and the directive).
467
- const declRe = /(?:^|[\s;{}])(async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g;
468
- let match: RegExpExecArray | null;
469
- let best: { asyncKeyword: string; name: string; params: string; headStart: number; bodyOpen: number } | null =
470
- null;
468
+ const declRe = /(?:^|[\s;{}])(async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
469
+ let match: RegExpExecArray | null
470
+ let best: {
471
+ asyncKeyword: string
472
+ name: string
473
+ params: string
474
+ headStart: number
475
+ bodyOpen: number
476
+ } | null = null
471
477
  while ((match = declRe.exec(prefix)) !== null) {
472
478
  // Clean head start: the position of the `async`/`function` keyword (the
473
479
  // match may include a leading whitespace/`{`/`;` separator char).
474
- const kwOffset = match[0].search(/async|function/);
475
- const cleanStart = match.index + kwOffset;
476
- if (!isStatementFunctionHead(source, cleanStart)) continue;
477
- const bodyOpen = match.index + match[0].length; // just after `{`
480
+ const kwOffset = match[0].search(/async|function/)
481
+ const cleanStart = match.index + kwOffset
482
+ if (!isStatementFunctionHead(source, cleanStart)) continue
483
+ const bodyOpen = match.index + match[0].length // just after `{`
478
484
  // Balance braces from bodyOpen to directiveIndex — must be exactly 0 open
479
485
  // *within* this function head region, i.e. the directive is the first stmt.
480
- const between = source.slice(bodyOpen, directiveIndex);
481
- let balance = 0;
486
+ const between = source.slice(bodyOpen, directiveIndex)
487
+ let balance = 0
482
488
  for (const ch of between) {
483
- if (ch === '{') balance += 1;
484
- else if (ch === '}') balance -= 1;
489
+ if (ch === '{') balance += 1
490
+ else if (ch === '}') balance -= 1
485
491
  }
486
492
  if (balance === 0) {
487
- if (match[2] === 'function') continue;
493
+ if (match[2] === 'function') continue
488
494
  best = {
489
495
  asyncKeyword: match[1] ? 'async ' : '',
490
496
  name: match[2]!,
491
497
  params: match[3]!.trim(),
492
498
  headStart: cleanStart,
493
499
  bodyOpen,
494
- };
500
+ }
495
501
  }
496
502
  }
497
- if (!best) return null;
503
+ if (!best) return null
498
504
 
499
505
  // The closure must be genuinely nested: there is an enclosing named function
500
506
  // whose body contains `best`. Find the nearest such enclosing declaration.
501
- const enclosing = findEnclosingNamedFunction(source, best.headStart);
502
- if (!enclosing) return null;
507
+ const enclosing = findEnclosingNamedFunction(source, best.headStart)
508
+ if (!enclosing) return null
503
509
 
504
510
  // Find the matching close brace of the closure body.
505
- const bodyEnd = matchClosingBrace(source, best.bodyOpen);
506
- if (bodyEnd < 0) return null;
507
- const body = source.slice(best.bodyOpen, bodyEnd);
511
+ const bodyEnd = matchClosingBrace(source, best.bodyOpen)
512
+ if (bodyEnd < 0) return null
513
+ const body = source.slice(best.bodyOpen, bodyEnd)
508
514
 
509
515
  // Captures: enclosing params referenced (whole-word) in the closure body and
510
516
  // not shadowed by the closure's own params.
511
- const closureParams = new Set(paramNames(best.params));
517
+ const closureParams = new Set(paramNames(best.params))
512
518
  const captures = enclosing.params.filter(
513
519
  param => !closureParams.has(param) && new RegExp(`\\b${escapeIdent(param)}\\b`).test(body),
514
- );
520
+ )
515
521
 
516
522
  return {
517
523
  name: best.name,
@@ -521,7 +527,7 @@ function findNestedClosure(source: string, directiveIndex: number): NestedClosur
521
527
  bodyEnd,
522
528
  enclosingName: enclosing.name,
523
529
  captures,
524
- };
530
+ }
525
531
  }
526
532
 
527
533
  /** Nearest enclosing `[async ]function NAME(params)` whose body wraps `pos`. */
@@ -529,62 +535,62 @@ function findEnclosingNamedFunction(
529
535
  source: string,
530
536
  pos: number,
531
537
  ): { name: string; params: string[] } | null {
532
- const before = source.slice(0, pos);
533
- const re = /(?:^|[\s;{}])(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g;
534
- let match: RegExpExecArray | null;
535
- let candidate: { name: string; params: string[] } | null = null;
538
+ const before = source.slice(0, pos)
539
+ const re = /(?:^|[\s;{}])(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
540
+ let match: RegExpExecArray | null
541
+ let candidate: { name: string; params: string[] } | null = null
536
542
  while ((match = re.exec(before)) !== null) {
537
- const kwOffset = match[0].search(/async|function/);
538
- if (!isStatementFunctionHead(source, match.index + kwOffset)) continue;
539
- const bodyOpen = match.index + match[0].length;
540
- const close = matchClosingBrace(source, bodyOpen);
543
+ const kwOffset = match[0].search(/async|function/)
544
+ if (!isStatementFunctionHead(source, match.index + kwOffset)) continue
545
+ const bodyOpen = match.index + match[0].length
546
+ const close = matchClosingBrace(source, bodyOpen)
541
547
  if (close > pos) {
542
- candidate = { name: match[1]!, params: paramNames(match[2]!) };
548
+ candidate = { name: match[1]!, params: paramNames(match[2]!) }
543
549
  }
544
550
  }
545
- return candidate;
551
+ return candidate
546
552
  }
547
553
 
548
554
  function isStatementFunctionHead(source: string, index: number): boolean {
549
- if (/(^|[\s;{}])export\s+(?:default\s+)?$/.test(source.slice(0, index))) return true;
550
- let previous = index - 1;
551
- while (previous >= 0 && /\s/.test(source[previous]!)) previous -= 1;
552
- if (previous < 0) return true;
553
- if (source[previous] === '=') return false;
555
+ if (/(^|[\s;{}])export\s+(?:default\s+)?$/.test(source.slice(0, index))) return true
556
+ let previous = index - 1
557
+ while (previous >= 0 && /\s/.test(source[previous]!)) previous -= 1
558
+ if (previous < 0) return true
559
+ if (source[previous] === '=') return false
554
560
  if (source[previous] === '{') {
555
- let beforeBrace = previous - 1;
556
- while (beforeBrace >= 0 && /\s/.test(source[beforeBrace]!)) beforeBrace -= 1;
557
- return source[beforeBrace] !== '=';
561
+ let beforeBrace = previous - 1
562
+ while (beforeBrace >= 0 && /\s/.test(source[beforeBrace]!)) beforeBrace -= 1
563
+ return source[beforeBrace] !== '='
558
564
  }
559
- return source[previous] === ';' || source[previous] === '}';
565
+ return source[previous] === ';' || source[previous] === '}'
560
566
  }
561
567
 
562
568
  /** Index just past the `}` matching the `{` at `openIndex`, or -1. */
563
569
  function matchClosingBrace(source: string, openIndex: number): number {
564
- let balance = 1;
570
+ let balance = 1
565
571
  for (let i = openIndex; i < source.length; i += 1) {
566
- const ch = source[i];
567
- if (ch === '{') balance += 1;
572
+ const ch = source[i]
573
+ if (ch === '{') balance += 1
568
574
  else if (ch === '}') {
569
- balance -= 1;
570
- if (balance === 0) return i;
575
+ balance -= 1
576
+ if (balance === 0) return i
571
577
  }
572
578
  }
573
- return -1;
579
+ return -1
574
580
  }
575
581
 
576
582
  function escapeIdent(name: string): string {
577
- return name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
583
+ return name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
578
584
  }
579
585
 
580
586
  /** Extract simple parameter identifier names from a `(...)` param string. */
581
587
  function paramNames(params: string): string[] {
582
- const names: string[] = [];
588
+ const names: string[] = []
583
589
  for (const part of params.split(',')) {
584
- const name = part.trim().split(/[=:\s]/)[0];
585
- if (name) names.push(name);
590
+ const name = part.trim().split(/[=:\s]/)[0]
591
+ if (name) names.push(name)
586
592
  }
587
- return names;
593
+ return names
588
594
  }
589
595
 
590
596
  /**
@@ -593,121 +599,132 @@ function paramNames(params: string): string[] {
593
599
  * reassignable.
594
600
  */
595
601
  function rewriteModuleLevelDirective(source: string, wrapped: Set<string>): string {
596
- const prologue = /^(?:#![^\n]*\n)?(?:\s|\/\/[^\n]*\n|\/\*[\s\S]*?\*\/)*/.exec(source);
597
- const offset = prologue?.[0].length ?? 0;
598
- const head = source.slice(offset);
599
- const directive = /^(['"])use cache(?:\s*:\s*[\w-]+)?\1\s*;?/.exec(head);
600
- if (!directive) return source;
602
+ const prologue = /^(?:#![^\n]*\n)?(?:\s|\/\/[^\n]*\n|\/\*[\s\S]*?\*\/)*/.exec(source)
603
+ const offset = prologue?.[0].length ?? 0
604
+ const head = source.slice(offset)
605
+ const directive = /^(['"])use cache(?:\s*:\s*[\w-]+)?\1\s*;?/.exec(head)
606
+ if (!directive) return source
601
607
 
602
- let out = source.slice(0, offset) + head.slice(directive[0].length);
608
+ let out = source.slice(0, offset) + head.slice(directive[0].length)
603
609
 
604
610
  // export [async] function NAME / export [default] [async] function NAME
605
611
  for (const match of out.matchAll(
606
612
  /\bexport\s+(?:default\s+)?(?:async\s+)?function\s*\*?\s+([A-Za-z_$][\w$]*)/g,
607
613
  )) {
608
- if (match[1]) wrapped.add(match[1]);
614
+ if (match[1]) wrapped.add(match[1])
609
615
  }
610
616
  // export const|let|var NAME = ... (assume function values; const -> let)
611
617
  out = out.replace(
612
618
  /\bexport\s+(const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/g,
613
619
  (full, keyword: string, name: string) => {
614
- wrapped.add(name);
615
- return full.replace(keyword, keyword === 'const' ? 'let' : keyword);
620
+ wrapped.add(name)
621
+ return full.replace(keyword, keyword === 'const' ? 'let' : keyword)
616
622
  },
617
- );
623
+ )
618
624
 
619
625
  // `const NAME = ...; export { NAME }` — a re-exported binding under a
620
626
  // module-level directive. Wrap each re-exported name declared as a top-level
621
627
  // const/let/var function value (flip its const to let so it's reassignable).
622
- const reExported = new Set<string>();
628
+ const reExported = new Set<string>()
623
629
  for (const clause of out.matchAll(/\bexport\s*\{([^}]*)\}/g)) {
624
630
  for (const spec of clause[1]!.split(',')) {
625
- const local = spec.trim().split(/\s+as\s+/)[0]?.trim();
626
- if (local && /^[A-Za-z_$][\w$]*$/.test(local)) reExported.add(local);
631
+ const local = spec
632
+ .trim()
633
+ .split(/\s+as\s+/)[0]
634
+ ?.trim()
635
+ if (local && /^[A-Za-z_$][\w$]*$/.test(local)) reExported.add(local)
627
636
  }
628
637
  }
629
638
  for (const name of reExported) {
630
- if (wrapped.has(name)) continue;
631
- const declRe = new RegExp(`(^|[\\n;])\\s*(const|let|var)\\s+${escapeIdent(name)}\\s*=`);
632
- const declMatch = declRe.exec(out);
633
- if (!declMatch) continue;
634
- wrapped.add(name);
639
+ if (wrapped.has(name)) continue
640
+ const declRe = new RegExp(`(^|[\\n;])\\s*(const|let|var)\\s+${escapeIdent(name)}\\s*=`)
641
+ const declMatch = declRe.exec(out)
642
+ if (!declMatch) continue
643
+ wrapped.add(name)
635
644
  if (declMatch[2] === 'const') {
636
645
  out =
637
646
  out.slice(0, declMatch.index) +
638
647
  declMatch[0].replace(/\bconst\b/, 'let') +
639
- out.slice(declMatch.index + declMatch[0].length);
648
+ out.slice(declMatch.index + declMatch[0].length)
640
649
  }
641
650
  }
642
651
 
643
652
  for (const match of out.matchAll(/\bexport\s+default\s+([A-Za-z_$][\w$]*)\s*;?/g)) {
644
- const name = match[1]!;
645
- if (wrapped.has(name)) continue;
646
- const declRe = new RegExp(`(^|[\\n;])\\s*(const|let|var)\\s+${escapeIdent(name)}\\s*=`);
647
- const declMatch = declRe.exec(out);
648
- if (!declMatch) continue;
649
- wrapped.add(name);
653
+ const name = match[1]!
654
+ if (wrapped.has(name)) continue
655
+ const declRe = new RegExp(`(^|[\\n;])\\s*(const|let|var)\\s+${escapeIdent(name)}\\s*=`)
656
+ const declMatch = declRe.exec(out)
657
+ if (!declMatch) continue
658
+ wrapped.add(name)
650
659
  if (declMatch[2] === 'const') {
651
660
  out =
652
661
  out.slice(0, declMatch.index) +
653
662
  declMatch[0].replace(/\bconst\b/, 'let') +
654
- out.slice(declMatch.index + declMatch[0].length);
663
+ out.slice(declMatch.index + declMatch[0].length)
655
664
  }
656
665
  }
657
- return out;
666
+ return out
658
667
  }
659
668
 
660
- function rewriteObjectMethods(source: string, idBase: string): { source: string; wrapped: boolean } {
661
- let out = source;
662
- let wrapped = false;
663
- let searchFrom = 0;
669
+ function rewriteObjectMethods(
670
+ source: string,
671
+ idBase: string,
672
+ ): { source: string; wrapped: boolean } {
673
+ let out = source
674
+ let wrapped = false
675
+ let searchFrom = 0
664
676
  for (;;) {
665
- const tail = out.slice(searchFrom);
666
- const directive = directiveText.exec(tail);
667
- if (!directive) break;
668
- const index = searchFrom + directive.index;
677
+ const tail = out.slice(searchFrom)
678
+ const directive = directiveText.exec(tail)
679
+ if (!directive) break
680
+ const index = searchFrom + directive.index
669
681
  if (!/\{\s*$/.test(out.slice(0, index))) {
670
- searchFrom = index + directive[0].length;
671
- continue;
682
+ searchFrom = index + directive[0].length
683
+ continue
672
684
  }
673
- const method = findObjectMethod(out, index);
685
+ const method = findObjectMethod(out, index)
674
686
  if (!method) {
675
- searchFrom = index + directive[0].length;
676
- continue;
687
+ searchFrom = index + directive[0].length
688
+ continue
677
689
  }
678
- const id = `${idBase}#${method.enclosingName}.${method.name}`;
679
- const captureList = method.captures.join(', ');
680
- const wrappedHead = `${method.name}: ${boundHelper}(${JSON.stringify(id)}, ${method.asyncKeyword}function ${method.name}(${method.params}) {`;
690
+ const id = `${idBase}#${method.enclosingName}.${method.name}`
691
+ const captureList = method.captures.join(', ')
692
+ const wrappedHead = `${method.name}: ${boundHelper}(${JSON.stringify(id)}, ${method.asyncKeyword}function ${method.name}(${method.params}) {`
681
693
  out =
682
694
  out.slice(0, method.declStart) +
683
695
  wrappedHead +
684
696
  out.slice(index + directive[0].length, method.bodyEnd) +
685
697
  `}, () => [${captureList}])` +
686
- out.slice(method.bodyEnd + 1);
687
- wrapped = true;
688
- searchFrom = method.declStart + wrappedHead.length;
698
+ out.slice(method.bodyEnd + 1)
699
+ wrapped = true
700
+ searchFrom = method.declStart + wrappedHead.length
689
701
  }
690
- return { source: out, wrapped };
702
+ return { source: out, wrapped }
691
703
  }
692
704
 
693
705
  function findObjectMethod(source: string, directiveIndex: number): NestedClosure | null {
694
- const prefix = source.slice(0, directiveIndex);
695
- const methodRe = /(?:^|[,{]\s*)(async\s+)?([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g;
696
- let match: RegExpExecArray | null;
697
- let best: { asyncKeyword: string; name: string; params: string; headStart: number; bodyOpen: number } | null =
698
- null;
706
+ const prefix = source.slice(0, directiveIndex)
707
+ const methodRe = /(?:^|[,{]\s*)(async\s+)?([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
708
+ let match: RegExpExecArray | null
709
+ let best: {
710
+ asyncKeyword: string
711
+ name: string
712
+ params: string
713
+ headStart: number
714
+ bodyOpen: number
715
+ } | null = null
699
716
  while ((match = methodRe.exec(prefix)) !== null) {
700
- let beforeMatch = match.index - 1;
701
- while (beforeMatch >= 0 && /\s/.test(source[beforeMatch]!)) beforeMatch -= 1;
702
- if (source[beforeMatch] === '=') continue;
703
- const kwOffset = match[0].search(/async|[A-Za-z_$]/);
704
- const cleanStart = match.index + kwOffset;
705
- const bodyOpen = match.index + match[0].length;
706
- const between = source.slice(bodyOpen, directiveIndex);
707
- let balance = 0;
717
+ let beforeMatch = match.index - 1
718
+ while (beforeMatch >= 0 && /\s/.test(source[beforeMatch]!)) beforeMatch -= 1
719
+ if (source[beforeMatch] === '=') continue
720
+ const kwOffset = match[0].search(/async|[A-Za-z_$]/)
721
+ const cleanStart = match.index + kwOffset
722
+ const bodyOpen = match.index + match[0].length
723
+ const between = source.slice(bodyOpen, directiveIndex)
724
+ let balance = 0
708
725
  for (const ch of between) {
709
- if (ch === '{') balance += 1;
710
- else if (ch === '}') balance -= 1;
726
+ if (ch === '{') balance += 1
727
+ else if (ch === '}') balance -= 1
711
728
  }
712
729
  if (balance === 0) {
713
730
  best = {
@@ -716,19 +733,19 @@ function findObjectMethod(source: string, directiveIndex: number): NestedClosure
716
733
  params: match[3]!.trim(),
717
734
  headStart: cleanStart,
718
735
  bodyOpen,
719
- };
736
+ }
720
737
  }
721
738
  }
722
- if (!best) return null;
723
- const enclosing = findEnclosingNamedFunction(source, best.headStart);
724
- if (!enclosing) return null;
725
- const bodyEnd = matchClosingBrace(source, best.bodyOpen);
726
- if (bodyEnd < 0) return null;
727
- const body = source.slice(best.bodyOpen, bodyEnd);
728
- const methodParams = new Set(paramNames(best.params));
739
+ if (!best) return null
740
+ const enclosing = findEnclosingNamedFunction(source, best.headStart)
741
+ if (!enclosing) return null
742
+ const bodyEnd = matchClosingBrace(source, best.bodyOpen)
743
+ if (bodyEnd < 0) return null
744
+ const body = source.slice(best.bodyOpen, bodyEnd)
745
+ const methodParams = new Set(paramNames(best.params))
729
746
  const captures = enclosing.params.filter(
730
747
  param => !methodParams.has(param) && new RegExp(`\\b${escapeIdent(param)}\\b`).test(body),
731
- );
748
+ )
732
749
  return {
733
750
  name: best.name,
734
751
  params: best.params,
@@ -737,59 +754,67 @@ function findObjectMethod(source: string, directiveIndex: number): NestedClosure
737
754
  bodyEnd,
738
755
  enclosingName: enclosing.name,
739
756
  captures,
740
- };
757
+ }
741
758
  }
742
759
 
743
- function rewriteStaticClassMethods(source: string, idBase: string): { source: string; wrapped: boolean } {
744
- let out = source;
745
- let wrapped = false;
746
- let searchFrom = 0;
760
+ function rewriteStaticClassMethods(
761
+ source: string,
762
+ idBase: string,
763
+ ): { source: string; wrapped: boolean } {
764
+ let out = source
765
+ let wrapped = false
766
+ let searchFrom = 0
747
767
  for (;;) {
748
- const tail = out.slice(searchFrom);
749
- const directive = directiveText.exec(tail);
750
- if (!directive) break;
751
- const index = searchFrom + directive.index;
768
+ const tail = out.slice(searchFrom)
769
+ const directive = directiveText.exec(tail)
770
+ if (!directive) break
771
+ const index = searchFrom + directive.index
752
772
  if (!/\{\s*$/.test(out.slice(0, index))) {
753
- searchFrom = index + directive[0].length;
754
- continue;
773
+ searchFrom = index + directive[0].length
774
+ continue
755
775
  }
756
- const method = findStaticClassMethod(out, index);
776
+ const method = findStaticClassMethod(out, index)
757
777
  if (!method) {
758
- searchFrom = index + directive[0].length;
759
- continue;
778
+ searchFrom = index + directive[0].length
779
+ continue
760
780
  }
761
- const id = `${idBase}#${method.className}.${method.name}`;
762
- const wrappedHead = `static ${method.name} = __pnextUseCache(${JSON.stringify(id)}, ${method.asyncKeyword}function ${method.name}(${method.params}) {`;
781
+ const id = `${idBase}#${method.className}.${method.name}`
782
+ const wrappedHead = `static ${method.name} = __pnextUseCache(${JSON.stringify(id)}, ${method.asyncKeyword}function ${method.name}(${method.params}) {`
763
783
  out =
764
784
  out.slice(0, method.declStart) +
765
785
  wrappedHead +
766
786
  out.slice(index + directive[0].length, method.bodyEnd) +
767
787
  `})` +
768
- out.slice(method.bodyEnd + 1);
769
- wrapped = true;
770
- searchFrom = method.declStart + wrappedHead.length;
788
+ out.slice(method.bodyEnd + 1)
789
+ wrapped = true
790
+ searchFrom = method.declStart + wrappedHead.length
771
791
  }
772
- return { source: out, wrapped };
792
+ return { source: out, wrapped }
773
793
  }
774
794
 
775
795
  function findStaticClassMethod(
776
796
  source: string,
777
797
  directiveIndex: number,
778
798
  ): (NestedClosure & { className: string }) | null {
779
- const prefix = source.slice(0, directiveIndex);
780
- const methodRe = /(?:^|[\s;{}])static\s+(async\s+)?([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g;
781
- let match: RegExpExecArray | null;
782
- let best: { asyncKeyword: string; name: string; params: string; headStart: number; bodyOpen: number } | null =
783
- null;
799
+ const prefix = source.slice(0, directiveIndex)
800
+ const methodRe = /(?:^|[\s;{}])static\s+(async\s+)?([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
801
+ let match: RegExpExecArray | null
802
+ let best: {
803
+ asyncKeyword: string
804
+ name: string
805
+ params: string
806
+ headStart: number
807
+ bodyOpen: number
808
+ } | null = null
784
809
  while ((match = methodRe.exec(prefix)) !== null) {
785
- const kwOffset = match[0].indexOf('static');
786
- const cleanStart = match.index + kwOffset;
787
- const bodyOpen = match.index + match[0].length;
788
- const between = source.slice(bodyOpen, directiveIndex);
789
- let balance = 0;
810
+ const kwOffset = match[0].indexOf('static')
811
+ const cleanStart = match.index + kwOffset
812
+ const bodyOpen = match.index + match[0].length
813
+ const between = source.slice(bodyOpen, directiveIndex)
814
+ let balance = 0
790
815
  for (const ch of between) {
791
- if (ch === '{') balance += 1;
792
- else if (ch === '}') balance -= 1;
816
+ if (ch === '{') balance += 1
817
+ else if (ch === '}') balance -= 1
793
818
  }
794
819
  if (balance === 0) {
795
820
  best = {
@@ -798,14 +823,14 @@ function findStaticClassMethod(
798
823
  params: match[3]!.trim(),
799
824
  headStart: cleanStart,
800
825
  bodyOpen,
801
- };
826
+ }
802
827
  }
803
828
  }
804
- if (!best) return null;
805
- const className = findEnclosingClassName(source, best.headStart);
806
- if (!className) return null;
807
- const bodyEnd = matchClosingBrace(source, best.bodyOpen);
808
- if (bodyEnd < 0) return null;
829
+ if (!best) return null
830
+ const className = findEnclosingClassName(source, best.headStart)
831
+ if (!className) return null
832
+ const bodyEnd = matchClosingBrace(source, best.bodyOpen)
833
+ if (bodyEnd < 0) return null
809
834
  return {
810
835
  name: best.name,
811
836
  params: best.params,
@@ -815,20 +840,20 @@ function findStaticClassMethod(
815
840
  enclosingName: className,
816
841
  className,
817
842
  captures: [],
818
- };
843
+ }
819
844
  }
820
845
 
821
846
  function findEnclosingClassName(source: string, pos: number): string | null {
822
- const before = source.slice(0, pos);
823
- const re = /(?:^|[\s;{}])class\s+([A-Za-z_$][\w$]*)[^{]*\{/g;
824
- let match: RegExpExecArray | null;
825
- let candidate: string | null = null;
847
+ const before = source.slice(0, pos)
848
+ const re = /(?:^|[\s;{}])class\s+([A-Za-z_$][\w$]*)[^{]*\{/g
849
+ let match: RegExpExecArray | null
850
+ let candidate: string | null = null
826
851
  while ((match = re.exec(before)) !== null) {
827
- const bodyOpen = match.index + match[0].length;
828
- const close = matchClosingBrace(source, bodyOpen);
829
- if (close > pos) candidate = match[1]!;
852
+ const bodyOpen = match.index + match[0].length
853
+ const close = matchClosingBrace(source, bodyOpen)
854
+ if (close > pos) candidate = match[1]!
830
855
  }
831
- return candidate;
856
+ return candidate
832
857
  }
833
858
 
834
859
  /**
@@ -843,36 +868,36 @@ function rewriteFunctionLevelDirectives(
843
868
  wrapped: Set<string>,
844
869
  privateWrapped: Set<string>,
845
870
  ): string {
846
- let out = source;
847
- let searchFrom = 0;
871
+ let out = source
872
+ let searchFrom = 0
848
873
  for (;;) {
849
- const tail = out.slice(searchFrom);
850
- const directive = directiveText.exec(tail);
851
- if (!directive) break;
852
- const index = searchFrom + directive.index;
853
- const prefix = out.slice(0, index);
874
+ const tail = out.slice(searchFrom)
875
+ const directive = directiveText.exec(tail)
876
+ if (!directive) break
877
+ const index = searchFrom + directive.index
878
+ const prefix = out.slice(0, index)
854
879
 
855
880
  // Only a directive when it opens a function body; anything else is a plain
856
881
  // string literal.
857
- const declaration = opensFunctionBody(prefix) ? findEnclosingDeclaration(prefix) : null;
882
+ const declaration = opensFunctionBody(prefix) ? findEnclosingDeclaration(prefix) : null
858
883
  if (!declaration) {
859
- searchFrom = index + directive[0].length;
860
- continue;
884
+ searchFrom = index + directive[0].length
885
+ continue
861
886
  }
862
887
 
863
- let head = prefix;
888
+ let head = prefix
864
889
  if (declaration.constKeywordIndex !== undefined) {
865
890
  head =
866
891
  prefix.slice(0, declaration.constKeywordIndex) +
867
892
  'let ' +
868
- prefix.slice(declaration.constKeywordIndex + 'const'.length);
893
+ prefix.slice(declaration.constKeywordIndex + 'const'.length)
869
894
  }
870
- wrapped.add(declaration.name);
871
- if (directive[2] === 'private') privateWrapped.add(declaration.name);
872
- out = head + out.slice(index + directive[0].length);
873
- searchFrom = index;
895
+ wrapped.add(declaration.name)
896
+ if (directive[2] === 'private') privateWrapped.add(declaration.name)
897
+ out = head + out.slice(index + directive[0].length)
898
+ searchFrom = index
874
899
  }
875
- return out;
900
+ return out
876
901
  }
877
902
 
878
903
  /**
@@ -884,78 +909,78 @@ function rewriteFunctionLevelDirectives(
884
909
  function opensFunctionBody(prefix: string): boolean {
885
910
  // The match is a comment's own text, not a directive, when `prefix` ends
886
911
  // inside a comment — e.g. a line explaining `'use cache'` semantics.
887
- if (prefix.includes('//', prefix.lastIndexOf('\n') + 1)) return false;
888
- const blockOpen = prefix.lastIndexOf('/*');
889
- if (blockOpen !== -1 && !prefix.includes('*/', blockOpen)) return false;
912
+ if (prefix.includes('//', prefix.lastIndexOf('\n') + 1)) return false
913
+ const blockOpen = prefix.lastIndexOf('/*')
914
+ if (blockOpen !== -1 && !prefix.includes('*/', blockOpen)) return false
890
915
 
891
- let end = prefix.length;
916
+ let end = prefix.length
892
917
  for (;;) {
893
- end = prefix.slice(0, end).replace(/\s+$/, '').length;
894
- if (end === 0) return false;
918
+ end = prefix.slice(0, end).replace(/\s+$/, '').length
919
+ if (end === 0) return false
895
920
  if (prefix.startsWith('*/', end - 2)) {
896
- const open = prefix.lastIndexOf('/*', end - 2);
897
- if (open === -1) return false;
898
- end = open;
899
- continue;
921
+ const open = prefix.lastIndexOf('/*', end - 2)
922
+ if (open === -1) return false
923
+ end = open
924
+ continue
900
925
  }
901
- const comment = prefix.indexOf('//', prefix.lastIndexOf('\n', end - 1) + 1);
926
+ const comment = prefix.indexOf('//', prefix.lastIndexOf('\n', end - 1) + 1)
902
927
  if (comment !== -1 && comment < end) {
903
- end = comment;
904
- continue;
928
+ end = comment
929
+ continue
905
930
  }
906
- return prefix[end - 1] === '{';
931
+ return prefix[end - 1] === '{'
907
932
  }
908
933
  }
909
934
 
910
935
  interface EnclosingDeclaration {
911
- name: string;
936
+ name: string
912
937
  /** Index of a `const` keyword to rewrite to `let`, when applicable. */
913
- constKeywordIndex?: number;
938
+ constKeywordIndex?: number
914
939
  }
915
940
 
916
941
  function findEnclosingDeclaration(prefix: string): EnclosingDeclaration | null {
917
- const candidates: { name: string; end: number; constKeywordIndex?: number }[] = [];
942
+ const candidates: { name: string; end: number; constKeywordIndex?: number }[] = []
918
943
 
919
944
  // Only top-level declarations (line starts at column 0): a nested closure's
920
945
  // binding is function-scoped and cannot be reassigned from module level —
921
946
  // those run uncached (the directive string is inert).
922
- const topLevel = (index: number) => index === 0 || prefix[index - 1] === '\n';
947
+ const topLevel = (index: number) => index === 0 || prefix[index - 1] === '\n'
923
948
 
924
- const fnPattern = /(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s*\*?\s+([A-Za-z_$][\w$]*)/g;
949
+ const fnPattern = /(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s*\*?\s+([A-Za-z_$][\w$]*)/g
925
950
  for (const match of prefix.matchAll(fnPattern)) {
926
951
  if (match[1] && topLevel(match.index)) {
927
- candidates.push({ name: match[1], end: match.index + match[0].length });
952
+ candidates.push({ name: match[1], end: match.index + match[0].length })
928
953
  }
929
954
  }
930
- const bindingPattern = /(?:export\s+)?(const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/g;
955
+ const bindingPattern = /(?:export\s+)?(const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/g
931
956
  for (const match of prefix.matchAll(bindingPattern)) {
932
- if (!match[2] || !topLevel(match.index)) continue;
957
+ if (!match[2] || !topLevel(match.index)) continue
933
958
  candidates.push({
934
959
  name: match[2],
935
960
  end: match.index + match[0].length,
936
961
  ...(match[1] === 'const'
937
962
  ? { constKeywordIndex: match.index + match[0].indexOf('const') }
938
963
  : {}),
939
- });
964
+ })
940
965
  }
941
- if (candidates.length === 0) return null;
942
- candidates.sort((a, b) => a.end - b.end);
943
- const nearest = candidates[candidates.length - 1]!;
966
+ if (candidates.length === 0) return null
967
+ candidates.sort((a, b) => a.end - b.end)
968
+ const nearest = candidates[candidates.length - 1]!
944
969
 
945
970
  // The span between the declaration head and the directive must look like a
946
971
  // signature followed by the opening body brace: exactly one unclosed '{'.
947
- const span = prefix.slice(nearest.end);
948
- let balance = 0;
972
+ const span = prefix.slice(nearest.end)
973
+ let balance = 0
949
974
  for (const char of span) {
950
- if (char === '{') balance += 1;
951
- else if (char === '}') balance -= 1;
975
+ if (char === '{') balance += 1
976
+ else if (char === '}') balance -= 1
952
977
  }
953
- if (balance !== 1) return null;
978
+ if (balance !== 1) return null
954
979
 
955
980
  return {
956
981
  name: nearest.name,
957
982
  ...(nearest.constKeywordIndex !== undefined
958
983
  ? { constKeywordIndex: nearest.constKeywordIndex }
959
984
  : {}),
960
- };
985
+ }
961
986
  }