@sigil-dev/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.
@@ -0,0 +1,366 @@
1
+ import { types as t } from "@babel/core";
2
+
3
+ /**
4
+ * Mount dynamic (non-primitive, non-keyed) content using an anchor comment node.
5
+ * Generates a mount function that handles arrays, Nodes, and primitive values,
6
+ * plus a createEffect that diffs by removing previous nodes and re-mounting.
7
+ * In hydrate mode, claims the comment from the pool instead of creating.
8
+ */
9
+ export function buildAnchorMount(
10
+ varName: string,
11
+ expr: t.Expression,
12
+ statements: t.Statement[],
13
+ genId: () => string,
14
+ hydrate?: boolean,
15
+ nodesVar?: string,
16
+ parentVar?: string,
17
+ ): void {
18
+ const anchorVar = genId();
19
+ const prevVar = genId();
20
+ const mountFn = genId();
21
+
22
+ if (hydrate) {
23
+ // Claim opening <!--g--> delimiter as anchor from pool
24
+ const args: t.Expression[] = [
25
+ t.identifier(nodesVar ?? "__nodes"),
26
+ t.stringLiteral("g"),
27
+ ];
28
+ if (parentVar) args.push(t.identifier(parentVar));
29
+ statements.push(
30
+ t.variableDeclaration("const", [
31
+ t.variableDeclarator(
32
+ t.identifier(anchorVar),
33
+ t.callExpression(t.identifier("claimComment"), args),
34
+ ),
35
+ ]),
36
+ );
37
+ // Consume closing <!--/g--> delimiter from pool
38
+ const closeArgs: t.Expression[] = [
39
+ t.identifier(nodesVar ?? "__nodes"),
40
+ t.stringLiteral("/g"),
41
+ ];
42
+ if (parentVar) closeArgs.push(t.identifier(parentVar));
43
+ statements.push(
44
+ t.expressionStatement(
45
+ t.callExpression(t.identifier("claimComment"), closeArgs),
46
+ ),
47
+ );
48
+ // Collect and remove SSR content between <!--g--> and <!--/g-->
49
+ // so the effect doesn't duplicate it on first run.
50
+ // Generates: let _prev = []; { let _n = anchor.nextSibling; while (_n) {
51
+ // const _cur = _n; _n = _n.nextSibling;
52
+ // if (_cur instanceof Comment && _cur.data === "/g") { _cur.remove(); break; }
53
+ // _cur.remove(); _prev.push(_cur); } }
54
+ const nextVar = genId();
55
+ const curVar = genId();
56
+ statements.push(
57
+ t.variableDeclaration("let", [
58
+ t.variableDeclarator(t.identifier(prevVar), t.arrayExpression([])),
59
+ ]),
60
+ );
61
+ statements.push(
62
+ t.blockStatement([
63
+ // let _n = anchor.nextSibling
64
+ t.variableDeclaration("let", [
65
+ t.variableDeclarator(
66
+ t.identifier(nextVar),
67
+ t.memberExpression(
68
+ t.identifier(anchorVar),
69
+ t.identifier("nextSibling"),
70
+ ),
71
+ ),
72
+ ]),
73
+ // while (_n) { ... }
74
+ t.whileStatement(
75
+ t.identifier(nextVar),
76
+ t.blockStatement([
77
+ // const _cur = _n
78
+ t.variableDeclaration("const", [
79
+ t.variableDeclarator(t.identifier(curVar), t.identifier(nextVar)),
80
+ ]),
81
+ // _n = _n.nextSibling
82
+ t.expressionStatement(
83
+ t.assignmentExpression(
84
+ "=",
85
+ t.identifier(nextVar),
86
+ t.memberExpression(
87
+ t.identifier(curVar),
88
+ t.identifier("nextSibling"),
89
+ ),
90
+ ),
91
+ ),
92
+ // if (_cur instanceof Comment && _cur.data === "/g") { _cur.remove(); break; }
93
+ t.ifStatement(
94
+ t.logicalExpression(
95
+ "&&",
96
+ t.binaryExpression(
97
+ "instanceof",
98
+ t.identifier(curVar),
99
+ t.identifier("Comment"),
100
+ ),
101
+ t.binaryExpression(
102
+ "===",
103
+ t.memberExpression(
104
+ t.identifier(curVar),
105
+ t.identifier("data"),
106
+ ),
107
+ t.stringLiteral("/g"),
108
+ ),
109
+ ),
110
+ t.blockStatement([
111
+ t.expressionStatement(
112
+ t.callExpression(
113
+ t.memberExpression(
114
+ t.identifier(curVar),
115
+ t.identifier("remove"),
116
+ ),
117
+ [],
118
+ ),
119
+ ),
120
+ t.breakStatement(),
121
+ ]),
122
+ ),
123
+ // _cur.remove()
124
+ t.expressionStatement(
125
+ t.callExpression(
126
+ t.memberExpression(
127
+ t.identifier(curVar),
128
+ t.identifier("remove"),
129
+ ),
130
+ [],
131
+ ),
132
+ ),
133
+ // _prev.push(_cur)
134
+ t.expressionStatement(
135
+ t.callExpression(
136
+ t.memberExpression(t.identifier(prevVar), t.identifier("push")),
137
+ [t.identifier(curVar)],
138
+ ),
139
+ ),
140
+ ]),
141
+ ),
142
+ ]),
143
+ );
144
+ } else {
145
+ // const _anchor = document.createComment('')
146
+ statements.push(
147
+ t.variableDeclaration("const", [
148
+ t.variableDeclarator(
149
+ t.identifier(anchorVar),
150
+ t.callExpression(
151
+ t.memberExpression(
152
+ t.identifier("document"),
153
+ t.identifier("createComment"),
154
+ ),
155
+ [t.stringLiteral("")],
156
+ ),
157
+ ),
158
+ ]),
159
+ );
160
+
161
+ // parent.append(_anchor)
162
+ statements.push(
163
+ t.expressionStatement(
164
+ t.callExpression(
165
+ t.memberExpression(t.identifier(varName), t.identifier("append")),
166
+ [t.identifier(anchorVar)],
167
+ ),
168
+ ),
169
+ );
170
+ }
171
+
172
+ // let _prev = [] — already declared in hydrate block above
173
+ if (!hydrate) {
174
+ statements.push(
175
+ t.variableDeclaration("let", [
176
+ t.variableDeclarator(t.identifier(prevVar), t.arrayExpression([])),
177
+ ]),
178
+ );
179
+ }
180
+
181
+ // function _mountFn(_v, _a) { ... }
182
+ const mountBody = t.blockStatement([
183
+ t.variableDeclaration("const", [
184
+ t.variableDeclarator(t.identifier("_nodes"), t.arrayExpression([])),
185
+ ]),
186
+ t.ifStatement(
187
+ t.callExpression(
188
+ t.memberExpression(t.identifier("Array"), t.identifier("isArray")),
189
+ [t.identifier("_v")],
190
+ ),
191
+ t.blockStatement([
192
+ t.forOfStatement(
193
+ t.variableDeclaration("const", [
194
+ t.variableDeclarator(t.identifier("_item")),
195
+ ]),
196
+ t.identifier("_v"),
197
+ t.blockStatement([
198
+ t.variableDeclaration("const", [
199
+ t.variableDeclarator(
200
+ t.identifier("_n"),
201
+ t.conditionalExpression(
202
+ t.binaryExpression(
203
+ "instanceof",
204
+ t.identifier("_item"),
205
+ t.identifier("Node"),
206
+ ),
207
+ t.identifier("_item"),
208
+ t.callExpression(
209
+ t.memberExpression(
210
+ t.identifier("document"),
211
+ t.identifier("createTextNode"),
212
+ ),
213
+ [
214
+ t.callExpression(t.identifier("String"), [
215
+ t.logicalExpression(
216
+ "??",
217
+ t.identifier("_item"),
218
+ t.stringLiteral(""),
219
+ ),
220
+ ]),
221
+ ],
222
+ ),
223
+ ),
224
+ ),
225
+ ]),
226
+ t.expressionStatement(
227
+ t.callExpression(
228
+ t.memberExpression(t.identifier("_a"), t.identifier("before")),
229
+ [t.identifier("_n")],
230
+ ),
231
+ ),
232
+ t.expressionStatement(
233
+ t.callExpression(
234
+ t.memberExpression(
235
+ t.identifier("_nodes"),
236
+ t.identifier("push"),
237
+ ),
238
+ [t.identifier("_n")],
239
+ ),
240
+ ),
241
+ ]),
242
+ ),
243
+ ]),
244
+ t.ifStatement(
245
+ t.binaryExpression(
246
+ "instanceof",
247
+ t.identifier("_v"),
248
+ t.identifier("Node"),
249
+ ),
250
+ t.blockStatement([
251
+ t.expressionStatement(
252
+ t.callExpression(
253
+ t.memberExpression(t.identifier("_a"), t.identifier("before")),
254
+ [t.identifier("_v")],
255
+ ),
256
+ ),
257
+ t.expressionStatement(
258
+ t.callExpression(
259
+ t.memberExpression(t.identifier("_nodes"), t.identifier("push")),
260
+ [t.identifier("_v")],
261
+ ),
262
+ ),
263
+ ]),
264
+ t.ifStatement(
265
+ t.logicalExpression(
266
+ "&&",
267
+ t.logicalExpression(
268
+ "&&",
269
+ t.binaryExpression("!=", t.identifier("_v"), t.nullLiteral()),
270
+ t.binaryExpression(
271
+ "!==",
272
+ t.identifier("_v"),
273
+ t.booleanLiteral(false),
274
+ ),
275
+ ),
276
+ t.binaryExpression(
277
+ "!==",
278
+ t.identifier("_v"),
279
+ t.booleanLiteral(true),
280
+ ),
281
+ ),
282
+ t.blockStatement([
283
+ t.variableDeclaration("const", [
284
+ t.variableDeclarator(
285
+ t.identifier("_n"),
286
+ t.callExpression(
287
+ t.memberExpression(
288
+ t.identifier("document"),
289
+ t.identifier("createTextNode"),
290
+ ),
291
+ [
292
+ t.callExpression(t.identifier("String"), [
293
+ t.identifier("_v"),
294
+ ]),
295
+ ],
296
+ ),
297
+ ),
298
+ ]),
299
+ t.expressionStatement(
300
+ t.callExpression(
301
+ t.memberExpression(t.identifier("_a"), t.identifier("before")),
302
+ [t.identifier("_n")],
303
+ ),
304
+ ),
305
+ t.expressionStatement(
306
+ t.callExpression(
307
+ t.memberExpression(
308
+ t.identifier("_nodes"),
309
+ t.identifier("push"),
310
+ ),
311
+ [t.identifier("_n")],
312
+ ),
313
+ ),
314
+ ]),
315
+ ),
316
+ ),
317
+ ),
318
+ t.returnStatement(t.identifier("_nodes")),
319
+ ]);
320
+
321
+ statements.push(
322
+ t.functionDeclaration(
323
+ t.identifier(mountFn),
324
+ [t.identifier("_v"), t.identifier("_a")],
325
+ mountBody,
326
+ ),
327
+ );
328
+
329
+ // createEffect(() => { for (const n of _prev) n.remove(); _prev = _mountFn(expr, _anchor); })
330
+ statements.push(
331
+ t.expressionStatement(
332
+ t.callExpression(t.identifier("createEffect"), [
333
+ t.arrowFunctionExpression(
334
+ [],
335
+ t.blockStatement([
336
+ t.forOfStatement(
337
+ t.variableDeclaration("const", [
338
+ t.variableDeclarator(t.identifier("_n")),
339
+ ]),
340
+ t.identifier(prevVar),
341
+ t.expressionStatement(
342
+ t.callExpression(
343
+ t.memberExpression(
344
+ t.identifier("_n"),
345
+ t.identifier("remove"),
346
+ ),
347
+ [],
348
+ ),
349
+ ),
350
+ ),
351
+ t.expressionStatement(
352
+ t.assignmentExpression(
353
+ "=",
354
+ t.identifier(prevVar),
355
+ t.callExpression(t.identifier(mountFn), [
356
+ expr,
357
+ t.identifier(anchorVar),
358
+ ]),
359
+ ),
360
+ ),
361
+ ]),
362
+ ),
363
+ ]),
364
+ ),
365
+ );
366
+ }
@@ -0,0 +1,124 @@
1
+ import { types as t } from "@babel/core";
2
+ import { processElement } from "./element";
3
+ import { processFragment } from "./fragment";
4
+
5
+ /**
6
+ * Collect children from a JSX element into a children expression.
7
+ * Returns null if no meaningful children exist.
8
+ */
9
+ export function collectChildren(
10
+ node: t.JSXElement,
11
+ statements: t.Statement[],
12
+ genId: () => string,
13
+ signals: Set<string>,
14
+ hash?: string,
15
+ hydrate?: boolean,
16
+ nodesVar?: string,
17
+ ): t.Expression | null {
18
+ const childrenElements: t.Expression[] = [];
19
+ let hasOnlyText = true;
20
+
21
+ for (const child of node.children) {
22
+ if (t.isJSXText(child)) {
23
+ const text = child.value.trim();
24
+ if (!text) continue;
25
+ childrenElements.push(t.stringLiteral(text));
26
+ } else if (t.isJSXElement(child) || t.isJSXFragment(child)) {
27
+ hasOnlyText = false;
28
+ const childStatements: t.Statement[] = [];
29
+ let childVar: string;
30
+ if (t.isJSXElement(child)) {
31
+ childVar = processElement(
32
+ child,
33
+ childStatements,
34
+ genId,
35
+ signals,
36
+ hash,
37
+ hydrate,
38
+ nodesVar,
39
+ );
40
+ } else {
41
+ childVar = processFragment(
42
+ child,
43
+ childStatements,
44
+ genId,
45
+ signals,
46
+ hash,
47
+ hydrate,
48
+ nodesVar,
49
+ );
50
+ }
51
+ // Build an IIFE from the child statements
52
+ childrenElements.push(
53
+ t.callExpression(
54
+ t.arrowFunctionExpression(
55
+ [],
56
+ t.blockStatement([
57
+ ...childStatements,
58
+ t.returnStatement(t.identifier(childVar)),
59
+ ]),
60
+ ),
61
+ [],
62
+ ),
63
+ );
64
+ } else if (t.isJSXExpressionContainer(child)) {
65
+ const expr = child.expression as t.Expression;
66
+ if (t.isJSXElement(expr) || t.isJSXFragment(expr)) {
67
+ hasOnlyText = false;
68
+ const childStatements: t.Statement[] = [];
69
+ let childVar: string;
70
+ if (t.isJSXElement(expr)) {
71
+ childVar = processElement(
72
+ expr,
73
+ childStatements,
74
+ genId,
75
+ signals,
76
+ hash,
77
+ hydrate,
78
+ nodesVar,
79
+ );
80
+ } else {
81
+ childVar = processFragment(
82
+ expr,
83
+ childStatements,
84
+ genId,
85
+ signals,
86
+ hash,
87
+ hydrate,
88
+ nodesVar,
89
+ );
90
+ }
91
+ childrenElements.push(
92
+ t.callExpression(
93
+ t.arrowFunctionExpression(
94
+ [],
95
+ t.blockStatement([
96
+ ...childStatements,
97
+ t.returnStatement(t.identifier(childVar)),
98
+ ]),
99
+ ),
100
+ [],
101
+ ),
102
+ );
103
+ } else {
104
+ hasOnlyText = false;
105
+ // For dynamic expressions, just pass the expression directly
106
+ childrenElements.push(expr);
107
+ }
108
+ }
109
+ }
110
+
111
+ if (childrenElements.length === 0) return null;
112
+ // Build the inner value: string for single text, array otherwise
113
+ let inner: t.Expression;
114
+ if (
115
+ childrenElements.length === 1 &&
116
+ hasOnlyText &&
117
+ t.isStringLiteral(childrenElements[0])
118
+ ) {
119
+ inner = childrenElements[0];
120
+ } else {
121
+ inner = t.arrayExpression(childrenElements);
122
+ }
123
+ return inner;
124
+ }