@lovrozagar/flare 0.1.0

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 (387) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/package.json +146 -0
  4. package/scripts/download-fonts.ts +136 -0
  5. package/scripts/populate-fonts.ts +721 -0
  6. package/src/await.ts +2 -0
  7. package/src/boundaries/index.ts +99 -0
  8. package/src/broadcast/channel.ts +65 -0
  9. package/src/broadcast/context.ts +12 -0
  10. package/src/broadcast/index.ts +75 -0
  11. package/src/broadcast/provider.tsx +7 -0
  12. package/src/build-location.ts +2 -0
  13. package/src/caches/index.ts +384 -0
  14. package/src/client/index.ts +75 -0
  15. package/src/client.tsx +2 -0
  16. package/src/codegen/index.ts +41 -0
  17. package/src/components/await.tsx +140 -0
  18. package/src/components/dev-error-overlay.tsx +154 -0
  19. package/src/components/direction-script.tsx +22 -0
  20. package/src/components/index.ts +150 -0
  21. package/src/components/locale-script.tsx +26 -0
  22. package/src/components/reset-css.tsx +14 -0
  23. package/src/components/ssr-context.tsx +37 -0
  24. package/src/components/theme-script.tsx +22 -0
  25. package/src/components/view-transition-css.tsx +21 -0
  26. package/src/config/index.ts +84 -0
  27. package/src/create-client-only-fn.ts +1 -0
  28. package/src/create-isomorphic-fn.ts +2 -0
  29. package/src/create-layout.ts +1 -0
  30. package/src/create-page.ts +1 -0
  31. package/src/create-path-segment.ts +1 -0
  32. package/src/create-query-client-getter.ts +2 -0
  33. package/src/create-root-layout.ts +1 -0
  34. package/src/create-router.ts +8 -0
  35. package/src/create-server-only-fn.ts +1 -0
  36. package/src/dedupe/index.ts +151 -0
  37. package/src/defer/index.ts +82 -0
  38. package/src/direction/index.tsx +163 -0
  39. package/src/direction.ts +3 -0
  40. package/src/duration/index.ts +18 -0
  41. package/src/env-fn/index.ts +101 -0
  42. package/src/env-fn.ts +2 -0
  43. package/src/errors/index.ts +166 -0
  44. package/src/fonts/abeezee.ts +50 -0
  45. package/src/fonts/abel.ts +26 -0
  46. package/src/fonts/abril-fatface.ts +34 -0
  47. package/src/fonts/acme.ts +26 -0
  48. package/src/fonts/advent-pro.ts +92 -0
  49. package/src/fonts/alegreya-sans.ts +228 -0
  50. package/src/fonts/alegreya.ts +228 -0
  51. package/src/fonts/alex-brush.ts +42 -0
  52. package/src/fonts/alfa-slab-one.ts +42 -0
  53. package/src/fonts/alice.ts +48 -0
  54. package/src/fonts/amatic-sc.ts +94 -0
  55. package/src/fonts/amiri.ts +114 -0
  56. package/src/fonts/antic-slab.ts +26 -0
  57. package/src/fonts/anton.ts +42 -0
  58. package/src/fonts/archivo-black.ts +34 -0
  59. package/src/fonts/archivo-narrow.ts +114 -0
  60. package/src/fonts/archivo.ts +66 -0
  61. package/src/fonts/arimo.ts +256 -0
  62. package/src/fonts/arvo.ts +50 -0
  63. package/src/fonts/asap.ts +66 -0
  64. package/src/fonts/assistant.ts +64 -0
  65. package/src/fonts/baloo-2.ts +82 -0
  66. package/src/fonts/barlow-condensed.ts +114 -0
  67. package/src/fonts/barlow-semi-condensed.ts +114 -0
  68. package/src/fonts/barlow.ts +114 -0
  69. package/src/fonts/be-vietnam-pro.ts +114 -0
  70. package/src/fonts/bebas-neue.ts +34 -0
  71. package/src/fonts/besley.ts +82 -0
  72. package/src/fonts/biorhyme.ts +50 -0
  73. package/src/fonts/bitter.ts +94 -0
  74. package/src/fonts/black-ops-one.ts +49 -0
  75. package/src/fonts/bree-serif.ts +34 -0
  76. package/src/fonts/bricolage-grotesque.ts +66 -0
  77. package/src/fonts/cabin.ts +114 -0
  78. package/src/fonts/cairo.ts +66 -0
  79. package/src/fonts/cardo.ts +193 -0
  80. package/src/fonts/catamaran.ts +41 -0
  81. package/src/fonts/caveat.ts +78 -0
  82. package/src/fonts/chakra-petch.ts +142 -0
  83. package/src/fonts/chivo.ts +66 -0
  84. package/src/fonts/cinzel.ts +50 -0
  85. package/src/fonts/comfortaa.ts +109 -0
  86. package/src/fonts/commissioner.ts +64 -0
  87. package/src/fonts/component.tsx +27 -0
  88. package/src/fonts/cormorant-garamond.ts +171 -0
  89. package/src/fonts/cormorant.ts +170 -0
  90. package/src/fonts/courgette.ts +34 -0
  91. package/src/fonts/courier-prime.ts +82 -0
  92. package/src/fonts/create-font.ts +47 -0
  93. package/src/fonts/create-registry-font.ts +40 -0
  94. package/src/fonts/crete-round.ts +50 -0
  95. package/src/fonts/crimson-text.ts +114 -0
  96. package/src/fonts/css.ts +86 -0
  97. package/src/fonts/dancing-script.ts +66 -0
  98. package/src/fonts/dm-mono.ts +50 -0
  99. package/src/fonts/dm-sans.ts +50 -0
  100. package/src/fonts/dm-serif-display.ts +50 -0
  101. package/src/fonts/domine.ts +50 -0
  102. package/src/fonts/dosis.ts +66 -0
  103. package/src/fonts/eb-garamond.ts +228 -0
  104. package/src/fonts/eczar.ts +94 -0
  105. package/src/fonts/encode-sans.ts +42 -0
  106. package/src/fonts/epilogue.ts +66 -0
  107. package/src/fonts/exo-2.ts +94 -0
  108. package/src/fonts/exo.ts +66 -0
  109. package/src/fonts/figtree.ts +82 -0
  110. package/src/fonts/fira-code.ts +121 -0
  111. package/src/fonts/fira-sans-condensed.ts +228 -0
  112. package/src/fonts/fira-sans.ts +228 -0
  113. package/src/fonts/fjalla-one.ts +49 -0
  114. package/src/fonts/font-urls.gen.json +1962 -0
  115. package/src/fonts/fraunces.ts +66 -0
  116. package/src/fonts/fredoka.ts +64 -0
  117. package/src/fonts/gelasio.ts +114 -0
  118. package/src/fonts/gloria-hallelujah.ts +34 -0
  119. package/src/fonts/gothic-a1.ts +124 -0
  120. package/src/fonts/great-vibes.ts +64 -0
  121. package/src/fonts/gudea.ts +66 -0
  122. package/src/fonts/heebo.ts +57 -0
  123. package/src/fonts/hind-siliguri.ts +66 -0
  124. package/src/fonts/hind.ts +66 -0
  125. package/src/fonts/ibm-plex-mono.ts +172 -0
  126. package/src/fonts/ibm-plex-sans-arabic.ts +80 -0
  127. package/src/fonts/ibm-plex-sans.ts +199 -0
  128. package/src/fonts/ibm-plex-serif.ts +171 -0
  129. package/src/fonts/inconsolata.ts +66 -0
  130. package/src/fonts/index.ts +4 -0
  131. package/src/fonts/indie-flower.ts +34 -0
  132. package/src/fonts/instrument-sans.ts +82 -0
  133. package/src/fonts/instrument-serif.ts +50 -0
  134. package/src/fonts/inter-display.ts +52 -0
  135. package/src/fonts/inter-tight.ts +124 -0
  136. package/src/fonts/inter.ts +123 -0
  137. package/src/fonts/jetbrains-mono.ts +199 -0
  138. package/src/fonts/josefin-sans.ts +114 -0
  139. package/src/fonts/josefin-slab.ts +50 -0
  140. package/src/fonts/jost.ts +64 -0
  141. package/src/fonts/kalam.ts +66 -0
  142. package/src/fonts/kanit.ts +142 -0
  143. package/src/fonts/karla.ts +82 -0
  144. package/src/fonts/khand.ts +66 -0
  145. package/src/fonts/lato.ts +82 -0
  146. package/src/fonts/league-spartan.ts +42 -0
  147. package/src/fonts/lexend-deca.ts +42 -0
  148. package/src/fonts/lexend.ts +42 -0
  149. package/src/fonts/libre-baskerville.ts +82 -0
  150. package/src/fonts/libre-franklin.ts +95 -0
  151. package/src/fonts/lilita-one.ts +34 -0
  152. package/src/fonts/lobster-two.ts +50 -0
  153. package/src/fonts/lobster.ts +56 -0
  154. package/src/fonts/lora.ts +235 -0
  155. package/src/fonts/m-plus-rounded-1c.ts +138 -0
  156. package/src/fonts/manrope.ts +109 -0
  157. package/src/fonts/maven-pro.ts +66 -0
  158. package/src/fonts/merriweather-sans.ts +142 -0
  159. package/src/fonts/merriweather.ts +171 -0
  160. package/src/fonts/montserrat-alternates.ts +171 -0
  161. package/src/fonts/montserrat.ts +94 -0
  162. package/src/fonts/mukta.ts +66 -0
  163. package/src/fonts/mulish.ts +170 -0
  164. package/src/fonts/nanum-gothic.ts +34 -0
  165. package/src/fonts/nanum-myeongjo.ts +34 -0
  166. package/src/fonts/neuton.ts +66 -0
  167. package/src/fonts/newsreader.ts +114 -0
  168. package/src/fonts/noticia-text.ts +114 -0
  169. package/src/fonts/noto-sans-arabic.ts +58 -0
  170. package/src/fonts/noto-sans-hk.ts +49 -0
  171. package/src/fonts/noto-sans-jp.ts +49 -0
  172. package/src/fonts/noto-sans-kr.ts +49 -0
  173. package/src/fonts/noto-sans-sc.ts +49 -0
  174. package/src/fonts/noto-sans-tc.ts +49 -0
  175. package/src/fonts/noto-sans.ts +140 -0
  176. package/src/fonts/noto-serif-jp.ts +80 -0
  177. package/src/fonts/noto-serif.ts +140 -0
  178. package/src/fonts/nunito-sans.ts +170 -0
  179. package/src/fonts/nunito.ts +170 -0
  180. package/src/fonts/old-standard-tt.ts +133 -0
  181. package/src/fonts/onest.ts +48 -0
  182. package/src/fonts/open-sans.ts +340 -0
  183. package/src/fonts/orbitron.ts +34 -0
  184. package/src/fonts/oswald.ts +94 -0
  185. package/src/fonts/outfit.ts +34 -0
  186. package/src/fonts/overpass.ts +94 -0
  187. package/src/fonts/oxygen.ts +50 -0
  188. package/src/fonts/pacifico.ts +56 -0
  189. package/src/fonts/passion-one.ts +50 -0
  190. package/src/fonts/pathway-gothic-one.ts +34 -0
  191. package/src/fonts/permanent-marker.ts +26 -0
  192. package/src/fonts/philosopher.ts +172 -0
  193. package/src/fonts/play.ts +109 -0
  194. package/src/fonts/playfair-display.ts +142 -0
  195. package/src/fonts/plus-jakarta-sans.ts +142 -0
  196. package/src/fonts/poppins.ts +114 -0
  197. package/src/fonts/prompt.ts +142 -0
  198. package/src/fonts/pt-sans-narrow.ts +78 -0
  199. package/src/fonts/pt-sans.ts +138 -0
  200. package/src/fonts/pt-serif.ts +138 -0
  201. package/src/fonts/public-sans.ts +66 -0
  202. package/src/fonts/questrial.ts +42 -0
  203. package/src/fonts/quicksand.ts +66 -0
  204. package/src/fonts/rajdhani.ts +66 -0
  205. package/src/fonts/raleway.ts +94 -0
  206. package/src/fonts/recursive.ts +80 -0
  207. package/src/fonts/red-hat-display.ts +82 -0
  208. package/src/fonts/red-rose.ts +66 -0
  209. package/src/fonts/registry.gen.ts +173 -0
  210. package/src/fonts/righteous.ts +34 -0
  211. package/src/fonts/roboto-condensed.ts +124 -0
  212. package/src/fonts/roboto-flex.ts +64 -0
  213. package/src/fonts/roboto-mono.ts +199 -0
  214. package/src/fonts/roboto-slab.ts +72 -0
  215. package/src/fonts/roboto.ts +156 -0
  216. package/src/fonts/rokkitt.ts +66 -0
  217. package/src/fonts/rubik.ts +199 -0
  218. package/src/fonts/sacramento.ts +34 -0
  219. package/src/fonts/saira.ts +66 -0
  220. package/src/fonts/sarabun.ts +142 -0
  221. package/src/fonts/satisfy.ts +26 -0
  222. package/src/fonts/sawarabi-mincho.ts +41 -0
  223. package/src/fonts/shadows-into-light.ts +34 -0
  224. package/src/fonts/signika-negative.ts +66 -0
  225. package/src/fonts/signika.ts +66 -0
  226. package/src/fonts/slabo-27px.ts +34 -0
  227. package/src/fonts/sora.ts +50 -0
  228. package/src/fonts/source-code-pro.ts +228 -0
  229. package/src/fonts/source-sans-3.ts +228 -0
  230. package/src/fonts/source-serif-4.ts +199 -0
  231. package/src/fonts/space-grotesk.ts +66 -0
  232. package/src/fonts/space-mono.ts +114 -0
  233. package/src/fonts/spectral.ts +170 -0
  234. package/src/fonts/tajawal.ts +50 -0
  235. package/src/fonts/teko.ts +66 -0
  236. package/src/fonts/tenor-sans.ts +41 -0
  237. package/src/fonts/tinos.ts +256 -0
  238. package/src/fonts/titillium-web.ts +82 -0
  239. package/src/fonts/types.ts +43 -0
  240. package/src/fonts/ubuntu-condensed.ts +63 -0
  241. package/src/fonts/ubuntu-mono.ts +195 -0
  242. package/src/fonts/ubuntu.ts +195 -0
  243. package/src/fonts/unbounded.ts +94 -0
  244. package/src/fonts/unicode-ranges.gen.ts +28 -0
  245. package/src/fonts/urbanist.ts +50 -0
  246. package/src/fonts/varela-round.ts +49 -0
  247. package/src/fonts/vollkorn.ts +199 -0
  248. package/src/fonts/work-sans.ts +66 -0
  249. package/src/fonts/yanone-kaffeesatz.ts +128 -0
  250. package/src/fonts/yantramanav.ts +66 -0
  251. package/src/fonts/yellowtail.ts +34 -0
  252. package/src/fonts/young-serif.ts +34 -0
  253. package/src/fonts/zen-kaku-gothic-new.ts +64 -0
  254. package/src/fonts/zilla-slab.ts +82 -0
  255. package/src/form/index.tsx +205 -0
  256. package/src/generators/index.ts +1589 -0
  257. package/src/head-client/index.ts +649 -0
  258. package/src/history/index.ts +210 -0
  259. package/src/hooks.ts +61 -0
  260. package/src/hydrate/index.tsx +381 -0
  261. package/src/hydration/index.ts +187 -0
  262. package/src/i18n/index.ts +250 -0
  263. package/src/i18n/jsx.tsx +42 -0
  264. package/src/i18n/message-format.ts +174 -0
  265. package/src/image/index.tsx +473 -0
  266. package/src/image.ts +12 -0
  267. package/src/intercept-outlet/index.tsx +18 -0
  268. package/src/intercept-outlet.ts +2 -0
  269. package/src/internal/index.ts +143 -0
  270. package/src/internal/once-idle.ts +24 -0
  271. package/src/internal.ts +12 -0
  272. package/src/lazy/index.tsx +205 -0
  273. package/src/link/index.tsx +367 -0
  274. package/src/loader-pipeline/index.ts +639 -0
  275. package/src/locale/index.tsx +137 -0
  276. package/src/locale.ts +3 -0
  277. package/src/logger.ts +22 -0
  278. package/src/middleware/builtins/api-proxy.ts +64 -0
  279. package/src/middleware/builtins/cdn-proxy.ts +58 -0
  280. package/src/middleware/builtins/i18n.ts +256 -0
  281. package/src/middleware/builtins/keepalive.ts +24 -0
  282. package/src/middleware/builtins/markdown-negotiation.ts +141 -0
  283. package/src/middleware/builtins/static-assets.ts +32 -0
  284. package/src/middleware/index.ts +223 -0
  285. package/src/module-graph/index.ts +181 -0
  286. package/src/mount/index.ts +146 -0
  287. package/src/navigate.ts +1 -0
  288. package/src/navigation/index.ts +1278 -0
  289. package/src/navigation/types.ts +59 -0
  290. package/src/navigation-progress/index.tsx +247 -0
  291. package/src/navigation-progress.ts +1 -0
  292. package/src/ndjson-client/index.ts +376 -0
  293. package/src/ndjson-server/index.ts +284 -0
  294. package/src/outlet/index.tsx +633 -0
  295. package/src/outlet/types.ts +243 -0
  296. package/src/plugins/css-transform.ts +70 -0
  297. package/src/plugins/dev-cdn-cache.ts +454 -0
  298. package/src/plugins/dev-dashboard/client.tsx +2603 -0
  299. package/src/plugins/dev-dashboard/match-route-tree.ts +159 -0
  300. package/src/plugins/dev-dashboard/plugin.ts +404 -0
  301. package/src/plugins/dev-prerender.ts +168 -0
  302. package/src/plugins/dev-server.ts +327 -0
  303. package/src/plugins/generate-watch.ts +32 -0
  304. package/src/plugins/image-plugin.ts +171 -0
  305. package/src/plugins/index.ts +445 -0
  306. package/src/plugins/options.ts +37 -0
  307. package/src/plugins/parse-utils.ts +59 -0
  308. package/src/plugins/prerender-plugin.ts +144 -0
  309. package/src/plugins/purge.ts +242 -0
  310. package/src/plugins/server-fn.ts +641 -0
  311. package/src/plugins/service-worker.ts +225 -0
  312. package/src/plugins/solid-jsx.d.ts +12 -0
  313. package/src/plugins/sx-ast/atomic.ts +166 -0
  314. package/src/plugins/sx-ast/evaluator.ts +235 -0
  315. package/src/plugins/sx-ast/index.ts +372 -0
  316. package/src/plugins/sx-ast/parser.ts +22 -0
  317. package/src/plugins/sx-ast/rewrite.ts +763 -0
  318. package/src/plugins/sx.d.ts +14 -0
  319. package/src/plugins/tw-compile.ts +240 -0
  320. package/src/plugins/types.ts +41 -0
  321. package/src/plugins/virtual.d.ts +110 -0
  322. package/src/plugins/virtual.ts +184 -0
  323. package/src/preload/index.ts +77 -0
  324. package/src/prerender/index.ts +431 -0
  325. package/src/query-client/index.tsx +168 -0
  326. package/src/registry/index.ts +88 -0
  327. package/src/reset-css.ts +1 -0
  328. package/src/revalidation/index.ts +84 -0
  329. package/src/rewrite/index.ts +83 -0
  330. package/src/route-builder/create-layout.ts +655 -0
  331. package/src/route-builder/create-page.ts +806 -0
  332. package/src/route-builder/create-path-segment.ts +34 -0
  333. package/src/route-builder/create-root-layout.ts +608 -0
  334. package/src/route-builder/index.ts +49 -0
  335. package/src/route-builder/register.ts +242 -0
  336. package/src/route-builder/types.ts +294 -0
  337. package/src/router-config/index.ts +91 -0
  338. package/src/router-primitives/index.ts +25 -0
  339. package/src/router-primitives/location.ts +29 -0
  340. package/src/router-primitives/match-id.ts +111 -0
  341. package/src/router-primitives/paths.ts +109 -0
  342. package/src/router-primitives/tree.ts +243 -0
  343. package/src/router-primitives/types.ts +74 -0
  344. package/src/router.ts +31 -0
  345. package/src/search-engine/bing.ts +32 -0
  346. package/src/search-engine/google-jwt.ts +95 -0
  347. package/src/search-engine/google.ts +139 -0
  348. package/src/search-engine/index-now.ts +64 -0
  349. package/src/search-engine/index.ts +12 -0
  350. package/src/security/index.ts +230 -0
  351. package/src/server/index.ts +286 -0
  352. package/src/server-context/browser.ts +113 -0
  353. package/src/server-context/index.ts +217 -0
  354. package/src/server-fn/index.ts +710 -0
  355. package/src/server-fn-query.ts +169 -0
  356. package/src/server-handler/etag.ts +51 -0
  357. package/src/server-handler/headers.ts +5 -0
  358. package/src/server-handler/index.ts +1664 -0
  359. package/src/server-handler/types.ts +24 -0
  360. package/src/server-handler/validate-static-params.ts +130 -0
  361. package/src/server.ts +2 -0
  362. package/src/service-worker/template.ts +153 -0
  363. package/src/service-worker-hook/index.ts +70 -0
  364. package/src/sitemap/index.ts +231 -0
  365. package/src/ssr/critical-css.ts +150 -0
  366. package/src/ssr/head-prefix.ts +65 -0
  367. package/src/ssr/head.ts +259 -0
  368. package/src/ssr/index.tsx +879 -0
  369. package/src/state-parser/index.ts +266 -0
  370. package/src/store/filesystem.ts +131 -0
  371. package/src/store/index.ts +29 -0
  372. package/src/styles/index.ts +1200 -0
  373. package/src/styles/sx-types.ts +11 -0
  374. package/src/testing/index.ts +251 -0
  375. package/src/testing.ts +2 -0
  376. package/src/theme/index.tsx +232 -0
  377. package/src/theme.ts +3 -0
  378. package/src/tracing/index.ts +4 -0
  379. package/src/tracing/noop.ts +13 -0
  380. package/src/tracing/otel.ts +37 -0
  381. package/src/tracing/timing.ts +34 -0
  382. package/src/tracing/types.ts +9 -0
  383. package/src/url/index.ts +140 -0
  384. package/src/use-router.ts +11 -0
  385. package/src/use-suspense-query.ts +2 -0
  386. package/src/validation/index.ts +57 -0
  387. package/src/view-transition-css.ts +1 -0
