@fluixi/compiler 1.0.0-alpha.100

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 (303) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/dist/analyze/expr-shape.cjs +62 -0
  4. package/dist/analyze/expr-shape.d.cts +14 -0
  5. package/dist/analyze/expr-shape.d.ts +15 -0
  6. package/dist/analyze/expr-shape.d.ts.map +1 -0
  7. package/dist/analyze/expr-shape.js +37 -0
  8. package/dist/analyze/expr-shape.mjs +41 -0
  9. package/dist/analyze/intrinsics.cjs +112 -0
  10. package/dist/analyze/intrinsics.d.cts +80 -0
  11. package/dist/analyze/intrinsics.d.ts +81 -0
  12. package/dist/analyze/intrinsics.d.ts.map +1 -0
  13. package/dist/analyze/intrinsics.js +126 -0
  14. package/dist/analyze/intrinsics.mjs +91 -0
  15. package/dist/analyze/paren.cjs +134 -0
  16. package/dist/analyze/paren.d.cts +23 -0
  17. package/dist/analyze/paren.d.ts +24 -0
  18. package/dist/analyze/paren.d.ts.map +1 -0
  19. package/dist/analyze/paren.js +168 -0
  20. package/dist/analyze/paren.mjs +113 -0
  21. package/dist/analyze/props-destructure.cjs +226 -0
  22. package/dist/analyze/props-destructure.d.cts +83 -0
  23. package/dist/analyze/props-destructure.d.ts +84 -0
  24. package/dist/analyze/props-destructure.d.ts.map +1 -0
  25. package/dist/analyze/props-destructure.js +272 -0
  26. package/dist/analyze/props-destructure.mjs +205 -0
  27. package/dist/analyze/reactive-bindings.cjs +210 -0
  28. package/dist/analyze/reactive-bindings.d.cts +42 -0
  29. package/dist/analyze/reactive-bindings.d.ts +43 -0
  30. package/dist/analyze/reactive-bindings.d.ts.map +1 -0
  31. package/dist/analyze/reactive-bindings.js +191 -0
  32. package/dist/analyze/reactive-bindings.mjs +187 -0
  33. package/dist/analyze/reactive-expr.cjs +38 -0
  34. package/dist/analyze/reactive-expr.d.cts +38 -0
  35. package/dist/analyze/reactive-expr.d.ts +39 -0
  36. package/dist/analyze/reactive-expr.d.ts.map +1 -0
  37. package/dist/analyze/reactive-expr.js +31 -0
  38. package/dist/analyze/reactive-expr.mjs +17 -0
  39. package/dist/analyze/static-graph.cjs +831 -0
  40. package/dist/analyze/static-graph.d.cts +102 -0
  41. package/dist/analyze/static-graph.d.ts +103 -0
  42. package/dist/analyze/static-graph.d.ts.map +1 -0
  43. package/dist/analyze/static-graph.js +871 -0
  44. package/dist/analyze/static-graph.mjs +813 -0
  45. package/dist/babel-I6BUWUCU.mjs +636 -0
  46. package/dist/chunk-3AICWNF7.mjs +92 -0
  47. package/dist/chunk-6CWJWGE2.mjs +2170 -0
  48. package/dist/chunk-C3XPDB4Y.mjs +16 -0
  49. package/dist/chunk-GX5XV6Z7.mjs +190 -0
  50. package/dist/chunk-HXEXPWJ2.mjs +999 -0
  51. package/dist/chunk-NU3W67UZ.mjs +584 -0
  52. package/dist/chunk-SKAVY75W.mjs +11 -0
  53. package/dist/codegen/backend.cjs +18 -0
  54. package/dist/codegen/backend.d.cts +35 -0
  55. package/dist/codegen/backend.d.ts +36 -0
  56. package/dist/codegen/backend.d.ts.map +1 -0
  57. package/dist/codegen/backend.js +1 -0
  58. package/dist/codegen/backend.mjs +0 -0
  59. package/dist/codegen/backends/imperative.cjs +577 -0
  60. package/dist/codegen/backends/imperative.d.cts +11 -0
  61. package/dist/codegen/backends/imperative.d.ts +12 -0
  62. package/dist/codegen/backends/imperative.d.ts.map +1 -0
  63. package/dist/codegen/backends/imperative.js +362 -0
  64. package/dist/codegen/backends/imperative.mjs +554 -0
  65. package/dist/codegen/contract.cjs +52 -0
  66. package/dist/codegen/contract.d.cts +24 -0
  67. package/dist/codegen/contract.d.ts +25 -0
  68. package/dist/codegen/contract.d.ts.map +1 -0
  69. package/dist/codegen/contract.js +30 -0
  70. package/dist/codegen/contract.mjs +31 -0
  71. package/dist/codegen/partial-template.cjs +176 -0
  72. package/dist/codegen/partial-template.d.cts +61 -0
  73. package/dist/codegen/partial-template.d.ts +62 -0
  74. package/dist/codegen/partial-template.d.ts.map +1 -0
  75. package/dist/codegen/partial-template.js +0 -0
  76. package/dist/codegen/partial-template.mjs +153 -0
  77. package/dist/codegen/serialize-static.cjs +125 -0
  78. package/dist/codegen/serialize-static.d.cts +38 -0
  79. package/dist/codegen/serialize-static.d.ts +39 -0
  80. package/dist/codegen/serialize-static.d.ts.map +1 -0
  81. package/dist/codegen/serialize-static.js +165 -0
  82. package/dist/codegen/serialize-static.mjs +104 -0
  83. package/dist/frontend/babel/build-ir-lit.cjs +1933 -0
  84. package/dist/frontend/babel/build-ir-lit.d.cts +26 -0
  85. package/dist/frontend/babel/build-ir-lit.d.ts +27 -0
  86. package/dist/frontend/babel/build-ir-lit.d.ts.map +1 -0
  87. package/dist/frontend/babel/build-ir-lit.js +13 -0
  88. package/dist/frontend/babel/build-ir-lit.mjs +1902 -0
  89. package/dist/frontend/babel/build-ir.cjs +1948 -0
  90. package/dist/frontend/babel/build-ir.d.cts +35 -0
  91. package/dist/frontend/babel/build-ir.d.ts +36 -0
  92. package/dist/frontend/babel/build-ir.d.ts.map +1 -0
  93. package/dist/frontend/babel/build-ir.js +124 -0
  94. package/dist/frontend/babel/build-ir.mjs +1919 -0
  95. package/dist/frontend/babel/index.cjs +2610 -0
  96. package/dist/frontend/babel/index.d.cts +33 -0
  97. package/dist/frontend/babel/index.d.ts +34 -0
  98. package/dist/frontend/babel/index.d.ts.map +1 -0
  99. package/dist/frontend/babel/index.js +65 -0
  100. package/dist/frontend/babel/index.mjs +2580 -0
  101. package/dist/frontend/babel/lower-template.cjs +1931 -0
  102. package/dist/frontend/babel/lower-template.d.cts +23 -0
  103. package/dist/frontend/babel/lower-template.d.ts +24 -0
  104. package/dist/frontend/babel/lower-template.d.ts.map +1 -0
  105. package/dist/frontend/babel/lower-template.js +64 -0
  106. package/dist/frontend/babel/lower-template.mjs +1902 -0
  107. package/dist/frontend/babel/plugin.cjs +2556 -0
  108. package/dist/frontend/babel/plugin.d.cts +95 -0
  109. package/dist/frontend/babel/plugin.d.ts +96 -0
  110. package/dist/frontend/babel/plugin.d.ts.map +1 -0
  111. package/dist/frontend/babel/plugin.js +523 -0
  112. package/dist/frontend/babel/plugin.mjs +2527 -0
  113. package/dist/frontend/babel/server-functions.cjs +110 -0
  114. package/dist/frontend/babel/server-functions.d.cts +10 -0
  115. package/dist/frontend/babel/server-functions.d.ts +11 -0
  116. package/dist/frontend/babel/server-functions.d.ts.map +1 -0
  117. package/dist/frontend/babel/server-functions.js +81 -0
  118. package/dist/frontend/babel/server-functions.mjs +89 -0
  119. package/dist/frontend/babel/types.cjs +18 -0
  120. package/dist/frontend/babel/types.d.cts +33 -0
  121. package/dist/frontend/babel/types.d.ts +34 -0
  122. package/dist/frontend/babel/types.d.ts.map +1 -0
  123. package/dist/frontend/babel/types.js +1 -0
  124. package/dist/frontend/babel/types.mjs +0 -0
  125. package/dist/frontend/types.cjs +18 -0
  126. package/dist/frontend/types.d.cts +25 -0
  127. package/dist/frontend/types.d.ts +26 -0
  128. package/dist/frontend/types.d.ts.map +1 -0
  129. package/dist/frontend/types.js +1 -0
  130. package/dist/frontend/types.mjs +0 -0
  131. package/dist/frontend/vocabulary.cjs +98 -0
  132. package/dist/frontend/vocabulary.d.cts +79 -0
  133. package/dist/frontend/vocabulary.d.ts +80 -0
  134. package/dist/frontend/vocabulary.d.ts.map +1 -0
  135. package/dist/frontend/vocabulary.js +124 -0
  136. package/dist/frontend/vocabulary.mjs +77 -0
  137. package/dist/index.cjs +1957 -0
  138. package/dist/index.d.cts +27 -0
  139. package/dist/index.d.ts +28 -0
  140. package/dist/index.d.ts.map +1 -0
  141. package/dist/index.js +38 -0
  142. package/dist/index.mjs +1949 -0
  143. package/dist/integrations.cjs +5705 -0
  144. package/dist/integrations.d.cts +215 -0
  145. package/dist/integrations.d.ts +216 -0
  146. package/dist/integrations.d.ts.map +1 -0
  147. package/dist/integrations.js +566 -0
  148. package/dist/integrations.mjs +810 -0
  149. package/dist/ir/nodes.cjs +18 -0
  150. package/dist/ir/nodes.d.cts +168 -0
  151. package/dist/ir/nodes.d.ts +169 -0
  152. package/dist/ir/nodes.d.ts.map +1 -0
  153. package/dist/ir/nodes.js +1 -0
  154. package/dist/ir/nodes.mjs +0 -0
  155. package/dist/lower/compile-template.cjs +1670 -0
  156. package/dist/lower/compile-template.d.cts +82 -0
  157. package/dist/lower/compile-template.d.ts +83 -0
  158. package/dist/lower/compile-template.d.ts.map +1 -0
  159. package/dist/lower/compile-template.js +79 -0
  160. package/dist/lower/compile-template.mjs +1662 -0
  161. package/dist/lower/entities.cjs +288 -0
  162. package/dist/lower/entities.d.cts +1 -0
  163. package/dist/lower/entities.d.ts +2 -0
  164. package/dist/lower/entities.d.ts.map +1 -0
  165. package/dist/lower/entities.js +54 -0
  166. package/dist/lower/entities.mjs +267 -0
  167. package/dist/lower/jsx.cjs +1101 -0
  168. package/dist/lower/jsx.d.cts +33 -0
  169. package/dist/lower/jsx.d.ts +34 -0
  170. package/dist/lower/jsx.d.ts.map +1 -0
  171. package/dist/lower/jsx.js +414 -0
  172. package/dist/lower/jsx.mjs +1078 -0
  173. package/dist/lower/template.cjs +1354 -0
  174. package/dist/lower/template.d.cts +53 -0
  175. package/dist/lower/template.d.ts +54 -0
  176. package/dist/lower/template.d.ts.map +1 -0
  177. package/dist/lower/template.js +542 -0
  178. package/dist/lower/template.mjs +1335 -0
  179. package/dist/lower/text.cjs +42 -0
  180. package/dist/lower/text.d.cts +10 -0
  181. package/dist/lower/text.d.ts +11 -0
  182. package/dist/lower/text.d.ts.map +1 -0
  183. package/dist/lower/text.js +31 -0
  184. package/dist/lower/text.mjs +21 -0
  185. package/dist/optimize/analyze-static.cjs +112 -0
  186. package/dist/optimize/analyze-static.d.cts +39 -0
  187. package/dist/optimize/analyze-static.d.ts +40 -0
  188. package/dist/optimize/analyze-static.d.ts.map +1 -0
  189. package/dist/optimize/analyze-static.js +131 -0
  190. package/dist/optimize/analyze-static.mjs +91 -0
  191. package/dist/optimize/collapse-strategies.cjs +90 -0
  192. package/dist/optimize/collapse-strategies.d.cts +30 -0
  193. package/dist/optimize/collapse-strategies.d.ts +31 -0
  194. package/dist/optimize/collapse-strategies.d.ts.map +1 -0
  195. package/dist/optimize/collapse-strategies.js +33 -0
  196. package/dist/optimize/collapse-strategies.mjs +67 -0
  197. package/dist/options.cjs +33 -0
  198. package/dist/options.d.cts +39 -0
  199. package/dist/options.d.ts +40 -0
  200. package/dist/options.d.ts.map +1 -0
  201. package/dist/options.js +8 -0
  202. package/dist/options.mjs +12 -0
  203. package/dist/resolve/builtins.cjs +73 -0
  204. package/dist/resolve/builtins.d.cts +68 -0
  205. package/dist/resolve/builtins.d.ts +69 -0
  206. package/dist/resolve/builtins.d.ts.map +1 -0
  207. package/dist/resolve/builtins.js +64 -0
  208. package/dist/resolve/builtins.mjs +53 -0
  209. package/dist/resolve/component-globals.cjs +272 -0
  210. package/dist/resolve/component-globals.d.cts +88 -0
  211. package/dist/resolve/component-globals.d.ts +89 -0
  212. package/dist/resolve/component-globals.d.ts.map +1 -0
  213. package/dist/resolve/component-globals.js +160 -0
  214. package/dist/resolve/component-globals.mjs +249 -0
  215. package/dist/resolve/component-resolver.cjs +80 -0
  216. package/dist/resolve/component-resolver.d.cts +68 -0
  217. package/dist/resolve/component-resolver.d.ts +69 -0
  218. package/dist/resolve/component-resolver.d.ts.map +1 -0
  219. package/dist/resolve/component-resolver.js +95 -0
  220. package/dist/resolve/component-resolver.mjs +59 -0
  221. package/dist/resolve/load-directive.cjs +93 -0
  222. package/dist/resolve/load-directive.d.cts +53 -0
  223. package/dist/resolve/load-directive.d.ts +54 -0
  224. package/dist/resolve/load-directive.d.ts.map +1 -0
  225. package/dist/resolve/load-directive.js +93 -0
  226. package/dist/resolve/load-directive.mjs +72 -0
  227. package/dist/resolve/resolve-ir.cjs +108 -0
  228. package/dist/resolve/resolve-ir.d.cts +53 -0
  229. package/dist/resolve/resolve-ir.d.ts +54 -0
  230. package/dist/resolve/resolve-ir.d.ts.map +1 -0
  231. package/dist/resolve/resolve-ir.js +58 -0
  232. package/dist/resolve/resolve-ir.mjs +85 -0
  233. package/dist/resolve/write-globals.cjs +383 -0
  234. package/dist/resolve/write-globals.d.cts +36 -0
  235. package/dist/resolve/write-globals.d.ts +37 -0
  236. package/dist/resolve/write-globals.d.ts.map +1 -0
  237. package/dist/resolve/write-globals.js +169 -0
  238. package/dist/resolve/write-globals.mjs +362 -0
  239. package/dist/server-fns-VZLHMQTC.mjs +79 -0
  240. package/dist/server-functions-UAZKOLZW.mjs +82 -0
  241. package/dist/transform/bindings.cjs +71 -0
  242. package/dist/transform/bindings.d.cts +16 -0
  243. package/dist/transform/bindings.d.ts +17 -0
  244. package/dist/transform/bindings.d.ts.map +1 -0
  245. package/dist/transform/bindings.js +69 -0
  246. package/dist/transform/bindings.mjs +50 -0
  247. package/dist/transform/index.cjs +3837 -0
  248. package/dist/transform/index.d.cts +15 -0
  249. package/dist/transform/index.d.ts +16 -0
  250. package/dist/transform/index.d.ts.map +1 -0
  251. package/dist/transform/index.js +12 -0
  252. package/dist/transform/index.mjs +4882 -0
  253. package/dist/transform/intrinsics.cjs +261 -0
  254. package/dist/transform/intrinsics.d.cts +33 -0
  255. package/dist/transform/intrinsics.d.ts +34 -0
  256. package/dist/transform/intrinsics.d.ts.map +1 -0
  257. package/dist/transform/intrinsics.js +120 -0
  258. package/dist/transform/intrinsics.mjs +238 -0
  259. package/dist/transform/props.cjs +441 -0
  260. package/dist/transform/props.d.cts +45 -0
  261. package/dist/transform/props.d.ts +46 -0
  262. package/dist/transform/props.d.ts.map +1 -0
  263. package/dist/transform/props.js +221 -0
  264. package/dist/transform/props.mjs +1487 -0
  265. package/dist/transform/server-fn-id.cjs +35 -0
  266. package/dist/transform/server-fn-id.d.cts +9 -0
  267. package/dist/transform/server-fn-id.d.ts +10 -0
  268. package/dist/transform/server-fn-id.d.ts.map +1 -0
  269. package/dist/transform/server-fn-id.js +18 -0
  270. package/dist/transform/server-fn-id.mjs +14 -0
  271. package/dist/transform/server-fns.cjs +114 -0
  272. package/dist/transform/server-fns.d.cts +12 -0
  273. package/dist/transform/server-fns.d.ts +13 -0
  274. package/dist/transform/server-fns.d.ts.map +1 -0
  275. package/dist/transform/server-fns.js +94 -0
  276. package/dist/transform/server-fns.mjs +1160 -0
  277. package/dist/transform/source-locations.cjs +393 -0
  278. package/dist/transform/source-locations.d.cts +48 -0
  279. package/dist/transform/source-locations.d.ts +49 -0
  280. package/dist/transform/source-locations.d.ts.map +1 -0
  281. package/dist/transform/source-locations.js +303 -0
  282. package/dist/transform/source-locations.mjs +370 -0
  283. package/dist/transform/styles.cjs +606 -0
  284. package/dist/transform/styles.d.cts +237 -0
  285. package/dist/transform/styles.d.ts +238 -0
  286. package/dist/transform/styles.d.ts.map +1 -0
  287. package/dist/transform/styles.js +743 -0
  288. package/dist/transform/styles.mjs +671 -0
  289. package/dist/transform/templates.cjs +3806 -0
  290. package/dist/transform/templates.d.cts +82 -0
  291. package/dist/transform/templates.d.ts +83 -0
  292. package/dist/transform/templates.d.ts.map +1 -0
  293. package/dist/transform/templates.js +613 -0
  294. package/dist/transform/templates.mjs +4852 -0
  295. package/dist/transform-3W45L3EA.mjs +951 -0
  296. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  297. package/dist/vite-styles.cjs +223 -0
  298. package/dist/vite-styles.d.cts +68 -0
  299. package/dist/vite-styles.d.ts +69 -0
  300. package/dist/vite-styles.d.ts.map +1 -0
  301. package/dist/vite-styles.js +241 -0
  302. package/dist/vite-styles.mjs +214 -0
  303. package/package.json +141 -0
