@fluixi/compiler 1.0.0-alpha.55 → 1.0.0-alpha.56
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.
- package/dist/babel-DZ7BC6T2.mjs +2 -0
- package/dist/frontend/babel/build-ir-lit.cjs +1 -0
- package/dist/frontend/babel/build-ir-lit.d.ts +26 -0
- package/dist/frontend/babel/build-ir-lit.d.ts.map +1 -0
- package/dist/frontend/babel/build-ir-lit.js +9 -0
- package/dist/frontend/babel/build-ir-lit.mjs +1 -0
- package/dist/frontend/babel/build-ir.cjs +1 -1
- package/dist/frontend/babel/build-ir.d.ts +11 -0
- package/dist/frontend/babel/build-ir.d.ts.map +1 -1
- package/dist/frontend/babel/build-ir.js +72 -13
- package/dist/frontend/babel/build-ir.mjs +1 -1
- package/dist/frontend/babel/index.cjs +2 -2
- package/dist/frontend/babel/index.mjs +2 -2
- package/dist/frontend/babel/lower-template.cjs +1 -0
- package/dist/frontend/babel/lower-template.d.ts +21 -0
- package/dist/frontend/babel/lower-template.d.ts.map +1 -0
- package/dist/frontend/babel/lower-template.js +447 -0
- package/dist/frontend/babel/lower-template.mjs +1 -0
- package/dist/frontend/babel/plugin.cjs +2 -2
- package/dist/frontend/babel/plugin.d.ts +21 -0
- package/dist/frontend/babel/plugin.d.ts.map +1 -1
- package/dist/frontend/babel/plugin.js +144 -206
- package/dist/frontend/babel/plugin.mjs +2 -2
- package/dist/integrations.cjs +7 -7
- package/dist/integrations.d.ts +19 -0
- package/dist/integrations.d.ts.map +1 -1
- package/dist/integrations.js +3 -0
- package/dist/integrations.mjs +4 -4
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/dist/babel-OGRQKOZA.mjs +0 -2
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lower a parsed Template AST (`@fluixi/template-parser`) into the compiler's
|
|
3
|
+
* host-agnostic IR. This is the Babel-aware half the parser deliberately is not:
|
|
4
|
+
* it binds each opaque hole slot to its Babel `${…}` expression (via `genExpr`,
|
|
5
|
+
* which also compiles any JSX/`` html`` `` nested inside the hole) and reproduces
|
|
6
|
+
* the exact IR the old single-pass `build-ir-lit` scanner produced — the byte-
|
|
7
|
+
* identity gate — while adding the new directive lowerings on top.
|
|
8
|
+
*
|
|
9
|
+
* Pipeline: pieces → parseTemplate → Template AST → lowerTemplate → IRNode
|
|
10
|
+
*/
|
|
11
|
+
import { types as t } from '@babel/core';
|
|
12
|
+
import _generate from '@babel/generator';
|
|
13
|
+
import { parseTemplate, } from '@fluixi/template-parser';
|
|
14
|
+
import { imperativeBackend } from '../../codegen/backends/imperative.js';
|
|
15
|
+
import { cleanJSXText, genExpr, isReactiveExpr } from './build-ir.js';
|
|
16
|
+
const generate = _generate.default ?? _generate;
|
|
17
|
+
function gen(node) {
|
|
18
|
+
return generate(node, { concise: true }).code;
|
|
19
|
+
}
|
|
20
|
+
const DELEGATED = new Set([
|
|
21
|
+
'click', 'dblclick', 'input', 'change', 'submit', 'focus', 'blur',
|
|
22
|
+
'keydown', 'keyup', 'keypress', 'mousedown', 'mouseup',
|
|
23
|
+
]);
|
|
24
|
+
/** Escape a static text run for embedding in a template literal. */
|
|
25
|
+
function escapeTemplate(s) {
|
|
26
|
+
return s.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
|
|
27
|
+
}
|
|
28
|
+
function capitalize(s) {
|
|
29
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
30
|
+
}
|
|
31
|
+
class Lowerer {
|
|
32
|
+
holes;
|
|
33
|
+
used;
|
|
34
|
+
constructor(holes, used) {
|
|
35
|
+
this.holes = holes;
|
|
36
|
+
this.used = used;
|
|
37
|
+
}
|
|
38
|
+
hole(i) {
|
|
39
|
+
// -1 marks a recovered-from missing binding; emit a harmless undefined.
|
|
40
|
+
return this.holes[i] ?? { node: t.identifier('undefined'), code: 'undefined', reactive: false };
|
|
41
|
+
}
|
|
42
|
+
/** Emit a nested IR subtree to code (for `Show` fallback / `For` children). */
|
|
43
|
+
emit(node) {
|
|
44
|
+
const { code, imports } = imperativeBackend.emit(node, {});
|
|
45
|
+
for (const s of imports)
|
|
46
|
+
this.used.add(s);
|
|
47
|
+
return code;
|
|
48
|
+
}
|
|
49
|
+
// --- content ------------------------------------------------------------
|
|
50
|
+
lowerRoot(children) {
|
|
51
|
+
const nodes = this.lowerChildren(children);
|
|
52
|
+
if (nodes.length === 1)
|
|
53
|
+
return nodes[0];
|
|
54
|
+
return { kind: 'fragment', children: nodes };
|
|
55
|
+
}
|
|
56
|
+
/** Lower a sibling list, handling text-drop and if/else/each restructuring. */
|
|
57
|
+
lowerChildren(children) {
|
|
58
|
+
const out = [];
|
|
59
|
+
for (let i = 0; i < children.length; i++) {
|
|
60
|
+
const child = children[i];
|
|
61
|
+
if (child.kind === 'Element' || child.kind === 'Component') {
|
|
62
|
+
const ifDir = child.attributes.find((a) => a.kind === 'IfDirective');
|
|
63
|
+
if (ifDir && ifDir.kind === 'IfDirective') {
|
|
64
|
+
// Pair with a following `else` sibling (skipping a whitespace-only text).
|
|
65
|
+
let elseIdx = i + 1;
|
|
66
|
+
if (elseIdx < children.length && this.isBlankText(children[elseIdx]))
|
|
67
|
+
elseIdx++;
|
|
68
|
+
const elseNode = children[elseIdx];
|
|
69
|
+
const hasElse = elseNode &&
|
|
70
|
+
(elseNode.kind === 'Element' || elseNode.kind === 'Component') &&
|
|
71
|
+
elseNode.attributes.some((a) => a.kind === 'ElseDirective');
|
|
72
|
+
out.push(this.lowerIf(child, ifDir.hole, hasElse ? elseNode : null));
|
|
73
|
+
if (hasElse)
|
|
74
|
+
i = elseIdx;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (child.attributes.some((a) => a.kind === 'EachDirective')) {
|
|
78
|
+
out.push(this.lowerEach(child));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const node = this.lowerNode(child);
|
|
83
|
+
if (node)
|
|
84
|
+
out.push(node);
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
isBlankText(n) {
|
|
89
|
+
return n.kind === 'Text' && !n.raw && cleanJSXText(n.value) === '';
|
|
90
|
+
}
|
|
91
|
+
lowerNode(node) {
|
|
92
|
+
switch (node.kind) {
|
|
93
|
+
case 'Text': {
|
|
94
|
+
if (node.raw)
|
|
95
|
+
return { kind: 'text', value: node.value };
|
|
96
|
+
const cleaned = cleanJSXText(node.value);
|
|
97
|
+
return cleaned ? { kind: 'text', value: cleaned } : null;
|
|
98
|
+
}
|
|
99
|
+
case 'Comment':
|
|
100
|
+
return null; // dropped, like JSX comments
|
|
101
|
+
case 'Expression': {
|
|
102
|
+
const h = this.hole(node.hole);
|
|
103
|
+
return { kind: 'expr', code: h.code, reactive: h.reactive };
|
|
104
|
+
}
|
|
105
|
+
case 'Fragment':
|
|
106
|
+
return { kind: 'fragment', children: this.lowerChildren(node.children) };
|
|
107
|
+
case 'Element':
|
|
108
|
+
return this.lowerElement(node);
|
|
109
|
+
case 'Component':
|
|
110
|
+
return this.lowerComponent(node);
|
|
111
|
+
default:
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
lowerElement(el) {
|
|
116
|
+
// `<component is=${C} …>…</component>` → `<Dynamic component=${C} …>` (a
|
|
117
|
+
// reactive component swap). Dynamic reads `component` via normalizeAccessor,
|
|
118
|
+
// so it must be an ACCESSOR `() => C` (a bare function would be mis-called).
|
|
119
|
+
const isAttr = el.attributes.find((a) => a.kind === 'Attribute' && a.name === 'is');
|
|
120
|
+
if (el.tag === 'component' && isAttr && isAttr.value && isAttr.value.kind === 'hole') {
|
|
121
|
+
const rest = el.attributes.filter((a) => a !== isAttr);
|
|
122
|
+
const node = this.lowerComponent({ ...el, kind: 'Component', tag: 'Dynamic', tagHole: null, attributes: rest });
|
|
123
|
+
node.props.unshift({ name: 'component', kind: 'attr', expr: `() => (${this.hole(isAttr.value.hole).code})` });
|
|
124
|
+
return node;
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
kind: 'element',
|
|
128
|
+
tag: el.tag,
|
|
129
|
+
svg: el.namespace === 'svg',
|
|
130
|
+
props: this.lowerAttributes(el.attributes),
|
|
131
|
+
children: this.lowerChildren(el.children),
|
|
132
|
+
static: false,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
lowerComponent(el) {
|
|
136
|
+
// `<${Comp} />`: the tag is an interpolated expression — use the hole's code
|
|
137
|
+
// as the component identity, so `createComponent(<expr>, …)` is emitted.
|
|
138
|
+
const name = el.tagHole != null ? this.hole(el.tagHole).code : el.tag;
|
|
139
|
+
const props = this.lowerAttributes(el.attributes);
|
|
140
|
+
// Named slots: a child with `slot="header"` becomes `props.header` (a lazy
|
|
141
|
+
// getter, like `children`); the rest stay the default `children`. This is the
|
|
142
|
+
// Solid convention — the component reads `props.header` / `props.children`.
|
|
143
|
+
const { slots, rest } = this.partitionSlots(el.children);
|
|
144
|
+
for (const [slotName, nodes] of slots) {
|
|
145
|
+
const ir = nodes.length === 1 ? nodes[0] : { kind: 'fragment', children: nodes };
|
|
146
|
+
props.push({ name: slotName, kind: 'attr', expr: this.emit(ir), jsxElement: true });
|
|
147
|
+
}
|
|
148
|
+
return { kind: 'component', name, props, children: this.lowerChildren(rest) };
|
|
149
|
+
}
|
|
150
|
+
/** Split a component's children into named slots (`slot="x"`) and the rest. */
|
|
151
|
+
partitionSlots(children) {
|
|
152
|
+
const slots = new Map();
|
|
153
|
+
const rest = [];
|
|
154
|
+
for (const child of children) {
|
|
155
|
+
if (child.kind === 'Element' || child.kind === 'Component') {
|
|
156
|
+
const slot = child.attributes.find((a) => a.kind === 'Attribute' && a.name === 'slot');
|
|
157
|
+
if (slot && slot.value && slot.value.kind === 'static') {
|
|
158
|
+
const stripped = { ...child, attributes: child.attributes.filter((a) => a !== slot) };
|
|
159
|
+
const ir = stripped.kind === 'Element' ? this.lowerElement(stripped) : this.lowerComponent(stripped);
|
|
160
|
+
const bucket = slots.get(slot.value.value) ?? [];
|
|
161
|
+
bucket.push(ir);
|
|
162
|
+
slots.set(slot.value.value, bucket);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
rest.push(child);
|
|
167
|
+
}
|
|
168
|
+
return { slots, rest };
|
|
169
|
+
}
|
|
170
|
+
// --- control-flow sugar -------------------------------------------------
|
|
171
|
+
/** `<el if=${cond}>…</el>` (+ optional `<el else>`) → `<Show when fallback>`. */
|
|
172
|
+
lowerIf(el, condHole, elseEl) {
|
|
173
|
+
const cond = this.hole(condHole);
|
|
174
|
+
const props = [
|
|
175
|
+
{ name: 'when', kind: 'attr', expr: cond.code, reactive: cond.reactive },
|
|
176
|
+
];
|
|
177
|
+
if (elseEl) {
|
|
178
|
+
const elseIR = this.stripAndLower(elseEl, (a) => a.kind === 'ElseDirective');
|
|
179
|
+
props.push({ name: 'fallback', kind: 'attr', expr: this.emit(elseIR), jsxElement: true });
|
|
180
|
+
}
|
|
181
|
+
const child = this.stripAndLower(el, (a) => a.kind === 'IfDirective');
|
|
182
|
+
return { kind: 'component', name: 'Show', props, children: [child] };
|
|
183
|
+
}
|
|
184
|
+
/** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
|
|
185
|
+
lowerEach(el) {
|
|
186
|
+
const eachDir = el.attributes.find((a) => a.kind === 'EachDirective');
|
|
187
|
+
if (!eachDir || eachDir.kind !== 'EachDirective')
|
|
188
|
+
return this.lowerNode(el);
|
|
189
|
+
const each = this.hole(eachDir.hole);
|
|
190
|
+
const props = [{ name: 'each', kind: 'attr', expr: each.code, reactive: each.reactive }];
|
|
191
|
+
if (eachDir.key) {
|
|
192
|
+
// `<For by>` wants a KEY FUNCTION, not a string. `key="id"` selects the
|
|
193
|
+
// `id` field: `(item) => item["id"]`; `key=${fn}` passes the function.
|
|
194
|
+
const by = 'static' in eachDir.key
|
|
195
|
+
? `(item) => item[${JSON.stringify(eachDir.key.static)}]`
|
|
196
|
+
: this.hole(eachDir.key.hole).code;
|
|
197
|
+
props.push({ name: 'by', kind: 'attr', expr: by });
|
|
198
|
+
}
|
|
199
|
+
// The element minus its each/key, repeated per item.
|
|
200
|
+
const arrow = this.itemArrow(el);
|
|
201
|
+
let childrenCode;
|
|
202
|
+
if (arrow) {
|
|
203
|
+
const bodyExpr = arrow.body;
|
|
204
|
+
const bodyIR = { kind: 'expr', code: genExpr(bodyExpr, this.used), reactive: isReactiveExpr(bodyExpr) };
|
|
205
|
+
const repeated = this.rebuildWithChildren(el, [bodyIR]);
|
|
206
|
+
childrenCode = `(${arrow.params.map((p) => gen(p)).join(', ')}) => (${this.emit(repeated)})`;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
const repeated = this.stripAndLower(el, (a) => a.kind === 'EachDirective');
|
|
210
|
+
childrenCode = `() => (${this.emit(repeated)})`;
|
|
211
|
+
}
|
|
212
|
+
const children = [{ kind: 'expr', code: childrenCode, reactive: false }];
|
|
213
|
+
return { kind: 'component', name: 'For', props, children };
|
|
214
|
+
}
|
|
215
|
+
/** The single `${item => expr}` child arrow, if that's the element's content. */
|
|
216
|
+
itemArrow(el) {
|
|
217
|
+
const kids = el.children.filter((c) => !this.isBlankText(c));
|
|
218
|
+
if (kids.length !== 1 || kids[0].kind !== 'Expression')
|
|
219
|
+
return null;
|
|
220
|
+
const node = this.hole(kids[0].hole).node;
|
|
221
|
+
if (t.isArrowFunctionExpression(node) && !t.isBlockStatement(node.body))
|
|
222
|
+
return node;
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
/** Lower an element to IR, dropping the directive attribute matched by `drop`. */
|
|
226
|
+
stripAndLower(el, drop) {
|
|
227
|
+
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
228
|
+
return clone.kind === 'Element' ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
229
|
+
}
|
|
230
|
+
/** Same element (minus each/key) but with explicit IR children. */
|
|
231
|
+
rebuildWithChildren(el, children) {
|
|
232
|
+
const props = this.lowerAttributes(el.attributes.filter((a) => a.kind !== 'EachDirective'));
|
|
233
|
+
if (el.kind === 'Element') {
|
|
234
|
+
return { kind: 'element', tag: el.tag, svg: el.namespace === 'svg', props, children, static: false };
|
|
235
|
+
}
|
|
236
|
+
return { kind: 'component', name: el.tag, props, children };
|
|
237
|
+
}
|
|
238
|
+
// --- attributes ---------------------------------------------------------
|
|
239
|
+
lowerAttributes(attrs) {
|
|
240
|
+
const props = [];
|
|
241
|
+
const classEntries = [];
|
|
242
|
+
let classReactive = false;
|
|
243
|
+
const styleEntries = [];
|
|
244
|
+
let styleReactive = false;
|
|
245
|
+
const usePairs = [];
|
|
246
|
+
for (const a of attrs) {
|
|
247
|
+
switch (a.kind) {
|
|
248
|
+
case 'IfDirective':
|
|
249
|
+
case 'EachDirective':
|
|
250
|
+
case 'ElseDirective':
|
|
251
|
+
break; // handled as control-flow at the parent level
|
|
252
|
+
case 'Attribute':
|
|
253
|
+
props.push(this.plainAttr(a));
|
|
254
|
+
break;
|
|
255
|
+
case 'PropertyBinding':
|
|
256
|
+
props.push({ name: a.name, kind: 'prop', expr: this.hole(a.hole).code, reactive: this.hole(a.hole).reactive });
|
|
257
|
+
break;
|
|
258
|
+
case 'EventBinding':
|
|
259
|
+
props.push(this.eventProp(a));
|
|
260
|
+
break;
|
|
261
|
+
case 'RefBinding':
|
|
262
|
+
props.push({ name: 'ref', kind: 'ref', expr: this.hole(a.hole).code, reactive: this.hole(a.hole).reactive });
|
|
263
|
+
break;
|
|
264
|
+
case 'Spread':
|
|
265
|
+
props.push({ name: '', kind: 'spread', expr: this.hole(a.hole).code });
|
|
266
|
+
break;
|
|
267
|
+
case 'ClassDirective': {
|
|
268
|
+
const h = this.hole(a.hole);
|
|
269
|
+
classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
|
|
270
|
+
classReactive = classReactive || h.reactive;
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
case 'StyleDirective': {
|
|
274
|
+
const h = this.hole(a.hole);
|
|
275
|
+
styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
|
|
276
|
+
styleReactive = styleReactive || h.reactive;
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
case 'BindDirective':
|
|
280
|
+
props.push(...this.bindProps(a.name, this.hole(a.hole).code));
|
|
281
|
+
break;
|
|
282
|
+
case 'UseDirective': {
|
|
283
|
+
// `use=${dir}` → [dir]; `use:name` → [name]; `use:name=${opts}` →
|
|
284
|
+
// [name, () => opts]. `name` is an in-scope identifier (like Solid).
|
|
285
|
+
const dir = a.name ?? (a.hole != null ? this.hole(a.hole).code : null);
|
|
286
|
+
if (!dir)
|
|
287
|
+
break;
|
|
288
|
+
// parens so an object literal opts isn't read as a block body
|
|
289
|
+
usePairs.push(a.name != null && a.hole != null ? `[${dir}, () => (${this.hole(a.hole).code})]` : `[${dir}]`);
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Merge all class:/style: directives into one classList/style prop so they
|
|
295
|
+
// don't collide as duplicate keys in the emitted props object.
|
|
296
|
+
if (classEntries.length > 0) {
|
|
297
|
+
props.push({ name: 'classList', kind: 'attr', expr: `{ ${classEntries.join(', ')} }`, reactive: classReactive });
|
|
298
|
+
}
|
|
299
|
+
if (styleEntries.length > 0) {
|
|
300
|
+
props.push({ name: 'style', kind: 'attr', expr: `{ ${styleEntries.join(', ')} }`, reactive: styleReactive });
|
|
301
|
+
}
|
|
302
|
+
// All use: directives normalize to one `use` prop = an array of [dir, accessor?] pairs.
|
|
303
|
+
if (usePairs.length > 0) {
|
|
304
|
+
props.push({ name: 'use', kind: 'attr', expr: `[${usePairs.join(', ')}]` });
|
|
305
|
+
}
|
|
306
|
+
return props;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Lower an event binding. Plain `@click`/`onClick` (no modifiers) stays a
|
|
310
|
+
* DELEGATED handler (unchanged, byte-identical with JSX). `on:click` or any
|
|
311
|
+
* modifier (`.capture`/`.once`/`.passive`/`.prevent`/`.stop`/`.self`) emits a
|
|
312
|
+
* NATIVE `on:name` prop the runtime attaches via addEventListener.
|
|
313
|
+
*/
|
|
314
|
+
eventProp(a) {
|
|
315
|
+
const dom = a.name.toLowerCase();
|
|
316
|
+
const handler = this.hole(a.hole).code;
|
|
317
|
+
const native = a.syntax === 'colon' || a.modifiers.length > 0;
|
|
318
|
+
if (!native) {
|
|
319
|
+
return {
|
|
320
|
+
name: 'on' + capitalize(a.name),
|
|
321
|
+
kind: 'event',
|
|
322
|
+
event: { name: dom, delegated: DELEGATED.has(dom) },
|
|
323
|
+
expr: handler,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
const wrapped = this.wrapHandler(handler, a.modifiers);
|
|
327
|
+
const options = this.eventOptions(a.modifiers);
|
|
328
|
+
const value = options ? `[${wrapped}, ${options}]` : wrapped;
|
|
329
|
+
return { name: 'on:' + dom, kind: 'attr', expr: value };
|
|
330
|
+
}
|
|
331
|
+
/** Wrap a handler for `.prevent`/`.stop`/`.self` (no wrap when none apply). */
|
|
332
|
+
wrapHandler(handler, mods) {
|
|
333
|
+
const guard = mods.includes('self') ? 'if (e.target !== e.currentTarget) return; ' : '';
|
|
334
|
+
const pre = [];
|
|
335
|
+
if (mods.includes('prevent'))
|
|
336
|
+
pre.push('e.preventDefault();');
|
|
337
|
+
if (mods.includes('stop'))
|
|
338
|
+
pre.push('e.stopPropagation();');
|
|
339
|
+
if (!guard && pre.length === 0)
|
|
340
|
+
return handler;
|
|
341
|
+
return `(e) => { ${guard}${pre.join(' ')} return (${handler})(e); }`;
|
|
342
|
+
}
|
|
343
|
+
/** addEventListener options object from `.capture`/`.once`/`.passive`, or null. */
|
|
344
|
+
eventOptions(mods) {
|
|
345
|
+
const opts = [];
|
|
346
|
+
if (mods.includes('capture'))
|
|
347
|
+
opts.push('capture: true');
|
|
348
|
+
if (mods.includes('once'))
|
|
349
|
+
opts.push('once: true');
|
|
350
|
+
if (mods.includes('passive'))
|
|
351
|
+
opts.push('passive: true');
|
|
352
|
+
return opts.length ? `{ ${opts.join(', ')} }` : null;
|
|
353
|
+
}
|
|
354
|
+
/** `bind:value=${sig}` → `value={sig[0]()}` + `onInput={e => sig[1](e…value)}`. */
|
|
355
|
+
bindProps(prop, sigCode) {
|
|
356
|
+
const checked = prop === 'checked';
|
|
357
|
+
const evt = checked ? 'change' : 'input';
|
|
358
|
+
const accessor = checked ? 'checked' : 'value';
|
|
359
|
+
// Parenthesize the signal expression — it may be more than an identifier
|
|
360
|
+
// (`[get, set] as const`, `props.model`, a call), and `expr[0]()` would then
|
|
361
|
+
// mis-parse (e.g. `[...] as const[0]`).
|
|
362
|
+
const sig = `(${sigCode})`;
|
|
363
|
+
return [
|
|
364
|
+
{ name: prop, kind: 'attr', expr: `${sig}[0]()`, reactive: true },
|
|
365
|
+
{
|
|
366
|
+
name: 'on' + capitalize(evt),
|
|
367
|
+
kind: 'event',
|
|
368
|
+
event: { name: evt, delegated: DELEGATED.has(evt) },
|
|
369
|
+
// delegated handler: `target` is the bound element (currentTarget is the root)
|
|
370
|
+
expr: `(e) => ${sig}[1](e.target.${accessor})`,
|
|
371
|
+
},
|
|
372
|
+
];
|
|
373
|
+
}
|
|
374
|
+
/** A plain attribute (incl. lit `?bool`, mixed values) — old `mkProp` behavior. */
|
|
375
|
+
plainAttr(a) {
|
|
376
|
+
const v = a.value;
|
|
377
|
+
// `class=${{...}}` (object literal) -> classList (Solid convention), so it
|
|
378
|
+
// doesn't collide with a static `class="box"` (which stays className) — both
|
|
379
|
+
// apply. `class` -> className; `style=${{...}}` stays `style` (runtime merges).
|
|
380
|
+
// `html=${x}` is the raw-HTML escape hatch -> innerHTML.
|
|
381
|
+
let name = a.name;
|
|
382
|
+
if (a.name === 'class') {
|
|
383
|
+
name = v && v.kind === 'hole' && t.isObjectExpression(this.hole(v.hole).node) ? 'classList' : 'className';
|
|
384
|
+
}
|
|
385
|
+
else if (a.name === 'html') {
|
|
386
|
+
name = 'innerHTML';
|
|
387
|
+
}
|
|
388
|
+
const kind = 'attr';
|
|
389
|
+
const base = { name, kind };
|
|
390
|
+
if (v == null) {
|
|
391
|
+
base.literal = true;
|
|
392
|
+
return base;
|
|
393
|
+
}
|
|
394
|
+
if (v.kind === 'static') {
|
|
395
|
+
base.literal = v.value;
|
|
396
|
+
return base;
|
|
397
|
+
}
|
|
398
|
+
if (v.kind === 'hole') {
|
|
399
|
+
const h = this.hole(v.hole);
|
|
400
|
+
base.expr = h.code;
|
|
401
|
+
base.reactive = h.reactive;
|
|
402
|
+
return base;
|
|
403
|
+
}
|
|
404
|
+
// mixed: template literal, reactive if any hole is
|
|
405
|
+
let reactive = false;
|
|
406
|
+
const body = v.parts
|
|
407
|
+
.map((p) => {
|
|
408
|
+
if ('text' in p)
|
|
409
|
+
return escapeTemplate(p.text);
|
|
410
|
+
const h = this.hole(p.hole);
|
|
411
|
+
reactive = reactive || h.reactive;
|
|
412
|
+
return '${' + h.code + '}';
|
|
413
|
+
})
|
|
414
|
+
.join('');
|
|
415
|
+
base.expr = '`' + body + '`';
|
|
416
|
+
base.reactive = reactive;
|
|
417
|
+
return base;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Build IR from a lit `` html`...` `` / `` svg`...` `` TaggedTemplateExpression.
|
|
422
|
+
* Parses (Babel-free) then lowers (Babel-aware) — a drop-in for the old
|
|
423
|
+
* single-pass scanner, with the same signature.
|
|
424
|
+
*/
|
|
425
|
+
export function lowerLitTemplate(node, used, opts = {}) {
|
|
426
|
+
const quasi = node.quasi;
|
|
427
|
+
const holes = quasi.expressions.map((e) => {
|
|
428
|
+
const expr = e;
|
|
429
|
+
return { node: expr, code: genExpr(expr, used), reactive: isReactiveExpr(expr) };
|
|
430
|
+
});
|
|
431
|
+
const pieces = buildPieces(quasi);
|
|
432
|
+
const { root } = parseTemplate(pieces, { svg: opts.svg });
|
|
433
|
+
return new Lowerer(holes, used).lowerRoot(root.children);
|
|
434
|
+
}
|
|
435
|
+
/** Turn a Babel TemplateLiteral into position-carrying parser pieces. */
|
|
436
|
+
function buildPieces(quasi) {
|
|
437
|
+
const pieces = [];
|
|
438
|
+
quasi.quasis.forEach((q, i) => {
|
|
439
|
+
const text = q.value.cooked ?? q.value.raw;
|
|
440
|
+
pieces.push({ kind: 'static', text, start: q.start ?? 0 });
|
|
441
|
+
if (i < quasi.expressions.length) {
|
|
442
|
+
const e = quasi.expressions[i];
|
|
443
|
+
pieces.push({ kind: 'hole', index: i, start: e.start ?? 0, end: e.end ?? 0 });
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
return pieces;
|
|
447
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{types as v}from"@babel/core";import A from"@babel/generator";import{parseTemplate as Z}from"@fluixi/template-parser";var x={version:1,module:"@fluixi/dom",symbols:["createNativeElement","insert","spread","setAttribute","delegateEvents","createComponent","createMemo","untrack","Show","For","Index","Switch","Match","Dynamic","ErrorBoundary"]},te={version:2,module:"@fluixi/dom",symbols:[...x.symbols,"template","cloneTemplate","walk"]};var X={show:"Show",for:"For",index:"Index",switch:"Switch",match:"Match",dynamic:"Dynamic",errorBoundary:"ErrorBoundary",suspense:"Suspense"};function j(n){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(n)}function b(n){return j(n)?n:JSON.stringify(n)}function L(n){return n.expr!==void 0?n.reactive?`() => (${n.expr})`:`(${n.expr})`:JSON.stringify(n.literal??!0)}function D(n){return n.expr!==void 0?`(${n.expr})`:JSON.stringify(n.literal??!0)}function O(n,e,t){let r=n.filter(l=>l.kind==="spread"),a=n.filter(l=>l.kind!=="spread").map(l=>`${b(l.name)}: ${L(l)}`);e!=null&&a.push(`children: ${e}`);let o=`{ ${a.join(", ")} }`;return r.length>0?(t.add("mergeProps"),`mergeProps(${r.map(l=>l.expr).join(", ")}, ${o})`):o}function y(n,e){return n.length===1?f(n[0],e):`[${n.map(t=>f(t,e)).join(", ")}]`}function N(n,e,t,r){r.add("createMemo"),r.add("createComponent");let i=e.filter(s=>s.kind==="spread"),o=e.filter(s=>s.kind!=="spread").map(s=>`get ${b(s.name)}() { return ${D(s)}; }`);t.length>0&&o.push(`get children() { return ${y(t,r)}; }`);let l=`{ ${o.join(", ")} }`;return i.length>0&&(r.add("mergeProps"),l=`mergeProps(${i.map(s=>s.expr).join(", ")}, ${l})`),`createMemo(() => createComponent(${n}, ${l}))`}function f(n,e){switch(n.kind){case"text":return JSON.stringify(n.value);case"expr":return n.reactive?`() => (${n.code})`:`(${n.code})`;case"fragment":return n.children.length===0?"null":y(n.children,e);case"component":return N(n.name,n.props,n.children,e);case"control":{let t=X[n.control]??n.control;return e.add(t),N(t,n.props,n.children,e)}case"element":{e.add("createNativeElement");let t=JSON.stringify(n.tag),r="_el$",i=[],a=n.svg?`${t}, true`:t;if(i.push(`const ${r} = createNativeElement(${a});`),n.props.length>0){e.add("spread");let o=n.svg?", isSVG: true":"";i.push(`spread({ element: ${r}, props: ${O(n.props,null,e)}${o} });`)}for(let o of n.children){e.add("insert");let l=f(o,e),s=o.kind==="expr"&&o.reactive||o.kind==="component"||o.kind==="control";i.push(s?`insert(${r}, ${l}, null);`:`insert(${r}, ${l});`)}return i.push(`return ${r};`),`(() => { ${i.join(" ")} })()`}}}var h={name:"imperative",contract:x,emit(n,e){let t=new Set;return{code:f(n,t),imports:Array.from(t)}}};import{types as c,traverse as B,template as F}from"@babel/core";import I from"@babel/generator";function S(n,e=new Set,t={}){return R(n,e,t)}var M=I.default??I;function C(n){return M(n,{concise:!0}).code}function _(n,e){let t=c.cloneNode(n,!0),r=c.file(c.program([c.expressionStatement(t)])),i=!1,a=(o,l,s)=>{for(let p of s)e.add(p);o.replaceWith(F.expression(l,{placeholderPattern:!1,plugins:["typescript"]})()),o.skip(),i=!0};return B(r,{"JSXElement|JSXFragment"(o){let l=o.node,{code:s,imports:p}=h.emit(k(l,e),{});a(o,s,p)},TaggedTemplateExpression(o){let l=o.node.tag;if(!c.isIdentifier(l)||l.name!=="html"&&l.name!=="svg")return;let s=S(o.node,e,{svg:l.name==="svg"}),{code:p,imports:m}=h.emit(s,{});a(o,p,m)}}),i?r.program.body[0].expression:n}function d(n,e){return C(_(n,e))}var H=new Set(["svg","path","circle","rect","line","polygon","polyline","ellipse","g","defs","clipPath","text"]),V=/^on[A-Z]/,G=new Set(["click","dblclick","input","change","submit","focus","blur","keydown","keyup","keypress","mousedown","mouseup"]);function K(n){if(c.isJSXIdentifier(n)){let e=n.name[0]!==n.name[0].toLowerCase();return{tag:n.name,component:e}}return c.isJSXMemberExpression(n)?{tag:C(n),component:!0}:c.isJSXNamespacedName(n)?{tag:`${n.namespace.name}:${n.name.name}`,component:!1}:{tag:"div",component:!1}}function U(n){return c.isJSXIdentifier(n)?n.name==="class"?"className":n.name:c.isJSXNamespacedName(n)?`${n.namespace.name}:${n.name.name}`:"unknown"}function u(n){return c.isCallExpression(n)||c.isOptionalCallExpression(n)?!0:c.isMemberExpression(n)||c.isOptionalMemberExpression(n)?!(!n.computed&&c.isIdentifier(n.property,{name:"children"})):c.isConditionalExpression(n)?u(n.test)||u(n.consequent)||u(n.alternate):c.isLogicalExpression(n)||c.isBinaryExpression(n)?u(n.left)||u(n.right):c.isTemplateLiteral(n)?n.expressions.some(e=>u(e)):c.isObjectExpression(n)?n.properties.some(e=>c.isObjectProperty(e)&&!e.computed&&u(e.value)):c.isArrayExpression(n)?n.elements.some(e=>e!=null&&!c.isSpreadElement(e)&&u(e)):!1}function W(n,e){let t=U(n.name),r=V.test(t),i=r||t.startsWith("on:"),o={name:t,kind:r?"event":"attr"};r&&(o.event={name:t.slice(2).toLowerCase(),delegated:G.has(t.slice(2).toLowerCase())});let l=n.value;if(l==null)return o.literal=!0,o;if(c.isStringLiteral(l))return o.literal=l.value,o;if(c.isJSXExpressionContainer(l)&&!c.isJSXEmptyExpression(l.expression)){let s=l.expression;return c.isStringLiteral(s)||c.isNumericLiteral(s)||c.isBooleanLiteral(s)?(o.literal=s.value,o):(o.expr=d(s,e),o.reactive=i?!1:u(s),(c.isJSXElement(s)||c.isJSXFragment(s))&&(o.jsxElement=!0),o)}return o.literal=!0,o}function $(n,e){let t=n.value;return t==null?null:c.isStringLiteral(t)?JSON.stringify(t.value):c.isJSXExpressionContainer(t)&&!c.isJSXEmptyExpression(t.expression)?d(t.expression,e):null}function z(n,e){let t=[],r=[];for(let i of n){if(c.isJSXSpreadAttribute(i)){t.push({name:"",kind:"spread",expr:d(i.argument,e)});continue}if(!c.isJSXAttribute(i))continue;let a=c.isJSXNamespacedName(i.name)?i.name.namespace.name:null;if(a==="use"){let o=i.name.name.name;e.add(o);let l=$(i,e);r.push(l!=null?`[${o}, () => (${l})]`:`[${o}]`);continue}if(a==="oncapture"){let o=i.name.name.name.toLowerCase(),l=$(i,e)??"undefined";t.push({name:"on:"+o,kind:"attr",expr:`[${l}, { capture: true }]`});continue}t.push(W(i,e))}return r.length>0&&t.push({name:"use",kind:"attr",expr:`[${r.join(", ")}]`}),t}function g(n){let e=n.split(/\r\n|\n|\r/),t=0;for(let i=0;i<e.length;i++)/[^ \t]/.test(e[i])&&(t=i);let r="";for(let i=0;i<e.length;i++){let a=e[i].replace(/\t/g," ");i!==0&&(a=a.replace(/^ +/,"")),i!==e.length-1&&(a=a.replace(/ +$/,"")),a&&(i!==t&&(a+=" "),r+=a)}return r}function w(n,e){let t=[];for(let r of n)if(c.isJSXText(r)){let i=g(r.value);i&&t.push({kind:"text",value:i})}else if(c.isJSXExpressionContainer(r)){if(!c.isJSXEmptyExpression(r.expression)){let i=r.expression;t.push({kind:"expr",code:d(i,e),reactive:u(i)})}}else c.isJSXElement(r)||c.isJSXFragment(r)?t.push(k(r,e)):c.isJSXSpreadChild(r)&&t.push({kind:"expr",code:d(r.expression,e),reactive:!1});return t}function k(n,e=new Set){if(c.isJSXFragment(n))return{kind:"fragment",children:w(n.children,e)};let{tag:t,component:r}=K(n.openingElement.name),i=z(n.openingElement.attributes,e),a=w(n.children,e);return r?{kind:"component",name:t,props:i,children:a}:{kind:"element",tag:t,svg:H.has(t),props:i,children:a,static:!1}}var q=A.default??A;function Q(n){return q(n,{concise:!0}).code}var J=new Set(["click","dblclick","input","change","submit","focus","blur","keydown","keyup","keypress","mousedown","mouseup"]);function Y(n){return n.replace(/\\/g,"\\\\").replace(/`/g,"\\`").replace(/\$\{/g,"\\${")}function P(n){return n.charAt(0).toUpperCase()+n.slice(1)}var E=class{constructor(e,t){this.holes=e;this.used=t}hole(e){return this.holes[e]??{node:v.identifier("undefined"),code:"undefined",reactive:!1}}emit(e){let{code:t,imports:r}=h.emit(e,{});for(let i of r)this.used.add(i);return t}lowerRoot(e){let t=this.lowerChildren(e);return t.length===1?t[0]:{kind:"fragment",children:t}}lowerChildren(e){let t=[];for(let r=0;r<e.length;r++){let i=e[r];if(i.kind==="Element"||i.kind==="Component"){let o=i.attributes.find(l=>l.kind==="IfDirective");if(o&&o.kind==="IfDirective"){let l=r+1;l<e.length&&this.isBlankText(e[l])&&l++;let s=e[l],p=s&&(s.kind==="Element"||s.kind==="Component")&&s.attributes.some(m=>m.kind==="ElseDirective");t.push(this.lowerIf(i,o.hole,p?s:null)),p&&(r=l);continue}if(i.attributes.some(l=>l.kind==="EachDirective")){t.push(this.lowerEach(i));continue}}let a=this.lowerNode(i);a&&t.push(a)}return t}isBlankText(e){return e.kind==="Text"&&!e.raw&&g(e.value)===""}lowerNode(e){switch(e.kind){case"Text":{if(e.raw)return{kind:"text",value:e.value};let t=g(e.value);return t?{kind:"text",value:t}:null}case"Comment":return null;case"Expression":{let t=this.hole(e.hole);return{kind:"expr",code:t.code,reactive:t.reactive}}case"Fragment":return{kind:"fragment",children:this.lowerChildren(e.children)};case"Element":return this.lowerElement(e);case"Component":return this.lowerComponent(e);default:return null}}lowerElement(e){let t=e.attributes.find(r=>r.kind==="Attribute"&&r.name==="is");if(e.tag==="component"&&t&&t.value&&t.value.kind==="hole"){let r=e.attributes.filter(a=>a!==t),i=this.lowerComponent({...e,kind:"Component",tag:"Dynamic",tagHole:null,attributes:r});return i.props.unshift({name:"component",kind:"attr",expr:`() => (${this.hole(t.value.hole).code})`}),i}return{kind:"element",tag:e.tag,svg:e.namespace==="svg",props:this.lowerAttributes(e.attributes),children:this.lowerChildren(e.children),static:!1}}lowerComponent(e){let t=e.tagHole!=null?this.hole(e.tagHole).code:e.tag,r=this.lowerAttributes(e.attributes),{slots:i,rest:a}=this.partitionSlots(e.children);for(let[o,l]of i){let s=l.length===1?l[0]:{kind:"fragment",children:l};r.push({name:o,kind:"attr",expr:this.emit(s),jsxElement:!0})}return{kind:"component",name:t,props:r,children:this.lowerChildren(a)}}partitionSlots(e){let t=new Map,r=[];for(let i of e){if(i.kind==="Element"||i.kind==="Component"){let a=i.attributes.find(o=>o.kind==="Attribute"&&o.name==="slot");if(a&&a.value&&a.value.kind==="static"){let o={...i,attributes:i.attributes.filter(p=>p!==a)},l=o.kind==="Element"?this.lowerElement(o):this.lowerComponent(o),s=t.get(a.value.value)??[];s.push(l),t.set(a.value.value,s);continue}}r.push(i)}return{slots:t,rest:r}}lowerIf(e,t,r){let i=this.hole(t),a=[{name:"when",kind:"attr",expr:i.code,reactive:i.reactive}];if(r){let l=this.stripAndLower(r,s=>s.kind==="ElseDirective");a.push({name:"fallback",kind:"attr",expr:this.emit(l),jsxElement:!0})}let o=this.stripAndLower(e,l=>l.kind==="IfDirective");return{kind:"component",name:"Show",props:a,children:[o]}}lowerEach(e){let t=e.attributes.find(s=>s.kind==="EachDirective");if(!t||t.kind!=="EachDirective")return this.lowerNode(e);let r=this.hole(t.hole),i=[{name:"each",kind:"attr",expr:r.code,reactive:r.reactive}];if(t.key){let s="static"in t.key?`(item) => item[${JSON.stringify(t.key.static)}]`:this.hole(t.key.hole).code;i.push({name:"by",kind:"attr",expr:s})}let a=this.itemArrow(e),o;if(a){let s=a.body,p={kind:"expr",code:d(s,this.used),reactive:u(s)},m=this.rebuildWithChildren(e,[p]);o=`(${a.params.map(T=>Q(T)).join(", ")}) => (${this.emit(m)})`}else{let s=this.stripAndLower(e,p=>p.kind==="EachDirective");o=`() => (${this.emit(s)})`}return{kind:"component",name:"For",props:i,children:[{kind:"expr",code:o,reactive:!1}]}}itemArrow(e){let t=e.children.filter(i=>!this.isBlankText(i));if(t.length!==1||t[0].kind!=="Expression")return null;let r=this.hole(t[0].hole).node;return v.isArrowFunctionExpression(r)&&!v.isBlockStatement(r.body)?r:null}stripAndLower(e,t){let r={...e,attributes:e.attributes.filter(i=>!t(i))};return r.kind==="Element"?this.lowerElement(r):this.lowerComponent(r)}rebuildWithChildren(e,t){let r=this.lowerAttributes(e.attributes.filter(i=>i.kind!=="EachDirective"));return e.kind==="Element"?{kind:"element",tag:e.tag,svg:e.namespace==="svg",props:r,children:t,static:!1}:{kind:"component",name:e.tag,props:r,children:t}}lowerAttributes(e){let t=[],r=[],i=!1,a=[],o=!1,l=[];for(let s of e)switch(s.kind){case"IfDirective":case"EachDirective":case"ElseDirective":break;case"Attribute":t.push(this.plainAttr(s));break;case"PropertyBinding":t.push({name:s.name,kind:"prop",expr:this.hole(s.hole).code,reactive:this.hole(s.hole).reactive});break;case"EventBinding":t.push(this.eventProp(s));break;case"RefBinding":t.push({name:"ref",kind:"ref",expr:this.hole(s.hole).code,reactive:this.hole(s.hole).reactive});break;case"Spread":t.push({name:"",kind:"spread",expr:this.hole(s.hole).code});break;case"ClassDirective":{let p=this.hole(s.hole);r.push(`${JSON.stringify(s.name)}: ${p.code}`),i=i||p.reactive;break}case"StyleDirective":{let p=this.hole(s.hole);a.push(`${JSON.stringify(s.name)}: ${p.code}`),o=o||p.reactive;break}case"BindDirective":t.push(...this.bindProps(s.name,this.hole(s.hole).code));break;case"UseDirective":{let p=s.name??(s.hole!=null?this.hole(s.hole).code:null);if(!p)break;l.push(s.name!=null&&s.hole!=null?`[${p}, () => (${this.hole(s.hole).code})]`:`[${p}]`);break}}return r.length>0&&t.push({name:"classList",kind:"attr",expr:`{ ${r.join(", ")} }`,reactive:i}),a.length>0&&t.push({name:"style",kind:"attr",expr:`{ ${a.join(", ")} }`,reactive:o}),l.length>0&&t.push({name:"use",kind:"attr",expr:`[${l.join(", ")}]`}),t}eventProp(e){let t=e.name.toLowerCase(),r=this.hole(e.hole).code;if(!(e.syntax==="colon"||e.modifiers.length>0))return{name:"on"+P(e.name),kind:"event",event:{name:t,delegated:J.has(t)},expr:r};let a=this.wrapHandler(r,e.modifiers),o=this.eventOptions(e.modifiers),l=o?`[${a}, ${o}]`:a;return{name:"on:"+t,kind:"attr",expr:l}}wrapHandler(e,t){let r=t.includes("self")?"if (e.target !== e.currentTarget) return; ":"",i=[];return t.includes("prevent")&&i.push("e.preventDefault();"),t.includes("stop")&&i.push("e.stopPropagation();"),!r&&i.length===0?e:`(e) => { ${r}${i.join(" ")} return (${e})(e); }`}eventOptions(e){let t=[];return e.includes("capture")&&t.push("capture: true"),e.includes("once")&&t.push("once: true"),e.includes("passive")&&t.push("passive: true"),t.length?`{ ${t.join(", ")} }`:null}bindProps(e,t){let r=e==="checked",i=r?"change":"input",a=r?"checked":"value",o=`(${t})`;return[{name:e,kind:"attr",expr:`${o}[0]()`,reactive:!0},{name:"on"+P(i),kind:"event",event:{name:i,delegated:J.has(i)},expr:`(e) => ${o}[1](e.target.${a})`}]}plainAttr(e){let t=e.value,r=e.name;e.name==="class"?r=t&&t.kind==="hole"&&v.isObjectExpression(this.hole(t.hole).node)?"classList":"className":e.name==="html"&&(r="innerHTML");let a={name:r,kind:"attr"};if(t==null)return a.literal=!0,a;if(t.kind==="static")return a.literal=t.value,a;if(t.kind==="hole"){let s=this.hole(t.hole);return a.expr=s.code,a.reactive=s.reactive,a}let o=!1,l=t.parts.map(s=>{if("text"in s)return Y(s.text);let p=this.hole(s.hole);return o=o||p.reactive,"${"+p.code+"}"}).join("");return a.expr="`"+l+"`",a.reactive=o,a}};function R(n,e,t={}){let r=n.quasi,i=r.expressions.map(l=>{let s=l;return{node:s,code:d(s,e),reactive:u(s)}}),a=ee(r),{root:o}=Z(a,{svg:t.svg});return new E(i,e).lowerRoot(o.children)}function ee(n){let e=[];return n.quasis.forEach((t,r)=>{let i=t.value.cooked??t.value.raw;if(e.push({kind:"static",text:i,start:t.start??0}),r<n.expressions.length){let a=n.expressions[r];e.push({kind:"hole",index:r,start:a.start??0,end:a.end??0})}}),e}export{R as lowerLitTemplate};
|