@llui/compiler 0.3.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 (310) hide show
  1. package/LICENSE +21 -0
  2. package/dist/accessor-resolver.d.ts +58 -0
  3. package/dist/accessor-resolver.d.ts.map +1 -0
  4. package/dist/accessor-resolver.js +119 -0
  5. package/dist/accessor-resolver.js.map +1 -0
  6. package/dist/binding-descriptors.d.ts +105 -0
  7. package/dist/binding-descriptors.d.ts.map +1 -0
  8. package/dist/binding-descriptors.js +340 -0
  9. package/dist/binding-descriptors.js.map +1 -0
  10. package/dist/collect-deps.d.ts +49 -0
  11. package/dist/collect-deps.d.ts.map +1 -0
  12. package/dist/collect-deps.js +444 -0
  13. package/dist/collect-deps.js.map +1 -0
  14. package/dist/compiler-cache.d.ts +20 -0
  15. package/dist/compiler-cache.d.ts.map +1 -0
  16. package/dist/compiler-cache.js +20 -0
  17. package/dist/compiler-cache.js.map +1 -0
  18. package/dist/cross-file-resolver.d.ts +109 -0
  19. package/dist/cross-file-resolver.d.ts.map +1 -0
  20. package/dist/cross-file-resolver.js +530 -0
  21. package/dist/cross-file-resolver.js.map +1 -0
  22. package/dist/cross-file-walker.d.ts +63 -0
  23. package/dist/cross-file-walker.d.ts.map +1 -0
  24. package/dist/cross-file-walker.js +516 -0
  25. package/dist/cross-file-walker.js.map +1 -0
  26. package/dist/diagnostic.d.ts +76 -0
  27. package/dist/diagnostic.d.ts.map +1 -0
  28. package/dist/diagnostic.js +59 -0
  29. package/dist/diagnostic.js.map +1 -0
  30. package/dist/index.d.ts +27 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +37 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/introspection-factory.d.ts +54 -0
  35. package/dist/introspection-factory.d.ts.map +1 -0
  36. package/dist/introspection-factory.js +46 -0
  37. package/dist/introspection-factory.js.map +1 -0
  38. package/dist/manifest.d.ts +144 -0
  39. package/dist/manifest.d.ts.map +1 -0
  40. package/dist/manifest.js +209 -0
  41. package/dist/manifest.js.map +1 -0
  42. package/dist/module.d.ts +222 -0
  43. package/dist/module.d.ts.map +1 -0
  44. package/dist/module.js +256 -0
  45. package/dist/module.js.map +1 -0
  46. package/dist/modules/_element-helpers.d.ts +4 -0
  47. package/dist/modules/_element-helpers.d.ts.map +1 -0
  48. package/dist/modules/_element-helpers.js +138 -0
  49. package/dist/modules/_element-helpers.js.map +1 -0
  50. package/dist/modules/_msg-variants.d.ts +10 -0
  51. package/dist/modules/_msg-variants.d.ts.map +1 -0
  52. package/dist/modules/_msg-variants.js +97 -0
  53. package/dist/modules/_msg-variants.js.map +1 -0
  54. package/dist/modules/_shared.d.ts +16 -0
  55. package/dist/modules/_shared.d.ts.map +1 -0
  56. package/dist/modules/_shared.js +30 -0
  57. package/dist/modules/_shared.js.map +1 -0
  58. package/dist/modules/accessibility.d.ts +3 -0
  59. package/dist/modules/accessibility.d.ts.map +1 -0
  60. package/dist/modules/accessibility.js +82 -0
  61. package/dist/modules/accessibility.js.map +1 -0
  62. package/dist/modules/accessor-side-effect.d.ts +3 -0
  63. package/dist/modules/accessor-side-effect.d.ts.map +1 -0
  64. package/dist/modules/accessor-side-effect.js +113 -0
  65. package/dist/modules/accessor-side-effect.js.map +1 -0
  66. package/dist/modules/agent-emits-drift.d.ts +3 -0
  67. package/dist/modules/agent-emits-drift.d.ts.map +1 -0
  68. package/dist/modules/agent-emits-drift.js +158 -0
  69. package/dist/modules/agent-emits-drift.js.map +1 -0
  70. package/dist/modules/agent-example-on-payload.d.ts +3 -0
  71. package/dist/modules/agent-example-on-payload.d.ts.map +1 -0
  72. package/dist/modules/agent-example-on-payload.js +53 -0
  73. package/dist/modules/agent-example-on-payload.js.map +1 -0
  74. package/dist/modules/agent-exclusive-annotations.d.ts +3 -0
  75. package/dist/modules/agent-exclusive-annotations.d.ts.map +1 -0
  76. package/dist/modules/agent-exclusive-annotations.js +68 -0
  77. package/dist/modules/agent-exclusive-annotations.js.map +1 -0
  78. package/dist/modules/agent-missing-intent.d.ts +3 -0
  79. package/dist/modules/agent-missing-intent.d.ts.map +1 -0
  80. package/dist/modules/agent-missing-intent.js +47 -0
  81. package/dist/modules/agent-missing-intent.js.map +1 -0
  82. package/dist/modules/agent-msg-resolvable.d.ts +3 -0
  83. package/dist/modules/agent-msg-resolvable.d.ts.map +1 -0
  84. package/dist/modules/agent-msg-resolvable.js +161 -0
  85. package/dist/modules/agent-msg-resolvable.js.map +1 -0
  86. package/dist/modules/agent-nonextractable-handler.d.ts +3 -0
  87. package/dist/modules/agent-nonextractable-handler.d.ts.map +1 -0
  88. package/dist/modules/agent-nonextractable-handler.js +127 -0
  89. package/dist/modules/agent-nonextractable-handler.js.map +1 -0
  90. package/dist/modules/agent-optional-field-undocumented.d.ts +3 -0
  91. package/dist/modules/agent-optional-field-undocumented.d.ts.map +1 -0
  92. package/dist/modules/agent-optional-field-undocumented.js +67 -0
  93. package/dist/modules/agent-optional-field-undocumented.js.map +1 -0
  94. package/dist/modules/agent-tagsend-translator-missing.d.ts +3 -0
  95. package/dist/modules/agent-tagsend-translator-missing.d.ts.map +1 -0
  96. package/dist/modules/agent-tagsend-translator-missing.js +58 -0
  97. package/dist/modules/agent-tagsend-translator-missing.js.map +1 -0
  98. package/dist/modules/agent-warning-on-confirm.d.ts +3 -0
  99. package/dist/modules/agent-warning-on-confirm.d.ts.map +1 -0
  100. package/dist/modules/agent-warning-on-confirm.js +46 -0
  101. package/dist/modules/agent-warning-on-confirm.js.map +1 -0
  102. package/dist/modules/async-update.d.ts +3 -0
  103. package/dist/modules/async-update.d.ts.map +1 -0
  104. package/dist/modules/async-update.js +86 -0
  105. package/dist/modules/async-update.js.map +1 -0
  106. package/dist/modules/binding-descriptors.d.ts +4 -0
  107. package/dist/modules/binding-descriptors.d.ts.map +1 -0
  108. package/dist/modules/binding-descriptors.js +48 -0
  109. package/dist/modules/binding-descriptors.js.map +1 -0
  110. package/dist/modules/bitmask-overflow.d.ts +3 -0
  111. package/dist/modules/bitmask-overflow.d.ts.map +1 -0
  112. package/dist/modules/bitmask-overflow.js +152 -0
  113. package/dist/modules/bitmask-overflow.js.map +1 -0
  114. package/dist/modules/compiler-stamp.d.ts +3 -0
  115. package/dist/modules/compiler-stamp.d.ts.map +1 -0
  116. package/dist/modules/compiler-stamp.js +44 -0
  117. package/dist/modules/compiler-stamp.js.map +1 -0
  118. package/dist/modules/component-meta.d.ts +3 -0
  119. package/dist/modules/component-meta.d.ts.map +1 -0
  120. package/dist/modules/component-meta.js +44 -0
  121. package/dist/modules/component-meta.js.map +1 -0
  122. package/dist/modules/controlled-input.d.ts +3 -0
  123. package/dist/modules/controlled-input.d.ts.map +1 -0
  124. package/dist/modules/controlled-input.js +68 -0
  125. package/dist/modules/controlled-input.js.map +1 -0
  126. package/dist/modules/core-synthesis.d.ts +18 -0
  127. package/dist/modules/core-synthesis.d.ts.map +1 -0
  128. package/dist/modules/core-synthesis.js +748 -0
  129. package/dist/modules/core-synthesis.js.map +1 -0
  130. package/dist/modules/direct-state-in-view.d.ts +3 -0
  131. package/dist/modules/direct-state-in-view.d.ts.map +1 -0
  132. package/dist/modules/direct-state-in-view.js +103 -0
  133. package/dist/modules/direct-state-in-view.js.map +1 -0
  134. package/dist/modules/each-closure-violation.d.ts +3 -0
  135. package/dist/modules/each-closure-violation.d.ts.map +1 -0
  136. package/dist/modules/each-closure-violation.js +255 -0
  137. package/dist/modules/each-closure-violation.js.map +1 -0
  138. package/dist/modules/each-memo.d.ts +15 -0
  139. package/dist/modules/each-memo.d.ts.map +1 -0
  140. package/dist/modules/each-memo.js +115 -0
  141. package/dist/modules/each-memo.js.map +1 -0
  142. package/dist/modules/effect-without-handler.d.ts +3 -0
  143. package/dist/modules/effect-without-handler.d.ts.map +1 -0
  144. package/dist/modules/effect-without-handler.js +92 -0
  145. package/dist/modules/effect-without-handler.js.map +1 -0
  146. package/dist/modules/element-rewrite.d.ts +22 -0
  147. package/dist/modules/element-rewrite.d.ts.map +1 -0
  148. package/dist/modules/element-rewrite.js +1017 -0
  149. package/dist/modules/element-rewrite.js.map +1 -0
  150. package/dist/modules/empty-props.d.ts +3 -0
  151. package/dist/modules/empty-props.d.ts.map +1 -0
  152. package/dist/modules/empty-props.js +50 -0
  153. package/dist/modules/empty-props.js.map +1 -0
  154. package/dist/modules/exhaustive-effect-handling.d.ts +3 -0
  155. package/dist/modules/exhaustive-effect-handling.d.ts.map +1 -0
  156. package/dist/modules/exhaustive-effect-handling.js +61 -0
  157. package/dist/modules/exhaustive-effect-handling.js.map +1 -0
  158. package/dist/modules/exhaustive-update.d.ts +3 -0
  159. package/dist/modules/exhaustive-update.d.ts.map +1 -0
  160. package/dist/modules/exhaustive-update.js +146 -0
  161. package/dist/modules/exhaustive-update.js.map +1 -0
  162. package/dist/modules/forgotten-spread.d.ts +3 -0
  163. package/dist/modules/forgotten-spread.d.ts.map +1 -0
  164. package/dist/modules/forgotten-spread.js +51 -0
  165. package/dist/modules/forgotten-spread.js.map +1 -0
  166. package/dist/modules/form-boilerplate.d.ts +3 -0
  167. package/dist/modules/form-boilerplate.d.ts.map +1 -0
  168. package/dist/modules/form-boilerplate.js +101 -0
  169. package/dist/modules/form-boilerplate.js.map +1 -0
  170. package/dist/modules/imperative-dom-in-view.d.ts +3 -0
  171. package/dist/modules/imperative-dom-in-view.d.ts.map +1 -0
  172. package/dist/modules/imperative-dom-in-view.js +123 -0
  173. package/dist/modules/imperative-dom-in-view.js.map +1 -0
  174. package/dist/modules/item-dedup.d.ts +7 -0
  175. package/dist/modules/item-dedup.d.ts.map +1 -0
  176. package/dist/modules/item-dedup.js +204 -0
  177. package/dist/modules/item-dedup.js.map +1 -0
  178. package/dist/modules/map-on-state-array.d.ts +3 -0
  179. package/dist/modules/map-on-state-array.d.ts.map +1 -0
  180. package/dist/modules/map-on-state-array.js +84 -0
  181. package/dist/modules/map-on-state-array.js.map +1 -0
  182. package/dist/modules/mask-legend.d.ts +10 -0
  183. package/dist/modules/mask-legend.d.ts.map +1 -0
  184. package/dist/modules/mask-legend.js +50 -0
  185. package/dist/modules/mask-legend.js.map +1 -0
  186. package/dist/modules/missing-memo.d.ts +3 -0
  187. package/dist/modules/missing-memo.d.ts.map +1 -0
  188. package/dist/modules/missing-memo.js +114 -0
  189. package/dist/modules/missing-memo.js.map +1 -0
  190. package/dist/modules/msg-annotations.d.ts +9 -0
  191. package/dist/modules/msg-annotations.d.ts.map +1 -0
  192. package/dist/modules/msg-annotations.js +54 -0
  193. package/dist/modules/msg-annotations.js.map +1 -0
  194. package/dist/modules/msg-schema.d.ts +10 -0
  195. package/dist/modules/msg-schema.d.ts.map +1 -0
  196. package/dist/modules/msg-schema.js +70 -0
  197. package/dist/modules/msg-schema.js.map +1 -0
  198. package/dist/modules/namespace-import.d.ts +3 -0
  199. package/dist/modules/namespace-import.d.ts.map +1 -0
  200. package/dist/modules/namespace-import.js +80 -0
  201. package/dist/modules/namespace-import.js.map +1 -0
  202. package/dist/modules/nested-send-in-update.d.ts +3 -0
  203. package/dist/modules/nested-send-in-update.d.ts.map +1 -0
  204. package/dist/modules/nested-send-in-update.js +77 -0
  205. package/dist/modules/nested-send-in-update.js.map +1 -0
  206. package/dist/modules/no-barrel-import-when-subpath-exists.d.ts +3 -0
  207. package/dist/modules/no-barrel-import-when-subpath-exists.d.ts.map +1 -0
  208. package/dist/modules/no-barrel-import-when-subpath-exists.js +100 -0
  209. package/dist/modules/no-barrel-import-when-subpath-exists.js.map +1 -0
  210. package/dist/modules/no-eager-item-accessor.d.ts +3 -0
  211. package/dist/modules/no-eager-item-accessor.d.ts.map +1 -0
  212. package/dist/modules/no-eager-item-accessor.js +74 -0
  213. package/dist/modules/no-eager-item-accessor.js.map +1 -0
  214. package/dist/modules/no-let-reactive-accessor.d.ts +3 -0
  215. package/dist/modules/no-let-reactive-accessor.d.ts.map +1 -0
  216. package/dist/modules/no-let-reactive-accessor.js +227 -0
  217. package/dist/modules/no-let-reactive-accessor.js.map +1 -0
  218. package/dist/modules/no-list-render-in-sample.d.ts +3 -0
  219. package/dist/modules/no-list-render-in-sample.d.ts.map +1 -0
  220. package/dist/modules/no-list-render-in-sample.js +89 -0
  221. package/dist/modules/no-list-render-in-sample.js.map +1 -0
  222. package/dist/modules/no-sample-in-accessor.d.ts +3 -0
  223. package/dist/modules/no-sample-in-accessor.d.ts.map +1 -0
  224. package/dist/modules/no-sample-in-accessor.js +141 -0
  225. package/dist/modules/no-sample-in-accessor.js.map +1 -0
  226. package/dist/modules/no-sample-in-reactive-position.d.ts +3 -0
  227. package/dist/modules/no-sample-in-reactive-position.d.ts.map +1 -0
  228. package/dist/modules/no-sample-in-reactive-position.js +72 -0
  229. package/dist/modules/no-sample-in-reactive-position.js.map +1 -0
  230. package/dist/modules/pure-update-function.d.ts +3 -0
  231. package/dist/modules/pure-update-function.d.ts.map +1 -0
  232. package/dist/modules/pure-update-function.js +127 -0
  233. package/dist/modules/pure-update-function.js.map +1 -0
  234. package/dist/modules/reactive-paths.d.ts +3 -0
  235. package/dist/modules/reactive-paths.d.ts.map +1 -0
  236. package/dist/modules/reactive-paths.js +77 -0
  237. package/dist/modules/reactive-paths.js.map +1 -0
  238. package/dist/modules/row-factory.d.ts +12 -0
  239. package/dist/modules/row-factory.d.ts.map +1 -0
  240. package/dist/modules/row-factory.js +385 -0
  241. package/dist/modules/row-factory.js.map +1 -0
  242. package/dist/modules/schema-hash.d.ts +15 -0
  243. package/dist/modules/schema-hash.d.ts.map +1 -0
  244. package/dist/modules/schema-hash.js +70 -0
  245. package/dist/modules/schema-hash.js.map +1 -0
  246. package/dist/modules/spread-in-children.d.ts +3 -0
  247. package/dist/modules/spread-in-children.d.ts.map +1 -0
  248. package/dist/modules/spread-in-children.js +144 -0
  249. package/dist/modules/spread-in-children.js.map +1 -0
  250. package/dist/modules/state-mutation.d.ts +3 -0
  251. package/dist/modules/state-mutation.d.ts.map +1 -0
  252. package/dist/modules/state-mutation.js +138 -0
  253. package/dist/modules/state-mutation.js.map +1 -0
  254. package/dist/modules/state-schema.d.ts +8 -0
  255. package/dist/modules/state-schema.d.ts.map +1 -0
  256. package/dist/modules/state-schema.js +55 -0
  257. package/dist/modules/state-schema.js.map +1 -0
  258. package/dist/modules/static-items.d.ts +3 -0
  259. package/dist/modules/static-items.d.ts.map +1 -0
  260. package/dist/modules/static-items.js +125 -0
  261. package/dist/modules/static-items.js.map +1 -0
  262. package/dist/modules/static-on.d.ts +3 -0
  263. package/dist/modules/static-on.d.ts.map +1 -0
  264. package/dist/modules/static-on.js +100 -0
  265. package/dist/modules/static-on.js.map +1 -0
  266. package/dist/modules/string-effect-callback.d.ts +3 -0
  267. package/dist/modules/string-effect-callback.d.ts.map +1 -0
  268. package/dist/modules/string-effect-callback.js +50 -0
  269. package/dist/modules/string-effect-callback.js.map +1 -0
  270. package/dist/modules/structural-mask.d.ts +8 -0
  271. package/dist/modules/structural-mask.d.ts.map +1 -0
  272. package/dist/modules/structural-mask.js +76 -0
  273. package/dist/modules/structural-mask.js.map +1 -0
  274. package/dist/modules/subapp-requires-reason.d.ts +3 -0
  275. package/dist/modules/subapp-requires-reason.d.ts.map +1 -0
  276. package/dist/modules/subapp-requires-reason.js +129 -0
  277. package/dist/modules/subapp-requires-reason.js.map +1 -0
  278. package/dist/modules/text-mask.d.ts +12 -0
  279. package/dist/modules/text-mask.d.ts.map +1 -0
  280. package/dist/modules/text-mask.js +63 -0
  281. package/dist/modules/text-mask.js.map +1 -0
  282. package/dist/modules/view-bag-import.d.ts +3 -0
  283. package/dist/modules/view-bag-import.d.ts.map +1 -0
  284. package/dist/modules/view-bag-import.js +80 -0
  285. package/dist/modules/view-bag-import.js.map +1 -0
  286. package/dist/msg-annotations.d.ts +104 -0
  287. package/dist/msg-annotations.d.ts.map +1 -0
  288. package/dist/msg-annotations.js +242 -0
  289. package/dist/msg-annotations.js.map +1 -0
  290. package/dist/msg-schema.d.ts +130 -0
  291. package/dist/msg-schema.d.ts.map +1 -0
  292. package/dist/msg-schema.js +770 -0
  293. package/dist/msg-schema.js.map +1 -0
  294. package/dist/schema-hash.d.ts +16 -0
  295. package/dist/schema-hash.d.ts.map +1 -0
  296. package/dist/schema-hash.js +31 -0
  297. package/dist/schema-hash.js.map +1 -0
  298. package/dist/state-schema.d.ts +41 -0
  299. package/dist/state-schema.d.ts.map +1 -0
  300. package/dist/state-schema.js +156 -0
  301. package/dist/state-schema.js.map +1 -0
  302. package/dist/transform.d.ts +109 -0
  303. package/dist/transform.d.ts.map +1 -0
  304. package/dist/transform.js +1390 -0
  305. package/dist/transform.js.map +1 -0
  306. package/dist/version.d.ts +11 -0
  307. package/dist/version.d.ts.map +1 -0
  308. package/dist/version.js +11 -0
  309. package/dist/version.js.map +1 -0
  310. package/package.json +47 -0