@@ -0,0 +1,523 @@
1
+ /**
2
+ * @fileoverview Babel front-end for the Fluixi compiler.
3
+ * @module @fluixi/babel-plugin-jsx
4
+ *
5
+ * This is a host adapter, not the compiler. It parses with babel and, for each
6
+ * JSX element/fragment and each `` html`...` `` template, hands the node to the
7
+ * shared pipeline: build IR, run the imperative backend, then splices the
8
+ * emitted code back into the program. Everything that decides *what* the output
9
+ * means (directives, control flow, component resolution, load strategies,
10
+ * reactivity, templates) lives in ../../{ir,resolve,optimize,codegen}, which
11
+ * import no babel at all.
12
+ *
13
+ * What stays here is genuinely host work: dispatching the visitors, collecting
14
+ * the runtime symbols the backend reported so the imports can be injected, and
15
+ * hoisting static templates to module scope.
16
+ */
17
+ import { declare } from '@babel/helper-plugin-utils';
18
+ import { createComponentResolver, } from '../../resolve/component-resolver.js';
19
+ import { resolveComponentSources } from '../../resolve/resolve-ir.js';
20
+ import { collapseStrategies } from '../../optimize/collapse-strategies.js';
21
+ import { isDeferred } from '../../resolve/load-directive.js';
22
+ import { CORE_CONTROL_FLOW, ROUTER_COMPONENTS as ROUTER_COMPONENT_NAMES, } from '../../resolve/builtins.js';
23
+ import { types as t, template } from '@babel/core';
24
+ import { buildIR } from './build-ir.js';
25
+ import { buildIRFromLit } from './build-ir-lit.js';
26
+ import { imperativeBackend } from '../../codegen/backends/imperative.js';
27
+ // ============================================================================
28
+ // Constants
29
+ // ============================================================================
30
+ // Control-flow components that live in fluixi, NOT @fluixi/dom, they must
31
+ // be auto-imported from core (dom doesn't export them).
32
+ const CORE_ONLY_CONTROL_FLOW = new Set(CORE_CONTROL_FLOW);
33
+ // Router components auto-imported (from routerModule) the same way control flow is:
34
+ // use `<Router>`/`<Outlet>`/… in a template and the import is injected if absent.
35
+ const ROUTER_COMPONENTS = new Set(ROUTER_COMPONENT_NAMES);
36
+ // ============================================================================
37
+ // Main Plugin
38
+ // ============================================================================
39
+ export default declare((api, options = {}) => {
40
+ api.assertVersion(7);
41
+ const { delegateEvents = true, signalModule = '@fluixi/dom', controlFlowModule = '@fluixi/dom', routerModule = '@fluixi/core/router', reactiveModule = '@fluixi/reactive/signal', templateClone = true, partialTemplates = true,
42
+ // `both` by default: JSX and html`` are compiled out of the box, so
43
+ // `fluixi()` needs no `format` option. Injected imports are deduped, so a
44
+ // file mixing the two styles won't redeclare a binding.
45
+ format = 'both', litTag = 'html', resolve: resolveRules = [], } = options;
46
+ // Built once per plugin instance, not per file: the rules are static and the
47
+ // resolver memoises lookups, so every file in the build shares one cache.
48
+ const componentResolver = createComponentResolver(resolveRules);
49
+ const compileLit = format === 'lit' || format === 'both';
50
+ const pluginState = {
51
+ irImports: new Set(),
52
+ libModule: '@fluixi/core',
53
+ controlFlowModule,
54
+ routerModule,
55
+ templateClone,
56
+ partialTemplates,
57
+ hasJSX: false,
58
+ hasLitHTML: false,
59
+ needsControlFlowImport: false,
60
+ needsCreateMemo: false,
61
+ controlFlowComponents: new Set(),
62
+ resolvedComponents: new Map(),
63
+ deferredComponents: new Map(),
64
+ componentResolver,
65
+ delegatedEvents: new Set(),
66
+ staticTemplates: new Map(),
67
+ staticCounter: 0,
68
+ };
69
+ return {
70
+ name: '@fluixi/babel-plugin-jsx',
71
+ manipulateOptions(opts, parserOpts) {
72
+ // Enable JSX parsing
73
+ parserOpts.plugins.push('jsx', 'typescript');
74
+ },
75
+ pre(state) {
76
+ // Initialize plugin state
77
+ Object.assign(state, pluginState);
78
+ },
79
+ visitor: {
80
+ Program: {
81
+ enter(path, state) {
82
+ // Reset state for new file
83
+ Object.assign(state, {
84
+ hasJSX: false,
85
+ hasLitHTML: false,
86
+ hasLitIR: false,
87
+ needsControlFlowImport: false,
88
+ needsCreateMemo: false,
89
+ controlFlowComponents: new Set(),
90
+ resolvedComponents: new Map(),
91
+ deferredComponents: new Map(),
92
+ componentResolver,
93
+ delegatedEvents: new Set(),
94
+ staticTemplates: new Map(),
95
+ staticCounter: 0,
96
+ libModule: '@fluixi/core',
97
+ controlFlowModule,
98
+ routerModule,
99
+ templateClone,
100
+ partialTemplates,
101
+ irImports: new Set(),
102
+ needsCreateNativeElement: false,
103
+ needsInsert: false,
104
+ needsSpread: false,
105
+ needsCreateComponent: false,
106
+ needsMergeProps: false,
107
+ });
108
+ },
109
+ exit(path, state) {
110
+ const imports = [];
111
+ if (state.hasJSX || state.hasLitIR) {
112
+ if (state.irImports.size > 0) {
113
+ // Import exactly the symbols emit() reported, splitting control-flow
114
+ // components to the control-flow module.
115
+ const CF = new Set([
116
+ 'Show', 'For', 'Index', 'Switch', 'Match',
117
+ 'Dynamic', 'ErrorBoundary', 'Portal', 'Suspense',
118
+ ]);
119
+ // Reactive primitives come from the signal core, not the dom module.
120
+ const REACTIVE = new Set([
121
+ 'createMemo', 'createEffect', 'createRenderEffect', 'createRoot',
122
+ 'createSignal', 'onCleanup', 'batch', 'untrack',
123
+ ]);
124
+ const all = Array.from(state.irImports);
125
+ const spec = (n) => t.importSpecifier(t.identifier(n), t.identifier(n));
126
+ const unbound = (s) => !path.scope.hasBinding(s);
127
+ const reactive = all.filter((s) => REACTIVE.has(s) && unbound(s));
128
+ const core = all.filter((s) => !CF.has(s) && !REACTIVE.has(s) && unbound(s));
129
+ const cf = all.filter((s) => CF.has(s) && unbound(s));
130
+ if (reactive.length > 0) {
131
+ imports.push(t.importDeclaration(reactive.map(spec), t.stringLiteral(reactiveModule)));
132
+ }
133
+ if (core.length > 0) {
134
+ imports.push(t.importDeclaration(core.map(spec), t.stringLiteral(signalModule)));
135
+ }
136
+ if (cf.length > 0) {
137
+ imports.push(t.importDeclaration(cf.map(spec), t.stringLiteral(controlFlowModule)));
138
+ }
139
+ }
140
+ // createMemo (wraps conditionals/components) is a reactive primitive,
141
+ // so it comes from the reactive module, not dom.
142
+ if (state.needsCreateMemo && !path.scope.hasBinding('createMemo')) {
143
+ imports.push(t.importDeclaration([t.importSpecifier(t.identifier('createMemo'), t.identifier('createMemo'))], t.stringLiteral(reactiveModule)));
144
+ }
145
+ // Add control flow imports for auto-transformed components (Show, etc.)
146
+ // Only add components that aren't already bound in the file scope.
147
+ if (state.needsControlFlowImport &&
148
+ state.controlFlowComponents.size > 0) {
149
+ const unbound = Array.from(state.controlFlowComponents).filter((name) => !path.scope.hasBinding(name));
150
+ // Suspense/SuspenseList live in fluixi, not @fluixi/dom, so
151
+ // they must be auto-imported from core (importing them from the dom
152
+ // controlFlowModule would resolve to nothing). Everything else comes
153
+ // from controlFlowModule.
154
+ const coreModule = state.libModule ?? '@fluixi/core';
155
+ const fromRouter = unbound.filter((n) => ROUTER_COMPONENTS.has(n));
156
+ const fromCore = unbound.filter((n) => CORE_ONLY_CONTROL_FLOW.has(n));
157
+ const fromDom = unbound.filter((n) => !CORE_ONLY_CONTROL_FLOW.has(n) && !ROUTER_COMPONENTS.has(n));
158
+ const declFor = (names, mod) => t.importDeclaration(names.map((name) => t.importSpecifier(t.identifier(name), t.identifier(name))), t.stringLiteral(mod));
159
+ if (fromDom.length > 0)
160
+ imports.push(declFor(fromDom, controlFlowModule));
161
+ if (fromCore.length > 0)
162
+ imports.push(declFor(fromCore, coreModule));
163
+ if (fromRouter.length > 0)
164
+ imports.push(declFor(fromRouter, routerModule));
165
+ }
166
+ // Imports for tags matched by the `resolve` rules. Same binding guard as
167
+ // control flow: a hand-written import or a local of the same name wins, so
168
+ // enabling resolution can never change what an existing file means.
169
+ //
170
+ // Grouped by module so several components from one package share a single
171
+ // declaration, and emitted with the local name equal to the tag, the
172
+ // generated code refers to the tag, not to the export.
173
+ // Deferred components become value bindings, not imports: a literal
174
+ // `import()` is what lets the bundler split, hash and tree-shake the chunk
175
+ // as ordinary behaviour rather than as an integration.
176
+ const deferredDecls = [];
177
+ if (state.deferredComponents.size > 0) {
178
+ for (const [local, entry] of state.deferredComponents) {
179
+ if (path.scope.hasBinding(local))
180
+ continue;
181
+ deferredDecls.push(t.variableDeclaration('const', [
182
+ t.variableDeclarator(t.identifier(local), t.callExpression(t.identifier('deferred'), [
183
+ t.arrowFunctionExpression([], t.callExpression(t.import(), [t.stringLiteral(entry.module)])),
184
+ t.objectExpression([
185
+ t.objectProperty(t.identifier('strategy'), strategyLiteral(entry.strategy)),
186
+ t.objectProperty(t.identifier('export'), t.stringLiteral(entry.export)),
187
+ ]),
188
+ ])),
189
+ ]));
190
+ }
191
+ if (deferredDecls.length > 0 && !path.scope.hasBinding('deferred')) {
192
+ imports.push(t.importDeclaration([t.importSpecifier(t.identifier('deferred'), t.identifier('deferred'))], t.stringLiteral(state.libModule ?? '@fluixi/core')));
193
+ }
194
+ }
195
+ if (state.resolvedComponents.size > 0) {
196
+ const byModule = new Map();
197
+ for (const [local, resolved] of state.resolvedComponents) {
198
+ if (path.scope.hasBinding(local))
199
+ continue;
200
+ const list = byModule.get(resolved.module) ?? [];
201
+ list.push({ local, exported: resolved.export });
202
+ byModule.set(resolved.module, list);
203
+ }
204
+ for (const [mod, entries] of byModule) {
205
+ imports.push(t.importDeclaration(entries.map(({ local, exported }) => exported === 'default'
206
+ ? t.importDefaultSpecifier(t.identifier(local))
207
+ : t.importSpecifier(t.identifier(local), t.identifier(exported))), t.stringLiteral(mod)));
208
+ }
209
+ }
210
+ // Add event delegation setup if needed
211
+ if (delegateEvents && state.delegatedEvents.size > 0) {
212
+ imports.push(t.importDeclaration([
213
+ t.importSpecifier(t.identifier('delegateEvents'), t.identifier('delegateEvents')),
214
+ ], t.stringLiteral(signalModule)));
215
+ // Add delegateEvents call at top of program
216
+ const eventsArray = t.arrayExpression(Array.from(state.delegatedEvents).map((event) => t.stringLiteral(event)));
217
+ const delegateCall = t.expressionStatement(t.callExpression(t.identifier('delegateEvents'), [eventsArray]));
218
+ path.node.body.unshift(delegateCall);
219
+ }
220
+ // Before imports are unshifted, so the
221
+ // final order is: imports, deferred bindings, everything else.
222
+ if (deferredDecls.length > 0) {
223
+ path.node.body.unshift(...deferredDecls);
224
+ }
225
+ // Static templates, parsed once per module rather than per instance.
226
+ if (state.staticTemplates.size > 0) {
227
+ const decls = [];
228
+ for (const [key, name] of state.staticTemplates) {
229
+ const svg = key.startsWith('svg:');
230
+ const html = svg ? key.slice(4) : key;
231
+ decls.push(t.variableDeclaration('const', [
232
+ t.variableDeclarator(t.identifier(name),
233
+ // Just the HTML. Parsing is deferred to `templateNode`, which
234
+ // knows whether this is a server render, a hydration or a fresh
235
+ // client render: a module-scope `createTemplate` would touch
236
+ // `document` at import time and break SSR outright.
237
+ t.stringLiteral(html)),
238
+ ]));
239
+ }
240
+ path.node.body.unshift(...decls);
241
+ }
242
+ }
243
+ // Add all imports at the top, deduped. When a file mixes inline JSX
244
+ // and html`` (format 'both'), the two injection paths can each emit an
245
+ // import for the same module/name: merge per-module and drop repeated
246
+ // specifiers so we never redeclare a binding.
247
+ if (imports.length > 0) {
248
+ path.node.body.unshift(...dedupeImports(imports));
249
+ }
250
+ },
251
+ },
252
+ // Both JSX forms take the one path: build IR, run the imperative backend,
253
+ // splice the emitted code back in. It handles the whole subtree, so nothing
254
+ // below this node needs its own visitor.
255
+ JSXElement(path, state) {
256
+ state.hasJSX = true;
257
+ path.replaceWith(emitViaIR(path.node, state));
258
+ },
259
+ JSXFragment(path, state) {
260
+ state.hasJSX = true;
261
+ path.replaceWith(emitViaIR(path.node, state));
262
+ },
263
+ TaggedTemplateExpression(path, state) {
264
+ // `html`...`` and `svg`...`` lower through the same IR and the same
265
+ // backend as JSX: one pipeline, two authoring syntaxes.
266
+ if (compileLit &&
267
+ t.isIdentifier(path.node.tag) &&
268
+ (path.node.tag.name === litTag || path.node.tag.name === 'svg')) {
269
+ path.replaceWith(emitLitViaIR(path.node, state));
270
+ }
271
+ },
272
+ },
273
+ };
274
+ });
275
+ // ============================================================================
276
+ // JSX Transformation
277
+ // ============================================================================
278
+ /**
279
+ * Two usages of one component in the same file share a binding, so their strategies
280
+ * have to agree. The earlier-loading one wins: the module is in whichever chunk loads
281
+ * first regardless, so deferring the other buys nothing, the same reasoning as the
282
+ * collapse pass, applied within a file.
283
+ */
284
+ /**
285
+ * What to do with JSX or a template found inside an expression: resolve its
286
+ * component references and record what the module needs, exactly as the
287
+ * top-level node gets. The IR itself is emitted and dropped by the walk, so
288
+ * anything not taken off it here is lost: which is how `{(b) => <Input/>}`
289
+ * came out referring to a component nothing imported.
290
+ */
291
+ function nestedHook(state) {
292
+ return (ir) => {
293
+ resolveComponentSources(ir, {
294
+ resolver: state.componentResolver,
295
+ modules: {
296
+ controlFlowModule: state.controlFlowModule,
297
+ coreModule: state.libModule,
298
+ routerModule: state.routerModule,
299
+ },
300
+ strategyFor: (node) => node.load,
301
+ });
302
+ collapseStrategies(ir);
303
+ collectRuntimeNeeds(ir, state);
304
+ };
305
+ }
306
+ /** The strategy as an object literal the runtime can read. */
307
+ function strategyLiteral(strategy) {
308
+ const props = [
309
+ t.objectProperty(t.identifier('kind'), t.stringLiteral(strategy.kind)),
310
+ ];
311
+ if (strategy.kind === 'visible' && strategy.rootMargin) {
312
+ props.push(t.objectProperty(t.identifier('rootMargin'), t.stringLiteral(strategy.rootMargin)));
313
+ }
314
+ if (strategy.kind === 'interaction') {
315
+ props.push(t.objectProperty(t.identifier('events'), t.arrayExpression(strategy.events.map((e) => t.stringLiteral(e)))));
316
+ }
317
+ if (strategy.kind === 'media') {
318
+ props.push(t.objectProperty(t.identifier('query'), t.stringLiteral(strategy.query)));
319
+ }
320
+ return t.objectExpression(props);
321
+ }
322
+ /**
323
+ * Hoist the backend's static templates to module scope and rewrite the emitted code to
324
+ * reference them. Deduped by HTML, so a subtree repeated in a file is parsed once.
325
+ */
326
+ function adoptTemplates(code, templates, state) {
327
+ if (!templates || templates.length === 0)
328
+ return code;
329
+ const rename = new Map();
330
+ for (const tpl of templates) {
331
+ const key = `${tpl.svg ? 'svg:' : ''}${tpl.html}`;
332
+ let name = state.staticTemplates.get(key);
333
+ if (!name) {
334
+ name = `_tmpl$${state.staticTemplates.size}`;
335
+ state.staticTemplates.set(key, name);
336
+ }
337
+ rename.set(tpl.id, name);
338
+ }
339
+ // One pass, not one replacement per id. The backend's local ids and the hoisted names
340
+ // share the `_tmpl$N` namespace, so replacing them in sequence can rewrite a name that
341
+ // an earlier replacement just produced: which silently points a template at another
342
+ // template's HTML.
343
+ return code.replace(/_tmpl\$\d+/g, (id) => rename.get(id) ?? id);
344
+ }
345
+ function emitViaIR(node, state) {
346
+ // `used` collects runtime symbols emitted for JSX nested inside expressions
347
+ // (render-props, ternaries, fallback={<p/>}, …) so we import them too.
348
+ const used = new Set();
349
+ const ir = buildIR(node, used, nestedHook(state));
350
+ // The same passes the lit path runs. Without them `codegen: 'ir'` silently skipped
351
+ // resolution entirely: a resolved tag compiled to a reference with no import.
352
+ resolveComponentSources(ir, {
353
+ resolver: state.componentResolver,
354
+ modules: {
355
+ controlFlowModule: state.controlFlowModule,
356
+ coreModule: state.libModule,
357
+ routerModule: state.routerModule,
358
+ },
359
+ strategyFor: (n) => n.load,
360
+ });
361
+ collapseStrategies(ir);
362
+ const { code, imports, templates } = imperativeBackend.emit(ir, { templateClone: state.templateClone, partialTemplates: state.partialTemplates });
363
+ for (const s of imports)
364
+ used.add(s);
365
+ for (const s of used)
366
+ state.irImports.add(s);
367
+ collectRuntimeNeeds(ir, state);
368
+ const adopted = adoptTemplates(code, templates, state);
369
+ return template.expression(adopted, { placeholderPattern: false, plugins: ['typescript'] })();
370
+ }
371
+ /**
372
+ * Merge injected import declarations so no module/name pair is imported twice.
373
+ * Named imports from the same module collapse into one declaration with unique
374
+ * specifiers; anything non-trivial (default/namespace/side-effect) passes through
375
+ * untouched. Order is preserved by first appearance.
376
+ */
377
+ function dedupeImports(imports) {
378
+ const byModule = new Map();
379
+ const out = [];
380
+ for (const decl of imports) {
381
+ const source = decl.source.value;
382
+ const allNamed = decl.specifiers.every((s) => t.isImportSpecifier(s));
383
+ if (!allNamed) {
384
+ out.push(decl);
385
+ continue;
386
+ }
387
+ const existing = byModule.get(source);
388
+ if (!existing) {
389
+ const names = new Set(decl.specifiers.map((s) => (s.local.name)));
390
+ byModule.set(source, { decl, names });
391
+ out.push(decl);
392
+ continue;
393
+ }
394
+ for (const s of decl.specifiers) {
395
+ if (!existing.names.has(s.local.name)) {
396
+ existing.names.add(s.local.name);
397
+ existing.decl.specifiers.push(s);
398
+ }
399
+ }
400
+ }
401
+ return out;
402
+ }
403
+ /**
404
+ * Lit-pipeline path: build IR from a `` html`...` `` template, emit through the
405
+ * imperative backend, reparse. Mirrors emitViaIR, the whole point is that lit
406
+ * and JSX share one IR + one backend. Collects runtime imports and the delegated
407
+ * events used so Program.exit wires them.
408
+ */
409
+ function emitLitViaIR(node, state) {
410
+ const used = new Set();
411
+ const svg = t.isIdentifier(node.tag) && node.tag.name === 'svg';
412
+ const ir = buildIRFromLit(node, used, { svg, onNested: nestedHook(state) });
413
+ // Resolve pass: annotate each component reference with where it comes from, before
414
+ // codegen. Bindings are not checked here: Program.exit knows the whole file's scope
415
+ // and drops any injected import whose name turns out to be bound.
416
+ resolveComponentSources(ir, {
417
+ resolver: state.componentResolver,
418
+ modules: {
419
+ controlFlowModule: state.controlFlowModule,
420
+ coreModule: state.libModule,
421
+ routerModule: state.routerModule,
422
+ },
423
+ strategyFor: (node) => node.load,
424
+ });
425
+ // Demote deferred references whose module is already in this chunk: a lazy boundary
426
+ // around something already present adds a wrapper, a promise and a re-render to
427
+ // fetch nothing.
428
+ collapseStrategies(ir);
429
+ const { code, imports, templates } = imperativeBackend.emit(ir, { templateClone: state.templateClone, partialTemplates: state.partialTemplates });
430
+ for (const s of imports)
431
+ used.add(s);
432
+ for (const s of used)
433
+ state.irImports.add(s);
434
+ collectRuntimeNeeds(ir, state);
435
+ state.hasLitIR = true;
436
+ // The IR is emitted as a string and reparsed, so the resulting nodes carry no
437
+ // position: which is why compiled html`` had no source map at all. Give every
438
+ // generated node the template's own location so a breakpoint or stack frame
439
+ // lands on the template instead of nowhere.
440
+ const expression = template.expression(adoptTemplates(code, templates, state), {
441
+ placeholderPattern: false,
442
+ plugins: ['typescript'],
443
+ })();
444
+ inheritLocation(expression, node);
445
+ return expression;
446
+ }
447
+ /**
448
+ * Copy a source position onto generated nodes that lack one.
449
+ *
450
+ * Coarse by construction: everything lowered from one template maps back to that
451
+ * template. Per-hole precision would mean carrying spans through the emit, which
452
+ * the string round-trip discards.
453
+ */
454
+ function inheritLocation(target, source) {
455
+ if (!source.loc)
456
+ return;
457
+ const seen = new Set();
458
+ const walk = (node) => {
459
+ if (seen.has(node))
460
+ return;
461
+ seen.add(node);
462
+ if (!node.loc) {
463
+ node.loc = source.loc;
464
+ node.start = source.start;
465
+ node.end = source.end;
466
+ }
467
+ for (const key of t.VISITOR_KEYS[node.type] ?? []) {
468
+ const child = node[key];
469
+ if (Array.isArray(child)) {
470
+ for (const c of child)
471
+ if (c && typeof c === 'object' && 'type' in c)
472
+ walk(c);
473
+ }
474
+ else if (child && typeof child === 'object' && 'type' in child) {
475
+ walk(child);
476
+ }
477
+ }
478
+ };
479
+ walk(target);
480
+ }
481
+ /**
482
+ * Walk the lit IR for things Program.exit wires: delegated events (-> the
483
+ * delegateEvents([...]) call) and control-flow component names used as tags
484
+ * (-> auto-import from the control-flow module, like the inline JSX path).
485
+ */
486
+ function collectRuntimeNeeds(node, state) {
487
+ if (node.kind === 'component' && node.source) {
488
+ // Provenance was decided by the resolve pass and recorded on the node; this only
489
+ // translates it into the two things Program.exit wires. Reading it here rather
490
+ // than re-resolving is the point: one decision, so import injection and every
491
+ // later pass cannot disagree.
492
+ if (node.source.origin === 'builtin') {
493
+ state.controlFlowComponents.add(node.name);
494
+ state.needsControlFlowImport = true;
495
+ }
496
+ else if (isDeferred(node.source.loading) && !state.resolvedComponents.has(node.name)) {
497
+ state.deferredComponents.set(node.name, {
498
+ module: node.source.module,
499
+ export: node.source.export,
500
+ strategy: node.source.loading,
501
+ });
502
+ }
503
+ else {
504
+ state.deferredComponents.delete(node.name);
505
+ state.resolvedComponents.set(node.name, {
506
+ module: node.source.module,
507
+ export: node.source.export,
508
+ });
509
+ }
510
+ }
511
+ if (node.kind === 'element' || node.kind === 'component' || node.kind === 'control') {
512
+ if ('props' in node) {
513
+ for (const p of node.props) {
514
+ if (p.kind === 'event' && p.event?.delegated)
515
+ state.delegatedEvents.add(p.event.name);
516
+ }
517
+ }
518
+ }
519
+ if ('children' in node && node.children) {
520
+ for (const c of node.children)
521
+ collectRuntimeNeeds(c, state);
522
+ }
523
+ }