@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.
- package/README.md +3 -0
- package/index.ts +4 -0
- package/jsr.json +8 -0
- package/package.json +24 -0
- package/src/babel/handlers/dom/derived.ts +32 -0
- package/src/babel/handlers/dom/effect.ts +15 -0
- package/src/babel/handlers/dom/state.ts +78 -0
- package/src/babel/handlers/ssr/derived.ts +26 -0
- package/src/babel/handlers/ssr/state.ts +10 -0
- package/src/babel/index.ts +239 -0
- package/src/babel/jsx/anchor-mount.ts +366 -0
- package/src/babel/jsx/children.ts +124 -0
- package/src/babel/jsx/element.ts +520 -0
- package/src/babel/jsx/fragment.ts +279 -0
- package/src/babel/jsx/index.ts +2 -0
- package/src/babel/jsx/keyed-list.ts +278 -0
- package/src/babel/jsx/ssr.ts +309 -0
- package/src/babel/jsx/text-node.ts +192 -0
- package/src/babel/jsx/utils.ts +124 -0
- package/src/babel/util/bind.ts +75 -0
- package/src/babel/util/css.ts +151 -0
- package/src/babel/util/helpers.ts +32 -0
- package/src/babel/util/magic.ts +5 -0
- package/src/bun-plugin.ts +45 -0
- package/src/vite/index.ts +53 -0
- package/test/components.test.ts +129 -0
- package/test/fragments.test.ts +90 -0
- package/test/helpers/transform.ts +26 -0
- package/test/hydration.test.ts +147 -0
- package/test/jsx.test.ts +120 -0
- package/test/keyed-lists.test.ts +88 -0
- package/test/reactivity.test.ts +79 -0
- package/test/scoped-css.test.ts +129 -0
- package/test/ssr.test.ts +130 -0
- package/tsconfig.json +30 -0
|
@@ -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
|
+
}
|