@@ -0,0 +1,641 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { readdirSync, readFileSync, statSync, watch } from "node:fs";
3
+ import { join } from "node:path";
4
+ import MagicString from "magic-string";
5
+ import { parseSync } from "oxc-parser";
6
+ import { extractParenContent, findMatchingBraceSimple } from "./parse-utils.ts";
7
+ import type { VitePlugin } from "./types.ts";
8
+
9
+ const GEN_IGNORE_RE = /(_gen[/\\]|\.gen\.tsx?$)/;
10
+
11
+ /* ── Environment Function Transforms ─────────────────────────────────── */
12
+
13
+ const ENV_FN_RE = /create(ServerOnly|ClientOnly|Isomorphic)Fn/;
14
+
15
+ const SERVER_ONLY_STUB = '((..._) => { throw new Error("Server-only function called on client") })';
16
+ const CLIENT_ONLY_STUB = '((..._) => { throw new Error("Client-only function called on server") })';
17
+ const NOOP_STUB = "(() => undefined)";
18
+
19
+ /**
20
+ * Transform createServerOnlyFn / createClientOnlyFn / createIsomorphicFn.
21
+ * - Env-only fns: keep implementation on correct env, replace with throwing stub on other.
22
+ * - Isomorphic fns: extract the env-specific `.server()` or `.client()` branch.
23
+ */
24
+ export function transformEnvFns(code: string, isSSR: boolean): string {
25
+ let result = "";
26
+ let cursor = 0;
27
+
28
+ while (cursor < code.length) {
29
+ /* find next env-fn call */
30
+ const serverOnlyIdx = code.indexOf("createServerOnlyFn(", cursor);
31
+ const clientOnlyIdx = code.indexOf("createClientOnlyFn(", cursor);
32
+ const isomorphicIdx = code.indexOf("createIsomorphicFn(", cursor);
33
+
34
+ /* pick earliest match */
35
+ const candidates = [
36
+ { idx: serverOnlyIdx, type: "server-only" as const },
37
+ { idx: clientOnlyIdx, type: "client-only" as const },
38
+ { idx: isomorphicIdx, type: "isomorphic" as const },
39
+ ].filter((c) => c.idx !== -1);
40
+
41
+ if (candidates.length === 0) {
42
+ result += code.slice(cursor);
43
+ break;
44
+ }
45
+
46
+ candidates.sort((a, b) => a.idx - b.idx);
47
+ const match = candidates[0];
48
+
49
+ /* ── createServerOnlyFn(fn) / createClientOnlyFn(fn) ── */
50
+ if (match.type === "server-only" || match.type === "client-only") {
51
+ const token = match.type === "server-only" ? "createServerOnlyFn(" : "createClientOnlyFn(";
52
+ const parenStart = match.idx + token.length - 1;
53
+ const extracted = extractParenContent(code, parenStart);
54
+ if (!extracted) {
55
+ result += code.slice(cursor, match.idx + token.length);
56
+ cursor = match.idx + token.length;
57
+ continue;
58
+ }
59
+
60
+ const [innerFn, endIdx] = extracted;
61
+ result += code.slice(cursor, match.idx);
62
+
63
+ const keepOriginal = (match.type === "server-only" && isSSR) || (match.type === "client-only" && !isSSR);
64
+
65
+ if (keepOriginal) {
66
+ result += `(${innerFn})`;
67
+ } else {
68
+ result += match.type === "server-only" ? SERVER_ONLY_STUB : CLIENT_ONLY_STUB;
69
+ }
70
+
71
+ cursor = endIdx + 1;
72
+ continue;
73
+ }
74
+
75
+ /* ── createIsomorphicFn().server(fn).client(fn) ── */
76
+ const isoToken = "createIsomorphicFn(";
77
+ const parenStart = match.idx + isoToken.length - 1;
78
+ const initExtracted = extractParenContent(code, parenStart);
79
+ if (!initExtracted) {
80
+ result += code.slice(cursor, match.idx + isoToken.length);
81
+ cursor = match.idx + isoToken.length;
82
+ continue;
83
+ }
84
+
85
+ /* after the initial `()`, scan for .server() and .client() calls */
86
+ let scanPos = initExtracted[1] + 1;
87
+ let serverFnBody: string | null = null;
88
+ let clientFnBody: string | null = null;
89
+ let chainEnd = scanPos;
90
+
91
+ for (let pass = 0; pass < 2; pass++) {
92
+ /* skip whitespace */
93
+ while (scanPos < code.length && /\s/.test(code[scanPos])) scanPos++;
94
+
95
+ const serverMatch = code.startsWith(".server(", scanPos);
96
+ const clientMatch = code.startsWith(".client(", scanPos);
97
+
98
+ if (serverMatch) {
99
+ const p = extractParenContent(code, scanPos + ".server".length);
100
+ if (p) {
101
+ serverFnBody = p[0];
102
+ scanPos = p[1] + 1;
103
+ chainEnd = scanPos;
104
+ } else break;
105
+ } else if (clientMatch) {
106
+ const p = extractParenContent(code, scanPos + ".client".length);
107
+ if (p) {
108
+ clientFnBody = p[0];
109
+ scanPos = p[1] + 1;
110
+ chainEnd = scanPos;
111
+ } else break;
112
+ } else break;
113
+ }
114
+
115
+ result += code.slice(cursor, match.idx);
116
+
117
+ if (isSSR) {
118
+ result += serverFnBody ? `(${serverFnBody})` : NOOP_STUB;
119
+ } else {
120
+ result += clientFnBody ? `(${clientFnBody})` : NOOP_STUB;
121
+ }
122
+
123
+ cursor = chainEnd;
124
+ }
125
+
126
+ return result;
127
+ }
128
+
129
+ /* ── Server Function Plugin ──────────────────────────────────────────── */
130
+
131
+ const SERVER_FN_START = "createServerFn({";
132
+
133
+ /** Replace all `createServerFn({...})` with `__id`-injected versions using proper brace matching */
134
+ export function replaceServerFnConfigs(code: string, replacer: (configContent: string) => string): string {
135
+ let result = "";
136
+ let cursor = 0;
137
+
138
+ while (cursor < code.length) {
139
+ const idx = code.indexOf(SERVER_FN_START, cursor);
140
+ if (idx === -1) {
141
+ result += code.slice(cursor);
142
+ break;
143
+ }
144
+
145
+ result += code.slice(cursor, idx);
146
+ const braceStart = idx + SERVER_FN_START.length - 1;
147
+ const braceEnd = findMatchingBraceSimple(code, braceStart);
148
+ const configContent = code.slice(braceStart + 1, braceEnd);
149
+
150
+ /* Verify closing `)` after the `}` */
151
+ if (braceEnd + 1 < code.length && code[braceEnd + 1] === ")") {
152
+ result += `createServerFn({${replacer(configContent)}})`;
153
+ cursor = braceEnd + 2;
154
+ } else {
155
+ /* Not a valid createServerFn call — skip */
156
+ result += SERVER_FN_START;
157
+ cursor = idx + SERVER_FN_START.length;
158
+ }
159
+ }
160
+
161
+ return result;
162
+ }
163
+
164
+ function hasServerFnCalls(code: string): boolean {
165
+ return code.includes(SERVER_FN_START);
166
+ }
167
+ const HANDLER_RE = /\.(handler|stream)\s*\(/;
168
+ const buildSecret = randomBytes(32).toString("hex");
169
+
170
+ function computeFnId(fileId: string, fnContent: string): string {
171
+ const hash = createHash("sha256");
172
+ hash.update(fileId);
173
+ hash.update(fnContent);
174
+ return hash.digest("hex").slice(0, 8);
175
+ }
176
+
177
+ /**
178
+ * Scan directory recursively for .ts/.tsx files containing createServerFn.
179
+ * Returns absolute paths of matching files.
180
+ */
181
+ export function scanServerFnFiles(srcDir: string): string[] {
182
+ const results: string[] = [];
183
+
184
+ function walk(dir: string): void {
185
+ let entries: string[];
186
+ try {
187
+ entries = readdirSync(dir);
188
+ } catch {
189
+ return;
190
+ }
191
+
192
+ for (const entry of entries) {
193
+ const full = join(dir, entry);
194
+ /* skip generated files and _gen dirs */
195
+ if (GEN_IGNORE_RE.test(entry)) continue;
196
+ if (entry.startsWith("_gen")) continue;
197
+
198
+ let stat: ReturnType<typeof statSync> | undefined;
199
+ try {
200
+ stat = statSync(full);
201
+ } catch {
202
+ continue;
203
+ }
204
+
205
+ if (stat.isDirectory()) {
206
+ walk(full);
207
+ } else if (entry.endsWith(".ts") || entry.endsWith(".tsx")) {
208
+ try {
209
+ const content = readFileSync(full, "utf-8");
210
+ if (content.includes("createServerFn")) {
211
+ results.push(full.replace(/\\/g, "/"));
212
+ }
213
+ } catch {
214
+ /* skip unreadable files */
215
+ }
216
+ }
217
+ }
218
+ }
219
+
220
+ walk(srcDir);
221
+ return results.sort();
222
+ }
223
+
224
+ /**
225
+ * Generate the virtual module source that collects all server fn registrations.
226
+ */
227
+ export function generateServerFnMapSource(files: string[]): string {
228
+ if (files.length === 0) {
229
+ return "export default new Map()";
230
+ }
231
+
232
+ const lines: string[] = [];
233
+ for (let i = 0; i < files.length; i++) {
234
+ lines.push(`import * as _m${i} from "${files[i]}"`);
235
+ }
236
+
237
+ lines.push("");
238
+ lines.push("const map = new Map()");
239
+ lines.push("const _names = new Map()");
240
+ lines.push("function _c(mod) {");
241
+ lines.push(" for (const v of Object.values(mod)) {");
242
+ lines.push(' if (v && typeof v === "function" && v._registration) {');
243
+ lines.push(" const r = v._registration");
244
+ lines.push(" map.set(r.id, r)");
245
+ lines.push(" _names.set(r.name, (_names.get(r.name) || 0) + 1)");
246
+ lines.push(" }");
247
+ lines.push(" }");
248
+ lines.push("}");
249
+ lines.push("function _n() {");
250
+ lines.push(" for (const [, r] of map) {");
251
+ lines.push(" if (r.id !== r.name && _names.get(r.name) === 1) map.set(r.name, r)");
252
+ lines.push(" }");
253
+ lines.push("}");
254
+
255
+ for (let i = 0; i < files.length; i++) {
256
+ lines.push(`_c(_m${i})`);
257
+ }
258
+ lines.push("_n()");
259
+
260
+ lines.push("export default map");
261
+ return lines.join("\n");
262
+ }
263
+
264
+ /**
265
+ * Drop import declarations whose every binding is unreferenced in the post-transform body.
266
+ * Runs after `transformEnvFns` and `stripHandlerBodies`. The transforms delete code regions
267
+ * (env-fn branches, handler closures) but leave their imports intact; Vite dev does not
268
+ * tree-shake so the imports survive into the client bundle. For modules like
269
+ * `flare/server-context` that do `import { AsyncLocalStorage } from "node:async_hooks"`
270
+ * at top level, the surviving import triggers Vite's `node:*` browser shim and crashes the page.
271
+ *
272
+ * Whole-import drops only — partial-specifier removal is out of scope. Side-effect imports
273
+ * (`import "x"` with no specifiers) are always kept.
274
+ */
275
+ export function dropDeadImports(code: string, id: string): string {
276
+ let parsed: ReturnType<typeof parseSync>;
277
+ try {
278
+ parsed = parseSync(id, code, { lang: id.endsWith(".tsx") || id.endsWith(".jsx") ? "tsx" : "ts" });
279
+ } catch {
280
+ return code;
281
+ }
282
+ if (parsed.errors.length > 0) return code;
283
+
284
+ interface ImportInfo {
285
+ bindings: string[];
286
+ end: number;
287
+ start: number;
288
+ }
289
+ const imports: ImportInfo[] = [];
290
+
291
+ for (const stmt of parsed.program.body) {
292
+ if (stmt.type !== "ImportDeclaration") continue;
293
+ const bindings: string[] = [];
294
+ for (const spec of stmt.specifiers ?? []) {
295
+ if (
296
+ spec.type === "ImportDefaultSpecifier" ||
297
+ spec.type === "ImportNamespaceSpecifier" ||
298
+ spec.type === "ImportSpecifier"
299
+ ) {
300
+ bindings.push(spec.local.name);
301
+ }
302
+ }
303
+ /* side-effect-only imports (no specifiers) — always keep, may execute side effects */
304
+ if (bindings.length === 0) continue;
305
+ imports.push({ bindings, end: stmt.end, start: stmt.start });
306
+ }
307
+
308
+ if (imports.length === 0) return code;
309
+
310
+ /* Scan the whole file minus import declarations. Solid's JSX compiler
311
+ emits `_$template(...)` *between* import groups; slicing after the last
312
+ import treats that usage as dead and drops `template as _$template`. */
313
+ const usageScan = stripCommentsAndStrings(code);
314
+ const usageMasked = usageScan.split("");
315
+ for (const imp of imports) {
316
+ for (let i = imp.start; i < imp.end && i < usageMasked.length; i++) {
317
+ usageMasked[i] = " ";
318
+ }
319
+ }
320
+ const usedCode = usageMasked.join("");
321
+
322
+ const ms = new MagicString(code);
323
+ let removed = false;
324
+ for (const imp of imports) {
325
+ const allUnused = imp.bindings.every((name) => {
326
+ const re = new RegExp(`\\b${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`);
327
+ return !re.test(usedCode);
328
+ });
329
+ if (allUnused) {
330
+ ms.remove(imp.start, imp.end);
331
+ /* drop trailing newline if present so we don't accumulate blank lines */
332
+ if (code[imp.end] === "\n") ms.remove(imp.end, imp.end + 1);
333
+ removed = true;
334
+ }
335
+ }
336
+
337
+ return removed ? ms.toString() : code;
338
+ }
339
+
340
+ /** Replace block comments, line comments, and string literals with whitespace-equivalent placeholders. */
341
+ function stripCommentsAndStrings(code: string): string {
342
+ let result = "";
343
+ let i = 0;
344
+ while (i < code.length) {
345
+ const ch = code[i];
346
+ const next = code[i + 1];
347
+ /* block comment */
348
+ if (ch === "/" && next === "*") {
349
+ const end = code.indexOf("*/", i + 2);
350
+ const stop = end === -1 ? code.length : end + 2;
351
+ result += " ".repeat(stop - i);
352
+ i = stop;
353
+ continue;
354
+ }
355
+ /* line comment */
356
+ if (ch === "/" && next === "/") {
357
+ const end = code.indexOf("\n", i);
358
+ const stop = end === -1 ? code.length : end;
359
+ result += " ".repeat(stop - i);
360
+ i = stop;
361
+ continue;
362
+ }
363
+ /* string literal — single, double, or template */
364
+ if (ch === '"' || ch === "'" || ch === "`") {
365
+ const quote = ch;
366
+ result += quote;
367
+ i++;
368
+ while (i < code.length) {
369
+ const c = code[i];
370
+ if (c === "\\") {
371
+ result += " ";
372
+ i += 2;
373
+ continue;
374
+ }
375
+ if (c === quote) {
376
+ result += quote;
377
+ i++;
378
+ break;
379
+ }
380
+ if (quote === "`" && c === "$" && code[i + 1] === "{") {
381
+ /* template expression — keep as-is to allow identifier scanning inside */
382
+ result += "${";
383
+ i += 2;
384
+ let depth = 1;
385
+ while (i < code.length && depth > 0) {
386
+ const cc = code[i];
387
+ if (cc === "{") depth++;
388
+ else if (cc === "}") depth--;
389
+ if (depth === 0) {
390
+ result += "}";
391
+ i++;
392
+ break;
393
+ }
394
+ result += cc;
395
+ i++;
396
+ }
397
+ continue;
398
+ }
399
+ result += " ";
400
+ i++;
401
+ }
402
+ continue;
403
+ }
404
+ result += ch;
405
+ i++;
406
+ }
407
+ return result;
408
+ }
409
+
410
+ /**
411
+ * Strip .handler(fn) closure body for client builds.
412
+ * Preserves _registration (client needs id, name, method for RPC).
413
+ * Uses paren-depth tracking to find matching closing paren.
414
+ */
415
+ export function stripHandlerBodies(code: string): string {
416
+ let result = "";
417
+ let cursor = 0;
418
+
419
+ while (cursor < code.length) {
420
+ const handlerIdx = code.indexOf(".handler(", cursor);
421
+ const streamIdx = code.indexOf(".stream(", cursor);
422
+
423
+ /* pick whichever comes first, skip -1 (not found) */
424
+ let idx: number;
425
+ let token: string;
426
+ if (handlerIdx === -1 && streamIdx === -1) {
427
+ result += code.slice(cursor);
428
+ break;
429
+ }
430
+ if (handlerIdx === -1) {
431
+ idx = streamIdx;
432
+ token = ".stream(";
433
+ } else if (streamIdx === -1) {
434
+ idx = handlerIdx;
435
+ token = ".handler(";
436
+ } else if (handlerIdx <= streamIdx) {
437
+ idx = handlerIdx;
438
+ token = ".handler(";
439
+ } else {
440
+ idx = streamIdx;
441
+ token = ".stream(";
442
+ }
443
+
444
+ /* include everything up to and including the token */
445
+ const handlerStart = idx + token.length;
446
+ result += code.slice(cursor, handlerStart);
447
+
448
+ /* find matching closing paren via depth tracking (string-aware) */
449
+ let depth = 1;
450
+ let i = handlerStart;
451
+ for (; i < code.length; i++) {
452
+ const ch = code[i];
453
+ /* Skip string literals */
454
+ if (ch === '"' || ch === "'") {
455
+ const q = ch;
456
+ i++;
457
+ while (i < code.length && code[i] !== q) {
458
+ if (code[i] === "\\") i++;
459
+ i++;
460
+ }
461
+ continue;
462
+ }
463
+ /* Skip template literals */
464
+ if (ch === "`") {
465
+ i++;
466
+ while (i < code.length && code[i] !== "`") {
467
+ if (code[i] === "\\") i++;
468
+ else if (code[i] === "$" && i + 1 < code.length && code[i + 1] === "{") {
469
+ /* Skip ${...} expression inside template (simple depth tracking) */
470
+ i += 2;
471
+ let tDepth = 1;
472
+ while (i < code.length && tDepth > 0) {
473
+ if (code[i] === "{") tDepth++;
474
+ else if (code[i] === "}") tDepth--;
475
+ if (tDepth > 0) i++;
476
+ }
477
+ }
478
+ i++;
479
+ }
480
+ continue;
481
+ }
482
+ /* Skip block comments */
483
+ if (ch === "/" && i + 1 < code.length && code[i + 1] === "*") {
484
+ i += 2;
485
+ while (i < code.length - 1 && !(code[i] === "*" && code[i + 1] === "/")) i++;
486
+ i++;
487
+ continue;
488
+ }
489
+ /* Skip line comments */
490
+ if (ch === "/" && i + 1 < code.length && code[i + 1] === "/") {
491
+ i += 2;
492
+ while (i < code.length && code[i] !== "\n") i++;
493
+ continue;
494
+ }
495
+ if (ch === "(") depth++;
496
+ else if (ch === ")") {
497
+ depth--;
498
+ if (depth === 0) break;
499
+ }
500
+ }
501
+
502
+ /* replace inner content with no-op stub */
503
+ result += '() => { throw new Error("Server function called on client") }';
504
+ cursor = i;
505
+ }
506
+
507
+ return result;
508
+ }
509
+
510
+ export function createServerFnPlugin(_config?: { ignorePrefix?: string }): VitePlugin {
511
+ let serverFnFiles: string[] = [];
512
+
513
+ /* track vite dev server for HMR invalidation */
514
+ let devServer:
515
+ | {
516
+ config?: { root?: string };
517
+ environments?: Record<
518
+ string,
519
+ {
520
+ moduleGraph?: {
521
+ invalidateModule?: (mod: unknown) => void;
522
+ getModuleById?: (id: string) => unknown;
523
+ };
524
+ }
525
+ >;
526
+ httpServer?: { on?: (event: string, fn: () => void) => void } | null;
527
+ }
528
+ | undefined;
529
+
530
+ return {
531
+ buildStart(this: { environment?: { config?: { root?: string } } }) {
532
+ const root = this.environment?.config?.root ?? process.cwd();
533
+ const srcDir = join(root, "src");
534
+ serverFnFiles = scanServerFnFiles(srcDir);
535
+ },
536
+ configureServer(server: unknown) {
537
+ devServer = server as typeof devServer;
538
+ const root = devServer?.config?.root ?? process.cwd();
539
+ const srcDir = join(root, "src");
540
+ let debounceTimer: ReturnType<typeof setTimeout> | undefined;
541
+
542
+ const watcher = watch(srcDir, { recursive: true }, (_event, filename) => {
543
+ if (!filename) return;
544
+ const name = String(filename);
545
+ if (GEN_IGNORE_RE.test(name)) return;
546
+ if (!name.endsWith(".ts") && !name.endsWith(".tsx")) return;
547
+
548
+ clearTimeout(debounceTimer);
549
+ debounceTimer = setTimeout(() => {
550
+ const prev = serverFnFiles;
551
+ serverFnFiles = scanServerFnFiles(srcDir);
552
+
553
+ /* invalidate virtual module if file set changed */
554
+ if (prev.length !== serverFnFiles.length || prev.some((f, idx) => f !== serverFnFiles[idx])) {
555
+ const ssrEnv = devServer?.environments?.["ssr"];
556
+ const mod = ssrEnv?.moduleGraph?.getModuleById?.("\0virtual:flare-server-fn-map");
557
+ if (mod) {
558
+ ssrEnv?.moduleGraph?.invalidateModule?.(mod);
559
+ }
560
+ }
561
+ }, 150);
562
+ });
563
+
564
+ devServer?.httpServer?.on?.("close", () => {
565
+ clearTimeout(debounceTimer);
566
+ watcher.close();
567
+ });
568
+ return undefined;
569
+ },
570
+ load(id: string): { code: string; moduleType: string } | null {
571
+ if (id === "\0virtual:flare-server-fn-secret") {
572
+ return { code: `export default "${buildSecret}"`, moduleType: "js" };
573
+ }
574
+ if (id === "\0virtual:flare-server-fn-map") {
575
+ return { code: generateServerFnMapSource(serverFnFiles), moduleType: "js" };
576
+ }
577
+ return null;
578
+ },
579
+ name: "flare:server-fn",
580
+ resolveId(this: { environment?: { name?: string } }, id: string): string | null {
581
+ if (id === "virtual:flare-server-fn-secret") {
582
+ /* SSR-only: secret must never leak to client bundles */
583
+ const env = this.environment?.name ?? "client";
584
+ if (env !== "ssr") return null;
585
+ return "\0virtual:flare-server-fn-secret";
586
+ }
587
+ if (id === "virtual:flare-server-fn-map") {
588
+ /* SSR-only: return null for client env so map is not bundled */
589
+ const env = this.environment?.name ?? "client";
590
+ if (env !== "ssr") return null;
591
+ return "\0virtual:flare-server-fn-map";
592
+ }
593
+ return null;
594
+ },
595
+ transform(this: { environment?: { name?: string } }, code: string, id: string): { code: string; map: null } | null {
596
+ const env = this.environment?.name ?? "client";
597
+ const isSSR = env === "ssr";
598
+ let transformed = code;
599
+ let changed = false;
600
+
601
+ /* env-fn transforms: both environments (skip the runtime implementation itself) */
602
+ if (ENV_FN_RE.test(transformed) && !id.includes("/env-fn/")) {
603
+ transformed = transformEnvFns(transformed, isSSR);
604
+ changed = true;
605
+ }
606
+
607
+ /* handler stripping: client-only transform */
608
+ if (!isSSR && HANDLER_RE.test(transformed) && transformed.includes("createServerFn")) {
609
+ /* inject __id first */
610
+ if (hasServerFnCalls(transformed)) {
611
+ transformed = replaceServerFnConfigs(transformed, (content) => {
612
+ const fnId = computeFnId(id, content);
613
+ return ` __id: "${fnId}",${content}`;
614
+ });
615
+ }
616
+
617
+ /* strip handler bodies */
618
+ transformed = stripHandlerBodies(transformed);
619
+ /* After body strips, sweep imports unused by remaining code — keeps node:* deps
620
+ (e.g. flare/server-context → node:async_hooks) out of the client bundle. */
621
+ transformed = dropDeadImports(transformed, id);
622
+ return { code: transformed, map: null };
623
+ }
624
+
625
+ /* SSR: only inject __id for server fns */
626
+ if (hasServerFnCalls(transformed)) {
627
+ transformed = replaceServerFnConfigs(transformed, (content) => {
628
+ const fnId = computeFnId(id, content);
629
+ return ` __id: "${fnId}",${content}`;
630
+ });
631
+ changed = true;
632
+ }
633
+
634
+ if (changed) {
635
+ transformed = dropDeadImports(transformed, id);
636
+ return { code: transformed, map: null };
637
+ }
638
+ return null;
639
+ },
640
+ };
641
+ }