@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,613 @@
1
+ /**
2
+ * Compile `` html`...` `` / `` svg`...` `` templates in a module.
3
+ *
4
+ * We own the transform here rather than running as a plugin inside someone
5
+ * else's: parse once, find the templates, splice the emitted code over their
6
+ * spans with magic-string. Holes are source slices, so nothing is reprinted and
7
+ * the output keeps the author's formatting; the map comes out of the splice.
8
+ */
9
+ import { parse } from '@babel/parser';
10
+ import MagicString from 'magic-string';
11
+ import { compilePieces } from '../lower/compile-template.js';
12
+ import { isDeferred } from '../resolve/load-directive.js';
13
+ import { isReactiveShape } from '../analyze/reactive-expr.js';
14
+ import { shapeOf } from '../analyze/expr-shape.js';
15
+ import { buildJsxIR } from '../lower/jsx.js';
16
+ import { analyzeStatic } from '../optimize/analyze-static.js';
17
+ import { collapseStrategies } from '../optimize/collapse-strategies.js';
18
+ import { resolveComponentSources } from '../resolve/resolve-ir.js';
19
+ import { createComponentResolver } from '../resolve/component-resolver.js';
20
+ import { imperativeBackend } from '../codegen/backends/imperative.js';
21
+ import { moduleBindings } from './bindings.js';
22
+ import { collectPropsEdits } from './props.js';
23
+ import { avoidBindingClashes, collectIntrinsicEdits, intrinsicImports } from './intrinsics.js';
24
+ import { collectSourceEdits, mayHaveSourceMarks } from './source-locations.js';
25
+ import { collectStyles, finishShadowStyles } from './styles.js';
26
+ import { mayHaveIntrinsic, tagIntrinsics } from '../analyze/intrinsics.js';
27
+ import { declaredNames } from '../analyze/reactive-bindings.js';
28
+ import { DOM_CONTROL_FLOW, CORE_CONTROL_FLOW, ROUTER_COMPONENTS } from '../resolve/builtins.js';
29
+ // Which builtin comes from which module is published by resolve/builtins.ts ,
30
+ // the same lists the resolve pass and the TS plugin read. Restating them here is
31
+ // how `Await` ended up imported from the dom runtime, which does not export it.
32
+ const CONTROL_FLOW = new Set(DOM_CONTROL_FLOW);
33
+ const CORE_ONLY = new Set(CORE_CONTROL_FLOW);
34
+ const ROUTER = new Set(ROUTER_COMPONENTS);
35
+ const REACTIVE = new Set([
36
+ 'createMemo', 'createEffect', 'createRenderEffect', 'createRoot',
37
+ 'createSignal', 'onCleanup', 'batch', 'untrack',
38
+ ]);
39
+ /** Depth-first walk, carrying the parent and the field the node came from. */
40
+ function walk(node, visit, parent = null, key = '') {
41
+ if (!node || typeof node !== 'object')
42
+ return;
43
+ if (Array.isArray(node)) {
44
+ for (const child of node)
45
+ walk(child, visit, parent, key);
46
+ return;
47
+ }
48
+ if (typeof node.type === 'string')
49
+ visit(node, parent, key);
50
+ for (const k of Object.keys(node)) {
51
+ if (k === 'loc' || k === 'leadingComments' || k === 'trailingComments')
52
+ continue;
53
+ walk(node[k], visit, typeof node.type === 'string' ? node : parent, k);
54
+ }
55
+ }
56
+ const isJsx = (node) => node.type === 'JSXElement' || node.type === 'JSXFragment';
57
+ /**
58
+ * A JSX node the transform compiles on its own.
59
+ *
60
+ * A direct child: `<li>` in `<ul><li/></ul>`: is not one: the builder recurses
61
+ * into it, and compiling it separately would splice its output into the middle of
62
+ * its parent's. JSX reached through an expression or an attribute value *is* one,
63
+ * because the builder asks for that expression as source text and needs it
64
+ * already compiled.
65
+ */
66
+ function isJsxRoot(node, parent, key) {
67
+ if (!isJsx(node))
68
+ return false;
69
+ return !(parent && isJsx(parent) && key === 'children');
70
+ }
71
+ function isTemplateTag(node, tags) {
72
+ return node.type === 'TaggedTemplateExpression' && node.tag?.type === 'Identifier' && tags.has(node.tag.name);
73
+ }
74
+ /** The edits in `list` that no other edit in `list` contains. */
75
+ function outermost(list) {
76
+ return list.filter((e) => !list.some((o) => o !== e &&
77
+ o.start <= e.start &&
78
+ o.end >= e.end &&
79
+ o.end - o.start > e.end - e.start &&
80
+ // An insertion at either edge of the container sits beside it, not inside it, so
81
+ // it is not something the container's code already folded in. A statement can
82
+ // start where its callee starts (`$effect(…)`) and a declarator can end where its
83
+ // JSX ends (`const Row = () => <b/>`).
84
+ !(e.start === e.end && (e.start === o.start || e.start === o.end))));
85
+ }
86
+ /**
87
+ * Apply edits to a slice of the source.
88
+ *
89
+ * Only the outermost ones: a contained edit was already folded into its
90
+ * container's emitted code, and applying both would splice it twice, the second
91
+ * write landing at an offset the first has already moved.
92
+ */
93
+ function slice(code, start, end, edits) {
94
+ const inside = outermost(edits.filter((e) => e.start >= start && e.end <= end))
95
+ .sort((a, b) => b.start - a.start);
96
+ let out = code.slice(start, end);
97
+ for (const e of inside)
98
+ out = out.slice(0, e.start - start) + e.code + out.slice(e.end - start);
99
+ return out;
100
+ }
101
+ /** Describe one `${…}` for the lowering, substituting any template already compiled inside it. */
102
+ function bindHole(code, expr, edits, keepPosition = false) {
103
+ const at = keepPosition ? expr.loc?.start : undefined;
104
+ const hole = {
105
+ code: slice(code, expr.start, expr.end, edits),
106
+ reactive: isReactiveShape(shapeOf(expr)),
107
+ // Where the `${…}` is, so a binding made from it can say so. The source map cannot:
108
+ // the whole template is one overwrite. Only when source locations were asked for.
109
+ ...(at ? { at: { line: at.line, column: at.column } } : {}),
110
+ };
111
+ if (expr.type === 'ArrowFunctionExpression' && expr.body?.type !== 'BlockStatement') {
112
+ hole.arrow = {
113
+ params: expr.params.map((p) => slice(code, p.start, p.end, edits)),
114
+ body: slice(code, expr.body.start, expr.body.end, edits),
115
+ bodyReactive: isReactiveShape(shapeOf(expr.body)),
116
+ };
117
+ }
118
+ if (expr.type === 'ObjectExpression')
119
+ hole.object = true;
120
+ return hole;
121
+ }
122
+ /**
123
+ * What the emitted code needs beyond the runtime symbols the backend reports:
124
+ * component references the resolve pass annotated, and the events it wants
125
+ * delegated. Both live on the IR rather than in the emit result.
126
+ */
127
+ /**
128
+ * Static templates hoisted to module scope, keyed by their HTML so two components
129
+ * with the same markup share one string.
130
+ *
131
+ * Hoisted names deliberately do not use the backend's `_tmpl$N` namespace. Roots
132
+ * are emitted innermost first and spliced into their parent, so by the time the
133
+ * outer one is renamed its code already contains hoisted names, and with a
134
+ * shared prefix the rename rewrites those too, pointing two templates at one
135
+ * string. A separate prefix makes the two impossible to confuse.
136
+ */
137
+ function adoptTemplates(code, templates, hoisted) {
138
+ if (!templates || templates.length === 0)
139
+ return code;
140
+ const rename = new Map();
141
+ for (const tpl of templates) {
142
+ const key = `${tpl.svg ? 'svg:' : ''}${tpl.html}`;
143
+ let name = hoisted.get(key);
144
+ if (!name) {
145
+ name = `_fxTmpl$${hoisted.size}`;
146
+ hoisted.set(key, name);
147
+ }
148
+ rename.set(tpl.id, name);
149
+ }
150
+ return code.replace(/_tmpl\$\d+/g, (id) => rename.get(id) ?? id);
151
+ }
152
+ function collectNeeds(node, out) {
153
+ if (node.kind === 'component' && node.load && !node.source && isDeferred(node.load)) {
154
+ // One report per name, not one per usage.
155
+ if (!out.ignoredLoad.has(node.name))
156
+ out.ignoredLoad.set(node.name, node.load);
157
+ }
158
+ if (node.kind === 'component' && node.source) {
159
+ const src = node.source;
160
+ if (src.origin === 'builtin')
161
+ out.builtins.add(node.name);
162
+ else if (isDeferred(src.loading) && !out.resolved.has(node.name))
163
+ out.deferred.set(node.name, src);
164
+ else {
165
+ out.deferred.delete(node.name);
166
+ out.resolved.set(node.name, src);
167
+ }
168
+ }
169
+ if ('props' in node && node.props) {
170
+ for (const p of node.props) {
171
+ if (p.kind === 'event' && p.event?.delegated)
172
+ out.delegatedEvents.add(p.event.name);
173
+ }
174
+ }
175
+ if ('children' in node && node.children)
176
+ for (const c of node.children)
177
+ collectNeeds(c, out);
178
+ }
179
+ /**
180
+ * An import this file wrote that we can turn into a deferred binding instead.
181
+ *
182
+ * `load:` on a component the author imported did nothing, because deferring means
183
+ * *creating* the reference as a dynamic import and there was already a static one. Here we
184
+ * take the static one away: remove the specifier, emit the `deferred()` binding, and the
185
+ * chunk splits like any other.
186
+ *
187
+ * Only when the name is used **exclusively as a tag**. JSX element names are
188
+ * `JSXIdentifier`, so a component used only in markup has zero `Identifier` references ,
189
+ * which makes the check exact rather than a guess. Anything else (passed to a function,
190
+ * re-exported, `Chart.displayName`) keeps its import and its warning: replacing the real
191
+ * binding with a lazy wrapper would change what those sites see.
192
+ */
193
+ function deferrableImport(program, name) {
194
+ for (const statement of program.body ?? []) {
195
+ if (statement.type !== 'ImportDeclaration')
196
+ continue;
197
+ for (const specifier of statement.specifiers ?? []) {
198
+ if (specifier.local?.name !== name)
199
+ continue;
200
+ // A namespace import is the whole module; there is no single export to defer.
201
+ if (specifier.type === 'ImportNamespaceSpecifier')
202
+ return undefined;
203
+ const exported = specifier.type === 'ImportDefaultSpecifier'
204
+ ? 'default'
205
+ : (specifier.imported?.name ?? specifier.imported?.value);
206
+ return { module: statement.source.value, export: exported, declaration: statement, specifier };
207
+ }
208
+ }
209
+ return undefined;
210
+ }
211
+ /** Whether `name` came in through `import * as name`, which has no single export. */
212
+ function hasNamespaceImport(program, name) {
213
+ for (const statement of program.body ?? []) {
214
+ if (statement.type !== 'ImportDeclaration')
215
+ continue;
216
+ for (const specifier of statement.specifiers ?? []) {
217
+ if (specifier.type === 'ImportNamespaceSpecifier' && specifier.local?.name === name)
218
+ return true;
219
+ }
220
+ }
221
+ return false;
222
+ }
223
+ /** Whether `name` is referenced as a value anywhere, a tag alone does not count. */
224
+ function usedAsValue(program, name, specifier) {
225
+ let seen = false;
226
+ walk(program, (node) => {
227
+ if (seen || node.type !== 'Identifier' || node.name !== name)
228
+ return;
229
+ // The import specifier's own local binding is the declaration, not a use.
230
+ if (node.start === specifier.local?.start && node.end === specifier.local?.end)
231
+ return;
232
+ seen = true;
233
+ });
234
+ return seen;
235
+ }
236
+ /** The strategy object a `deferred()` binding is given. */
237
+ function strategyLiteral(s) {
238
+ const parts = [`kind: ${JSON.stringify(s.kind)}`];
239
+ if (s.kind === 'visible' && s.rootMargin)
240
+ parts.push(`rootMargin: ${JSON.stringify(s.rootMargin)}`);
241
+ if (s.kind === 'interaction')
242
+ parts.push(`events: ${JSON.stringify(s.events)}`);
243
+ if (s.kind === 'media')
244
+ parts.push(`query: ${JSON.stringify(s.query)}`);
245
+ return `{ ${parts.join(', ')} }`;
246
+ }
247
+ /**
248
+ * Offset → line and column, built once per module.
249
+ *
250
+ * The parser reports offsets into this file, since the pieces it is given carry the
251
+ * positions Babel recorded for the quasis.
252
+ */
253
+ function positionsIn(code) {
254
+ const starts = [0];
255
+ for (let i = 0; i < code.length; i++)
256
+ if (code.charCodeAt(i) === 10)
257
+ starts.push(i + 1);
258
+ return (offset) => {
259
+ let low = 0;
260
+ let high = starts.length - 1;
261
+ while (low < high) {
262
+ const mid = (low + high + 1) >> 1;
263
+ if (starts[mid] <= offset)
264
+ low = mid;
265
+ else
266
+ high = mid - 1;
267
+ }
268
+ return { line: low + 1, column: offset - starts[low] };
269
+ };
270
+ }
271
+ /** Quasis and holes as position-carrying parser pieces. */
272
+ function toPieces(node) {
273
+ const pieces = [];
274
+ node.quasi.quasis.forEach((q, i) => {
275
+ pieces.push({ kind: 'static', text: q.value.cooked ?? q.value.raw, start: q.start });
276
+ if (i < node.quasi.expressions.length) {
277
+ const e = node.quasi.expressions[i];
278
+ pieces.push({ kind: 'hole', index: i, start: e.start, end: e.end });
279
+ }
280
+ });
281
+ return pieces;
282
+ }
283
+ /**
284
+ * Compile every template in `code`. Returns null when the module has none, so a
285
+ * bundler can skip the file untouched.
286
+ */
287
+ /** No path module here: this runs in the browser as well as in a bundler. */
288
+ function relativeToRoot(id, root) {
289
+ if (!root)
290
+ return id;
291
+ const base = root.endsWith('/') ? root : `${root}/`;
292
+ return id.startsWith(base) ? id.slice(base.length) : id;
293
+ }
294
+ export function transformTemplates(code, id, options = {}) {
295
+ const litTag = options.litTag ?? 'html';
296
+ const format = options.format ?? 'both';
297
+ const wantsLit = format !== 'jsx';
298
+ const wantsJsx = format !== 'lit';
299
+ const mayHaveTemplate = wantsLit && (code.includes(`${litTag}\``) || code.includes('svg`'));
300
+ // A module can be nothing but `$signal` calls, so the skip check has to look for those
301
+ // too or the file is never parsed.
302
+ const wantsIntrinsics = options.intrinsics !== false && mayHaveIntrinsic(code);
303
+ // A module can be nothing but reactive calls and still need marking, so the skip check
304
+ // has to look for those too when source locations are wanted.
305
+ const wantsSources = options.sourceLocations === true && mayHaveSourceMarks(code);
306
+ // What a mark calls this file. The declaration marks and the usage marks are read back by
307
+ // name, so both passes have to spell it the same way.
308
+ const sourceName = relativeToRoot(id, options.sourceRoot);
309
+ // A module of nothing but css`` values still has literals to compile.
310
+ const wantsStyles = options.styles !== undefined && (code.includes('<style') || code.includes('css`'));
311
+ if (!mayHaveTemplate && !(wantsJsx && code.includes('<')) && !wantsIntrinsics && !wantsSources && !wantsStyles)
312
+ return null;
313
+ const ast = parse(code, {
314
+ sourceType: 'module',
315
+ plugins: ['jsx', 'typescript'],
316
+ errorRecovery: true,
317
+ });
318
+ // `html`, `svg`, and in an app `$html`/`$svg`/`$css`, which need no import.
319
+ const intrinsicTags = options.intrinsics === false ? new Map() : tagIntrinsics(moduleBindings(ast.program));
320
+ const templateTags = new Map([[litTag, 'html'], ['svg', 'svg']]);
321
+ for (const [name, tag] of intrinsicTags)
322
+ if (tag !== 'css')
323
+ templateTags.set(name, tag);
324
+ const cssTags = [...intrinsicTags].filter(([, tag]) => tag === 'css').map(([name]) => name);
325
+ const found = [];
326
+ walk(ast.program, (n, parent, key) => {
327
+ if (wantsLit && isTemplateTag(n, templateTags))
328
+ found.push(n);
329
+ else if (wantsJsx && isJsxRoot(n, parent, key))
330
+ found.push(n);
331
+ });
332
+ // Props first: holes are sliced out of the function body, so the rewrite has to be
333
+ // in the edit list before any template is compiled.
334
+ const props = options.propsDestructure === false
335
+ ? { edits: [], diagnostics: [], used: new Set() }
336
+ : collectPropsEdits(ast.program, code, { runtimeModule: options.runtimeModule });
337
+ // `$signal(…)` → `createSignal(…)`. Same edit list, so the callee is already rewritten
338
+ // by the time a template slices a hole containing one.
339
+ const intrinsics = options.intrinsics === false
340
+ ? { edits: [], diagnostics: [], imports: new Map() }
341
+ : collectIntrinsicEdits(ast.program);
342
+ if (intrinsics.edits.length)
343
+ avoidBindingClashes(ast.program, intrinsics, moduleBindings(ast.program));
344
+ // Where each reactive primitive was created, for the devtools graph. Off unless asked
345
+ // for: it is development tooling, and production should carry none of it.
346
+ // `wantsSources` only decides whether a module with nothing else in it is worth parsing.
347
+ // Past that point the ast exists, and a component declaration is worth marking too.
348
+ const sources = options.sourceLocations === true
349
+ ? collectSourceEdits(ast.program, sourceName)
350
+ : { edits: [], marked: 0 };
351
+ // `<style>` blocks, processed by the host's CSS engine and scoped. Only when the host asked:
352
+ // without a processor a `<style>` stays an ordinary element.
353
+ const styles = wantsStyles ? collectStyles(ast.program, sourceName, options.styles, { html: templateTags, css: cssTags }, { id, code }) : undefined;
354
+ // Nothing to emit still returns a result when there's something to say, a shadowed
355
+ // intrinsic is a diagnostic even though the code comes back untouched.
356
+ const silent = found.length === 0 &&
357
+ props.edits.length === 0 &&
358
+ sources.edits.length === 0 &&
359
+ intrinsics.edits.length === 0 &&
360
+ intrinsics.diagnostics.length === 0 &&
361
+ props.diagnostics.length === 0 &&
362
+ !styles?.edits.length &&
363
+ !styles?.diagnostics.length;
364
+ if (silent)
365
+ return null;
366
+ // Innermost first, so a nested template is already compiled by the time the
367
+ // hole containing it is sliced.
368
+ found.sort((a, b) => b.start - a.start || a.end - b.end);
369
+ // Seeded with whatever the props rewrite needs, so `splitProps` joins the prologue
370
+ // the templates build rather than getting an import of its own.
371
+ const used = new Set(props.used);
372
+ const needs = {
373
+ builtins: new Set(), resolved: new Map(), deferred: new Map(), delegatedEvents: new Set(),
374
+ ignoredLoad: new Map(), unresolved: new Set(),
375
+ declared: declaredNames(ast.program),
376
+ };
377
+ const hoisted = new Map();
378
+ const edits = [...props.edits, ...intrinsics.edits, ...sources.edits, ...(styles?.edits ?? [])];
379
+ for (const node of found) {
380
+ // A root inside another root's hole is emitted without partial templates,
381
+ // matching what the plugin does for nested emits. Marker layout has to agree
382
+ // between server and client, so this is not the place to change it.
383
+ const nested = found.some((o) => o !== node && o.start <= node.start && o.end >= node.end && o.end - o.start > node.end - node.start);
384
+ if (isJsx(node)) {
385
+ edits.push({ start: node.start, end: node.end, code: compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceName, styles) });
386
+ continue;
387
+ }
388
+ const holes = node.quasi.expressions.map((e) => bindHole(code, e, edits, options.sourceLocations === true));
389
+ const scope = styles?.scopes.find((s) => node.start >= s.start && node.end <= s.end)?.value;
390
+ const owner = styles?.templateOwners.get(node.start);
391
+ const templateStyles = options.styles && styles && (scope || owner)
392
+ ? {
393
+ options: options.styles,
394
+ ...(scope ? { scope } : {}),
395
+ ...(owner ? { owner } : {}),
396
+ line: node.loc?.start.line ?? 0,
397
+ column: node.loc?.start.column ?? 0,
398
+ styles: styles.styles,
399
+ diagnostics: styles.diagnostics,
400
+ }
401
+ : undefined;
402
+ const result = compilePieces(toPieces(node), holes, {
403
+ ...options,
404
+ ...(templateStyles ? { templateStyles } : {}),
405
+ hoistTemplates: true,
406
+ // A tag inside a template gets the same mark a JSX tag does. Without one, the nodes it
407
+ // makes are the only ones in the module carrying no mark, and they take whichever was
408
+ // left behind by the last declaration that did.
409
+ ...(options.sourceLocations === true
410
+ ? { sourceFile: sourceName, positionAt: positionsIn(code) }
411
+ : {}),
412
+ // Same defaults as the plugin, so moving a build over is not a rendering change.
413
+ templateClone: options.templateClone ?? true,
414
+ partialTemplates: nested ? false : options.partialTemplates ?? true,
415
+ svg: templateTags.get(node.tag.name) === 'svg',
416
+ });
417
+ for (const symbol of result.imports)
418
+ used.add(symbol);
419
+ collectNeeds(result.ir, needs);
420
+ edits.push({ start: node.start, end: node.end, code: adoptTemplates(result.code, result.templates, hoisted) });
421
+ }
422
+ // A shadow-DOM element's blocks and css`` values go into its `static styles`, once every
423
+ // template has been compiled and its blocks numbered.
424
+ if (styles && options.styles)
425
+ edits.push(...finishShadowStyles(styles, code, id));
426
+ const out = new MagicString(code);
427
+ // Same reason as in `slice`: a nested edit is already inside its parent's code.
428
+ for (const e of outermost(edits)) {
429
+ // A zero-width edit is an insertion (the splitProps call), not a replacement.
430
+ if (e.start === e.end)
431
+ out.appendLeft(e.start, e.code);
432
+ else
433
+ out.overwrite(e.start, e.end, e.code);
434
+ }
435
+ const bound = moduleBindings(ast.program);
436
+ // A `load:` on an import the author wrote: take the import away and let the prologue
437
+ // emit the deferred binding, so this spelling and a `resolve` rule produce the same
438
+ // output rather than merely equivalent output.
439
+ const why = new Map();
440
+ for (const [name] of needs.ignoredLoad) {
441
+ if (!bound.has(name))
442
+ why.set(name, 'unsupplied');
443
+ }
444
+ for (const [name, strategy] of [...needs.ignoredLoad]) {
445
+ if (!bound.has(name))
446
+ continue;
447
+ const found = deferrableImport(ast.program, name);
448
+ if (!found) {
449
+ // Bound, but not by an import we can turn into a dynamic one.
450
+ why.set(name, hasNamespaceImport(ast.program, name) ? 'namespace' : 'local');
451
+ continue;
452
+ }
453
+ if (usedAsValue(ast.program, name, found.specifier)) {
454
+ why.set(name, 'value');
455
+ continue;
456
+ }
457
+ const rest = found.declaration.specifiers.filter((sp) => sp !== found.specifier);
458
+ // Take the line's newline with it when the whole statement goes, or the output keeps
459
+ // a blank line exactly where the import used to be.
460
+ const end = rest.length === 0 && code[found.declaration.end] === '\n'
461
+ ? found.declaration.end + 1
462
+ : found.declaration.end;
463
+ out.overwrite(found.declaration.start, end, rest.length === 0
464
+ ? ''
465
+ : `import { ${rest.map((sp) => code.slice(sp.start, sp.end)).join(', ')} } from ` +
466
+ `${JSON.stringify(found.declaration.source.value)};`);
467
+ needs.deferred.set(name, { module: found.module, export: found.export, origin: 'rule', loading: strategy });
468
+ // The prologue skips names the file binds, and this one no longer does.
469
+ bound.delete(name);
470
+ needs.ignoredLoad.delete(name);
471
+ }
472
+ const prologue = buildPrologue(used, needs, hoisted, bound, options);
473
+ if (prologue)
474
+ out.prepend(prologue);
475
+ const runtimeImports = intrinsicImports(intrinsics, bound);
476
+ if (runtimeImports)
477
+ out.prepend(runtimeImports);
478
+ // Each of these has a different fix, so the message has to name the actual reason.
479
+ // Saying "drop the import" to someone whose component is used as a value sends them
480
+ // somewhere that does not work.
481
+ const reasons = {
482
+ value: (name) => `${name} is used as a value here, not only as a tag, so its import has to stay — ` +
483
+ `deferring would hand those references a lazy wrapper instead of the component. ` +
484
+ `Defer it at the point it is rendered, or keep it eager.`,
485
+ namespace: (name) => `${name} came in through a namespace import, which has no single export to defer. ` +
486
+ `Import ${name} by name.`,
487
+ local: (name) => `${name} is declared in this file, so there is no module to load separately. ` +
488
+ `Move it to its own file and import it.`,
489
+ unsupplied: (name) => `nothing supplies ${name}, so there is no import to defer. Import it, or add a ` +
490
+ `\`resolve\` rule for it.`,
491
+ };
492
+ const loadDiagnostics = [...needs.ignoredLoad].map(([name, strategy]) => ({
493
+ name,
494
+ strategy,
495
+ message: `load:${strategy.kind} has no effect on <${name}>: ${reasons[why.get(name) ?? 'unsupplied'](name)}`,
496
+ }));
497
+ const unresolvedDiagnostics = [...needs.unresolved].map((name) => ({
498
+ name,
499
+ message: `<${name}> is not defined: nothing imports it and no \`resolve\` rule supplies it. ` +
500
+ `This compiles to a reference to a name that does not exist, so the component renders ` +
501
+ `nothing. Import ${name}, or add a \`resolve\` rule for it.`,
502
+ }));
503
+ return {
504
+ code: out.toString(),
505
+ map: out.generateMap({ source: id, includeContent: true, hires: true }),
506
+ ...(loadDiagnostics.length ? { loadDiagnostics } : {}),
507
+ ...(unresolvedDiagnostics.length ? { unresolvedDiagnostics } : {}),
508
+ ...(props.diagnostics.length ? { propsDiagnostics: props.diagnostics } : {}),
509
+ ...(intrinsics.diagnostics.length ? { intrinsicDiagnostics: intrinsics.diagnostics } : {}),
510
+ ...(styles?.styles.length ? { styles: styles.styles } : {}),
511
+ ...(styles?.diagnostics.length ? { styleDiagnostics: styles.diagnostics } : {}),
512
+ ...(styles?.literals.length ? { styleLiterals: styles.literals } : {}),
513
+ ...(styles?.references.length ? { styleReferences: styles.references } : {}),
514
+ };
515
+ }
516
+ /**
517
+ * A JSX root, emitted the same way a template is. Expressions are source slices
518
+ * with anything already compiled inside them folded in, so nested JSX and nested
519
+ * `` html`` `` both come along for free.
520
+ */
521
+ function compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceFile, styles) {
522
+ const ir = buildJsxIR(node, {
523
+ code: (n) => slice(code, n.start, n.end, edits),
524
+ used,
525
+ ...(styles?.scopes.length ? { scopes: styles.scopes } : {}),
526
+ ...(styles?.drop.size ? { dropStyles: styles.drop } : {}),
527
+ ...(options.sourceLocations === true && sourceFile ? { sourceFile } : {}),
528
+ });
529
+ analyzeStatic(ir);
530
+ const report = resolveComponentSources(ir, {
531
+ resolver: createComponentResolver(options.resolve ?? []),
532
+ // Without this the pass calls every author-imported component unresolved, and the
533
+ // report is unusable for diagnostics.
534
+ isBound: (name) => needs.declared.has(name),
535
+ modules: {
536
+ controlFlowModule: options.controlFlowModule ?? options.runtimeModule ?? '@fluixi/dom',
537
+ coreModule: options.coreModule ?? '@fluixi/core',
538
+ routerModule: options.routerModule ?? '@fluixi/core/router',
539
+ },
540
+ strategyFor: (n) => n.load,
541
+ });
542
+ for (const name of report.unresolved)
543
+ needs.unresolved.add(name);
544
+ collapseStrategies(ir);
545
+ const emitted = imperativeBackend.emit(ir, {
546
+ templateClone: options.templateClone ?? true,
547
+ partialTemplates: nested ? false : options.partialTemplates ?? true,
548
+ });
549
+ for (const symbol of emitted.imports)
550
+ used.add(symbol);
551
+ collectNeeds(ir, needs);
552
+ return adoptTemplates(emitted.code, emitted.templates, hoisted);
553
+ }
554
+ /**
555
+ * Everything the emitted code needs at the top of the module: runtime imports,
556
+ * imports for components a resolve rule supplied, `deferred()` bindings for the
557
+ * lazy ones, and the delegateEvents call. A name already bound in the file wins,
558
+ * so enabling resolution can never change what an existing module means.
559
+ */
560
+ function buildPrologue(used, needs, hoisted, bound, options) {
561
+ const runtime = options.runtimeModule ?? '@fluixi/dom';
562
+ const core = options.coreModule ?? '@fluixi/core';
563
+ const groups = new Map();
564
+ const add = (mod, name) => groups.set(mod, [...(groups.get(mod) ?? []), name]);
565
+ const wanted = new Set(used);
566
+ for (const name of needs.builtins)
567
+ wanted.add(name);
568
+ if (needs.delegatedEvents.size > 0)
569
+ wanted.add('delegateEvents');
570
+ for (const name of [...wanted].sort()) {
571
+ if (bound.has(name))
572
+ continue;
573
+ if (REACTIVE.has(name))
574
+ add(options.reactiveModule ?? '@fluixi/reactive/signal', name);
575
+ else if (ROUTER.has(name))
576
+ add(options.routerModule ?? '@fluixi/core/router', name);
577
+ else if (CORE_ONLY.has(name))
578
+ add(core, name);
579
+ else if (CONTROL_FLOW.has(name))
580
+ add(options.controlFlowModule ?? runtime, name);
581
+ else
582
+ add(runtime, name);
583
+ }
584
+ for (const [name, src] of needs.resolved) {
585
+ if (!bound.has(name))
586
+ add(src.module, src.export === 'default' ? `default as ${name}` : name === src.export ? name : `${src.export} as ${name}`);
587
+ }
588
+ const lines = [];
589
+ for (const [mod, names] of groups) {
590
+ lines.push(`import { ${names.join(', ')} } from ${JSON.stringify(mod)};`);
591
+ }
592
+ // A literal import() is what lets the bundler split and hash the chunk, so the
593
+ // deferred component is an ordinary binding rather than an integration.
594
+ const lazy = [...needs.deferred].filter(([name]) => !bound.has(name));
595
+ if (lazy.length > 0) {
596
+ if (!bound.has('deferred'))
597
+ lines.push(`import { deferred } from ${JSON.stringify(core)};`);
598
+ for (const [name, src] of lazy) {
599
+ lines.push(`const ${name} = deferred(() => import(${JSON.stringify(src.module)}), ` +
600
+ `{ strategy: ${strategyLiteral(src.loading)}, export: ${JSON.stringify(src.export)} });`);
601
+ }
602
+ }
603
+ // Just the HTML: parsing is deferred to `templateNode`, which knows whether this
604
+ // is a server render, a hydration or a fresh client render. A module-scope
605
+ // `createTemplate` would touch `document` at import time and break SSR.
606
+ for (const [key, name] of hoisted) {
607
+ lines.push(`const ${name} = ${JSON.stringify(key.startsWith('svg:') ? key.slice(4) : key)};`);
608
+ }
609
+ if (needs.delegatedEvents.size > 0) {
610
+ lines.push(`delegateEvents(${JSON.stringify([...needs.delegatedEvents])});`);
611
+ }
612
+ return lines.length > 0 ? lines.join('\n') + '\n' : null;
613
+ }