@@ -0,0 +1,1017 @@
1
+ // `element-rewrite` — transforms `div(...)` / `button(...)` / etc.
2
+ // (every helper in `ELEMENT_HELPERS`) into `elSplit(...)` /
3
+ // `elTemplate(...)` / `__cloneStaticTemplate(...)` calls.
4
+ //
5
+ // The transform classifies each prop of the element call:
6
+ // - Static literals → emit as a one-time `__e.prop = X` setter
7
+ // - Event handlers (`onClick`, `onInput`, ...) → emit as
8
+ // `[eventName, handler]` tuples
9
+ // - Reactive accessors (arrows / `memo(arrow)` / identifier-bound
10
+ // forms) → emit as `[mask, kind, key, accessor]` binding tuples
11
+ // - Per-item shapes (`item.field`, `item((r) => r.x)`) inside
12
+ // each() render callbacks → emit with `FULL_MASK` since the
13
+ // accessor closes over a per-row argument the runtime supplies
14
+ //
15
+ // When the element has only static + event content, the rewrite
16
+ // further specializes into `__cloneStaticTemplate(...)` (prerendered
17
+ // HTML clone). When the element's subtree has multiple nested
18
+ // elements, `analyzeSubtree` + `emitSubtreeTemplate` collapse the
19
+ // whole subtree into a single `elTemplate(...)` call with a
20
+ // patch-by-walk function.
21
+ //
22
+ // Owned by this module since v2c/decomp-22. The thin-wrapper
23
+ // migration (decomp-17) put the registry on the call path; the
24
+ // helpers (and `tryTransformElementCall` itself) moved here in this
25
+ // commit so the module is self-contained.
26
+ //
27
+ // Fires top-down (`transformCallEnter`). Sets module-level state
28
+ // via `ELEMENT_REWRITE_SLOT` for the umbrella's `cleanupImports`
29
+ // to decide whether `elSplit` / `elTemplate` / `__cloneStaticTemplate`
30
+ // need their runtime imports.
31
+ import ts from 'typescript';
32
+ import { resolveLocalConstInitializer, isMemoCallWithArrowArg } from '../accessor-resolver.js';
33
+ import { computeAccessorMask, createMaskLiteral } from '../transform.js';
34
+ export const ELEMENT_REWRITE_SLOT = 'element-rewrite:state';
35
+ export function elementRewriteModule(options) {
36
+ const { importedHelpers, fieldBits, fieldBitsHi } = options;
37
+ return {
38
+ name: 'element-rewrite',
39
+ compilerVersion: '^0.3.0',
40
+ diagnostics: [],
41
+ visitors: {},
42
+ transformCallEnter(ctx, node) {
43
+ const slot = ctx.analysis.perModule.get(ELEMENT_REWRITE_SLOT);
44
+ const state = slot ?? {
45
+ compiled: new Set(),
46
+ bailed: new Set(),
47
+ usesElSplit: false,
48
+ usesElTemplate: false,
49
+ usesCloneStaticTemplate: false,
50
+ };
51
+ if (!slot)
52
+ ctx.analysis.perModule.set(ELEMENT_REWRITE_SLOT, state);
53
+ const transformed = tryTransformElementCall(node, importedHelpers, fieldBits, state.compiled, state.bailed, ctx.factory, fieldBitsHi);
54
+ if (!transformed)
55
+ return null;
56
+ if (ts.isIdentifier(transformed.expression)) {
57
+ if (transformed.expression.text === 'elTemplate')
58
+ state.usesElTemplate = true;
59
+ else if (transformed.expression.text === 'elSplit')
60
+ state.usesElSplit = true;
61
+ else if (transformed.expression.text === '__cloneStaticTemplate')
62
+ state.usesCloneStaticTemplate = true;
63
+ }
64
+ return transformed;
65
+ },
66
+ };
67
+ }
68
+ // ─── Rewrite implementation (moved verbatim from transform.ts) ─────
69
+ const PROP_KEYS = new Set([
70
+ 'value',
71
+ 'checked',
72
+ 'selected',
73
+ 'disabled',
74
+ 'readOnly',
75
+ 'multiple',
76
+ 'indeterminate',
77
+ 'defaultValue',
78
+ 'defaultChecked',
79
+ 'innerHTML',
80
+ 'textContent',
81
+ ]);
82
+ function isStaticPrimitiveLiteral(expr) {
83
+ return (ts.isStringLiteral(expr) ||
84
+ ts.isNumericLiteral(expr) ||
85
+ ts.isNoSubstitutionTemplateLiteral(expr) ||
86
+ expr.kind === ts.SyntaxKind.TrueKeyword ||
87
+ expr.kind === ts.SyntaxKind.FalseKeyword ||
88
+ expr.kind === ts.SyntaxKind.NullKeyword);
89
+ }
90
+ /**
91
+ * Classify a reactive-prop value. See `ResolvedReactiveValue` for the
92
+ * contract. Returns `null` only when the value is none of the recognized
93
+ * shapes (caller can fall back to its own branches — currently only
94
+ * `tryTransformElementCall` does this for `isPerItemFieldAccess` /
95
+ * `isHoistedPerItem`).
96
+ */
97
+ function classifyReactiveValue(value) {
98
+ // Inline arrow / function expression at the call site
99
+ if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) {
100
+ return { kind: 'arrow', accessor: value, valueForBinding: value };
101
+ }
102
+ // Inline `memo(arrow)` at the call site
103
+ if (isMemoCallWithArrowArg(value)) {
104
+ return {
105
+ kind: 'memo-call',
106
+ accessor: value.arguments[0],
107
+ valueForBinding: value,
108
+ };
109
+ }
110
+ // Identifier — resolve and classify the resolved declaration
111
+ if (ts.isIdentifier(value)) {
112
+ const resolved = resolveLocalConstInitializer(value);
113
+ if (!resolved) {
114
+ // Imported / parameter / unbound — can't prove it's a primitive,
115
+ // can't prove it's a function. Caller must bail to runtime.
116
+ return { kind: 'bail' };
117
+ }
118
+ if (ts.isArrowFunction(resolved) || ts.isFunctionExpression(resolved)) {
119
+ return { kind: 'arrow', accessor: resolved, valueForBinding: value };
120
+ }
121
+ if (ts.isFunctionDeclaration(resolved)) {
122
+ return { kind: 'fn-decl', accessor: resolved, valueForBinding: value };
123
+ }
124
+ if (isMemoCallWithArrowArg(resolved)) {
125
+ return {
126
+ kind: 'memo-call',
127
+ accessor: resolved.arguments[0],
128
+ valueForBinding: value,
129
+ };
130
+ }
131
+ if (isStaticPrimitiveLiteral(resolved)) {
132
+ return { kind: 'static-literal' };
133
+ }
134
+ // Resolved to something else (object/array/expression) — conservative
135
+ // bail. We don't know if the runtime value is a function; the runtime
136
+ // element helper handles both cases correctly.
137
+ return { kind: 'bail' };
138
+ }
139
+ // Static literals at the call site
140
+ if (isStaticPrimitiveLiteral(value)) {
141
+ return { kind: 'static-literal' };
142
+ }
143
+ // CallExpression — caller decides (per-item, etc.)
144
+ return null;
145
+ }
146
+ function classifyKind(key) {
147
+ if (key === 'class' || key === 'className')
148
+ return 'class';
149
+ if (key.startsWith('style.'))
150
+ return 'style';
151
+ if (PROP_KEYS.has(key))
152
+ return 'prop';
153
+ return 'attr';
154
+ }
155
+ function resolveKey(key, kind) {
156
+ if (kind === 'class')
157
+ return 'class';
158
+ if (kind === 'style')
159
+ return key.slice(6);
160
+ if (kind === 'prop')
161
+ return key;
162
+ if (key === 'className')
163
+ return 'class';
164
+ return key;
165
+ }
166
+ // ─── emitStaticProp + tryTransformElementCall ────────────────────
167
+ function emitStaticProp(staticProps, f, kind, resolvedKey, value) {
168
+ switch (kind) {
169
+ case 'class':
170
+ staticProps.push(f.createExpressionStatement(f.createBinaryExpression(f.createPropertyAccessExpression(f.createIdentifier('__e'), 'className'), ts.SyntaxKind.EqualsToken, value)));
171
+ break;
172
+ case 'prop':
173
+ staticProps.push(f.createExpressionStatement(f.createBinaryExpression(f.createPropertyAccessExpression(f.createIdentifier('__e'), resolvedKey), ts.SyntaxKind.EqualsToken, value)));
174
+ break;
175
+ case 'style':
176
+ staticProps.push(f.createExpressionStatement(f.createCallExpression(f.createPropertyAccessExpression(f.createPropertyAccessExpression(f.createIdentifier('__e'), 'style'), 'setProperty'), undefined, [f.createStringLiteral(resolvedKey), value])));
177
+ break;
178
+ default: // attr
179
+ staticProps.push(f.createExpressionStatement(f.createCallExpression(f.createPropertyAccessExpression(f.createIdentifier('__e'), 'setAttribute'), undefined, [f.createStringLiteral(resolvedKey), value])));
180
+ }
181
+ }
182
+ // ── Pass 1: Element → elSplit ────────────────────────────────────
183
+ function tryTransformElementCall(node, helpers, fieldBits, compiled, bailed, f, fieldBitsHi = new Map()) {
184
+ if (!ts.isIdentifier(node.expression))
185
+ return null;
186
+ const localName = node.expression.text;
187
+ const originalName = helpers.get(localName);
188
+ if (!originalName)
189
+ return null;
190
+ // Handle children-only overload: `div([...])` — first arg is the children array.
191
+ // Normalize to props=undefined, children=firstArg so downstream logic works.
192
+ const firstArg = node.arguments[0];
193
+ const usesChildrenOnlyOverload = firstArg && ts.isArrayLiteralExpression(firstArg);
194
+ const propsArg = usesChildrenOnlyOverload ? undefined : firstArg;
195
+ if (propsArg && !ts.isObjectLiteralExpression(propsArg)) {
196
+ bailed.add(localName);
197
+ return null;
198
+ }
199
+ // Bail on spread assignments (`...parts.root`) — the compiler cannot
200
+ // statically classify spread contents, and silently dropping them would
201
+ // break consumers (e.g. @llui/components parts spreading). Fall back to
202
+ // the runtime element helper so spreads are applied normally.
203
+ if (propsArg &&
204
+ ts.isObjectLiteralExpression(propsArg) &&
205
+ propsArg.properties.some((p) => ts.isSpreadAssignment(p))) {
206
+ bailed.add(localName);
207
+ return null;
208
+ }
209
+ const tag = f.createStringLiteral(originalName);
210
+ // Classify props
211
+ const staticProps = [];
212
+ const events = [];
213
+ const bindings = [];
214
+ if (propsArg && ts.isObjectLiteralExpression(propsArg)) {
215
+ for (const prop of propsArg.properties) {
216
+ // Handle both PropertyAssignment (key: value) and ShorthandPropertyAssignment ({ id })
217
+ let key;
218
+ let value;
219
+ if (ts.isPropertyAssignment(prop)) {
220
+ if (!ts.isIdentifier(prop.name) && !ts.isStringLiteral(prop.name))
221
+ continue;
222
+ key = ts.isIdentifier(prop.name) ? prop.name.text : prop.name.text;
223
+ value = prop.initializer;
224
+ }
225
+ else if (ts.isShorthandPropertyAssignment(prop)) {
226
+ key = prop.name.text;
227
+ value = prop.name; // The identifier itself is the value
228
+ }
229
+ else {
230
+ continue;
231
+ }
232
+ if (key === 'key')
233
+ continue;
234
+ // Event handler
235
+ if (/^on[A-Z]/.test(key)) {
236
+ const eventName = key.slice(2).toLowerCase();
237
+ events.push(f.createArrayLiteralExpression([f.createStringLiteral(eventName), value]));
238
+ continue;
239
+ }
240
+ // Per-item shapes — handled before the general classifier because
241
+ // they appear inside `each().render` callbacks where `item` is a
242
+ // closed-over per-row accessor (zero-arg). The resolver above can't
243
+ // see them; they're shape-matched syntactically.
244
+ if (isPerItemFieldAccess(value) || isHoistedPerItem(value)) {
245
+ const kind = classifyKind(key);
246
+ const resolvedKey = resolveKey(key, kind);
247
+ bindings.push(f.createArrayLiteralExpression([
248
+ createMaskLiteral(f, 0xffffffff | 0),
249
+ f.createStringLiteral(kind),
250
+ f.createStringLiteral(resolvedKey),
251
+ value,
252
+ ]));
253
+ continue;
254
+ }
255
+ if (ts.isCallExpression(value) && isPerItemCall(value)) {
256
+ const kind = classifyKind(key);
257
+ const resolvedKey = resolveKey(key, kind);
258
+ bindings.push(f.createArrayLiteralExpression([
259
+ createMaskLiteral(f, 0xffffffff | 0),
260
+ f.createStringLiteral(kind),
261
+ f.createStringLiteral(resolvedKey),
262
+ value,
263
+ ]));
264
+ continue;
265
+ }
266
+ // Classify the value at a reactive-prop position:
267
+ // - inline arrow / fn-expr at the call site
268
+ // - inline `memo(arrow)` at the call site
269
+ // - Identifier referencing a const-bound arrow/fn-expr in scope
270
+ // - Identifier referencing a hoisted function declaration in scope
271
+ // - Identifier referencing `const x = memo(arrow)` in scope
272
+ // - Identifier referencing a static primitive literal
273
+ // - Anything else (imports, parameters, opaque expressions) — bail
274
+ // to runtime; the runtime helper handles `typeof v === 'function'`
275
+ // correctly for both function and primitive values.
276
+ const classified = classifyReactiveValue(value);
277
+ if (classified === null) {
278
+ // Unknown shape (a CallExpression that isn't memo/per-item, etc.)
279
+ // — historically bailed to runtime. Preserve that.
280
+ bailed.add(localName);
281
+ return null;
282
+ }
283
+ if (classified.kind === 'bail') {
284
+ bailed.add(localName);
285
+ return null;
286
+ }
287
+ if (classified.kind === 'static-literal') {
288
+ // Fall through to emitStaticProp (`__e.disabled = X`). Safe because
289
+ // we proved X is a primitive.
290
+ const kind = classifyKind(key);
291
+ const resolvedKey = resolveKey(key, kind);
292
+ emitStaticProp(staticProps, f, kind, resolvedKey, value);
293
+ continue;
294
+ }
295
+ // 'arrow' | 'fn-decl' | 'memo-call' — emit as a binding tuple. Mask is
296
+ // analyzed from the resolved accessor body (or the inner arrow inside
297
+ // a memo() call); the value emitted into the binding tuple is what the
298
+ // runtime calls as `accessor(state)` — for inline arrows we keep the
299
+ // arrow itself (preserves the historical inlining behavior), for
300
+ // identifier-bound forms we keep the identifier so consumers see
301
+ // a single canonical reference (and `memo()` proxies aren't rebuilt
302
+ // per render).
303
+ {
304
+ const kind = classifyKind(key);
305
+ const resolvedKey = resolveKey(key, kind);
306
+ const { mask, maskHi, readsState } = computeAccessorMask(classified.accessor, fieldBits, undefined, fieldBitsHi);
307
+ // Zero-mask constant folding only applies to inline arrows whose body
308
+ // we can safely call at compile time. For identifier-bound forms
309
+ // (`accessor !== value`) we skip the fold — calling the identifier's
310
+ // declaration at compile time would be unsafe (different scope) and
311
+ // calling the identifier in the emitted output would defeat the point.
312
+ if (classified.kind === 'arrow' &&
313
+ classified.accessor === value &&
314
+ mask === 0 &&
315
+ maskHi === 0 &&
316
+ !readsState) {
317
+ emitStaticProp(staticProps, f, kind, resolvedKey, f.createCallExpression(classified.accessor, undefined, []));
318
+ continue;
319
+ }
320
+ const effectiveMask = mask === 0 && maskHi === 0 && readsState ? 0xffffffff | 0 : mask;
321
+ // Emit a 5-tuple only when the accessor reads a high-word
322
+ // prefix (positions 31..61). For the common ≤31-prefix case
323
+ // the emit stays byte-identical to the pre-multi-word baseline,
324
+ // and stale runtime bundles ignore the 5th slot.
325
+ const tupleEls = [
326
+ createMaskLiteral(f, effectiveMask),
327
+ f.createStringLiteral(kind),
328
+ f.createStringLiteral(resolvedKey),
329
+ classified.valueForBinding,
330
+ ];
331
+ if (maskHi !== 0)
332
+ tupleEls.push(createMaskLiteral(f, maskHi));
333
+ bindings.push(f.createArrayLiteralExpression(tupleEls));
334
+ }
335
+ }
336
+ }
337
+ // Build elSplit args
338
+ const staticFn = staticProps.length > 0
339
+ ? f.createArrowFunction(undefined, undefined, [f.createParameterDeclaration(undefined, undefined, '__e')], undefined, f.createToken(ts.SyntaxKind.EqualsGreaterThanToken), f.createBlock(staticProps, true))
340
+ : f.createNull();
341
+ const eventsArr = events.length > 0 ? f.createArrayLiteralExpression(events) : f.createNull();
342
+ const bindingsArr = bindings.length > 0 ? f.createArrayLiteralExpression(bindings) : f.createNull();
343
+ const children = usesChildrenOnlyOverload
344
+ ? node.arguments[0]
345
+ : (node.arguments[1] ?? f.createNull());
346
+ compiled.add(localName);
347
+ // Subtree collapse: if children contain nested element helpers,
348
+ // collapse the entire tree into a single elTemplate() call
349
+ const analyzed = analyzeSubtree(node, helpers, fieldBits, [], fieldBitsHi);
350
+ if (analyzed && hasNestedElements(analyzed)) {
351
+ // Mark all descendant helpers as compiled for import cleanup
352
+ collectUsedHelpers(analyzed, compiled);
353
+ const templateCall = emitSubtreeTemplate(analyzed, fieldBits, f);
354
+ return templateCall;
355
+ }
356
+ // Static subtree prerendering: if no events, no bindings, and children
357
+ // are all static text, emit a <template> clone
358
+ if (events.length === 0 && bindings.length === 0 && isStaticChildren(children)) {
359
+ const html = buildStaticHTML(originalName, staticProps, children, f);
360
+ if (html) {
361
+ return emitTemplateClone(html, f);
362
+ }
363
+ }
364
+ const call = f.createCallExpression(f.createIdentifier('elSplit'), undefined, [
365
+ tag,
366
+ staticFn,
367
+ eventsArr,
368
+ bindingsArr,
369
+ children,
370
+ ]);
371
+ ts.addSyntheticLeadingComment(call, ts.SyntaxKind.MultiLineCommentTrivia, '@__PURE__', false);
372
+ return call;
373
+ }
374
+ // ─── Analysis + per-item heuristics ───────────────────────────────
375
+ const VOID_ELEMENTS = new Set([
376
+ 'area',
377
+ 'base',
378
+ 'br',
379
+ 'col',
380
+ 'embed',
381
+ 'hr',
382
+ 'img',
383
+ 'input',
384
+ 'link',
385
+ 'meta',
386
+ 'param',
387
+ 'source',
388
+ 'track',
389
+ 'wbr',
390
+ ]);
391
+ /**
392
+ * Try to analyze an element call and all its descendants as a collapsible subtree.
393
+ * Returns null if any part of the tree is not eligible for collapse.
394
+ */
395
+ function analyzeSubtree(node, helpers, fieldBits, path, fieldBitsHi = new Map()) {
396
+ if (!ts.isIdentifier(node.expression))
397
+ return null;
398
+ const localName = node.expression.text;
399
+ const tag = helpers.get(localName);
400
+ if (!tag)
401
+ return null;
402
+ // Handle children-only overload: `div([...])` — first arg is the children array.
403
+ // In that case, treat it as no props + children=firstArg.
404
+ const firstArg = node.arguments[0];
405
+ const usesChildrenOnlyOverload = firstArg && ts.isArrayLiteralExpression(firstArg);
406
+ const propsArg = usesChildrenOnlyOverload ? undefined : firstArg;
407
+ const childrenArg = usesChildrenOnlyOverload ? firstArg : node.arguments[1];
408
+ if (propsArg && !ts.isObjectLiteralExpression(propsArg))
409
+ return null;
410
+ const staticAttrs = [];
411
+ const events = [];
412
+ const bindings = [];
413
+ if (propsArg && ts.isObjectLiteralExpression(propsArg)) {
414
+ for (const prop of propsArg.properties) {
415
+ let key;
416
+ let value;
417
+ if (ts.isPropertyAssignment(prop)) {
418
+ if (!ts.isIdentifier(prop.name) && !ts.isStringLiteral(prop.name))
419
+ return null;
420
+ key = ts.isIdentifier(prop.name) ? prop.name.text : prop.name.text;
421
+ value = prop.initializer;
422
+ }
423
+ else if (ts.isShorthandPropertyAssignment(prop)) {
424
+ key = prop.name.text;
425
+ value = prop.name;
426
+ }
427
+ else {
428
+ return null;
429
+ }
430
+ if (key === 'key')
431
+ continue;
432
+ // Event handler
433
+ if (/^on[A-Z]/.test(key)) {
434
+ events.push([key.slice(2).toLowerCase(), value]);
435
+ continue;
436
+ }
437
+ // Resolve identifier → local const arrow initializer (see elSplit
438
+ // path for the full rationale).
439
+ if (ts.isIdentifier(value)) {
440
+ const resolved = resolveLocalConstInitializer(value);
441
+ if (resolved && (ts.isArrowFunction(resolved) || ts.isFunctionExpression(resolved))) {
442
+ value = resolved;
443
+ }
444
+ }
445
+ // Reactive binding
446
+ if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) {
447
+ const kind = classifyKind(key);
448
+ const resolvedKey = resolveKey(key, kind);
449
+ const { mask, maskHi, readsState } = computeAccessorMask(value, fieldBits, undefined, fieldBitsHi);
450
+ if (mask === 0 && maskHi === 0 && !readsState) {
451
+ // Constant fold — treat as static if we can extract a string
452
+ const staticVal = tryExtractStaticString(value);
453
+ if (staticVal !== null) {
454
+ const attrKey = kind === 'class' ? 'class' : resolvedKey;
455
+ staticAttrs.push([attrKey, staticVal]);
456
+ continue;
457
+ }
458
+ }
459
+ const finalMask = mask === 0 && maskHi === 0 && readsState ? 0xffffffff | 0 : mask;
460
+ bindings.push([finalMask, maskHi, kind, resolvedKey, value]);
461
+ continue;
462
+ }
463
+ // Per-item accessor call
464
+ if (ts.isCallExpression(value) && isPerItemCall(value)) {
465
+ const kind = classifyKind(key);
466
+ const resolvedKey = resolveKey(key, kind);
467
+ bindings.push([0xffffffff | 0, 0, kind, resolvedKey, value]);
468
+ continue;
469
+ }
470
+ // Per-item property access: item.field (or hoisted __a0/__a1/…)
471
+ if (isPerItemFieldAccess(value) || isHoistedPerItem(value)) {
472
+ const kind = classifyKind(key);
473
+ const resolvedKey = resolveKey(key, kind);
474
+ bindings.push([0xffffffff | 0, 0, kind, resolvedKey, value]);
475
+ continue;
476
+ }
477
+ // Static literal prop
478
+ if (ts.isStringLiteral(value)) {
479
+ const kind = classifyKind(key);
480
+ const attrKey = kind === 'class' ? 'class' : resolveKey(key, kind);
481
+ staticAttrs.push([attrKey, value.text]);
482
+ continue;
483
+ }
484
+ if (ts.isNumericLiteral(value)) {
485
+ const kind = classifyKind(key);
486
+ const attrKey = kind === 'class' ? 'class' : resolveKey(key, kind);
487
+ staticAttrs.push([attrKey, value.text]);
488
+ continue;
489
+ }
490
+ if (value.kind === ts.SyntaxKind.TrueKeyword) {
491
+ const kind = classifyKind(key);
492
+ const attrKey = kind === 'class' ? 'class' : resolveKey(key, kind);
493
+ staticAttrs.push([attrKey, '']);
494
+ continue;
495
+ }
496
+ // Non-literal prop — can't collapse
497
+ return null;
498
+ }
499
+ }
500
+ // Analyze children
501
+ const children = [];
502
+ if (childrenArg && ts.isArrayLiteralExpression(childrenArg)) {
503
+ let childIdx = 0;
504
+ for (const child of childrenArg.elements) {
505
+ // String literal child — static text node
506
+ if (ts.isStringLiteral(child) || ts.isNoSubstitutionTemplateLiteral(child)) {
507
+ children.push({ type: 'staticText', value: child.text });
508
+ childIdx++;
509
+ continue;
510
+ }
511
+ // text('literal') — static text
512
+ if (ts.isCallExpression(child) &&
513
+ ts.isIdentifier(child.expression) &&
514
+ child.expression.text === 'text') {
515
+ if (child.arguments.length >= 1 && ts.isStringLiteral(child.arguments[0])) {
516
+ children.push({ type: 'staticText', value: child.arguments[0].text });
517
+ childIdx++; // static text creates a text node in the template DOM
518
+ continue;
519
+ }
520
+ // Reactive text — accessor is first arg
521
+ const accessor = child.arguments[0];
522
+ if (ts.isArrowFunction(accessor) || ts.isFunctionExpression(accessor)) {
523
+ const { mask, maskHi, readsState } = computeAccessorMask(accessor, fieldBits, undefined, fieldBitsHi);
524
+ children.push({
525
+ type: 'reactiveText',
526
+ accessor,
527
+ mask: mask === 0 && maskHi === 0 && readsState ? 0xffffffff | 0 : mask,
528
+ maskHi,
529
+ childIdx,
530
+ });
531
+ childIdx++; // placeholder text node in template
532
+ continue;
533
+ }
534
+ // Per-item text: text(item(t => t.label))
535
+ if (ts.isCallExpression(accessor) && isPerItemCall(accessor)) {
536
+ children.push({
537
+ type: 'reactiveText',
538
+ accessor,
539
+ mask: 0xffffffff | 0,
540
+ maskHi: 0,
541
+ childIdx,
542
+ });
543
+ childIdx++; // placeholder text node in template
544
+ continue;
545
+ }
546
+ // Per-item text via property access: text(item.label)
547
+ // Also matches hoisted __a0/__a1/… identifiers produced by dedup.
548
+ if (isPerItemFieldAccess(accessor) || isHoistedPerItem(accessor)) {
549
+ children.push({
550
+ type: 'reactiveText',
551
+ accessor,
552
+ mask: 0xffffffff | 0,
553
+ maskHi: 0,
554
+ childIdx,
555
+ });
556
+ childIdx++;
557
+ continue;
558
+ }
559
+ return null; // unsupported text() form
560
+ }
561
+ // Element helper call — recurse
562
+ if (ts.isCallExpression(child) &&
563
+ ts.isIdentifier(child.expression) &&
564
+ helpers.has(child.expression.text)) {
565
+ const childNode = analyzeSubtree(child, helpers, fieldBits, [...path, childIdx], fieldBitsHi);
566
+ if (!childNode)
567
+ return null;
568
+ children.push({ type: 'element', node: childNode });
569
+ childIdx++;
570
+ continue;
571
+ }
572
+ // Anything else (each, branch, show, arbitrary expressions) — bail
573
+ return null;
574
+ }
575
+ // Note: mixed static + reactive text in the same parent is now supported
576
+ // because reactive text uses <!--$--> comment placeholders that break
577
+ // text-node merging at parse time.
578
+ }
579
+ else if (childrenArg && childrenArg.kind !== ts.SyntaxKind.NullKeyword) {
580
+ // Non-array children (e.g., spread, variable) — bail
581
+ return null;
582
+ }
583
+ return { tag, localName, staticAttrs, events, bindings, children, path };
584
+ }
585
+ function tryExtractStaticString(accessor) {
586
+ const body = ts.isArrowFunction(accessor) ? accessor.body : null;
587
+ if (body && ts.isStringLiteral(body))
588
+ return body.text;
589
+ return null;
590
+ }
591
+ /**
592
+ * Check if a subtree has any nested element children (worth collapsing).
593
+ */
594
+ function hasNestedElements(node) {
595
+ return node.children.some((c) => c.type === 'element');
596
+ }
597
+ /**
598
+ * Collect all local helper names used in the subtree for import cleanup.
599
+ */
600
+ function collectUsedHelpers(node, out) {
601
+ out.add(node.localName);
602
+ for (const child of node.children) {
603
+ if (child.type === 'element')
604
+ collectUsedHelpers(child.node, out);
605
+ }
606
+ }
607
+ /**
608
+ * Build the static HTML string from an analyzed subtree.
609
+ */
610
+ function buildTemplateHTML(node) {
611
+ let html = `<${node.tag}`;
612
+ for (const [key, value] of node.staticAttrs) {
613
+ html += ` ${key}="${escapeAttr(value)}"`;
614
+ }
615
+ html += '>';
616
+ if (VOID_ELEMENTS.has(node.tag))
617
+ return html;
618
+ for (let ci = 0; ci < node.children.length; ci++) {
619
+ const child = node.children[ci];
620
+ if (child.type === 'staticText') {
621
+ html += escapeHTML(child.value);
622
+ }
623
+ else if (child.type === 'element') {
624
+ html += buildTemplateHTML(child.node);
625
+ }
626
+ else if (child.type === 'reactiveText') {
627
+ // When the reactive text is not adjacent to another text-type child,
628
+ // we can use a literal text node placeholder instead of a comment.
629
+ // The cloned text node is reused in the patch function — no
630
+ // createTextNode + replaceChild needed. This saves 2 DOM operations
631
+ // per text binding per row.
632
+ //
633
+ // When adjacent text WOULD cause HTML-parser merging (two text nodes
634
+ // collapse into one), we fall back to the comment placeholder.
635
+ const prev = ci > 0 ? node.children[ci - 1] : null;
636
+ const next = ci < node.children.length - 1 ? node.children[ci + 1] : null;
637
+ const adjText = prev?.type === 'staticText' ||
638
+ prev?.type === 'reactiveText' ||
639
+ next?.type === 'staticText' ||
640
+ next?.type === 'reactiveText';
641
+ if (adjText) {
642
+ html += '<!--$-->';
643
+ }
644
+ else {
645
+ // Space character becomes a Text node in the cloned template.
646
+ // Mark the child so the patch codegen knows to skip replaceChild.
647
+ html += ' ';
648
+ child.inlineText = true;
649
+ }
650
+ }
651
+ }
652
+ html += `</${node.tag}>`;
653
+ return html;
654
+ }
655
+ /**
656
+ * Collect all patch operations from an analyzed subtree.
657
+ */
658
+ function collectPatchOps(node, f, rootExpr, ops, counter) {
659
+ const hasDynamic = node.events.length > 0 ||
660
+ node.bindings.length > 0 ||
661
+ node.children.some((c) => c.type === 'reactiveText');
662
+ let nodeExpr = rootExpr;
663
+ if (hasDynamic) {
664
+ const varName = `__n${counter.n++}`;
665
+ // Build walk expression: root.childNodes[i].childNodes[j]...
666
+ nodeExpr = f.createIdentifier(varName);
667
+ ops.push({
668
+ varName,
669
+ walkExpr: buildWalkExpr(node.path, f),
670
+ events: node.events,
671
+ bindings: node.bindings,
672
+ reactiveTexts: node.children.filter((c) => c.type === 'reactiveText'),
673
+ });
674
+ }
675
+ // Recurse into element children
676
+ for (const child of node.children) {
677
+ if (child.type === 'element') {
678
+ collectPatchOps(child.node, f, nodeExpr, ops, counter);
679
+ }
680
+ }
681
+ }
682
+ function buildWalkExpr(path, f) {
683
+ let expr = f.createIdentifier('root');
684
+ for (const idx of path) {
685
+ // Use firstChild + nextSibling chain instead of childNodes[n]
686
+ // firstChild/nextSibling are direct pointer lookups, childNodes is a live NodeList
687
+ expr = f.createPropertyAccessExpression(expr, 'firstChild');
688
+ for (let i = 0; i < idx; i++) {
689
+ expr = f.createPropertyAccessExpression(expr, 'nextSibling');
690
+ }
691
+ }
692
+ return expr;
693
+ }
694
+ /**
695
+ * Emit elTemplate(htmlString, (root, __bind) => { ... }) call.
696
+ */
697
+ function emitSubtreeTemplate(analyzed, fieldBits, f) {
698
+ const html = buildTemplateHTML(analyzed);
699
+ const ops = [];
700
+ const counter = { n: 0, t: 0 };
701
+ // Collect root-level patches
702
+ const rootHasDynamic = analyzed.events.length > 0 ||
703
+ analyzed.bindings.length > 0 ||
704
+ analyzed.children.some((c) => c.type === 'reactiveText');
705
+ if (rootHasDynamic) {
706
+ ops.push({
707
+ varName: '', // use 'root' directly
708
+ walkExpr: f.createIdentifier('root'),
709
+ events: analyzed.events,
710
+ bindings: analyzed.bindings,
711
+ reactiveTexts: analyzed.children.filter((c) => c.type === 'reactiveText'),
712
+ });
713
+ }
714
+ // Collect child patches
715
+ for (const child of analyzed.children) {
716
+ if (child.type === 'element') {
717
+ collectPatchOps(child.node, f, f.createIdentifier('root'), ops, counter);
718
+ }
719
+ }
720
+ // Collect delegatable events: group by event type across all ops
721
+ // Events on child nodes with the same type are delegated to the root
722
+ const delegatableEvents = new Map();
723
+ for (const op of ops) {
724
+ for (const [eventName, handler] of op.events) {
725
+ if (!op.varName) {
726
+ // Root-level events — can't delegate further up
727
+ continue;
728
+ }
729
+ const list = delegatableEvents.get(eventName);
730
+ if (list)
731
+ list.push({ nodeVar: op.varName, handler });
732
+ else
733
+ delegatableEvents.set(eventName, [{ nodeVar: op.varName, handler }]);
734
+ }
735
+ }
736
+ // Build patch function body
737
+ const stmts = [];
738
+ for (const op of ops) {
739
+ const nodeRef = op.varName ? f.createIdentifier(op.varName) : f.createIdentifier('root');
740
+ // Variable declaration for walking to node
741
+ if (op.varName) {
742
+ stmts.push(f.createVariableStatement(undefined, f.createVariableDeclarationList([f.createVariableDeclaration(op.varName, undefined, undefined, op.walkExpr)], ts.NodeFlags.Const)));
743
+ }
744
+ // Non-delegatable events (root-level or single-use event types)
745
+ for (const [eventName, handler] of op.events) {
746
+ const delegated = delegatableEvents.get(eventName);
747
+ if (op.varName && delegated && delegated.length >= 2)
748
+ continue; // handled below
749
+ stmts.push(f.createExpressionStatement(f.createCallExpression(f.createPropertyAccessExpression(nodeRef, 'addEventListener'), undefined, [f.createStringLiteral(eventName), handler])));
750
+ }
751
+ // Reactive text children — walk to placeholder, create text node, bind
752
+ for (const rt of op.reactiveTexts) {
753
+ const tVar = `__t${counter.t++}`;
754
+ const isInline = !!rt.inlineText;
755
+ if (isInline) {
756
+ // Inline text placeholder: the template HTML has a space character
757
+ // that cloneNode already created as a Text node. Walk to it and
758
+ // bind directly — no createTextNode, no replaceChild.
759
+ let walk = f.createPropertyAccessExpression(nodeRef, 'firstChild');
760
+ for (let i = 0; i < rt.childIdx; i++) {
761
+ walk = f.createPropertyAccessExpression(walk, 'nextSibling');
762
+ }
763
+ stmts.push(f.createVariableStatement(undefined, f.createVariableDeclarationList([f.createVariableDeclaration(tVar, undefined, undefined, walk)], ts.NodeFlags.Const)));
764
+ }
765
+ else {
766
+ // Comment placeholder: create a new text node and replace the comment.
767
+ const cVar = `__c${counter.t - 1}`;
768
+ let walk = f.createPropertyAccessExpression(nodeRef, 'firstChild');
769
+ for (let i = 0; i < rt.childIdx; i++) {
770
+ walk = f.createPropertyAccessExpression(walk, 'nextSibling');
771
+ }
772
+ stmts.push(f.createVariableStatement(undefined, f.createVariableDeclarationList([f.createVariableDeclaration(cVar, undefined, undefined, walk)], ts.NodeFlags.Const)));
773
+ stmts.push(f.createVariableStatement(undefined, f.createVariableDeclarationList([
774
+ f.createVariableDeclaration(tVar, undefined, undefined, f.createCallExpression(f.createPropertyAccessExpression(f.createIdentifier('__dom'), 'createTextNode'), undefined, [f.createStringLiteral('')])),
775
+ ], ts.NodeFlags.Const)));
776
+ stmts.push(f.createExpressionStatement(f.createCallExpression(f.createPropertyAccessExpression(f.createPropertyAccessExpression(f.createIdentifier(cVar), 'parentNode'), 'replaceChild'), undefined, [f.createIdentifier(tVar), f.createIdentifier(cVar)])));
777
+ }
778
+ // __bind(__t0, mask, 'text', undefined, accessor, [maskHi])
779
+ const rtArgs = [
780
+ f.createIdentifier(tVar),
781
+ createMaskLiteral(f, rt.mask),
782
+ f.createStringLiteral('text'),
783
+ f.createIdentifier('undefined'),
784
+ rt.accessor,
785
+ ];
786
+ // Only pass the 6th positional arg when the accessor reads a
787
+ // high-word prefix. Keeps the emit byte-identical to the
788
+ // pre-multi-word baseline for the common case.
789
+ if (rt.maskHi !== 0)
790
+ rtArgs.push(createMaskLiteral(f, rt.maskHi));
791
+ stmts.push(f.createExpressionStatement(f.createCallExpression(f.createIdentifier('__bind'), undefined, rtArgs)));
792
+ }
793
+ // Reactive bindings — __bind(node, mask, kind, key, accessor, [maskHi])
794
+ for (const [mask, maskHi, kind, key, accessor] of op.bindings) {
795
+ const args = [
796
+ nodeRef,
797
+ createMaskLiteral(f, mask),
798
+ f.createStringLiteral(kind),
799
+ key ? f.createStringLiteral(key) : f.createIdentifier('undefined'),
800
+ accessor,
801
+ ];
802
+ if (maskHi !== 0)
803
+ args.push(createMaskLiteral(f, maskHi));
804
+ stmts.push(f.createExpressionStatement(f.createCallExpression(f.createIdentifier('__bind'), undefined, args)));
805
+ }
806
+ }
807
+ // Emit delegated event listeners on root
808
+ for (const [eventName, entries] of delegatableEvents) {
809
+ if (entries.length < 2)
810
+ continue;
811
+ // root.onclick = (e) => { if (n1.contains(e.target)) { h1(); return } if (n2.contains(e.target)) { h2(); return } }
812
+ const eParam = f.createIdentifier('__e');
813
+ const eTarget = f.createPropertyAccessExpression(eParam, 'target');
814
+ const ifStmts = [];
815
+ for (const { nodeVar, handler } of entries) {
816
+ // if (nodeVar.contains(e.target)) { handler(e); return }
817
+ ifStmts.push(f.createIfStatement(f.createCallExpression(f.createPropertyAccessExpression(f.createIdentifier(nodeVar), 'contains'), undefined, [eTarget]), f.createBlock([
818
+ f.createExpressionStatement(f.createCallExpression(handler, undefined, [eParam])),
819
+ f.createReturnStatement(),
820
+ ], true)));
821
+ }
822
+ const delegateHandler = f.createArrowFunction(undefined, undefined, [f.createParameterDeclaration(undefined, undefined, '__e')], undefined, f.createToken(ts.SyntaxKind.EqualsGreaterThanToken), f.createBlock(ifStmts, true));
823
+ // root.addEventListener(eventName, handler)
824
+ stmts.push(f.createExpressionStatement(f.createCallExpression(f.createPropertyAccessExpression(f.createIdentifier('root'), 'addEventListener'), undefined, [f.createStringLiteral(eventName), delegateHandler])));
825
+ }
826
+ // (root, __bind, __dom) => { ... }
827
+ const patchFn = f.createArrowFunction(undefined, undefined, [
828
+ f.createParameterDeclaration(undefined, undefined, 'root'),
829
+ f.createParameterDeclaration(undefined, undefined, '__bind'),
830
+ f.createParameterDeclaration(undefined, undefined, '__dom'),
831
+ ], undefined, f.createToken(ts.SyntaxKind.EqualsGreaterThanToken), f.createBlock(stmts, true));
832
+ const call = f.createCallExpression(f.createIdentifier('elTemplate'), undefined, [
833
+ f.createStringLiteral(html),
834
+ patchFn,
835
+ ]);
836
+ return call;
837
+ }
838
+ // ── Static subtree detection ─────────────────────────────────────
839
+ function isStaticChildren(children) {
840
+ if (children.kind === ts.SyntaxKind.NullKeyword)
841
+ return true;
842
+ if (!ts.isArrayLiteralExpression(children))
843
+ return false;
844
+ return children.elements.every((child) => {
845
+ // text('literal') — static text
846
+ if (ts.isCallExpression(child) &&
847
+ ts.isIdentifier(child.expression) &&
848
+ child.expression.text === 'text') {
849
+ return child.arguments.length === 1 && ts.isStringLiteral(child.arguments[0]);
850
+ }
851
+ // Another elSplit or element helper that was already determined static
852
+ // For now, only handle text() children
853
+ return false;
854
+ });
855
+ }
856
+ function buildStaticHTML(tag, staticProps, children, _f) {
857
+ // Extract static attributes from staticFn statements
858
+ let attrs = '';
859
+ for (const stmt of staticProps) {
860
+ if (!ts.isExpressionStatement(stmt))
861
+ return null;
862
+ const expr = stmt.expression;
863
+ // __e.className = 'value'
864
+ if (ts.isBinaryExpression(expr) && ts.isPropertyAccessExpression(expr.left)) {
865
+ const prop = expr.left.name.text;
866
+ if (prop === 'className' && ts.isStringLiteral(expr.right)) {
867
+ attrs += ` class="${escapeAttr(expr.right.text)}"`;
868
+ }
869
+ }
870
+ // __e.setAttribute('key', 'value')
871
+ if (ts.isCallExpression(expr) && ts.isPropertyAccessExpression(expr.expression)) {
872
+ if (expr.expression.name.text === 'setAttribute' && expr.arguments.length === 2) {
873
+ const key = expr.arguments[0];
874
+ const val = expr.arguments[1];
875
+ if (key && val && ts.isStringLiteral(key) && ts.isStringLiteral(val)) {
876
+ attrs += ` ${key.text}="${escapeAttr(val.text)}"`;
877
+ }
878
+ else {
879
+ return null; // non-literal attribute
880
+ }
881
+ }
882
+ }
883
+ }
884
+ // Extract text children
885
+ let inner = '';
886
+ if (ts.isArrayLiteralExpression(children)) {
887
+ for (const child of children.elements) {
888
+ if (ts.isCallExpression(child) &&
889
+ ts.isIdentifier(child.expression) &&
890
+ child.expression.text === 'text') {
891
+ if (ts.isStringLiteral(child.arguments[0])) {
892
+ inner += escapeHTML(child.arguments[0].text);
893
+ }
894
+ else {
895
+ return null;
896
+ }
897
+ }
898
+ else {
899
+ return null;
900
+ }
901
+ }
902
+ }
903
+ return `<${tag}${attrs}>${inner}</${tag}>`;
904
+ }
905
+ function escapeHTML(s) {
906
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
907
+ }
908
+ function escapeAttr(s) {
909
+ return s.replace(/&/g, '&amp;').replace(/"/g, '&quot;');
910
+ }
911
+ function emitTemplateClone(html, f) {
912
+ // Emits: __cloneStaticTemplate("<html>")
913
+ //
914
+ // The helper lives in `@llui/dom` and threads through `ctx.dom` so SSR
915
+ // under jsdom/linkedom works without touching globalThis. The import
916
+ // cleanup pass (see cleanupImports) auto-injects the import when this
917
+ // emission fires.
918
+ return f.createCallExpression(f.createIdentifier('__cloneStaticTemplate'), undefined, [
919
+ f.createStringLiteral(html),
920
+ ]);
921
+ }
922
+ function isPerItemCall(node) {
923
+ // Matches: item(t => t.field) or item(t => expr)
924
+ // where item is an identifier (the scoped accessor from each() render)
925
+ if (!ts.isIdentifier(node.expression))
926
+ return false;
927
+ // Check that the first argument is an arrow function (the selector)
928
+ if (node.arguments.length !== 1)
929
+ return false;
930
+ const arg = node.arguments[0];
931
+ return ts.isArrowFunction(arg) || ts.isFunctionExpression(arg);
932
+ }
933
+ // Matches: item.FIELD — the item-proxy shorthand equivalent of item(t => t.FIELD).
934
+ // Lifetime-checked: the `item` identifier must resolve to a parameter of an
935
+ // `each({ render })` callback. Without this check, plain
936
+ // `arr.map((item) => item.field)` outside each() would be rewritten as a
937
+ // per-item binding and crash at runtime with "accessor is not a function"
938
+ // because `item.field` evaluates to a bare value (not a function) when
939
+ // treated as an accessor.
940
+ function isPerItemFieldAccess(node) {
941
+ if (!ts.isPropertyAccessExpression(node))
942
+ return false;
943
+ if (!ts.isIdentifier(node.expression))
944
+ return false;
945
+ if (node.expression.text !== 'item')
946
+ return false;
947
+ if (!ts.isIdentifier(node.name))
948
+ return false;
949
+ return isItemBoundToEachRender(node);
950
+ }
951
+ /**
952
+ * Walks up from a node and returns true iff the nearest enclosing function
953
+ * that binds an `item` parameter is the `render` property of an `each()`
954
+ * call. Handles both positional (`(item) => …`) and destructured
955
+ * (`({ item, index }) => …`) parameter bindings.
956
+ */
957
+ function isItemBoundToEachRender(node) {
958
+ let current = node.parent;
959
+ while (current) {
960
+ if (ts.isArrowFunction(current) || ts.isFunctionExpression(current)) {
961
+ if (functionParamsBindItem(current)) {
962
+ return isEachRenderCallback(current);
963
+ }
964
+ }
965
+ current = current.parent;
966
+ }
967
+ return false;
968
+ }
969
+ function functionParamsBindItem(fn) {
970
+ for (const param of fn.parameters) {
971
+ if (bindingNameBindsItem(param.name))
972
+ return true;
973
+ }
974
+ return false;
975
+ }
976
+ function bindingNameBindsItem(name) {
977
+ if (ts.isIdentifier(name))
978
+ return name.text === 'item';
979
+ if (ts.isObjectBindingPattern(name) || ts.isArrayBindingPattern(name)) {
980
+ for (const el of name.elements) {
981
+ if (ts.isBindingElement(el) && bindingNameBindsItem(el.name))
982
+ return true;
983
+ }
984
+ }
985
+ return false;
986
+ }
987
+ function isEachRenderCallback(fn) {
988
+ const parent = fn.parent;
989
+ if (!parent || !ts.isPropertyAssignment(parent))
990
+ return false;
991
+ if (!ts.isIdentifier(parent.name) || parent.name.text !== 'render')
992
+ return false;
993
+ const objLit = parent.parent;
994
+ if (!objLit || !ts.isObjectLiteralExpression(objLit))
995
+ return false;
996
+ const call = objLit.parent;
997
+ if (!call || !ts.isCallExpression(call))
998
+ return false;
999
+ if (!ts.isIdentifier(call.expression) || call.expression.text !== 'each')
1000
+ return false;
1001
+ return true;
1002
+ }
1003
+ // Matches the hoisted identifiers produced by tryDeduplicateItemSelectors: __a0, __a1, …
1004
+ // These represent already-cached per-item accessors.
1005
+ function isHoistedPerItem(node) {
1006
+ if (!ts.isIdentifier(node))
1007
+ return false;
1008
+ return /^__a\d+$/.test(node.text);
1009
+ }
1010
+ // ── Mask computation ─────────────────────────────────────────────
1011
+ // Returns { mask, readsState }
1012
+ // mask = 0 + readsState = false → constant (can fold to static)
1013
+ // mask = 0 + readsState = true → unresolvable state access (FULL_MASK)
1014
+ // mask > 0 → precise mask
1015
+ // See `NON_DELEGATION_HELPERS` in collect-deps.ts — same set of names
1016
+ // that aren't followed when scanning for `helper(s)` delegation calls.
1017
+ //# sourceMappingURL=element-rewrite.js.map