sibujs 4.6.0 → 4.7.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 (79) hide show
  1. package/README.md +1 -1
  2. package/dist/browser.cjs +18 -5
  3. package/dist/browser.js +4 -4
  4. package/dist/build.cjs +1956 -1007
  5. package/dist/build.d.cts +133 -44
  6. package/dist/build.d.ts +133 -44
  7. package/dist/build.js +1368 -569
  8. package/dist/cdn.dev.global.js +12 -12
  9. package/dist/cdn.full.dev.global.js +11 -11
  10. package/dist/cdn.full.global.js +10 -10
  11. package/dist/cdn.global.js +12 -12
  12. package/dist/{chunk-SXXVZMKZ.js → chunk-2BPG2XDA.js} +5 -5
  13. package/dist/{chunk-BAAG6ZTI.js → chunk-3QWBSL5R.js} +4 -4
  14. package/dist/{chunk-5MT6SJ3P.js → chunk-4AWA2PVD.js} +2 -2
  15. package/dist/{chunk-JSXPZCET.js → chunk-7LUJQAOJ.js} +1 -1
  16. package/dist/{chunk-3ISI6ACU.js → chunk-BC2SECJD.js} +1 -1
  17. package/dist/{chunk-DKGBBKOF.js → chunk-BSY63EM6.js} +2 -2
  18. package/dist/{chunk-7NCARGJW.js → chunk-DKXRACVN.js} +3 -3
  19. package/dist/{chunk-47M47FOM.js → chunk-FNJXNGYZ.js} +1 -1
  20. package/dist/{chunk-D33YTSX5.js → chunk-FQRUXCEE.js} +5 -4
  21. package/dist/{chunk-5Q4R7HCL.js → chunk-JWKYU5GV.js} +11 -2
  22. package/dist/{chunk-QE4TTDU3.js → chunk-RUSSKG6G.js} +12 -11
  23. package/dist/{chunk-UZQ6ALFS.js → chunk-SLM3IA34.js} +1 -1
  24. package/dist/{chunk-XRRZKZYX.js → chunk-UNWRJRKC.js} +1 -1
  25. package/dist/{chunk-VVWPJ543.js → chunk-UOL2ECCS.js} +40 -11
  26. package/dist/{chunk-ORMZXBKQ.js → chunk-VZSG24LS.js} +3 -3
  27. package/dist/{chunk-XC4MEKGA.js → chunk-WEQ3DMVL.js} +8 -2
  28. package/dist/{chunk-PK6FK2G2.js → chunk-WOLJZUFQ.js} +176 -27
  29. package/dist/{chunk-7GQCWFOE.js → chunk-XYV3EDB7.js} +143 -74
  30. package/dist/{chunk-AIF3Z2T7.js → chunk-XZR4PXRE.js} +3 -3
  31. package/dist/{chunk-HYXDKS4N.js → chunk-Z3OHK6QT.js} +4 -4
  32. package/dist/{chunk-OXUY2A6L.js → chunk-ZVL7TY4K.js} +2 -2
  33. package/dist/{contracts-DRIuclVT.d.ts → contracts-CLqzJnOV.d.ts} +28 -17
  34. package/dist/{contracts-DRIuclVT.d.cts → contracts-CTOJXu-x.d.cts} +28 -17
  35. package/dist/data.cjs +198 -39
  36. package/dist/data.d.cts +119 -3
  37. package/dist/data.d.ts +119 -3
  38. package/dist/data.js +5 -5
  39. package/dist/devtools.cjs +18 -5
  40. package/dist/devtools.js +4 -4
  41. package/dist/ecosystem.cjs +64 -23
  42. package/dist/ecosystem.d.cts +2 -1
  43. package/dist/ecosystem.d.ts +2 -1
  44. package/dist/ecosystem.js +7 -7
  45. package/dist/extras.cjs +236 -49
  46. package/dist/extras.d.cts +3 -2
  47. package/dist/extras.d.ts +3 -2
  48. package/dist/extras.js +20 -20
  49. package/dist/index.cjs +259 -108
  50. package/dist/index.d.cts +193 -168
  51. package/dist/index.d.ts +193 -168
  52. package/dist/index.js +10 -10
  53. package/dist/motion.cjs +12 -4
  54. package/dist/motion.js +3 -3
  55. package/dist/patterns.cjs +28 -15
  56. package/dist/patterns.d.cts +2 -1
  57. package/dist/patterns.d.ts +2 -1
  58. package/dist/patterns.js +5 -5
  59. package/dist/performance.cjs +18 -5
  60. package/dist/performance.js +4 -4
  61. package/dist/plugins.cjs +118 -34
  62. package/dist/plugins.d.cts +124 -11
  63. package/dist/plugins.d.ts +124 -11
  64. package/dist/plugins.js +68 -26
  65. package/dist/ssr.cjs +54 -13
  66. package/dist/ssr.js +7 -7
  67. package/dist/{tagFactory-DFstCLQV.d.cts → tagFactory-8qL9LCIx.d.cts} +58 -19
  68. package/dist/{tagFactory-DkaNVUNV.d.ts → tagFactory-BL2fymez.d.ts} +58 -19
  69. package/dist/testing.cjs +15 -2
  70. package/dist/testing.js +2 -2
  71. package/dist/types-CJFViL6Q.d.cts +26 -0
  72. package/dist/types-CJFViL6Q.d.ts +26 -0
  73. package/dist/ui.cjs +28 -15
  74. package/dist/ui.d.cts +2 -1
  75. package/dist/ui.d.ts +2 -1
  76. package/dist/ui.js +7 -7
  77. package/dist/widgets.cjs +22 -14
  78. package/dist/widgets.js +6 -6
  79. package/package.json +4 -2
package/dist/build.cjs CHANGED
@@ -59,8 +59,1162 @@ __export(build_exports, {
59
59
  });
60
60
  module.exports = __toCommonJS(build_exports);
61
61
 
62
- // src/build/compileTemplates.ts
62
+ // src/build/sourceEdit.ts
63
+ function isLineBreak(c) {
64
+ return c === 10 || c === 13 || c === 8232 || c === 8233;
65
+ }
66
+ function lineBreaksIn(code2, start, end) {
67
+ let out = "";
68
+ for (let i2 = start; i2 < end; i2++) {
69
+ if (isLineBreak(code2.charCodeAt(i2))) out += code2[i2];
70
+ }
71
+ return out;
72
+ }
73
+ var LINE_SEPARATOR = String.fromCharCode(8232);
74
+ var PARAGRAPH_SEPARATOR = String.fromCharCode(8233);
75
+ function jsStringLiteral(value) {
76
+ const json = JSON.stringify(value);
77
+ if (!json.includes(LINE_SEPARATOR) && !json.includes(PARAGRAPH_SEPARATOR)) return json;
78
+ const bs = "\\";
79
+ return json.split(LINE_SEPARATOR).join(`${bs}u2028`).split(PARAGRAPH_SEPARATOR).join(`${bs}u2029`);
80
+ }
81
+ function normalizeEdits(edits) {
82
+ const sorted = edits.map((edit, order) => ({ edit, order })).sort(
83
+ (a2, b2) => a2.edit.start - b2.edit.start || a2.edit.end - a2.edit.start - (b2.edit.end - b2.edit.start) || a2.order - b2.order
84
+ ).map((x) => x.edit);
85
+ for (let i2 = 1; i2 < sorted.length; i2++) {
86
+ if (sorted[i2].start < sorted[i2 - 1].end) return null;
87
+ }
88
+ return sorted;
89
+ }
90
+ function applyEdits(code2, edits, append = "") {
91
+ let out = "";
92
+ let pos = 0;
93
+ for (const edit of edits) {
94
+ out += code2.slice(pos, edit.start) + edit.text;
95
+ pos = edit.end;
96
+ }
97
+ return out + code2.slice(pos) + append;
98
+ }
99
+ var BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
100
+ function vlq(value) {
101
+ let v = value < 0 ? -value << 1 | 1 : value << 1;
102
+ let out = "";
103
+ do {
104
+ let digit = v & 31;
105
+ v >>>= 5;
106
+ if (v > 0) digit |= 32;
107
+ out += BASE64[digit];
108
+ } while (v > 0);
109
+ return out;
110
+ }
111
+ function isWordStart(c) {
112
+ return c >= 97 && c <= 122 || c >= 65 && c <= 90 || c === 95 || c === 36 || c > 127 || c >= 48 && c <= 57;
113
+ }
114
+ function editsSourceMap(code2, edits, append, source2) {
115
+ const lineStarts = [0];
116
+ for (let i2 = 0; i2 < code2.length; i2++) if (code2.charCodeAt(i2) === 10) lineStarts.push(i2 + 1);
117
+ const origLine = (offset) => {
118
+ let lo = 0;
119
+ let hi = lineStarts.length - 1;
120
+ while (lo < hi) {
121
+ const mid = lo + hi + 1 >> 1;
122
+ if (lineStarts[mid] <= offset) lo = mid;
123
+ else hi = mid - 1;
124
+ }
125
+ return lo;
126
+ };
127
+ const lines = [[]];
128
+ let col2 = 0;
129
+ const mark2 = (offset) => {
130
+ const line2 = lines[lines.length - 1];
131
+ const last = line2[line2.length - 1];
132
+ if (last && last[0] === col2) return;
133
+ const ol2 = origLine(offset);
134
+ line2.push([col2, ol2, offset - lineStarts[ol2]]);
135
+ };
136
+ const newline = () => {
137
+ lines.push([]);
138
+ col2 = 0;
139
+ };
140
+ const emitOriginal = (from, to) => {
141
+ for (let i2 = from; i2 < to; i2++) {
142
+ const c = code2.charCodeAt(i2);
143
+ if (c === 10) {
144
+ newline();
145
+ continue;
146
+ }
147
+ if (i2 === from || col2 === 0 || isWordStart(c) && !isWordStart(code2.charCodeAt(i2 - 1))) mark2(i2);
148
+ col2++;
149
+ }
150
+ };
151
+ const emitReplacement = (edit) => {
152
+ let origOffset = edit.start;
153
+ let pending = true;
154
+ for (let i2 = 0; i2 < edit.text.length; i2++) {
155
+ if (edit.text.charCodeAt(i2) === 10) {
156
+ newline();
157
+ const nl = code2.indexOf("\n", origOffset);
158
+ if (nl !== -1 && nl < edit.end) {
159
+ origOffset = nl + 1;
160
+ pending = true;
161
+ } else {
162
+ pending = false;
163
+ }
164
+ continue;
165
+ }
166
+ if (pending) {
167
+ mark2(origOffset);
168
+ pending = false;
169
+ }
170
+ col2++;
171
+ }
172
+ };
173
+ let pos = 0;
174
+ for (const edit of edits) {
175
+ emitOriginal(pos, edit.start);
176
+ emitReplacement(edit);
177
+ pos = edit.end;
178
+ }
179
+ emitOriginal(pos, code2.length);
180
+ for (let i2 = 0; i2 < append.length; i2++) {
181
+ if (append.charCodeAt(i2) === 10) newline();
182
+ }
183
+ let prevLine = 0;
184
+ let prevCol = 0;
185
+ let prevSource = 0;
186
+ const mappings = lines.map((segments) => {
187
+ let prevGen = 0;
188
+ return segments.map(([gen, ol2, oc]) => {
189
+ const s2 = vlq(gen - prevGen) + vlq(0 - prevSource) + vlq(ol2 - prevLine) + vlq(oc - prevCol);
190
+ prevGen = gen;
191
+ prevSource = 0;
192
+ prevLine = ol2;
193
+ prevCol = oc;
194
+ return s2;
195
+ }).join(",");
196
+ }).join(";");
197
+ return { version: 3, sources: [source2], sourcesContent: [code2], names: [], mappings };
198
+ }
199
+
200
+ // src/build/sourceScan.ts
201
+ var REGEX_AFTER_KEYWORD = /* @__PURE__ */ new Set([
202
+ "return",
203
+ "typeof",
204
+ "instanceof",
205
+ "in",
206
+ "of",
207
+ "new",
208
+ "delete",
209
+ "void",
210
+ "throw",
211
+ "case",
212
+ "do",
213
+ "else",
214
+ "yield",
215
+ "await"
216
+ ]);
217
+ function isIdentStart(c) {
218
+ return c >= 97 && c <= 122 || c >= 65 && c <= 90 || c === 95 || c === 36 || c > 127;
219
+ }
220
+ function isIdentPart(c) {
221
+ return isIdentStart(c) || c >= 48 && c <= 57;
222
+ }
223
+ function isDigit(c) {
224
+ return c >= 48 && c <= 57;
225
+ }
226
+ var ScanError = class extends Error {
227
+ };
228
+ var scanStats = { tokenize: 0 };
229
+ function tokenize(code2) {
230
+ scanStats.tokenize++;
231
+ let pos = 0;
232
+ const len = code2.length;
233
+ if (code2.startsWith("#!")) {
234
+ const nl = code2.indexOf("\n");
235
+ pos = nl === -1 ? len : nl;
236
+ }
237
+ function regexAllowed(prev) {
238
+ if (!prev) return true;
239
+ switch (prev.type) {
240
+ case "number":
241
+ case "string":
242
+ case "regex":
243
+ case "template":
244
+ return false;
245
+ case "ident":
246
+ return REGEX_AFTER_KEYWORD.has(prev.value);
247
+ case "punct":
248
+ return prev.value !== ")" && prev.value !== "]";
249
+ }
250
+ }
251
+ function skipTrivia() {
252
+ while (pos < len) {
253
+ const c = code2.charCodeAt(pos);
254
+ if (c === 32 || c === 9 || c === 10 || c === 13 || c === 11 || c === 12 || c === 160 || c === 65279) {
255
+ pos++;
256
+ } else if (c === 8232 || c === 8233) {
257
+ pos++;
258
+ } else if (c === 47 && code2.charCodeAt(pos + 1) === 47) {
259
+ pos += 2;
260
+ while (pos < len) {
261
+ const d = code2.charCodeAt(pos);
262
+ if (d === 10 || d === 13 || d === 8232 || d === 8233) break;
263
+ pos++;
264
+ }
265
+ } else if (c === 47 && code2.charCodeAt(pos + 1) === 42) {
266
+ const end = code2.indexOf("*/", pos + 2);
267
+ if (end === -1) throw new ScanError("unterminated comment");
268
+ pos = end + 2;
269
+ } else {
270
+ break;
271
+ }
272
+ }
273
+ }
274
+ function readString() {
275
+ const start = pos;
276
+ const quote2 = code2[pos];
277
+ pos++;
278
+ while (pos < len) {
279
+ const c = code2[pos];
280
+ if (c === "\\") {
281
+ pos += 2;
282
+ continue;
283
+ }
284
+ if (c === quote2) {
285
+ pos++;
286
+ return { type: "string", start, end: pos };
287
+ }
288
+ if (c === "\n" || c === "\r") throw new ScanError("line break in string");
289
+ pos++;
290
+ }
291
+ throw new ScanError("unterminated string");
292
+ }
293
+ function readRegex() {
294
+ const start = pos;
295
+ pos++;
296
+ let inClass = false;
297
+ while (pos < len) {
298
+ const c = code2[pos];
299
+ if (c === "\\") {
300
+ pos += 2;
301
+ continue;
302
+ }
303
+ if (c === "\n" || c === "\r") throw new ScanError("line break in regex");
304
+ if (inClass) {
305
+ if (c === "]") inClass = false;
306
+ } else if (c === "[") {
307
+ inClass = true;
308
+ } else if (c === "/") {
309
+ pos++;
310
+ while (pos < len && isIdentPart(code2.charCodeAt(pos))) pos++;
311
+ return { type: "regex", start, end: pos };
312
+ }
313
+ pos++;
314
+ }
315
+ throw new ScanError("unterminated regex");
316
+ }
317
+ function readTemplate() {
318
+ const start = pos;
319
+ pos++;
320
+ const quasis = [];
321
+ const exprs = [];
322
+ let quasiStart = pos;
323
+ while (pos < len) {
324
+ const c = code2[pos];
325
+ if (c === "\\") {
326
+ pos += 2;
327
+ continue;
328
+ }
329
+ if (c === "`") {
330
+ quasis.push({ start: quasiStart, end: pos });
331
+ pos++;
332
+ return { type: "template", start, end: pos, quasis, exprs };
333
+ }
334
+ if (c === "$" && code2[pos + 1] === "{") {
335
+ quasis.push({ start: quasiStart, end: pos });
336
+ pos += 2;
337
+ const exprStart = pos;
338
+ const tokens = readList(true);
339
+ exprs.push({ start: exprStart, end: pos, tokens });
340
+ pos++;
341
+ quasiStart = pos;
342
+ continue;
343
+ }
344
+ pos++;
345
+ }
346
+ throw new ScanError("unterminated template");
347
+ }
348
+ function readList(inTemplateExpr) {
349
+ const tokens = [];
350
+ let depth = 0;
351
+ for (; ; ) {
352
+ skipTrivia();
353
+ if (pos >= len) {
354
+ if (inTemplateExpr || depth !== 0) throw new ScanError("unexpected end of input");
355
+ return tokens;
356
+ }
357
+ const c = code2.charCodeAt(pos);
358
+ const prev = tokens[tokens.length - 1];
359
+ if (c === 34 || c === 39) {
360
+ tokens.push(readString());
361
+ } else if (c === 96) {
362
+ tokens.push(readTemplate());
363
+ } else if (isIdentStart(c) || c === 92) {
364
+ const start = pos;
365
+ pos++;
366
+ while (pos < len) {
367
+ const d = code2.charCodeAt(pos);
368
+ if (isIdentPart(d)) pos++;
369
+ else if (d === 92) pos += 2;
370
+ else break;
371
+ }
372
+ tokens.push({ type: "ident", start, end: pos, value: code2.slice(start, pos) });
373
+ } else if (isDigit(c) || c === 46 && isDigit(code2.charCodeAt(pos + 1))) {
374
+ const start = pos;
375
+ pos++;
376
+ while (pos < len) {
377
+ const d = code2.charCodeAt(pos);
378
+ if (isIdentPart(d) || d === 46) pos++;
379
+ else break;
380
+ }
381
+ tokens.push({ type: "number", start, end: pos, value: code2.slice(start, pos) });
382
+ } else if (c === 47 && regexAllowed(prev)) {
383
+ tokens.push(readRegex());
384
+ } else {
385
+ const start = pos;
386
+ let value = code2[pos];
387
+ if (code2.startsWith("...", pos)) value = "...";
388
+ else if (code2.startsWith("?.", pos) && !isDigit(code2.charCodeAt(pos + 2))) value = "?.";
389
+ else if (code2.startsWith("=>", pos)) value = "=>";
390
+ pos += value.length;
391
+ if (value === "{") {
392
+ depth++;
393
+ } else if (value === "}") {
394
+ if (depth === 0) {
395
+ if (inTemplateExpr) {
396
+ pos = start;
397
+ return tokens;
398
+ }
399
+ throw new ScanError("unbalanced brace");
400
+ }
401
+ depth--;
402
+ }
403
+ tokens.push({ type: "punct", start, end: pos, value });
404
+ }
405
+ }
406
+ }
407
+ try {
408
+ return readList(false);
409
+ } catch (err) {
410
+ if (err instanceof ScanError) return null;
411
+ throw err;
412
+ }
413
+ }
414
+ function cookEscapes(raw) {
415
+ let out = "";
416
+ let i2 = 0;
417
+ const n = raw.length;
418
+ while (i2 < n) {
419
+ const ch = raw[i2];
420
+ if (ch === "\r") {
421
+ out += "\n";
422
+ i2 += raw[i2 + 1] === "\n" ? 2 : 1;
423
+ continue;
424
+ }
425
+ if (ch !== "\\") {
426
+ out += ch;
427
+ i2++;
428
+ continue;
429
+ }
430
+ const e = raw[i2 + 1];
431
+ i2 += 2;
432
+ switch (e) {
433
+ case "n":
434
+ out += "\n";
435
+ break;
436
+ case "t":
437
+ out += " ";
438
+ break;
439
+ case "r":
440
+ out += "\r";
441
+ break;
442
+ case "b":
443
+ out += "\b";
444
+ break;
445
+ case "f":
446
+ out += "\f";
447
+ break;
448
+ case "v":
449
+ out += "\v";
450
+ break;
451
+ case "0":
452
+ if (i2 < n && isDigit(raw.charCodeAt(i2))) return null;
453
+ out += "\0";
454
+ break;
455
+ case "x": {
456
+ const hex = raw.slice(i2, i2 + 2);
457
+ if (!/^[0-9a-fA-F]{2}$/.test(hex)) return null;
458
+ out += String.fromCharCode(Number.parseInt(hex, 16));
459
+ i2 += 2;
460
+ break;
461
+ }
462
+ case "u": {
463
+ if (raw[i2] === "{") {
464
+ const close = raw.indexOf("}", i2);
465
+ if (close === -1) return null;
466
+ const hex = raw.slice(i2 + 1, close);
467
+ if (!/^[0-9a-fA-F]+$/.test(hex)) return null;
468
+ const cp = Number.parseInt(hex, 16);
469
+ if (cp > 1114111) return null;
470
+ out += String.fromCodePoint(cp);
471
+ i2 = close + 1;
472
+ } else {
473
+ const hex = raw.slice(i2, i2 + 4);
474
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return null;
475
+ out += String.fromCharCode(Number.parseInt(hex, 16));
476
+ i2 += 4;
477
+ }
478
+ break;
479
+ }
480
+ case "\r":
481
+ if (raw[i2] === "\n") i2++;
482
+ break;
483
+ case "\n":
484
+ case "\u2028":
485
+ case "\u2029":
486
+ break;
487
+ case void 0:
488
+ return null;
489
+ default:
490
+ if (isDigit(e.charCodeAt(0))) return null;
491
+ out += e;
492
+ }
493
+ }
494
+ return out;
495
+ }
496
+ function stringTokenValue(code2, tok) {
497
+ if (tok.type !== "string") return null;
498
+ return cookEscapes(code2.slice(tok.start + 1, tok.end - 1));
499
+ }
500
+ function collectImports(code2, tokens) {
501
+ const decls = [];
502
+ for (let i2 = 0; i2 < tokens.length; i2++) {
503
+ const tok = tokens[i2];
504
+ if (tok.type !== "ident" || tok.value !== "import") continue;
505
+ const prev = tokens[i2 - 1];
506
+ if (prev && prev.type === "punct" && (prev.value === "." || prev.value === "?.")) continue;
507
+ const next = tokens[i2 + 1];
508
+ if (!next) return null;
509
+ if (next.type === "punct" && (next.value === "(" || next.value === ".")) continue;
510
+ let j = i2 + 1;
511
+ const bindings = [];
512
+ let typeOnly = false;
513
+ if (next.type === "string") {
514
+ const source3 = stringTokenValue(code2, next);
515
+ if (source3 === null) return null;
516
+ let end2 = next.end;
517
+ const semi = tokens[j + 1];
518
+ if (semi && semi.type === "punct" && semi.value === ";") end2 = semi.end;
519
+ decls.push({ source: source3, bindings, start: tok.start, end: end2 });
520
+ continue;
521
+ }
522
+ const at = (k) => tokens[k];
523
+ const isPunct = (k, v) => {
524
+ const t = at(k);
525
+ return !!t && t.type === "punct" && t.value === v;
526
+ };
527
+ const isIdent = (k, v) => {
528
+ const t = at(k);
529
+ return !!t && t.type === "ident" && (v === void 0 || t.value === v);
530
+ };
531
+ if (isIdent(j, "type") && !isIdent(j + 1, "from") && !isPunct(j + 1, ",")) {
532
+ typeOnly = true;
533
+ j++;
534
+ }
535
+ if (isIdent(j) && !isIdent(j, "from")) {
536
+ bindings.push({ imported: "default", local: at(j).value });
537
+ j++;
538
+ if (isPunct(j, ",")) j++;
539
+ }
540
+ if (isPunct(j, "*")) {
541
+ if (!isIdent(j + 1, "as") || !isIdent(j + 2)) return null;
542
+ bindings.push({ imported: "*", local: at(j + 2).value });
543
+ j += 3;
544
+ } else if (isPunct(j, "{")) {
545
+ j++;
546
+ while (!isPunct(j, "}")) {
547
+ const t = at(j);
548
+ if (!t) return null;
549
+ let specTypeOnly = false;
550
+ if (isIdent(j, "type") && (isIdent(j + 1) || at(j + 1)?.type === "string") && !isIdent(j + 1, "as")) {
551
+ specTypeOnly = true;
552
+ j++;
553
+ }
554
+ const nameTok = at(j);
555
+ let imported = null;
556
+ if (nameTok?.type === "ident") imported = nameTok.value;
557
+ else if (nameTok?.type === "string") imported = stringTokenValue(code2, nameTok);
558
+ if (imported === null) return null;
559
+ j++;
560
+ let local = imported;
561
+ if (isIdent(j, "as")) {
562
+ if (!isIdent(j + 1)) return null;
563
+ local = at(j + 1).value;
564
+ j += 2;
565
+ }
566
+ if (!specTypeOnly) bindings.push({ imported, local });
567
+ if (isPunct(j, ",")) j++;
568
+ else if (!isPunct(j, "}")) return null;
569
+ }
570
+ j++;
571
+ }
572
+ if (!isIdent(j, "from")) return null;
573
+ const srcTok = at(j + 1);
574
+ if (!srcTok || srcTok.type !== "string") return null;
575
+ const source2 = stringTokenValue(code2, srcTok);
576
+ if (source2 === null) return null;
577
+ let end = srcTok.end;
578
+ j += 2;
579
+ if (isIdent(j, "with") || isIdent(j, "assert")) {
580
+ if (!isPunct(j + 1, "{")) return null;
581
+ let k = j + 2;
582
+ while (at(k) && !isPunct(k, "}")) k++;
583
+ if (!at(k)) return null;
584
+ end = at(k).end;
585
+ j = k + 1;
586
+ }
587
+ if (isPunct(j, ";")) end = at(j).end;
588
+ decls.push({ source: source2, bindings: typeOnly ? [] : bindings, start: tok.start, end });
589
+ i2 = j;
590
+ }
591
+ return decls;
592
+ }
593
+ var SCAN_CACHE_SIZE = 4;
594
+ var scanCache = /* @__PURE__ */ new Map();
595
+ function scanModule(code2) {
596
+ const hit = scanCache.get(code2);
597
+ if (hit !== void 0) return hit;
598
+ const tokens = tokenize(code2);
599
+ const imports = tokens && collectImports(code2, tokens);
600
+ const scan = tokens && imports ? { tokens, imports } : null;
601
+ if (scanCache.size >= SCAN_CACHE_SIZE) scanCache.delete(scanCache.keys().next().value);
602
+ scanCache.set(code2, scan);
603
+ return scan;
604
+ }
605
+ function isSibuSource(source2) {
606
+ return source2 === "sibujs" || source2.startsWith("sibujs/");
607
+ }
608
+ function sibuBindings(imports, mainOnly = false) {
609
+ const out = /* @__PURE__ */ new Map();
610
+ for (const decl of imports) {
611
+ if (mainOnly ? decl.source !== "sibujs" : !isSibuSource(decl.source)) continue;
612
+ for (const b2 of decl.bindings) {
613
+ if (b2.imported === "*" || b2.imported === "default") continue;
614
+ out.set(b2.local, b2.imported);
615
+ }
616
+ }
617
+ return out;
618
+ }
619
+ function forEachBindingRef(tokens, imports, visit) {
620
+ const inImport = (offset) => imports.some((d) => offset >= d.start && offset < d.end);
621
+ const walk = (list) => {
622
+ for (let i2 = 0; i2 < list.length; i2++) {
623
+ const tok = list[i2];
624
+ if (tok.type === "template") {
625
+ for (const e of tok.exprs) walk(e.tokens);
626
+ continue;
627
+ }
628
+ if (tok.type !== "ident") continue;
629
+ const prev = list[i2 - 1];
630
+ if (prev && prev.type === "punct" && (prev.value === "." || prev.value === "?." || prev.value === "#")) continue;
631
+ if (inImport(tok.start)) continue;
632
+ visit({ tok, list, index: i2 });
633
+ }
634
+ };
635
+ walk(tokens);
636
+ }
637
+ var EXPRESSION_KEYWORDS = /* @__PURE__ */ new Set([...REGEX_AFTER_KEYWORD, "default", "extends"]);
638
+ function isLineTerminator(c) {
639
+ return c === 10 || c === 13 || c === 8232 || c === 8233;
640
+ }
641
+ function lineBreakBetween(code2, from, to) {
642
+ for (let i2 = from; i2 < to; i2++) if (isLineTerminator(code2.charCodeAt(i2))) return true;
643
+ return false;
644
+ }
645
+ function endsValue(tok) {
646
+ if (!tok) return false;
647
+ switch (tok.type) {
648
+ case "number":
649
+ case "string":
650
+ case "regex":
651
+ case "template":
652
+ return true;
653
+ case "ident":
654
+ return !EXPRESSION_KEYWORDS.has(tok.value);
655
+ case "punct":
656
+ return tok.value === ")" || tok.value === "]";
657
+ }
658
+ }
659
+ function isDeclarationHead(code2, list, index) {
660
+ const tok = list[index];
661
+ const prev = list[index - 1];
662
+ if (prev?.type === "ident") {
663
+ if (prev.value === "function") return true;
664
+ if (!EXPRESSION_KEYWORDS.has(prev.value) && !lineBreakBetween(code2, prev.end, tok.start)) return true;
665
+ } else if (prev?.type === "punct") {
666
+ const before = list[index - 2];
667
+ if (prev.value === "*" && !endsValue(before) && !(before?.type === "ident" && before.value === "yield"))
668
+ return true;
669
+ if (prev.value === "@") return true;
670
+ }
671
+ let depth = 0;
672
+ let close = -1;
673
+ for (let k = index + 1; k < list.length; k++) {
674
+ const t = list[k];
675
+ if (t.type !== "punct") continue;
676
+ if (t.value === "(") depth++;
677
+ else if (t.value === ")" && --depth === 0) {
678
+ close = k;
679
+ break;
680
+ }
681
+ }
682
+ if (close === -1) return true;
683
+ const after = list[close + 1];
684
+ if (after?.type !== "punct") return false;
685
+ if (after.value === "{") return true;
686
+ if (after.value === ":") {
687
+ if (!prev) return true;
688
+ if (prev.type === "punct") return ["{", "}", ";", ",", "*"].includes(prev.value);
689
+ return prev.type === "ident" && !EXPRESSION_KEYWORDS.has(prev.value);
690
+ }
691
+ return false;
692
+ }
693
+ function bindingUses(code2, scan, names) {
694
+ const shadowed = /* @__PURE__ */ new Set();
695
+ const bound = /* @__PURE__ */ new Map();
696
+ for (const decl of scan.imports) {
697
+ for (const b2 of decl.bindings) bound.set(b2.local, (bound.get(b2.local) ?? 0) + 1);
698
+ }
699
+ for (const name of names) if ((bound.get(name) ?? 0) > 1) shadowed.add(name);
700
+ const calls = [];
701
+ const tagged = [];
702
+ forEachBindingRef(scan.tokens, scan.imports, (ref2) => {
703
+ const { tok, list, index } = ref2;
704
+ if (!names.has(tok.value)) return;
705
+ const next = list[index + 1];
706
+ if (next && next.type === "punct" && next.value === "(") {
707
+ if (isDeclarationHead(code2, list, index)) shadowed.add(tok.value);
708
+ else calls.push(ref2);
709
+ } else if (next && next.type === "template") {
710
+ const prev = list[index - 1];
711
+ if (prev?.type === "punct" && prev.value === "@") shadowed.add(tok.value);
712
+ else tagged.push(ref2);
713
+ } else {
714
+ shadowed.add(tok.value);
715
+ }
716
+ });
717
+ return {
718
+ calls: calls.filter((r) => !shadowed.has(r.tok.value)),
719
+ tagged: tagged.filter((r) => !shadowed.has(r.tok.value)),
720
+ shadowed
721
+ };
722
+ }
723
+ function uniquePrefix(code2, base2 = "__sibujs") {
724
+ let prefix = `${base2}$`;
725
+ for (let n = 1; code2.includes(prefix); n++) prefix = `${base2}${n}$`;
726
+ return prefix;
727
+ }
728
+ var PURE_FACTORIES = /* @__PURE__ */ new Set([
729
+ "tagFactory",
730
+ "context",
731
+ "defineComponent",
732
+ "withProps",
733
+ "withDefaults",
734
+ "pure",
735
+ "noSideEffect"
736
+ ]);
737
+ var PURE_COMMENT = "/*#__PURE__*/ ";
738
+ function pureAnnotationEdits(code2) {
739
+ if (!code2.includes("sibujs")) return [];
740
+ const scan = scanModule(code2);
741
+ if (!scan) return [];
742
+ const candidates = /* @__PURE__ */ new Set();
743
+ for (const [local, imported] of sibuBindings(scan.imports)) if (PURE_FACTORIES.has(imported)) candidates.add(local);
744
+ if (candidates.size === 0) return [];
745
+ const uses = bindingUses(code2, scan, candidates);
746
+ const unsafe = new Set(uses.shadowed);
747
+ for (const ref2 of uses.tagged) unsafe.add(ref2.tok.value);
748
+ return uses.calls.filter(({ tok }) => {
749
+ if (unsafe.has(tok.value)) return false;
750
+ return !/\/\*\s*[#@]__PURE__\s*\*\/\s*$/.test(code2.slice(Math.max(0, tok.start - 40), tok.start));
751
+ }).map(({ tok }) => ({ start: tok.start, end: tok.start, text: PURE_COMMENT }));
752
+ }
753
+ function injectPureAnnotations(code2) {
754
+ const edits = normalizeEdits(pureAnnotationEdits(code2));
755
+ return edits && edits.length > 0 ? applyEdits(code2, edits) : code2;
756
+ }
757
+ function importsSibu(code2) {
758
+ if (!code2.includes("sibujs")) return false;
759
+ const scan = scanModule(code2);
760
+ return !!scan?.imports.some((d) => isSibuSource(d.source));
761
+ }
762
+
763
+ // src/core/dev.ts
764
+ var DEV = typeof __SIBU_DEV__ !== "undefined" ? !!__SIBU_DEV__ : typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
765
+ // safe default: off in browser, on in test/dev Node
766
+ typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
767
+ );
768
+ function isDev() {
769
+ if (typeof globalThis.__SIBU_DEV__ !== "undefined") return !!globalThis.__SIBU_DEV__;
770
+ if (typeof __SIBU_DEV__ !== "undefined") return !!__SIBU_DEV__;
771
+ return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
772
+ }
773
+ function devAssert(condition, message) {
774
+ if ((typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) && !condition) {
775
+ throw new Error(`[SibuJS] ${message}`);
776
+ }
777
+ }
778
+ function devWarnLazy(build) {
779
+ if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
780
+ const message = build();
781
+ if (message) console.warn(`[SibuJS] ${message}`);
782
+ }
783
+ }
784
+ function devWarn(message) {
785
+ if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
786
+ console.warn(`[SibuJS] ${message}`);
787
+ }
788
+ }
789
+
790
+ // src/utils/sanitize.ts
791
+ function stripControlChars(value) {
792
+ return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
793
+ }
794
+ function isEventHandlerAttr(name) {
795
+ if (name.length < 3) return false;
796
+ const lower = name.toLowerCase();
797
+ return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
798
+ }
799
+ var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
800
+ var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
801
+ function sanitizeUrl(url) {
802
+ const probe = stripControlChars(url).trim();
803
+ if (!probe) return "";
804
+ const output2 = url.replace(OUTER_TRIM, "");
805
+ const lower = probe.toLowerCase();
806
+ let schemeEnd = -1;
807
+ for (let i2 = 0; i2 < lower.length; i2++) {
808
+ const ch = lower.charCodeAt(i2);
809
+ if (ch === 58) {
810
+ schemeEnd = i2;
811
+ break;
812
+ }
813
+ if (ch === 47 || ch === 63 || ch === 35) break;
814
+ }
815
+ if (schemeEnd === -1) return output2;
816
+ const scheme = lower.slice(0, schemeEnd + 1);
817
+ if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output2;
818
+ if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
819
+ return output2;
820
+ }
821
+ var SRCSET_DESCRIPTOR = /^\s+\d+(\.\d+)?[wx]$/;
822
+ function sanitizeSrcset(value) {
823
+ const parts = value.split(",");
824
+ const out = [];
825
+ for (let i2 = 0; i2 < parts.length; i2++) {
826
+ const part = parts[i2].trim();
827
+ if (!part) continue;
828
+ const m = part.match(/^(\S+)(\s+.+)?$/);
829
+ if (!m) continue;
830
+ if (m[2] !== void 0 && !SRCSET_DESCRIPTOR.test(m[2])) continue;
831
+ const safe = sanitizeUrl(m[1]);
832
+ if (!safe) continue;
833
+ out.push(m[2] ? `${safe}${m[2]}` : safe);
834
+ }
835
+ return out.join(", ");
836
+ }
837
+ var CSS_DANGER_GATE = /[(:@\\]/;
838
+ function isCssWhitespace(ch) {
839
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
840
+ }
841
+ function isHexDigit(ch) {
842
+ return ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
843
+ }
844
+ function decodeCssEscapes(value) {
845
+ let out = "";
846
+ for (let i2 = 0; i2 < value.length; i2++) {
847
+ const ch = value[i2];
848
+ if (ch !== "\\") {
849
+ out += ch;
850
+ continue;
851
+ }
852
+ if (i2 + 1 >= value.length) break;
853
+ const next = value[i2 + 1];
854
+ if (next === "\n" || next === "\f") {
855
+ i2 += 1;
856
+ continue;
857
+ }
858
+ if (next === "\r") {
859
+ i2 += value[i2 + 2] === "\n" ? 2 : 1;
860
+ continue;
861
+ }
862
+ if (isHexDigit(next)) {
863
+ let hex = "";
864
+ let j = i2 + 1;
865
+ while (j < value.length && hex.length < 6 && isHexDigit(value[j])) {
866
+ hex += value[j];
867
+ j++;
868
+ }
869
+ if (j < value.length && isCssWhitespace(value[j])) {
870
+ j += value[j] === "\r" && value[j + 1] === "\n" ? 2 : 1;
871
+ }
872
+ const code2 = Number.parseInt(hex, 16);
873
+ const invalid = code2 === 0 || code2 > 1114111 || code2 >= 55296 && code2 <= 57343;
874
+ out += invalid ? "\uFFFD" : String.fromCodePoint(code2);
875
+ i2 = j - 1;
876
+ continue;
877
+ }
878
+ out += next;
879
+ i2 += 1;
880
+ }
881
+ return out;
882
+ }
883
+ var BLOCKED_CSS_NEEDLES = [
884
+ "url(",
885
+ "expression(",
886
+ "javascript:",
887
+ "vbscript:",
888
+ "-moz-binding",
889
+ "behavior:",
890
+ "@import",
891
+ "image-set(",
892
+ "filter:progid"
893
+ ];
894
+ function describeElement(el) {
895
+ if (!el) return "an element";
896
+ let out = el.tagName.toLowerCase();
897
+ const id = el.getAttribute("id");
898
+ if (id) out += `#${id}`;
899
+ const cls = el.getAttribute("class");
900
+ if (cls) out += `.${cls.trim().split(/\s+/).slice(0, 3).join(".")}`;
901
+ return `<${out}>`;
902
+ }
903
+ var warnedDeclarations = /* @__PURE__ */ new Set();
904
+ var MAX_WARNED_DECLARATIONS = 100;
905
+ var declarationCapAnnounced = false;
906
+ function warnDroppedDeclaration(value, reason, ctx) {
907
+ devWarnLazy(() => {
908
+ const reasons = [
909
+ "url() in an inline style is an exfiltration channel: the browser fetches it immediately and the URL's path or query can carry page data off-origin",
910
+ "expression() executes arbitrary script in legacy engines",
911
+ "the javascript: scheme executes script",
912
+ "the vbscript: scheme executes script",
913
+ "-moz-binding loads and runs remote XBL",
914
+ "behavior: loads and runs a remote HTC script",
915
+ "@import pulls in a remote stylesheet",
916
+ "image-set() fetches remote images, the same exfiltration channel as url()",
917
+ "filter:progid activates legacy scriptable filters"
918
+ ];
919
+ const declaration = ctx?.property ? `${ctx.property}: ${value}` : value;
920
+ const where = describeElement(ctx?.element);
921
+ const key = `${where}|${declaration}`;
922
+ if (warnedDeclarations.has(key)) return "";
923
+ if (warnedDeclarations.size >= MAX_WARNED_DECLARATIONS) {
924
+ if (declarationCapAnnounced) return "";
925
+ declarationCapAnnounced = true;
926
+ return `${MAX_WARNED_DECLARATIONS} distinct dropped style declarations have been reported; suppressing further ones for the rest of this session so they cannot flood the console. The declarations are still dropped \u2014 only the reporting stops. Fix the reported ones and reload to see any that remain.`;
927
+ }
928
+ warnedDeclarations.add(key);
929
+ return `style declaration "${declaration}" was dropped by the style sanitizer on ${where} \u2014 ${reasons[reason]}. The element renders without it, which usually looks like a missing image rather than an error. Sanctioned alternatives: for a content image use an <img> element (or a <picture>); for decoration, put the rule in a stylesheet and set a class instead \u2014 stylesheet CSS is not sanitized, only inline style attributes are. If this declaration is not yours, it came from untrusted data and the drop just prevented an exfiltration.`;
930
+ });
931
+ }
932
+ function blockedCssReason(value) {
933
+ if (!CSS_DANGER_GATE.test(value)) return -1;
934
+ const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
935
+ const lower = normalized.toLowerCase().replace(/\s+/g, "");
936
+ for (let i2 = 0; i2 < BLOCKED_CSS_NEEDLES.length; i2++) {
937
+ if (lower.includes(BLOCKED_CSS_NEEDLES[i2])) return i2;
938
+ }
939
+ return -1;
940
+ }
941
+ function sanitizeCSSValue(value, context2) {
942
+ const reason = blockedCssReason(value);
943
+ if (reason === -1) return value;
944
+ warnDroppedDeclaration(value, reason, context2);
945
+ return "";
946
+ }
947
+ function sanitizeStyleAttribute(cssText, context2) {
948
+ const input2 = String(cssText);
949
+ if (input2.trim() === "") return "";
950
+ if (typeof document === "undefined") {
951
+ return sanitizeCSSValue(input2, context2) === "" ? "" : input2;
952
+ }
953
+ const probe = document.createElement("div");
954
+ try {
955
+ probe.style.cssText = input2;
956
+ } catch {
957
+ return "";
958
+ }
959
+ const declarations = [];
960
+ for (let i2 = 0; i2 < probe.style.length; i2++) {
961
+ const property = probe.style[i2];
962
+ const value = probe.style.getPropertyValue(property);
963
+ let reason = blockedCssReason(value);
964
+ if (reason === -1) reason = blockedCssReason(`${property}:${value}`);
965
+ if (reason !== -1) {
966
+ warnDroppedDeclaration(value, reason, { property, element: context2?.element });
967
+ continue;
968
+ }
969
+ const priority = probe.style.getPropertyPriority(property);
970
+ declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
971
+ }
972
+ return declarations.join("; ");
973
+ }
974
+ var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
975
+ "href",
976
+ "xlink:href",
977
+ "src",
978
+ "action",
979
+ "formaction",
980
+ "formtarget",
981
+ "cite",
982
+ "poster",
983
+ "background",
984
+ "srcset",
985
+ "ping",
986
+ "data",
987
+ // `<html manifest>` is a URL sink too. It was carried only by the SSR
988
+ // serializers' private copy of this set; folding that copy into this one would
989
+ // otherwise have silently dropped its coverage.
990
+ "manifest"
991
+ ]);
992
+ var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
993
+ function isHtmlContentAttribute(name) {
994
+ return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
995
+ }
996
+ function isUrlAttribute(attr) {
997
+ return URL_ATTRIBUTES.has(attr.toLowerCase());
998
+ }
999
+ function sanitizeAttributeString(attr, value, context2) {
1000
+ const lower = attr.toLowerCase();
1001
+ if (lower === "srcset") return sanitizeSrcset(value);
1002
+ if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
1003
+ if (lower === "style") return sanitizeStyleAttribute(value, context2);
1004
+ return value;
1005
+ }
1006
+
1007
+ // src/build/staticAnalysis.ts
1008
+ var STATIC_TAGS = /* @__PURE__ */ new Set([
1009
+ "div",
1010
+ "span",
1011
+ "section",
1012
+ "article",
1013
+ "header",
1014
+ "footer",
1015
+ "nav",
1016
+ "main",
1017
+ "aside",
1018
+ "p",
1019
+ "h1",
1020
+ "h2",
1021
+ "h3",
1022
+ "h4",
1023
+ "h5",
1024
+ "h6",
1025
+ "blockquote",
1026
+ "li",
1027
+ "ol",
1028
+ "ul",
1029
+ "a",
1030
+ "b",
1031
+ "em",
1032
+ "i",
1033
+ "strong",
1034
+ "small",
1035
+ "code",
1036
+ "mark",
1037
+ "img",
1038
+ "br",
1039
+ "hr",
1040
+ "input",
1041
+ "button",
1042
+ "form",
1043
+ "label",
1044
+ "details",
1045
+ "summary",
1046
+ "dialog"
1047
+ ]);
63
1048
  var VOID_ELEMENTS = /* @__PURE__ */ new Set([
1049
+ "br",
1050
+ "hr",
1051
+ "img",
1052
+ "input",
1053
+ "meta",
1054
+ "link",
1055
+ "area",
1056
+ "base",
1057
+ "col",
1058
+ "embed",
1059
+ "param",
1060
+ "source",
1061
+ "track",
1062
+ "wbr"
1063
+ ]);
1064
+ var SPECIAL_PROPS = /* @__PURE__ */ new Set(["style", "ref", "on", "onElement"]);
1065
+ var NON_REFLECTING_BOOLEANS = /* @__PURE__ */ new Set(["checked", "selected"]);
1066
+ function readLiteral(code2, tok) {
1067
+ if (!tok) return null;
1068
+ if (tok.type === "string") {
1069
+ const value = stringTokenValue(code2, tok);
1070
+ return value === null ? null : { kind: "string", value };
1071
+ }
1072
+ if (tok.type === "number" && /^(0|[1-9]\d*)(\.\d+)?$/.test(tok.value)) {
1073
+ return { kind: "number", value: String(Number(tok.value)) };
1074
+ }
1075
+ if (tok.type === "ident" && ["true", "false", "null", "undefined"].includes(tok.value)) {
1076
+ return { kind: "keyword", value: tok.value };
1077
+ }
1078
+ return null;
1079
+ }
1080
+ function parserSafeText(s2) {
1081
+ return !s2.includes("\r") && !s2.includes("\0");
1082
+ }
1083
+ function escapeHtml(str) {
1084
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1085
+ }
1086
+ function escapeAttr(str) {
1087
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1088
+ }
1089
+ function attributeHtml(name, lit) {
1090
+ if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(name)) return null;
1091
+ const lower = name.toLowerCase();
1092
+ if (SPECIAL_PROPS.has(name) || lower === "style" || lower === "srcset") return null;
1093
+ if (isEventHandlerAttr(name) || isUrlAttribute(name) || isHtmlContentAttribute(name)) return null;
1094
+ if (lit.kind === "keyword") {
1095
+ if (lit.value === "null" || lit.value === "undefined") return "";
1096
+ const on2 = lit.value === "true";
1097
+ if (lower.startsWith("aria-")) return `${lower}="${on2}"`;
1098
+ if (NON_REFLECTING_BOOLEANS.has(lower)) return null;
1099
+ return on2 ? `${lower}=""` : "";
1100
+ }
1101
+ if (!parserSafeText(lit.value)) return null;
1102
+ return `${lower}="${escapeAttr(lit.value)}"`;
1103
+ }
1104
+ function parseCall(code2, list, i2) {
1105
+ const at = (k2) => list[k2];
1106
+ const isPunct = (k2, v) => {
1107
+ const t = at(k2);
1108
+ return !!t && t.type === "punct" && t.value === v;
1109
+ };
1110
+ let k = i2 + 1;
1111
+ const result = { end: 0, cls: null, id: null, attrs: [], text: null };
1112
+ let nodesText = null;
1113
+ if (isPunct(k, "{")) {
1114
+ k++;
1115
+ const seen = /* @__PURE__ */ new Set();
1116
+ while (!isPunct(k, "}")) {
1117
+ const keyTok = at(k);
1118
+ let key = null;
1119
+ if (keyTok?.type === "ident") key = keyTok.value;
1120
+ else if (keyTok?.type === "string") key = stringTokenValue(code2, keyTok);
1121
+ if (key === null || !isPunct(k + 1, ":")) return null;
1122
+ if (seen.has(key.toLowerCase())) return null;
1123
+ seen.add(key.toLowerCase());
1124
+ const lit = readLiteral(code2, at(k + 2));
1125
+ if (lit === null) return null;
1126
+ k += 3;
1127
+ if (isPunct(k, ",")) k++;
1128
+ else if (!isPunct(k, "}")) return null;
1129
+ if (key === "class" || key === "id") {
1130
+ if (lit.kind !== "string" || !parserSafeText(lit.value)) return null;
1131
+ if (key === "class") result.cls = lit.value;
1132
+ else result.id = lit.value;
1133
+ } else if (key === "nodes") {
1134
+ if (lit.kind !== "string" || !parserSafeText(lit.value)) return null;
1135
+ nodesText = lit.value;
1136
+ } else {
1137
+ const html3 = attributeHtml(key, lit);
1138
+ if (html3 === null) return null;
1139
+ if (html3) result.attrs.push(html3);
1140
+ }
1141
+ }
1142
+ k++;
1143
+ if (isPunct(k, ",")) {
1144
+ const second = readLiteral(code2, at(k + 1));
1145
+ if (second === null) {
1146
+ if (!isPunct(k + 1, ")")) return null;
1147
+ k++;
1148
+ } else {
1149
+ if (second.kind !== "string" || !parserSafeText(second.value)) return null;
1150
+ result.text = second.value;
1151
+ k += 2;
1152
+ if (isPunct(k, ",")) k++;
1153
+ }
1154
+ }
1155
+ if (result.text === null) result.text = nodesText;
1156
+ } else {
1157
+ const first = readLiteral(code2, at(k));
1158
+ if (first === null || first.kind !== "string" || !parserSafeText(first.value)) return null;
1159
+ k++;
1160
+ if (isPunct(k, ",") && at(k + 1)?.type === "string") {
1161
+ const second = readLiteral(code2, at(k + 1));
1162
+ if (second === null || second.kind !== "string" || !parserSafeText(second.value)) return null;
1163
+ result.cls = first.value;
1164
+ result.text = second.value;
1165
+ k += 2;
1166
+ } else {
1167
+ result.text = first.value;
1168
+ }
1169
+ if (isPunct(k, ",")) k++;
1170
+ }
1171
+ if (!isPunct(k, ")")) return null;
1172
+ result.end = at(k).end;
1173
+ return result;
1174
+ }
1175
+ function toHtml(tag, call) {
1176
+ const attrs = [];
1177
+ if (call.cls !== null) attrs.push(`class="${escapeAttr(call.cls)}"`);
1178
+ if (call.id !== null) attrs.push(`id="${escapeAttr(call.id)}"`);
1179
+ attrs.push(...call.attrs);
1180
+ const attrStr = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
1181
+ if (VOID_ELEMENTS.has(tag)) {
1182
+ if (call.text) return null;
1183
+ return `<${tag}${attrStr} />`;
1184
+ }
1185
+ return `<${tag}${attrStr}>${escapeHtml(call.text ?? "")}</${tag}>`;
1186
+ }
1187
+ function analyzeStaticTemplates(code2) {
1188
+ const none = { hasStaticPatterns: false, patterns: [] };
1189
+ if (!code2.includes("sibujs")) return none;
1190
+ const scan = scanModule(code2);
1191
+ if (!scan) return none;
1192
+ const factories = /* @__PURE__ */ new Map();
1193
+ for (const decl of scan.imports) {
1194
+ if (decl.source !== "sibujs") continue;
1195
+ for (const b2 of decl.bindings) if (STATIC_TAGS.has(b2.imported)) factories.set(b2.local, b2.imported);
1196
+ }
1197
+ if (factories.size === 0) return none;
1198
+ const uses = bindingUses(code2, scan, new Set(factories.keys()));
1199
+ const unsafe = new Set(uses.shadowed);
1200
+ for (const ref2 of uses.tagged) unsafe.add(ref2.tok.value);
1201
+ const patterns = [];
1202
+ for (const { tok, list, index } of uses.calls) {
1203
+ if (unsafe.has(tok.value)) continue;
1204
+ const tag = factories.get(tok.value);
1205
+ const call = parseCall(code2, list, index + 1);
1206
+ if (!call) continue;
1207
+ const templateHtml = toHtml(tag, call);
1208
+ if (templateHtml === null) continue;
1209
+ const start = list[index].start;
1210
+ patterns.push({ original: code2.slice(start, call.end), tag, templateHtml, start, end: call.end });
1211
+ }
1212
+ return { hasStaticPatterns: patterns.length > 0, patterns };
1213
+ }
1214
+
1215
+ // src/build/templateCompiler.ts
1216
+ var RAW_TEXT_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
1217
+ var VOID_ELEMENTS2 = /* @__PURE__ */ new Set([
64
1218
  "area",
65
1219
  "base",
66
1220
  "br",
@@ -99,96 +1253,10 @@ var SVG_TAGS = /* @__PURE__ */ new Set([
99
1253
  "symbol",
100
1254
  "marker"
101
1255
  ]);
102
- function findHtmlTemplates(code2) {
103
- const results = [];
104
- const tagRegex = /\bhtml\s*`/g;
105
- let match2;
106
- while ((match2 = tagRegex.exec(code2)) !== null) {
107
- const start = match2.index;
108
- const backtickStart = code2.indexOf("`", start + 4);
109
- if (backtickStart === -1) continue;
110
- const parsed = parseTemplateLiteral(code2, backtickStart);
111
- if (!parsed) continue;
112
- results.push({
113
- start,
114
- end: parsed.end,
115
- strings: parsed.strings,
116
- exprCount: parsed.exprCount
117
- });
118
- tagRegex.lastIndex = parsed.end;
119
- }
120
- return results;
121
- }
122
- function parseTemplateLiteral(code2, openBacktick) {
123
- const strings = [];
124
- let exprCount = 0;
125
- let pos = openBacktick + 1;
126
- let current = "";
127
- while (pos < code2.length) {
128
- const ch = code2[pos];
129
- if (ch === "`") {
130
- strings.push(current);
131
- return { end: pos + 1, strings, exprCount };
132
- }
133
- if (ch === "$" && pos + 1 < code2.length && code2[pos + 1] === "{") {
134
- strings.push(current);
135
- current = "";
136
- exprCount++;
137
- pos += 2;
138
- let depth = 1;
139
- while (pos < code2.length && depth > 0) {
140
- const c = code2[pos];
141
- if (c === "{") depth++;
142
- else if (c === "}") depth--;
143
- else if (c === '"' || c === "'" || c === "`") {
144
- pos = skipString(code2, pos);
145
- continue;
146
- }
147
- if (depth > 0) pos++;
148
- }
149
- if (depth !== 0) return null;
150
- pos++;
151
- continue;
152
- }
153
- if (ch === "\\") {
154
- current += code2[pos] + (code2[pos + 1] || "");
155
- pos += 2;
156
- continue;
157
- }
158
- current += ch;
159
- pos++;
160
- }
161
- return null;
162
- }
163
- function skipString(code2, start) {
164
- const quote2 = code2[start];
165
- let pos = start + 1;
166
- while (pos < code2.length) {
167
- if (code2[pos] === "\\") {
168
- pos += 2;
169
- continue;
170
- }
171
- if (code2[pos] === quote2) return pos + 1;
172
- if (quote2 === "`" && code2[pos] === "$" && code2[pos + 1] === "{") {
173
- pos += 2;
174
- let depth = 1;
175
- while (pos < code2.length && depth > 0) {
176
- if (code2[pos] === "{") depth++;
177
- else if (code2[pos] === "}") depth--;
178
- else if (code2[pos] === '"' || code2[pos] === "'" || code2[pos] === "`") {
179
- pos = skipString(code2, pos);
180
- continue;
181
- }
182
- if (depth > 0) pos++;
183
- }
184
- pos++;
185
- continue;
186
- }
187
- pos++;
188
- }
189
- return pos;
190
- }
191
- function parseTemplateToAST(strings) {
1256
+ var SVG_NS = "http://www.w3.org/2000/svg";
1257
+ var RuntimeParseError = class extends Error {
1258
+ };
1259
+ function parseTemplate(strings) {
192
1260
  const exprCount = strings.length - 1;
193
1261
  let template2 = strings[0];
194
1262
  for (let i2 = 0; i2 < exprCount; i2++) {
@@ -197,26 +1265,34 @@ function parseTemplateToAST(strings) {
197
1265
  let pos = 0;
198
1266
  const len = template2.length;
199
1267
  function skipWs() {
200
- while (pos < len && /\s/.test(template2[pos])) pos++;
1268
+ while (pos < len && (template2[pos] === " " || template2[pos] === " " || template2[pos] === "\n" || template2[pos] === "\r"))
1269
+ pos++;
201
1270
  }
202
1271
  function tryExprIdx() {
203
- if (template2[pos] !== "\0") return -1;
1272
+ if (template2.charCodeAt(pos) !== 0) return -1;
204
1273
  const start = pos;
205
1274
  pos++;
206
- let idxStr = "";
207
- while (pos < len && template2[pos] !== "\0") idxStr += template2[pos++];
1275
+ let idx = 0;
1276
+ while (pos < len && template2.charCodeAt(pos) !== 0) {
1277
+ idx = idx * 10 + (template2.charCodeAt(pos) - 48);
1278
+ pos++;
1279
+ }
208
1280
  if (pos < len) pos++;
209
- const idx = Number.parseInt(idxStr, 10);
210
- if (Number.isNaN(idx) || idx < 0 || idx >= exprCount) {
1281
+ if (idx < 0 || idx >= exprCount) {
211
1282
  pos = start;
212
1283
  return -1;
213
1284
  }
214
1285
  return idx;
215
1286
  }
216
1287
  function readTagName() {
217
- let name = "";
218
- while (pos < len && /[a-zA-Z0-9-]/.test(template2[pos])) name += template2[pos++];
219
- return name;
1288
+ const start = pos;
1289
+ while (pos < len) {
1290
+ const c = template2.charCodeAt(pos);
1291
+ if (c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= 48 && c <= 57 || c === 45) {
1292
+ pos++;
1293
+ } else break;
1294
+ }
1295
+ return template2.slice(start, pos);
220
1296
  }
221
1297
  function parseAttrValue() {
222
1298
  skipWs();
@@ -224,51 +1300,72 @@ function parseTemplateToAST(strings) {
224
1300
  pos++;
225
1301
  skipWs();
226
1302
  const exprIdx = tryExprIdx();
227
- if (exprIdx >= 0) return { kind: "expr", index: exprIdx };
1303
+ if (exprIdx >= 0) return { kind: "expr", idx: exprIdx };
228
1304
  const quote2 = template2[pos];
229
1305
  if (quote2 === '"' || quote2 === "'") {
230
1306
  pos++;
231
- const parts = [];
232
- let current = "";
1307
+ const statics2 = [];
1308
+ const exprs2 = [];
1309
+ let current2 = "";
233
1310
  while (pos < len && template2[pos] !== quote2) {
234
1311
  const innerIdx = tryExprIdx();
235
1312
  if (innerIdx >= 0) {
236
- if (current) parts.push({ kind: "static", value: current });
237
- current = "";
238
- parts.push({ kind: "expr", index: innerIdx });
1313
+ statics2.push(current2);
1314
+ current2 = "";
1315
+ exprs2.push(innerIdx);
239
1316
  } else {
240
- current += template2[pos++];
1317
+ current2 += template2[pos++];
241
1318
  }
242
1319
  }
243
1320
  if (pos < len) pos++;
244
- if (current) parts.push({ kind: "static", value: current });
245
- if (parts.length === 1 && parts[0].kind === "static") {
246
- return { kind: "static", value: parts[0].value };
247
- }
248
- if (parts.some((p2) => p2.kind === "expr")) {
249
- return { kind: "mixed", parts };
1321
+ statics2.push(current2);
1322
+ if (exprs2.length === 0) return { kind: "static", value: statics2[0] };
1323
+ return { kind: "mixed", statics: statics2, exprs: exprs2 };
1324
+ }
1325
+ const statics = [];
1326
+ const exprs = [];
1327
+ let current = "";
1328
+ while (pos < len) {
1329
+ const c = template2.charCodeAt(pos);
1330
+ if (c === 32 || c === 9 || c === 10 || c === 13 || c === 62) break;
1331
+ const innerIdx = tryExprIdx();
1332
+ if (innerIdx >= 0) {
1333
+ statics.push(current);
1334
+ current = "";
1335
+ exprs.push(innerIdx);
1336
+ } else {
1337
+ current += template2[pos++];
250
1338
  }
251
- return { kind: "static", value: parts.map((p2) => p2.value).join("") };
252
1339
  }
253
- let val = "";
254
- while (pos < len && !/[\s>/]/.test(template2[pos])) val += template2[pos++];
255
- return { kind: "static", value: val };
1340
+ statics.push(current);
1341
+ if (exprs.length === 0) return { kind: "static", value: statics[0] };
1342
+ return { kind: "mixed", statics, exprs };
256
1343
  }
257
1344
  function parseAttrs() {
258
1345
  const attrs = [];
259
1346
  while (pos < len) {
260
1347
  skipWs();
261
1348
  if (template2[pos] === ">" || template2[pos] === "/") break;
262
- let attrName = "";
263
- while (pos < len && /[a-zA-Z0-9\-:_.]/.test(template2[pos])) attrName += template2[pos++];
1349
+ const attrStart = pos;
1350
+ while (pos < len) {
1351
+ const c = template2.charCodeAt(pos);
1352
+ if (c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= 48 && c <= 57 || c === 45 || c === 58 || c === 95 || c === 46) {
1353
+ pos++;
1354
+ } else break;
1355
+ }
1356
+ const attrName = template2.slice(attrStart, pos);
264
1357
  if (!attrName) break;
265
1358
  const val = parseAttrValue();
266
1359
  if (attrName.startsWith("on:")) {
267
- if (val.kind === "expr") {
268
- attrs.push({ name: attrName.slice(3), value: { kind: "event", index: val.index } });
269
- }
1360
+ if (val.kind === "expr") attrs.push({ t: 3, name: attrName.slice(3), idx: val.idx });
1361
+ } else if (val.kind === "bool") {
1362
+ attrs.push({ t: 4, name: attrName });
1363
+ } else if (val.kind === "static") {
1364
+ attrs.push({ t: 0, name: attrName, value: val.value });
1365
+ } else if (val.kind === "expr") {
1366
+ attrs.push({ t: 1, name: attrName, idx: val.idx });
270
1367
  } else {
271
- attrs.push({ name: attrName, value: val });
1368
+ attrs.push({ t: 2, name: attrName, statics: val.statics, exprs: val.exprs });
272
1369
  }
273
1370
  }
274
1371
  return attrs;
@@ -282,40 +1379,67 @@ function parseTemplateToAST(strings) {
282
1379
  const idx = tryExprIdx();
283
1380
  if (idx >= 0) {
284
1381
  const collapsed2 = collapseWs(text2);
285
- if (collapsed2) children.push({ kind: "text", value: collapsed2 });
1382
+ if (collapsed2) children.push({ t: 1, value: collapsed2 });
286
1383
  text2 = "";
287
- children.push({ kind: "expr", index: idx });
1384
+ children.push({ t: 2, idx });
288
1385
  } else {
289
1386
  text2 += template2[pos++];
290
1387
  }
291
1388
  }
292
1389
  const collapsed = collapseWs(text2);
293
- if (collapsed) children.push({ kind: "text", value: collapsed });
1390
+ if (collapsed) children.push({ t: 1, value: collapsed });
294
1391
  }
295
1392
  function parseChildren() {
296
1393
  const children = [];
297
1394
  while (pos < len) {
298
1395
  if (template2[pos] === "<" && pos + 1 < len && template2[pos + 1] === "/") break;
299
1396
  if (template2[pos] === "<") {
1397
+ const next = template2[pos + 1];
1398
+ if (next === "!") {
1399
+ if (template2.startsWith("<!--", pos)) {
1400
+ const end = template2.indexOf("-->", pos + 4);
1401
+ pos = end === -1 ? len : end + 3;
1402
+ } else if (template2.startsWith("<![CDATA[", pos)) {
1403
+ const end = template2.indexOf("]]>", pos + 9);
1404
+ pos = end === -1 ? len : end + 3;
1405
+ } else {
1406
+ const end = template2.indexOf(">", pos);
1407
+ pos = end === -1 ? len : end + 1;
1408
+ }
1409
+ continue;
1410
+ }
1411
+ if (next === "?") {
1412
+ const end = template2.indexOf(">", pos);
1413
+ pos = end === -1 ? len : end + 1;
1414
+ continue;
1415
+ }
1416
+ if (!(next >= "a" && next <= "z") && !(next >= "A" && next <= "Z")) {
1417
+ children.push({ t: 1, value: "<" });
1418
+ pos++;
1419
+ continue;
1420
+ }
300
1421
  pos++;
301
1422
  const tag = readTagName();
302
1423
  const attrs = parseAttrs();
303
1424
  skipWs();
304
- const isVoid = VOID_ELEMENTS.has(tag);
1425
+ const isVoid = VOID_ELEMENTS2.has(tag);
305
1426
  const isSelfClosing = template2[pos] === "/";
306
1427
  if (isSelfClosing) pos++;
307
1428
  if (pos < len) pos++;
308
1429
  if (isVoid || isSelfClosing) {
309
- children.push({ kind: "element", el: { tag, attrs, children: [] } });
1430
+ children.push({ t: 0, el: { tag, svg: SVG_TAGS.has(tag), attrs, children: [] } });
310
1431
  } else {
311
1432
  const inner = parseChildren();
1433
+ if (RAW_TEXT_TAGS.has(tag.toLowerCase()) && inner.some((c) => c.t === 2)) {
1434
+ throw new RuntimeParseError(tag);
1435
+ }
312
1436
  if (template2[pos] === "<" && pos + 1 < len && template2[pos + 1] === "/") {
313
1437
  pos += 2;
314
1438
  readTagName();
315
1439
  skipWs();
316
1440
  if (pos < len && template2[pos] === ">") pos++;
317
1441
  }
318
- children.push({ kind: "element", el: { tag, attrs, children: inner } });
1442
+ children.push({ t: 0, el: { tag, svg: SVG_TAGS.has(tag), attrs, children: inner } });
319
1443
  }
320
1444
  } else {
321
1445
  parseTextChildren(children);
@@ -325,303 +1449,230 @@ function parseTemplateToAST(strings) {
325
1449
  }
326
1450
  return parseChildren();
327
1451
  }
328
- function generateElement(el, valuesVar) {
329
- const tag = el.tag;
330
- const isSvg = SVG_TAGS.has(tag);
331
- const parts = [];
332
- for (const attr of el.attrs) {
333
- const v = attr.value;
334
- switch (v.kind) {
335
- case "static":
336
- parts.push(`${JSON.stringify(attr.name)}: ${JSON.stringify(v.value)}`);
337
- break;
338
- case "expr":
339
- parts.push(`${JSON.stringify(attr.name)}: ${valuesVar}[${v.index}]`);
340
- break;
341
- case "mixed": {
342
- const concat = v.parts.map((p2) => p2.kind === "static" ? JSON.stringify(p2.value) : `String(${valuesVar}[${p2.index}])`).join(" + ");
343
- parts.push(`${JSON.stringify(attr.name)}: ${concat}`);
344
- break;
345
- }
346
- case "bool":
347
- parts.push(`${JSON.stringify(attr.name)}: true`);
348
- break;
349
- case "event":
350
- break;
351
- }
352
- }
353
- const events = el.attrs.filter((a2) => a2.value.kind === "event");
354
- if (events.length > 0) {
355
- const eventEntries = events.map((a2) => {
356
- const v = a2.value;
357
- return `${JSON.stringify(a2.name)}: ${valuesVar}[${v.index}]`;
358
- });
359
- parts.push(`on: { ${eventEntries.join(", ")} }`);
360
- }
361
- if (el.children.length > 0) {
362
- const childExprs = el.children.map((c) => generateChild(c, valuesVar));
363
- if (childExprs.length === 1) {
364
- parts.push(`nodes: ${childExprs[0]}`);
1452
+ var IDL_SYNCED_ATTRS = /* @__PURE__ */ new Set(["value", "checked"]);
1453
+ function generateBody(roots, h) {
1454
+ const lines = [];
1455
+ let n = 0;
1456
+ const q2 = jsStringLiteral;
1457
+ const P = h.prefix;
1458
+ function genElement(el) {
1459
+ const v = `e${n++}`;
1460
+ h.tags.add(el.tag);
1461
+ if (el.svg) h.svg = true;
1462
+ lines.push(
1463
+ el.svg ? `const ${v} = document.createElementNS(${q2(SVG_NS)}, ${q2(el.tag)});` : `const ${v} = document.createElement(${q2(el.tag)});`
1464
+ );
1465
+ for (const attr of el.attrs) {
1466
+ switch (attr.t) {
1467
+ case 0:
1468
+ lines.push(`${v}.setAttribute(${q2(attr.name)}, ${q2(attr.value)});`);
1469
+ break;
1470
+ case 4:
1471
+ lines.push(`${v}.setAttribute(${q2(attr.name)}, "");`);
1472
+ break;
1473
+ case 1:
1474
+ case 2: {
1475
+ if (IDL_SYNCED_ATTRS.has(attr.name.toLowerCase())) return null;
1476
+ h.used.add("attr");
1477
+ if (attr.t === 1) {
1478
+ lines.push(`${P}attr(${v}, ${q2(attr.name)}, v${attr.idx});`);
1479
+ } else {
1480
+ h.used.add("str");
1481
+ let expr = q2(attr.statics[0]);
1482
+ for (let j = 0; j < attr.exprs.length; j++) {
1483
+ expr += ` + ${P}str(v${attr.exprs[j]}) + ${q2(attr.statics[j + 1])}`;
1484
+ }
1485
+ lines.push(`${P}attr(${v}, ${q2(attr.name)}, ${expr});`);
1486
+ }
1487
+ break;
1488
+ }
1489
+ case 3:
1490
+ h.used.add("on");
1491
+ lines.push(`${P}on(${v}, ${q2(attr.name)}, v${attr.idx});`);
1492
+ break;
1493
+ }
1494
+ }
1495
+ for (const child of el.children) {
1496
+ if (child.t === 0) {
1497
+ const c = genElement(child.el);
1498
+ if (c === null) return null;
1499
+ lines.push(`${v}.appendChild(${c});`);
1500
+ } else if (child.t === 1) {
1501
+ lines.push(`${v}.appendChild(document.createTextNode(${q2(child.value)}));`);
1502
+ } else {
1503
+ h.used.add("child");
1504
+ lines.push(`${P}child(${v}, v${child.idx});`);
1505
+ }
1506
+ }
1507
+ return v;
1508
+ }
1509
+ if (roots.length === 1 && roots[0].t === 0) {
1510
+ const root = genElement(roots[0].el);
1511
+ if (root === null) return null;
1512
+ lines.push(`return ${root};`);
1513
+ return lines.join("\n ");
1514
+ }
1515
+ lines.push(`const w = document.createElement("div");`);
1516
+ for (const root of roots) {
1517
+ if (root.t === 0) {
1518
+ const c = genElement(root.el);
1519
+ if (c === null) return null;
1520
+ lines.push(`w.appendChild(${c});`);
1521
+ } else if (root.t === 1) {
1522
+ lines.push(`w.appendChild(document.createTextNode(${q2(root.value)}));`);
365
1523
  } else {
366
- parts.push(`nodes: [${childExprs.join(", ")}]`);
1524
+ return null;
367
1525
  }
368
1526
  }
369
- const propsStr = parts.length > 0 ? `{ ${parts.join(", ")} }` : void 0;
370
- if (isSvg) {
371
- return propsStr ? `__sbTagFactory(${JSON.stringify(tag)}, __sbSVG_NS)(${propsStr})` : `__sbTagFactory(${JSON.stringify(tag)}, __sbSVG_NS)({})`;
372
- }
373
- return propsStr ? `${tag}(${propsStr})` : `${tag}({})`;
1527
+ lines.push("return w;");
1528
+ return lines.join("\n ");
374
1529
  }
375
- function generateChild(child, valuesVar) {
376
- switch (child.kind) {
377
- case "element":
378
- return generateElement(child.el, valuesVar);
379
- case "text":
380
- return JSON.stringify(child.value);
381
- case "expr":
382
- return `${valuesVar}[${child.index}]`;
1530
+ function helperSource(h) {
1531
+ const P = h.prefix;
1532
+ const out = [];
1533
+ if (h.used.has("attr")) {
1534
+ out.push(
1535
+ `function ${P}attr(el, name, value) {
1536
+ const teardown = ${P}bindAttrs(el, { [name]: value });
1537
+ if (typeof value === "function") ${P}registerDisposer(el, teardown);
1538
+ }`
1539
+ );
383
1540
  }
384
- }
385
- function compileHtmlTemplates(code2) {
386
- const templates = findHtmlTemplates(code2);
387
- if (templates.length === 0) {
388
- return { code: null, usedTags: /* @__PURE__ */ new Set(), usesSvg: false, compiledCount: 0 };
389
- }
390
- const usedTags = /* @__PURE__ */ new Set();
391
- let usesSvg = false;
392
- let result = code2;
393
- for (let i2 = templates.length - 1; i2 >= 0; i2--) {
394
- const tmpl = templates[i2];
395
- const ast = parseTemplateToAST(tmpl.strings);
396
- collectTags(ast, usedTags);
397
- if (ast.length > 1) usedTags.add("div");
398
- if (Array.from(usedTags).some((t) => SVG_TAGS.has(t))) usesSvg = true;
399
- const valuesVar = "__v";
400
- const childExprs = ast.map((c) => generateChild(c, valuesVar));
401
- const rootExpr = childExprs.length === 1 ? childExprs[0] : `div([${childExprs.join(", ")}])`;
402
- let compiled;
403
- if (tmpl.exprCount === 0) {
404
- compiled = rootExpr;
405
- } else {
406
- const body2 = rootExpr;
407
- const exprSource = extractExpressions(code2, tmpl.start, tmpl.end, tmpl.exprCount);
408
- compiled = `((${valuesVar}) => ${body2})([${exprSource.join(", ")}])`;
409
- }
410
- result = result.slice(0, tmpl.start) + compiled + result.slice(tmpl.end);
1541
+ if (h.used.has("str")) {
1542
+ out.push(`function ${P}str(value) {
1543
+ return value == null ? "" : String(value);
1544
+ }`);
411
1545
  }
412
- return { code: result, usedTags, usesSvg, compiledCount: templates.length };
413
- }
414
- function collectTags(children, tags) {
415
- for (const child of children) {
416
- if (child.kind === "element") {
417
- tags.add(child.el.tag);
418
- collectTags(child.el.children, tags);
419
- }
1546
+ if (h.used.has("on")) {
1547
+ out.push(
1548
+ `function ${P}on(el, name, handler) {
1549
+ if (typeof handler === "function") el.addEventListener(name, handler);
1550
+ }`
1551
+ );
420
1552
  }
421
- }
422
- function extractExpressions(code2, templateStart, templateEnd, count) {
423
- const exprs = [];
424
- const backtickStart = code2.indexOf("`", templateStart + 4);
425
- let pos = backtickStart + 1;
426
- for (let i2 = 0; i2 < count; i2++) {
427
- while (pos < templateEnd) {
428
- if (code2[pos] === "$" && code2[pos + 1] === "{") {
429
- pos += 2;
430
- break;
431
- }
432
- if (code2[pos] === "\\") {
433
- pos += 2;
434
- continue;
435
- }
436
- pos++;
437
- }
438
- const exprStart = pos;
439
- let depth = 1;
440
- while (pos < templateEnd && depth > 0) {
441
- const c = code2[pos];
442
- if (c === "{") depth++;
443
- else if (c === "}") {
444
- depth--;
445
- if (depth === 0) break;
446
- } else if (c === '"' || c === "'" || c === "`") {
447
- pos = skipString(code2, pos);
448
- continue;
449
- }
450
- pos++;
451
- }
452
- exprs.push(code2.slice(exprStart, pos).trim());
453
- pos++;
1553
+ if (h.used.has("child")) {
1554
+ out.push(
1555
+ `function ${P}child(el, value) {
1556
+ if (typeof value === "function") {
1557
+ el.appendChild(${P}Fragment([value]));
1558
+ } else if (value instanceof Node) {
1559
+ el.appendChild(value);
1560
+ } else if (Array.isArray(value)) {
1561
+ for (let i = 0; i < value.length; i++) {
1562
+ const item = value[i];
1563
+ if (item instanceof Node) el.appendChild(item);
1564
+ else if (item != null && typeof item !== "boolean") el.appendChild(document.createTextNode(String(item)));
1565
+ }
1566
+ } else if (value != null && typeof value !== "boolean") {
1567
+ el.appendChild(document.createTextNode(String(value)));
1568
+ }
1569
+ }`
1570
+ );
454
1571
  }
455
- return exprs;
1572
+ return out.join("\n");
456
1573
  }
457
-
458
- // src/build/staticAnalysis.ts
459
- var HTML_TAGS = /* @__PURE__ */ new Set([
460
- "div",
461
- "span",
462
- "section",
463
- "article",
464
- "header",
465
- "footer",
466
- "nav",
467
- "main",
468
- "aside",
469
- "p",
470
- "h1",
471
- "h2",
472
- "h3",
473
- "h4",
474
- "h5",
475
- "h6",
476
- "blockquote",
477
- "li",
478
- "ol",
479
- "ul",
480
- "pre",
481
- "a",
482
- "b",
483
- "em",
484
- "i",
485
- "strong",
486
- "small",
487
- "code",
488
- "mark",
489
- "img",
490
- "br",
491
- "hr",
492
- "input",
493
- "button",
494
- "form",
495
- "label",
496
- "select",
497
- "textarea",
498
- "option",
499
- "table",
500
- "tbody",
501
- "thead",
502
- "tfoot",
503
- "tr",
504
- "td",
505
- "th",
506
- "details",
507
- "summary",
508
- "dialog"
509
- ]);
510
- var VOID_ELEMENTS2 = /* @__PURE__ */ new Set([
511
- "br",
512
- "hr",
513
- "img",
514
- "input",
515
- "meta",
516
- "link",
517
- "area",
518
- "base",
519
- "col",
520
- "embed",
521
- "param",
522
- "source",
523
- "track",
524
- "wbr"
525
- ]);
526
- function analyzeStaticTemplates(code2) {
527
- const patterns = [];
528
- const tagCallRegex = new RegExp(`\\b(${Array.from(HTML_TAGS).join("|")})\\s*\\(\\s*\\{([^}]*)\\}\\s*\\)`, "g");
529
- let match2;
530
- while ((match2 = tagCallRegex.exec(code2)) !== null) {
531
- const [fullMatch, tag, propsContent] = match2;
532
- const start = match2.index;
533
- const end = start + fullMatch.length;
534
- if (isStaticPropsContent(propsContent)) {
535
- const templateHtml = propsToHtml(tag, propsContent);
536
- if (templateHtml) {
537
- patterns.push({ original: fullMatch, tag, templateHtml, start, end });
538
- }
539
- }
540
- }
541
- return {
542
- hasStaticPatterns: patterns.length > 0,
543
- patterns
544
- };
1574
+ function importSource(h) {
1575
+ const P = h.prefix;
1576
+ const core = [];
1577
+ if (h.used.has("child")) core.push(`Fragment as ${P}Fragment`);
1578
+ if (h.used.has("attr")) core.push(`registerDisposer as ${P}registerDisposer`);
1579
+ const lines = [];
1580
+ if (core.length > 0) lines.push(`import { ${core.join(", ")} } from "sibujs";`);
1581
+ if (h.used.has("attr")) lines.push(`import { bindAttrs as ${P}bindAttrs } from "sibujs/ui";`);
1582
+ return lines.join("\n");
545
1583
  }
546
- function isStaticPropsContent(propsContent) {
547
- if (/=>|function\s*\(|\bref\b|\bon\s*:/.test(propsContent)) {
548
- return false;
549
- }
550
- const props = propsContent.split(",").map((p2) => p2.trim()).filter(Boolean);
551
- for (const prop of props) {
552
- const colonIndex = prop.indexOf(":");
553
- if (colonIndex === -1) continue;
554
- const value = prop.substring(colonIndex + 1).trim();
555
- if (!isStaticValue(value)) {
556
- return false;
1584
+ function planHtmlTemplates(code2) {
1585
+ const plan = {
1586
+ edits: [],
1587
+ append: "",
1588
+ usedTags: /* @__PURE__ */ new Set(),
1589
+ usesSvg: false,
1590
+ compiledCount: 0,
1591
+ skippedCount: 0
1592
+ };
1593
+ if (!code2.includes("`") || !code2.includes("sibujs")) return plan;
1594
+ const scan = scanModule(code2);
1595
+ if (!scan) return plan;
1596
+ const tagNames = /* @__PURE__ */ new Set();
1597
+ for (const decl of scan.imports) {
1598
+ if (!isSibuSource(decl.source)) continue;
1599
+ for (const b2 of decl.bindings) if (b2.imported === "html") tagNames.add(b2.local);
1600
+ }
1601
+ if (tagNames.size === 0) return plan;
1602
+ const uses = bindingUses(code2, scan, tagNames);
1603
+ if (uses.shadowed.size > 0 || uses.calls.length > 0 || uses.tagged.length === 0) return plan;
1604
+ const sites = new Set(uses.tagged.map((ref2) => ref2.tok));
1605
+ const h = { prefix: uniquePrefix(code2), used: /* @__PURE__ */ new Set(), tags: /* @__PURE__ */ new Set(), svg: false };
1606
+ const functions = [];
1607
+ const compileTemplate = (tmpl) => {
1608
+ const strings = [];
1609
+ for (const qs of tmpl.quasis) {
1610
+ const cooked = cookEscapes(code2.slice(qs.start, qs.end));
1611
+ if (cooked === null) return null;
1612
+ strings.push(cooked);
1613
+ }
1614
+ let roots;
1615
+ try {
1616
+ roots = parseTemplate(strings);
1617
+ } catch (err) {
1618
+ if (err instanceof RuntimeParseError) return null;
1619
+ throw err;
1620
+ }
1621
+ const scratch = { prefix: h.prefix, used: /* @__PURE__ */ new Set(), tags: /* @__PURE__ */ new Set(), svg: false };
1622
+ const body2 = generateBody(roots, scratch);
1623
+ if (body2 === null) return null;
1624
+ for (const u2 of scratch.used) h.used.add(u2);
1625
+ for (const t of scratch.tags) h.tags.add(t);
1626
+ if (scratch.svg) h.svg = true;
1627
+ const name = `${h.prefix}t${functions.length}`;
1628
+ const params = tmpl.exprs.map((_, i2) => `v${i2}`).join(", ");
1629
+ functions.push(`function ${name}(${params}) {
1630
+ ${body2}
1631
+ }`);
1632
+ return name;
1633
+ };
1634
+ const replaceSite = (tag, tmpl, name) => {
1635
+ const edit = (start, end, before, after) => plan.edits.push({ start, end, text: `${before}${lineBreaksIn(code2, start, end)}${after}` });
1636
+ const exprs = tmpl.exprs;
1637
+ if (exprs.length === 0) {
1638
+ edit(tag.start, tmpl.end, `${name}(`, ")");
1639
+ return;
557
1640
  }
558
- }
559
- return true;
560
- }
561
- function isStaticValue(value) {
562
- if (/^["'].*["']$/.test(value)) return true;
563
- if (/^-?\d+(\.\d+)?$/.test(value)) return true;
564
- if (value === "true" || value === "false") return true;
565
- if (value === "null" || value === "undefined") return true;
566
- return false;
567
- }
568
- function propsToHtml(tag, propsContent) {
569
- const attrs = [];
570
- let nodesContent = "";
571
- const props = propsContent.split(",").map((p2) => p2.trim()).filter(Boolean);
572
- for (const prop of props) {
573
- const colonIndex = prop.indexOf(":");
574
- if (colonIndex === -1) continue;
575
- const key = prop.substring(0, colonIndex).trim();
576
- let value = prop.substring(colonIndex + 1).trim();
577
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
578
- value = value.slice(1, -1);
579
- }
580
- if (key === "nodes") {
581
- nodesContent = escapeHtml(value);
582
- } else if (key === "class" || key === "id") {
583
- attrs.push(`${key}="${escapeAttr(value)}"`);
584
- } else if (key !== "on" && key !== "ref" && key !== "style") {
585
- attrs.push(`${key}="${escapeAttr(String(value))}"`);
1641
+ edit(tag.start, exprs[0].start, `${name}((`, "");
1642
+ for (let i2 = 1; i2 < exprs.length; i2++) edit(exprs[i2 - 1].end, exprs[i2].start, ")", ", (");
1643
+ edit(exprs[exprs.length - 1].end, tmpl.end, "", "))");
1644
+ };
1645
+ const visit = (list) => {
1646
+ for (let i2 = 0; i2 < list.length; i2++) {
1647
+ const tok = list[i2];
1648
+ if (sites.has(tok)) {
1649
+ const tmpl = list[i2 + 1];
1650
+ for (const e of tmpl.exprs) visit(e.tokens);
1651
+ const name = compileTemplate(tmpl);
1652
+ if (name !== null) {
1653
+ plan.compiledCount++;
1654
+ replaceSite(tok, tmpl, name);
1655
+ } else {
1656
+ plan.skippedCount++;
1657
+ }
1658
+ i2++;
1659
+ } else if (tok.type === "template") {
1660
+ for (const e of tok.exprs) visit(e.tokens);
1661
+ }
586
1662
  }
587
- }
588
- const attrStr = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
589
- if (VOID_ELEMENTS2.has(tag)) {
590
- return `<${tag}${attrStr} />`;
591
- }
592
- return `<${tag}${attrStr}>${nodesContent}</${tag}>`;
593
- }
594
- function escapeHtml(str) {
595
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
596
- }
597
- function escapeAttr(str) {
598
- return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1663
+ };
1664
+ visit(scan.tokens);
1665
+ if (plan.compiledCount === 0) return { ...plan, edits: [] };
1666
+ const trailer = [importSource(h), helperSource(h), ...functions].filter(Boolean).join("\n");
1667
+ plan.append = `
1668
+ ${trailer}
1669
+ `;
1670
+ plan.usedTags = h.tags;
1671
+ plan.usesSvg = h.svg;
1672
+ return plan;
599
1673
  }
600
1674
 
601
1675
  // src/build/vite.ts
602
- var SVG_TAGS_SET = /* @__PURE__ */ new Set([
603
- "svg",
604
- "circle",
605
- "ellipse",
606
- "g",
607
- "line",
608
- "path",
609
- "polygon",
610
- "polyline",
611
- "rect",
612
- "text",
613
- "tspan",
614
- "defs",
615
- "clipPath",
616
- "mask",
617
- "pattern",
618
- "linearGradient",
619
- "radialGradient",
620
- "stop",
621
- "use",
622
- "symbol",
623
- "marker"
624
- ]);
625
1676
  var DEFAULT_INCLUDE = ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"];
626
1677
  var DEFAULT_EXCLUDE = ["node_modules/**", "dist/**", "**/*.test.*", "**/*.spec.*"];
627
1678
  function matchesPattern(filePath, patterns) {
@@ -631,30 +1682,42 @@ function matchesPattern(filePath, patterns) {
631
1682
  return new RegExp(regexStr).test(normalized);
632
1683
  });
633
1684
  }
634
- function injectPureAnnotations(code2) {
635
- const sibuFactories = [
636
- "tagFactory",
637
- "context",
638
- "defineComponent",
639
- "withProps",
640
- "withDefaults",
641
- "pure",
642
- "noSideEffect"
643
- ];
644
- let result = code2;
645
- for (const factory of sibuFactories) {
646
- const pattern2 = new RegExp(`(?<!/\\*#__PURE__\\*/\\s*)\\b(${factory})\\s*\\(`, "g");
647
- result = result.replace(pattern2, "/*#__PURE__*/ $1(");
1685
+ function devPrologueEdit(code2) {
1686
+ let at = 0;
1687
+ if (code2.startsWith("#!")) {
1688
+ const nl = code2.indexOf("\n");
1689
+ if (nl === -1) return null;
1690
+ at = nl + 1;
648
1691
  }
649
- return result;
1692
+ return {
1693
+ start: at,
1694
+ end: at,
1695
+ text: '/* SibuJS Dev Mode */ if (typeof globalThis !== "undefined") { globalThis.__SIBU_DEV__ = true; } '
1696
+ };
650
1697
  }
651
- function injectDevHelpers(code2) {
652
- if (code2.includes('from "sibu"') || code2.includes("from 'sibu'") || code2.includes('from "sibu/') || code2.includes("from 'sibu/")) {
653
- return `/* SibuJS Dev Mode */
654
- if (typeof globalThis !== 'undefined') { (globalThis as unknown as Record<string, unknown>).__SIBU_DEV__ = true; }
655
- ${code2}`;
656
- }
657
- return code2;
1698
+ function staticOptimizationEdits(code2) {
1699
+ const analysis = analyzeStaticTemplates(code2);
1700
+ if (!analysis.hasStaticPatterns) return null;
1701
+ const P = uniquePrefix(code2);
1702
+ const edits = analysis.patterns.map((pattern2) => ({
1703
+ start: pattern2.start,
1704
+ end: pattern2.end,
1705
+ // A call spanning several lines keeps its line breaks.
1706
+ text: `${P}static(${jsStringLiteral(pattern2.templateHtml)}${lineBreaksIn(code2, pattern2.start, pattern2.end)})`
1707
+ }));
1708
+ const append = `
1709
+ import { staticTemplate as ${P}staticTemplate } from "sibujs/performance";
1710
+ function ${P}static(markup) {
1711
+ return document.adoptNode(${P}staticTemplate(markup));
1712
+ }
1713
+ `;
1714
+ return { edits, append };
1715
+ }
1716
+ function resolveDevMode(explicit, info) {
1717
+ if (explicit !== void 0) return explicit;
1718
+ if (info?.command === "build") return info.mode === "development";
1719
+ if (info?.command === "serve") return true;
1720
+ return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
658
1721
  }
659
1722
  function sibuVitePlugin(options = {}) {
660
1723
  const {
@@ -663,22 +1726,23 @@ function sibuVitePlugin(options = {}) {
663
1726
  include = DEFAULT_INCLUDE,
664
1727
  exclude = DEFAULT_EXCLUDE,
665
1728
  devMode,
666
- staticOptimize,
1729
+ staticOptimize = false,
667
1730
  compileTemplates
668
1731
  } = options;
669
- const isDevMode = devMode ?? (typeof process !== "undefined" && process.env?.NODE_ENV !== "production");
1732
+ let isDevMode = resolveDevMode(devMode, void 0);
670
1733
  return {
671
1734
  name: "sibu-vite-plugin",
672
1735
  enforce: "pre",
673
- config() {
1736
+ config(_userConfig, env) {
1737
+ isDevMode = resolveDevMode(devMode, env);
674
1738
  return {
675
- // Optimize dependency pre-bundling for sibu
1739
+ // Optimize dependency pre-bundling for sibujs
676
1740
  optimizeDeps: {
677
- include: ["sibu"]
1741
+ include: ["sibujs"]
678
1742
  },
679
- // Ensure sibu is treated correctly for SSR
1743
+ // Ensure sibujs is treated correctly for SSR
680
1744
  ssr: {
681
- noExternal: ["sibu"]
1745
+ noExternal: ["sibujs"]
682
1746
  },
683
1747
  // Define global constants for dead code elimination
684
1748
  define: {
@@ -691,69 +1755,47 @@ function sibuVitePlugin(options = {}) {
691
1755
  }
692
1756
  };
693
1757
  },
1758
+ configResolved(config) {
1759
+ isDevMode = resolveDevMode(devMode, config);
1760
+ },
694
1761
  transform(code2, id) {
695
1762
  if (!matchesPattern(id, include) || matchesPattern(id, exclude)) {
696
1763
  return null;
697
1764
  }
698
- let transformed = code2;
699
- let modified = false;
700
- if (pureAnnotations) {
701
- const annotated = injectPureAnnotations(transformed);
702
- if (annotated !== transformed) {
703
- transformed = annotated;
704
- modified = true;
705
- }
1765
+ if (!code2.includes("sibujs")) return null;
1766
+ if (!scanModule(code2)) return null;
1767
+ const edits = [];
1768
+ let append = "";
1769
+ if (isDevMode && importsSibu(code2)) {
1770
+ const prologue = devPrologueEdit(code2);
1771
+ if (prologue) edits.push(prologue);
706
1772
  }
707
- if (isDevMode) {
708
- const withDevHelpers = injectDevHelpers(transformed);
709
- if (withDevHelpers !== transformed) {
710
- transformed = withDevHelpers;
711
- modified = true;
712
- }
1773
+ if (pureAnnotations) {
1774
+ edits.push(...pureAnnotationEdits(code2));
713
1775
  }
714
1776
  const shouldCompile = compileTemplates ?? !isDevMode;
715
- if (shouldCompile && transformed.includes("html`")) {
716
- const compiled = compileHtmlTemplates(transformed);
717
- if (compiled.code) {
718
- const tagImports = Array.from(compiled.usedTags).filter((t) => !SVG_TAGS_SET.has(t));
719
- const needsSvg = compiled.usesSvg;
720
- const imports = [];
721
- if (tagImports.length > 0) {
722
- imports.push(`import { ${tagImports.join(", ")} } from "sibu";`);
723
- }
724
- if (needsSvg) {
725
- imports.push(`import { tagFactory as __sbTagFactory, SVG_NS as __sbSVG_NS } from "sibu";`);
726
- }
727
- const newImports = imports.filter((imp) => !transformed.includes(imp));
728
- if (newImports.length > 0) {
729
- compiled.code = `${newImports.join("\n")}
730
- ${compiled.code}`;
731
- }
732
- transformed = compiled.code;
733
- modified = true;
1777
+ if (shouldCompile) {
1778
+ const plan = planHtmlTemplates(code2);
1779
+ if (plan.compiledCount > 0) {
1780
+ edits.push(...plan.edits);
1781
+ append += plan.append;
734
1782
  }
735
1783
  }
736
- const shouldOptimize = staticOptimize ?? !isDevMode;
737
- if (shouldOptimize) {
738
- const analysis = analyzeStaticTemplates(transformed);
739
- if (analysis.hasStaticPatterns) {
740
- const sorted = [...analysis.patterns].sort((a2, b2) => b2.start - a2.start);
741
- for (const pattern2 of sorted) {
742
- const replacement = `staticTemplate(${JSON.stringify(pattern2.templateHtml)})`;
743
- transformed = transformed.slice(0, pattern2.start) + replacement + transformed.slice(pattern2.end);
744
- }
745
- if (!transformed.includes("import") || !transformed.includes("staticTemplate")) {
746
- transformed = `import { staticTemplate } from "sibu";
747
- ${transformed}`;
748
- }
749
- modified = true;
1784
+ if (staticOptimize) {
1785
+ const optimized = staticOptimizationEdits(code2);
1786
+ if (optimized) {
1787
+ edits.push(...optimized.edits);
1788
+ append += optimized.append;
750
1789
  }
751
1790
  }
752
- if (!modified) return null;
1791
+ if (edits.length === 0 && append === "") return null;
1792
+ const sorted = normalizeEdits(edits);
1793
+ if (!sorted) return null;
1794
+ const transformed = applyEdits(code2, sorted, append);
1795
+ if (transformed === code2) return null;
753
1796
  return {
754
1797
  code: transformed,
755
- map: void 0
756
- // Let Vite handle source maps
1798
+ map: editsSourceMap(code2, sorted, append, id)
757
1799
  };
758
1800
  },
759
1801
  handleHotUpdate(ctx) {
@@ -812,7 +1854,7 @@ function createViteConfig(options = {}) {
812
1854
  // SSR configuration
813
1855
  ...ssr ? {
814
1856
  ssr: {
815
- noExternal: ["sibu"],
1857
+ noExternal: ["sibujs"],
816
1858
  target: "node"
817
1859
  },
818
1860
  build: {
@@ -829,8 +1871,8 @@ function createViteConfig(options = {}) {
829
1871
  } : {},
830
1872
  // Optimize dependency handling
831
1873
  optimizeDeps: {
832
- include: ["sibu"],
833
- // Force pre-bundling of sibu for faster dev startup
1874
+ include: ["sibujs"],
1875
+ // Force pre-bundling of sibujs for faster dev startup
834
1876
  force: false
835
1877
  },
836
1878
  // Environment variable handling
@@ -853,26 +1895,11 @@ function deepMerge(target, source2) {
853
1895
  }
854
1896
 
855
1897
  // src/build/webpack.ts
856
- var PURE_FACTORIES = [
857
- "tagFactory",
858
- "context",
859
- "defineComponent",
860
- "withProps",
861
- "withDefaults",
862
- "pure",
863
- "noSideEffect"
864
- ];
865
1898
  function addPureAnnotations(source2) {
866
- let result = source2;
867
- for (const factory of PURE_FACTORIES) {
868
- const pattern2 = new RegExp(`(?<!/\\*#__PURE__\\*/\\s*)\\b(${factory})\\s*\\(`, "g");
869
- result = result.replace(pattern2, "/*#__PURE__*/ $1(");
870
- }
871
- return result;
1899
+ return injectPureAnnotations(source2);
872
1900
  }
873
1901
  function sibuWebpackPlugin(options = {}) {
874
- const { pureAnnotations = true, devMode } = options;
875
- const isDevMode = devMode ?? (typeof process !== "undefined" && process.env?.NODE_ENV !== "production");
1902
+ const { devMode } = options;
876
1903
  return {
877
1904
  name: "SibuWebpackPlugin",
878
1905
  apply(compiler) {
@@ -880,34 +1907,9 @@ function sibuWebpackPlugin(options = {}) {
880
1907
  const comp = compilation;
881
1908
  if (comp.hooks?.optimizeModules) {
882
1909
  comp.hooks.optimizeModules.tap("SibuWebpackPlugin", () => {
883
- });
884
- }
885
- });
886
- if (pureAnnotations) {
887
- compiler.hooks?.afterEnvironment?.tap("SibuWebpackPlugin", () => {
888
- if (!compiler.options.module) {
889
- compiler.options.module = { rules: [] };
890
- }
891
- if (!compiler.options.module.rules) {
892
- compiler.options.module.rules = [];
893
- }
894
- compiler.options.module.rules.push({
895
- test: /\.[jt]sx?$/,
896
- exclude: /node_modules/,
897
- enforce: "pre",
898
- use: [
899
- {
900
- loader: {
901
- // Inline loader function
902
- ident: "sibu-pure-annotations-loader",
903
- loader: "__sibu_inline_loader__",
904
- options: {}
905
- }
906
- }
907
- ]
908
- });
909
- });
910
- }
1910
+ });
1911
+ }
1912
+ });
911
1913
  compiler.hooks?.afterResolvers?.tap("SibuWebpackPlugin", () => {
912
1914
  if (!compiler.options.resolve) {
913
1915
  compiler.options.resolve = {};
@@ -919,20 +1921,21 @@ function sibuWebpackPlugin(options = {}) {
919
1921
  compiler.options.resolve.mainFields.unshift("module");
920
1922
  }
921
1923
  });
922
- if (isDevMode) {
923
- compiler.hooks?.done?.tap("SibuWebpackPlugin", (stats) => {
924
- const statsObj = stats;
925
- const info = statsObj?.toJson?.({ modules: false, chunks: false });
926
- if (info) {
927
- console.log(`[SibuWebpackPlugin] Build completed in ${info.time || 0}ms`);
928
- const warnings = info.warnings;
929
- if (warnings?.length) {
930
- console.log(`[SibuWebpackPlugin] ${warnings.length} warning(s)`);
931
- }
1924
+ const tapDoneLogger = () => compiler.hooks?.done?.tap("SibuWebpackPlugin", (stats) => {
1925
+ const statsObj = stats;
1926
+ const info = statsObj?.toJson?.({ modules: false, chunks: false });
1927
+ if (info) {
1928
+ console.log(`[SibuWebpackPlugin] Build completed in ${info.time || 0}ms`);
1929
+ const warnings = info.warnings;
1930
+ if (warnings?.length) {
1931
+ console.log(`[SibuWebpackPlugin] ${warnings.length} warning(s)`);
932
1932
  }
933
- });
934
- }
1933
+ }
1934
+ });
1935
+ if (devMode === true) tapDoneLogger();
935
1936
  compiler.hooks?.environment?.tap("SibuWebpackPlugin", () => {
1937
+ const isDevMode = devMode ?? resolveWebpackDevMode(compiler.options);
1938
+ if (devMode === void 0 && isDevMode) tapDoneLogger();
936
1939
  if (!compiler.options.plugins) {
937
1940
  compiler.options.plugins = [];
938
1941
  }
@@ -940,10 +1943,20 @@ function sibuWebpackPlugin(options = {}) {
940
1943
  __SIBU_DEV__: JSON.stringify(isDevMode)
941
1944
  };
942
1945
  compiler.__sibuDefines = defines;
1946
+ const DefinePlugin = compiler.webpack?.DefinePlugin;
1947
+ if (DefinePlugin) {
1948
+ new DefinePlugin(defines).apply(compiler);
1949
+ }
943
1950
  });
944
1951
  }
945
1952
  };
946
1953
  }
1954
+ function resolveWebpackDevMode(options) {
1955
+ const mode = options.mode;
1956
+ if (mode === "development") return true;
1957
+ if (mode === "production" || mode === void 0) return false;
1958
+ return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
1959
+ }
947
1960
  function createPureAnnotationsLoader() {
948
1961
  return function sibuPureAnnotationsLoader(source2) {
949
1962
  return addPureAnnotations(source2);
@@ -1012,9 +2025,10 @@ function createWebpackConfig(options = {}) {
1012
2025
  splitChunks: isDev2 ? false : {
1013
2026
  chunks: "all",
1014
2027
  cacheGroups: {
1015
- // Separate sibu framework code into its own chunk
2028
+ // Separate sibujs framework code into its own chunk (the package
2029
+ // directory is `sibujs`; matching `sibu` never selected anything)
1016
2030
  sibu: {
1017
- test: /[\\/]node_modules[\\/]sibu[\\/]/,
2031
+ test: /[\\/]node_modules[\\/]sibujs[\\/]/,
1018
2032
  name: "sibu",
1019
2033
  chunks: "all",
1020
2034
  priority: 20
@@ -1065,7 +2079,7 @@ __export(index_exports, {
1065
2079
  Loading: () => Loading,
1066
2080
  MAX_DRAIN_TEARDOWNS: () => MAX_DRAIN_TEARDOWNS,
1067
2081
  Portal: () => Portal,
1068
- SVG_NS: () => SVG_NS2,
2082
+ SVG_NS: () => SVG_NS3,
1069
2083
  Suspense: () => Suspense,
1070
2084
  __resetIdCounter: () => __resetIdCounter,
1071
2085
  a: () => a,
@@ -1381,33 +2395,6 @@ function reportError(error, context2) {
1381
2395
  });
1382
2396
  }
1383
2397
 
1384
- // src/core/dev.ts
1385
- var DEV = typeof __SIBU_DEV__ !== "undefined" ? !!__SIBU_DEV__ : typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
1386
- // safe default: off in browser, on in test/dev Node
1387
- typeof process !== "undefined" && process.env?.NODE_ENV !== "production"
1388
- );
1389
- function isDev() {
1390
- if (typeof globalThis.__SIBU_DEV__ !== "undefined") return !!globalThis.__SIBU_DEV__;
1391
- if (typeof __SIBU_DEV__ !== "undefined") return !!__SIBU_DEV__;
1392
- return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
1393
- }
1394
- function devAssert(condition, message) {
1395
- if ((typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) && !condition) {
1396
- throw new Error(`[SibuJS] ${message}`);
1397
- }
1398
- }
1399
- function devWarnLazy(build) {
1400
- if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
1401
- const message = build();
1402
- if (message) console.warn(`[SibuJS] ${message}`);
1403
- }
1404
- }
1405
- function devWarn(message) {
1406
- if (typeof __SIBU_DEV__ !== "undefined" ? __SIBU_DEV__ : DEV) {
1407
- console.warn(`[SibuJS] ${message}`);
1408
- }
1409
- }
1410
-
1411
2398
  // src/core/rendering/dispose.ts
1412
2399
  var elementDisposers = /* @__PURE__ */ new WeakMap();
1413
2400
  var MAX_DRAIN_TEARDOWNS = 1e4;
@@ -1447,298 +2434,127 @@ function registerDisposer(node, teardown) {
1447
2434
  const capture = resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
1448
2435
  if (capture) capture.entries.push([node, teardown]);
1449
2436
  }
1450
- function unregisterDisposer(node, teardown) {
1451
- const disposers = elementDisposers.get(node);
1452
- if (!disposers) return false;
1453
- const index = disposers.indexOf(teardown);
1454
- if (index === -1) return false;
1455
- disposers.splice(index, 1);
1456
- if (DEV) activeBindingCount--;
1457
- if (disposers.length === 0) elementDisposers.delete(node);
1458
- return true;
1459
- }
1460
- function dispose(node) {
1461
- const stack = [node];
1462
- const order = [];
1463
- while (stack.length > 0) {
1464
- const current = stack.pop();
1465
- order.push(current);
1466
- const children = Array.from(current.childNodes);
1467
- for (let i2 = 0; i2 < children.length; i2++) {
1468
- stack.push(children[i2]);
1469
- }
1470
- }
1471
- for (let i2 = order.length - 1; i2 >= 0; i2--) {
1472
- disposeNodeOwn(order[i2]);
1473
- }
1474
- }
1475
- function disposeNodeOwn(node) {
1476
- if (!elementDisposers.has(node)) return;
1477
- let executed = 0;
1478
- let runaway = false;
1479
- while (!runaway) {
1480
- const pending = elementDisposers.get(node);
1481
- if (!pending || pending.length === 0) break;
1482
- const snapshot = pending.slice();
1483
- elementDisposers.delete(node);
1484
- if (DEV) activeBindingCount -= snapshot.length;
1485
- for (let i2 = 0; i2 < snapshot.length; i2++) {
1486
- if (executed >= MAX_DRAIN_TEARDOWNS) {
1487
- const rest = snapshot.slice(i2);
1488
- const added = elementDisposers.get(node);
1489
- elementDisposers.set(node, added ? rest.concat(added) : rest);
1490
- if (DEV) activeBindingCount += rest.length;
1491
- reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
1492
- runaway = true;
1493
- break;
1494
- }
1495
- executed++;
1496
- try {
1497
- snapshot[i2]();
1498
- } catch (err) {
1499
- reportError(err, { phase: "cleanup", name: "disposer" });
1500
- }
1501
- }
1502
- }
1503
- }
1504
- function replaceChildrenSafely(parent, ...next) {
1505
- for (let i2 = 0; i2 < next.length; i2++) {
1506
- const node = next[i2];
1507
- node.parentNode?.removeChild(node);
1508
- }
1509
- const current = Array.from(parent.childNodes);
1510
- for (let i2 = 0; i2 < current.length; i2++) {
1511
- dispose(current[i2]);
1512
- }
1513
- parent.replaceChildren(...next);
1514
- }
1515
- function checkLeaks(warnThreshold = 0) {
1516
- if (!DEV) return 0;
1517
- if (warnThreshold > 0 && activeBindingCount > warnThreshold) {
1518
- devWarn(
1519
- `checkLeaks: ${activeBindingCount} active DOM bindings detected. Expected \u2264${warnThreshold}. This may indicate a component was removed from the DOM without calling dispose().`
1520
- );
1521
- }
1522
- return activeBindingCount;
1523
- }
1524
-
1525
- // src/utils/sanitize.ts
1526
- function stripControlChars(value) {
1527
- return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
1528
- }
1529
- function isEventHandlerAttr(name) {
1530
- if (name.length < 3) return false;
1531
- const lower = name.toLowerCase();
1532
- return lower[0] === "o" && lower[1] === "n" && lower.charCodeAt(2) >= 97 && lower.charCodeAt(2) <= 122;
1533
- }
1534
- var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
1535
- var OUTER_TRIM = /^[\x00-\x20\x7f-\x9f]+|[\x00-\x20\x7f-\x9f]+$/g;
1536
- function sanitizeUrl(url) {
1537
- const probe = stripControlChars(url).trim();
1538
- if (!probe) return "";
1539
- const output2 = url.replace(OUTER_TRIM, "");
1540
- const lower = probe.toLowerCase();
1541
- let schemeEnd = -1;
1542
- for (let i2 = 0; i2 < lower.length; i2++) {
1543
- const ch = lower.charCodeAt(i2);
1544
- if (ch === 58) {
1545
- schemeEnd = i2;
1546
- break;
1547
- }
1548
- if (ch === 47 || ch === 63 || ch === 35) break;
1549
- }
1550
- if (schemeEnd === -1) return output2;
1551
- const scheme = lower.slice(0, schemeEnd + 1);
1552
- if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return output2;
1553
- if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
1554
- return output2;
1555
- }
1556
- var SRCSET_DESCRIPTOR = /^\s+\d+(\.\d+)?[wx]$/;
1557
- function sanitizeSrcset(value) {
1558
- const parts = value.split(",");
1559
- const out = [];
1560
- for (let i2 = 0; i2 < parts.length; i2++) {
1561
- const part = parts[i2].trim();
1562
- if (!part) continue;
1563
- const m = part.match(/^(\S+)(\s+.+)?$/);
1564
- if (!m) continue;
1565
- if (m[2] !== void 0 && !SRCSET_DESCRIPTOR.test(m[2])) continue;
1566
- const safe = sanitizeUrl(m[1]);
1567
- if (!safe) continue;
1568
- out.push(m[2] ? `${safe}${m[2]}` : safe);
1569
- }
1570
- return out.join(", ");
1571
- }
1572
- var CSS_DANGER_GATE = /[(:@\\]/;
1573
- function isCssWhitespace(ch) {
1574
- return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
1575
- }
1576
- function isHexDigit(ch) {
1577
- return ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
1578
- }
1579
- function decodeCssEscapes(value) {
1580
- let out = "";
1581
- for (let i2 = 0; i2 < value.length; i2++) {
1582
- const ch = value[i2];
1583
- if (ch !== "\\") {
1584
- out += ch;
1585
- continue;
1586
- }
1587
- if (i2 + 1 >= value.length) break;
1588
- const next = value[i2 + 1];
1589
- if (next === "\n" || next === "\f") {
1590
- i2 += 1;
1591
- continue;
1592
- }
1593
- if (next === "\r") {
1594
- i2 += value[i2 + 2] === "\n" ? 2 : 1;
1595
- continue;
1596
- }
1597
- if (isHexDigit(next)) {
1598
- let hex = "";
1599
- let j = i2 + 1;
1600
- while (j < value.length && hex.length < 6 && isHexDigit(value[j])) {
1601
- hex += value[j];
1602
- j++;
1603
- }
1604
- if (j < value.length && isCssWhitespace(value[j])) {
1605
- j += value[j] === "\r" && value[j + 1] === "\n" ? 2 : 1;
2437
+ function withDisposerRollback(build) {
2438
+ const frame = { entries: [], closed: false };
2439
+ const captured = frame.entries;
2440
+ registrationCaptures.push(frame);
2441
+ let result;
2442
+ try {
2443
+ result = build();
2444
+ } catch (err) {
2445
+ let executed = 0;
2446
+ try {
2447
+ while (captured.length > 0) {
2448
+ if (executed >= MAX_DRAIN_TEARDOWNS) {
2449
+ reportDrainRunaway("rollback", executed, captured.length);
2450
+ break;
2451
+ }
2452
+ const [node, teardown] = captured.pop();
2453
+ if (!unregisterDisposer(node, teardown)) continue;
2454
+ executed++;
2455
+ try {
2456
+ teardown();
2457
+ } catch (cleanupErr) {
2458
+ reportError(cleanupErr, { phase: "cleanup", name: "disposer" });
2459
+ }
1606
2460
  }
1607
- const code2 = Number.parseInt(hex, 16);
1608
- const invalid = code2 === 0 || code2 > 1114111 || code2 >= 55296 && code2 <= 57343;
1609
- out += invalid ? "\uFFFD" : String.fromCodePoint(code2);
1610
- i2 = j - 1;
1611
- continue;
1612
- }
1613
- out += next;
1614
- i2 += 1;
1615
- }
1616
- return out;
1617
- }
1618
- var BLOCKED_CSS_NEEDLES = [
1619
- "url(",
1620
- "expression(",
1621
- "javascript:",
1622
- "vbscript:",
1623
- "-moz-binding",
1624
- "behavior:",
1625
- "@import",
1626
- "image-set(",
1627
- "filter:progid"
1628
- ];
1629
- function describeElement(el) {
1630
- if (!el) return "an element";
1631
- let out = el.tagName.toLowerCase();
1632
- const id = el.getAttribute("id");
1633
- if (id) out += `#${id}`;
1634
- const cls = el.getAttribute("class");
1635
- if (cls) out += `.${cls.trim().split(/\s+/).slice(0, 3).join(".")}`;
1636
- return `<${out}>`;
1637
- }
1638
- var warnedDeclarations = /* @__PURE__ */ new Set();
1639
- var MAX_WARNED_DECLARATIONS = 100;
1640
- var declarationCapAnnounced = false;
1641
- function warnDroppedDeclaration(value, reason, ctx) {
1642
- devWarnLazy(() => {
1643
- const reasons = [
1644
- "url() in an inline style is an exfiltration channel: the browser fetches it immediately and the URL's path or query can carry page data off-origin",
1645
- "expression() executes arbitrary script in legacy engines",
1646
- "the javascript: scheme executes script",
1647
- "the vbscript: scheme executes script",
1648
- "-moz-binding loads and runs remote XBL",
1649
- "behavior: loads and runs a remote HTC script",
1650
- "@import pulls in a remote stylesheet",
1651
- "image-set() fetches remote images, the same exfiltration channel as url()",
1652
- "filter:progid activates legacy scriptable filters"
1653
- ];
1654
- const declaration = ctx?.property ? `${ctx.property}: ${value}` : value;
1655
- const where = describeElement(ctx?.element);
1656
- const key = `${where}|${declaration}`;
1657
- if (warnedDeclarations.has(key)) return "";
1658
- if (warnedDeclarations.size >= MAX_WARNED_DECLARATIONS) {
1659
- if (declarationCapAnnounced) return "";
1660
- declarationCapAnnounced = true;
1661
- return `${MAX_WARNED_DECLARATIONS} distinct dropped style declarations have been reported; suppressing further ones for the rest of this session so they cannot flood the console. The declarations are still dropped \u2014 only the reporting stops. Fix the reported ones and reload to see any that remain.`;
2461
+ } finally {
2462
+ frame.closed = true;
2463
+ captured.length = 0;
2464
+ registrationCaptures.pop();
1662
2465
  }
1663
- warnedDeclarations.add(key);
1664
- return `style declaration "${declaration}" was dropped by the style sanitizer on ${where} \u2014 ${reasons[reason]}. The element renders without it, which usually looks like a missing image rather than an error. Sanctioned alternatives: for a content image use an <img> element (or a <picture>); for decoration, put the rule in a stylesheet and set a class instead \u2014 stylesheet CSS is not sanitized, only inline style attributes are. If this declaration is not yours, it came from untrusted data and the drop just prevented an exfiltration.`;
1665
- });
1666
- }
1667
- function blockedCssReason(value) {
1668
- if (!CSS_DANGER_GATE.test(value)) return -1;
1669
- const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
1670
- const lower = normalized.toLowerCase().replace(/\s+/g, "");
1671
- for (let i2 = 0; i2 < BLOCKED_CSS_NEEDLES.length; i2++) {
1672
- if (lower.includes(BLOCKED_CSS_NEEDLES[i2])) return i2;
2466
+ throw err;
1673
2467
  }
1674
- return -1;
2468
+ frame.closed = true;
2469
+ registrationCaptures.pop();
2470
+ const parent = resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
2471
+ if (parent) {
2472
+ for (const entry of captured) {
2473
+ if (elementDisposers.get(entry[0])?.includes(entry[1])) parent.entries.push(entry);
2474
+ }
2475
+ frame.forwardTo = parent;
2476
+ }
2477
+ captured.length = 0;
2478
+ return result;
1675
2479
  }
1676
- function sanitizeCSSValue(value, context2) {
1677
- const reason = blockedCssReason(value);
1678
- if (reason === -1) return value;
1679
- warnDroppedDeclaration(value, reason, context2);
1680
- return "";
2480
+ function unregisterDisposer(node, teardown) {
2481
+ const disposers = elementDisposers.get(node);
2482
+ if (!disposers) return false;
2483
+ const index = disposers.indexOf(teardown);
2484
+ if (index === -1) return false;
2485
+ disposers.splice(index, 1);
2486
+ if (DEV) activeBindingCount--;
2487
+ if (disposers.length === 0) elementDisposers.delete(node);
2488
+ return true;
1681
2489
  }
1682
- function sanitizeStyleAttribute(cssText, context2) {
1683
- const input2 = String(cssText);
1684
- if (input2.trim() === "") return "";
1685
- if (typeof document === "undefined") {
1686
- return sanitizeCSSValue(input2, context2) === "" ? "" : input2;
2490
+ function dispose(node) {
2491
+ const stack = [node];
2492
+ const order = [];
2493
+ while (stack.length > 0) {
2494
+ const current = stack.pop();
2495
+ order.push(current);
2496
+ const children = Array.from(current.childNodes);
2497
+ for (let i2 = 0; i2 < children.length; i2++) {
2498
+ stack.push(children[i2]);
2499
+ }
1687
2500
  }
1688
- const probe = document.createElement("div");
1689
- try {
1690
- probe.style.cssText = input2;
1691
- } catch {
1692
- return "";
2501
+ for (let i2 = order.length - 1; i2 >= 0; i2--) {
2502
+ disposeNodeOwn(order[i2]);
1693
2503
  }
1694
- const declarations = [];
1695
- for (let i2 = 0; i2 < probe.style.length; i2++) {
1696
- const property = probe.style[i2];
1697
- const value = probe.style.getPropertyValue(property);
1698
- let reason = blockedCssReason(value);
1699
- if (reason === -1) reason = blockedCssReason(`${property}:${value}`);
1700
- if (reason !== -1) {
1701
- warnDroppedDeclaration(value, reason, { property, element: context2?.element });
1702
- continue;
2504
+ }
2505
+ function disposeNodeOwn(node) {
2506
+ if (!elementDisposers.has(node)) return;
2507
+ let executed = 0;
2508
+ let runaway = false;
2509
+ while (!runaway) {
2510
+ const pending = elementDisposers.get(node);
2511
+ if (!pending || pending.length === 0) break;
2512
+ const snapshot = pending.slice();
2513
+ elementDisposers.delete(node);
2514
+ if (DEV) activeBindingCount -= snapshot.length;
2515
+ for (let i2 = 0; i2 < snapshot.length; i2++) {
2516
+ if (executed >= MAX_DRAIN_TEARDOWNS) {
2517
+ const rest = snapshot.slice(i2);
2518
+ const added = elementDisposers.get(node);
2519
+ elementDisposers.set(node, added ? rest.concat(added) : rest);
2520
+ if (DEV) activeBindingCount += rest.length;
2521
+ reportDrainRunaway("dispose", executed, rest.length + (added?.length ?? 0));
2522
+ runaway = true;
2523
+ break;
2524
+ }
2525
+ executed++;
2526
+ try {
2527
+ snapshot[i2]();
2528
+ } catch (err) {
2529
+ reportError(err, { phase: "cleanup", name: "disposer" });
2530
+ }
1703
2531
  }
1704
- const priority = probe.style.getPropertyPriority(property);
1705
- declarations.push(`${property}: ${value}${priority ? ` !${priority}` : ""}`);
1706
2532
  }
1707
- return declarations.join("; ");
1708
2533
  }
1709
- var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
1710
- "href",
1711
- "xlink:href",
1712
- "src",
1713
- "action",
1714
- "formaction",
1715
- "formtarget",
1716
- "cite",
1717
- "poster",
1718
- "background",
1719
- "srcset",
1720
- "ping",
1721
- "data",
1722
- // `<html manifest>` is a URL sink too. It was carried only by the SSR
1723
- // serializers' private copy of this set; folding that copy into this one would
1724
- // otherwise have silently dropped its coverage.
1725
- "manifest"
1726
- ]);
1727
- var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
1728
- function isHtmlContentAttribute(name) {
1729
- return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
2534
+ function replaceChildrenSafely(parent, ...next) {
2535
+ for (let i2 = 0; i2 < next.length; i2++) {
2536
+ const node = next[i2];
2537
+ node.parentNode?.removeChild(node);
2538
+ }
2539
+ const current = Array.from(parent.childNodes);
2540
+ for (let i2 = 0; i2 < current.length; i2++) {
2541
+ dispose(current[i2]);
2542
+ }
2543
+ parent.replaceChildren(...next);
1730
2544
  }
1731
- function sanitizeAttributeString(attr, value, context2) {
1732
- const lower = attr.toLowerCase();
1733
- if (lower === "srcset") return sanitizeSrcset(value);
1734
- if (URL_ATTRIBUTES.has(lower)) return sanitizeUrl(value);
1735
- if (lower === "style") return sanitizeStyleAttribute(value, context2);
1736
- return value;
2545
+ function checkLeaks(warnThreshold = 0) {
2546
+ if (!DEV) return 0;
2547
+ if (warnThreshold > 0 && activeBindingCount > warnThreshold) {
2548
+ devWarn(
2549
+ `checkLeaks: ${activeBindingCount} active DOM bindings detected. Expected \u2264${warnThreshold}. This may indicate a component was removed from the DOM without calling dispose().`
2550
+ );
2551
+ }
2552
+ return activeBindingCount;
1737
2553
  }
1738
2554
 
1739
2555
  // src/utils/setSafeAttribute.ts
1740
2556
  var HTML_NS = "http://www.w3.org/1999/xhtml";
1741
- var SVG_NS = "http://www.w3.org/2000/svg";
2557
+ var SVG_NS2 = "http://www.w3.org/2000/svg";
1742
2558
  var XLINK_NS = "http://www.w3.org/1999/xlink";
1743
2559
  var XML_NS = "http://www.w3.org/XML/1998/namespace";
1744
2560
  var BOOLEAN_IDL_ATTRS = /* @__PURE__ */ new Set(["checked", "disabled", "selected"]);
@@ -1749,7 +2565,7 @@ function setProp(el, key, val) {
1749
2565
  el[key] = val;
1750
2566
  }
1751
2567
  function namespaceFor(el, name) {
1752
- if (el.namespaceURI !== SVG_NS) return null;
2568
+ if (el.namespaceURI !== SVG_NS2) return null;
1753
2569
  const lower = name.toLowerCase();
1754
2570
  if (lower.startsWith("xlink:")) return XLINK_NS;
1755
2571
  if (lower.startsWith("xml:")) return XML_NS;
@@ -2310,7 +3126,7 @@ function forEachSubscriber(signal2, visit) {
2310
3126
  }
2311
3127
 
2312
3128
  // src/reactivity/track.ts
2313
- var _runtimeVersion = true ? "4.6.0" : "dev";
3129
+ var _runtimeVersion = true ? "4.7.0" : "dev";
2314
3130
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
2315
3131
  function resolveReactiveApi() {
2316
3132
  const g2 = globalThis;
@@ -2587,7 +3403,7 @@ function bindChildNode(placeholder, getter) {
2587
3403
  }
2588
3404
 
2589
3405
  // src/core/rendering/tagFactory.ts
2590
- var SVG_NS2 = "http://www.w3.org/2000/svg";
3406
+ var SVG_NS3 = "http://www.w3.org/2000/svg";
2591
3407
  var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
2592
3408
  function isBlockedTag(tag) {
2593
3409
  return BLOCKED_TAGS.has(tag.toLowerCase());
@@ -2618,8 +3434,34 @@ function toKebab(prop) {
2618
3434
  }
2619
3435
  function applyStyle(el, style2) {
2620
3436
  if (typeof style2 === "function") {
3437
+ const getter = style2;
3438
+ let written = [];
3439
+ let wroteString = false;
2621
3440
  const teardown = reactiveBinding2(() => {
2622
- el.setAttribute("style", sanitizeStyleAttribute(String(style2()), { element: el }));
3441
+ const value = getter();
3442
+ if (value === null || typeof value !== "object") {
3443
+ written = [];
3444
+ wroteString = true;
3445
+ el.setAttribute("style", sanitizeStyleAttribute(String(value), { element: el }));
3446
+ return;
3447
+ }
3448
+ const decl = el.style;
3449
+ if (wroteString) {
3450
+ el.removeAttribute("style");
3451
+ wroteString = false;
3452
+ }
3453
+ const next = [];
3454
+ for (const prop in value) {
3455
+ const val = value[prop];
3456
+ if (val == null) continue;
3457
+ const name = toKebab(prop);
3458
+ next.push(name);
3459
+ decl.setProperty(name, sanitizeCSSValue(String(val), { property: name, element: el }));
3460
+ }
3461
+ for (let i2 = 0; i2 < written.length; i2++) {
3462
+ if (next.indexOf(written[i2]) === -1) decl.removeProperty(written[i2]);
3463
+ }
3464
+ written = next;
2623
3465
  }, el);
2624
3466
  registerDisposer(el, teardown);
2625
3467
  return;
@@ -2650,7 +3492,9 @@ function applyClass(el, cls) {
2650
3492
  }
2651
3493
  if (typeof cls === "function") {
2652
3494
  const teardown = reactiveBinding2(() => {
2653
- el.setAttribute("class", cls());
3495
+ const value = cls();
3496
+ if (value == null || value === false) el.removeAttribute("class");
3497
+ else el.setAttribute("class", value);
2654
3498
  }, el);
2655
3499
  registerDisposer(el, teardown);
2656
3500
  return;
@@ -2732,7 +3576,7 @@ function appendChildren(el, nodes) {
2732
3576
  }
2733
3577
  }
2734
3578
  }
2735
- var tagFactory = (tag, ns) => {
3579
+ function tagFactory(tag, ns) {
2736
3580
  const blocked = isBlockedTag(tag);
2737
3581
  return (first, second) => {
2738
3582
  if (blocked) {
@@ -2781,7 +3625,9 @@ var tagFactory = (tag, ns) => {
2781
3625
  const pClass = props.class;
2782
3626
  if (pClass != null) applyClass(el, pClass);
2783
3627
  const pId = props.id;
2784
- if (pId != null) {
3628
+ if (typeof pId === "function") {
3629
+ registerDisposer(el, bindAttribute(el, "id", pId));
3630
+ } else if (pId != null) {
2785
3631
  if (DEV && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
2786
3632
  devWarn(
2787
3633
  `tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
@@ -2831,12 +3677,10 @@ var tagFactory = (tag, ns) => {
2831
3677
  }
2832
3678
  }
2833
3679
  }
2834
- if (props.onElement && typeof props.onElement === "function") {
2835
- props.onElement(el);
2836
- }
3680
+ if (typeof props.onElement === "function") props.onElement(el);
2837
3681
  return el;
2838
3682
  };
2839
- };
3683
+ }
2840
3684
 
2841
3685
  // src/core/rendering/html.ts
2842
3686
  var html = tagFactory("html");
@@ -2911,7 +3755,7 @@ var param = tagFactory("param");
2911
3755
  var picture = tagFactory("picture");
2912
3756
  var portal = tagFactory("portal");
2913
3757
  var source = tagFactory("source");
2914
- var svg = tagFactory("svg", SVG_NS2);
3758
+ var svg = tagFactory("svg", SVG_NS3);
2915
3759
  var math = tagFactory("math");
2916
3760
  var canvas = tagFactory("canvas");
2917
3761
  var noscript = tagFactory("noscript");
@@ -2952,26 +3796,26 @@ var base = tagFactory("base");
2952
3796
  var link = tagFactory("link");
2953
3797
  var meta = tagFactory("meta");
2954
3798
  var style = tagFactory("style");
2955
- var circle = tagFactory("circle", SVG_NS2);
2956
- var ellipse = tagFactory("ellipse", SVG_NS2);
2957
- var g = tagFactory("g", SVG_NS2);
2958
- var line = tagFactory("line", SVG_NS2);
2959
- var path = tagFactory("path", SVG_NS2);
2960
- var polygon = tagFactory("polygon", SVG_NS2);
2961
- var polyline = tagFactory("polyline", SVG_NS2);
2962
- var rect = tagFactory("rect", SVG_NS2);
2963
- var text = tagFactory("text", SVG_NS2);
2964
- var tspan = tagFactory("tspan", SVG_NS2);
2965
- var defs = tagFactory("defs", SVG_NS2);
2966
- var clipPath = tagFactory("clipPath", SVG_NS2);
2967
- var mask = tagFactory("mask", SVG_NS2);
2968
- var pattern = tagFactory("pattern", SVG_NS2);
2969
- var linearGradient = tagFactory("linearGradient", SVG_NS2);
2970
- var radialGradient = tagFactory("radialGradient", SVG_NS2);
2971
- var stop = tagFactory("stop", SVG_NS2);
2972
- var use = tagFactory("use", SVG_NS2);
2973
- var symbol = tagFactory("symbol", SVG_NS2);
2974
- var marker = tagFactory("marker", SVG_NS2);
3799
+ var circle = tagFactory("circle", SVG_NS3);
3800
+ var ellipse = tagFactory("ellipse", SVG_NS3);
3801
+ var g = tagFactory("g", SVG_NS3);
3802
+ var line = tagFactory("line", SVG_NS3);
3803
+ var path = tagFactory("path", SVG_NS3);
3804
+ var polygon = tagFactory("polygon", SVG_NS3);
3805
+ var polyline = tagFactory("polyline", SVG_NS3);
3806
+ var rect = tagFactory("rect", SVG_NS3);
3807
+ var text = tagFactory("text", SVG_NS3);
3808
+ var tspan = tagFactory("tspan", SVG_NS3);
3809
+ var defs = tagFactory("defs", SVG_NS3);
3810
+ var clipPath = tagFactory("clipPath", SVG_NS3);
3811
+ var mask = tagFactory("mask", SVG_NS3);
3812
+ var pattern = tagFactory("pattern", SVG_NS3);
3813
+ var linearGradient = tagFactory("linearGradient", SVG_NS3);
3814
+ var radialGradient = tagFactory("radialGradient", SVG_NS3);
3815
+ var stop = tagFactory("stop", SVG_NS3);
3816
+ var use = tagFactory("use", SVG_NS3);
3817
+ var symbol = tagFactory("symbol", SVG_NS3);
3818
+ var marker = tagFactory("marker", SVG_NS3);
2975
3819
  var center = tagFactory("center");
2976
3820
  var font = tagFactory("font");
2977
3821
  var marquee = tagFactory("marquee");
@@ -3085,10 +3929,13 @@ function safeCall(cb, hookName) {
3085
3929
  return void 0;
3086
3930
  }
3087
3931
  }
3088
- function runMountCallback(callback, hookName, element) {
3932
+ function runMountCallback(callback, hookName, element, isDisposed) {
3089
3933
  const cleanup3 = safeCall(callback, hookName);
3090
- if (typeof cleanup3 === "function" && element) {
3091
- registerDisposer(element, cleanup3);
3934
+ if (typeof cleanup3 !== "function") return;
3935
+ if (isDisposed() || !element.isConnected) {
3936
+ safeCall(cleanup3, "onUnmount");
3937
+ } else {
3938
+ onUnmount(cleanup3, element);
3092
3939
  }
3093
3940
  }
3094
3941
  var mountWatchers = /* @__PURE__ */ new WeakMap();
@@ -3250,25 +4097,26 @@ function onMount(callback, element) {
3250
4097
  if (typeof document === "undefined") return;
3251
4098
  if (element) {
3252
4099
  let disposed = false;
4100
+ const isDisposed = () => disposed;
3253
4101
  registerDisposer(element, () => {
3254
4102
  disposed = true;
3255
4103
  });
3256
4104
  if (element.isConnected) {
3257
4105
  queueMicrotask(() => {
3258
4106
  if (disposed) return;
3259
- runMountCallback(callback, "onMount", element);
4107
+ runMountCallback(callback, "onMount", element, isDisposed);
3260
4108
  });
3261
4109
  return;
3262
4110
  }
3263
4111
  queueMicrotask(() => {
3264
4112
  if (disposed) return;
3265
4113
  if (element.isConnected) {
3266
- runMountCallback(callback, "onMount", element);
4114
+ runMountCallback(callback, "onMount", element, isDisposed);
3267
4115
  return;
3268
4116
  }
3269
4117
  const unregister = registerMountWatcher(element, () => {
3270
4118
  if (disposed) return;
3271
- runMountCallback(callback, "onMount", element);
4119
+ runMountCallback(callback, "onMount", element, isDisposed);
3272
4120
  });
3273
4121
  registerDisposer(element, unregister);
3274
4122
  });
@@ -3305,6 +4153,14 @@ function onCleanup(callback, element) {
3305
4153
  registerDisposer(element, callback);
3306
4154
  }
3307
4155
 
4156
+ // src/core/devtoolsHook.ts
4157
+ function emitDevtools(hook, event, payload) {
4158
+ try {
4159
+ hook.emit(event, payload);
4160
+ } catch {
4161
+ }
4162
+ }
4163
+
3308
4164
  // src/core/ssr-context.ts
3309
4165
  var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
3310
4166
  function detectSSRShared() {
@@ -3542,12 +4398,17 @@ function effect(effectFn, options) {
3542
4398
  try {
3543
4399
  retrack2(ctx.bodyFn, ctx.subscriber);
3544
4400
  if (ctx.rerunPending) drainReruns(ctx);
4401
+ } catch (err) {
4402
+ ctx.disposed = true;
4403
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
4404
+ cleanup2(ctx.subscriber);
4405
+ throw err;
3545
4406
  } finally {
3546
4407
  ctx.running = false;
3547
4408
  ctx.rerunPending = false;
3548
4409
  }
3549
4410
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3550
- if (hook) hook.emit("effect:create", { effectFn });
4411
+ if (hook) emitDevtools(hook, "effect:create", { effectFn });
3551
4412
  return () => disposeEffect(ctx);
3552
4413
  }
3553
4414
 
@@ -3634,7 +4495,7 @@ function signal(initial, options) {
3634
4495
  state2.__v++;
3635
4496
  if (DEV) {
3636
4497
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3637
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
4498
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
3638
4499
  }
3639
4500
  if (!enqueueBatchedSignal(state2)) {
3640
4501
  notifySubscribers2(state2);
@@ -3648,7 +4509,7 @@ function signal(initial, options) {
3648
4509
  state2.value = newValue;
3649
4510
  state2.__v++;
3650
4511
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3651
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
4512
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
3652
4513
  if (!enqueueBatchedSignal(state2)) {
3653
4514
  notifySubscribers2(state2);
3654
4515
  }
@@ -3667,7 +4528,7 @@ function signal(initial, options) {
3667
4528
  }
3668
4529
  if (DEV) {
3669
4530
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3670
- if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
4531
+ if (hook) emitDevtools(hook, "signal:create", { signal: state2, name: debugName, getter: get, initial });
3671
4532
  }
3672
4533
  return [get, set];
3673
4534
  }
@@ -4480,36 +5341,34 @@ function Loading(props = {}) {
4480
5341
  const { text: text2, variant = "spinner", size = "md" } = props;
4481
5342
  const sizeClass = size !== "md" ? ` sibu-loading-${size}` : "";
4482
5343
  if (variant === "dots") {
4483
- return div({
5344
+ return div(
5345
+ {
5346
+ class: `sibu-loading${sizeClass}`,
5347
+ role: "status",
5348
+ "aria-live": "polite",
5349
+ // When there's no visible text, give the live region an accessible name
5350
+ // so it isn't announced as an empty status.
5351
+ "aria-label": text2 ? void 0 : "Loading"
5352
+ },
5353
+ [
5354
+ div({ class: "sibu-loading-dots" }, [
5355
+ span({ class: "sibu-loading-dot" }),
5356
+ span({ class: "sibu-loading-dot" }),
5357
+ span({ class: "sibu-loading-dot" })
5358
+ ]),
5359
+ text2 ? span({ class: "sibu-loading-text" }, text2) : null
5360
+ ]
5361
+ );
5362
+ }
5363
+ return div(
5364
+ {
4484
5365
  class: `sibu-loading${sizeClass}`,
4485
5366
  role: "status",
4486
5367
  "aria-live": "polite",
4487
- // When there's no visible text, give the live region an accessible name
4488
- // so it isn't announced as an empty status.
4489
- "aria-label": text2 ? void 0 : "Loading",
4490
- nodes: [
4491
- div({
4492
- class: "sibu-loading-dots",
4493
- nodes: [
4494
- span({ class: "sibu-loading-dot" }),
4495
- span({ class: "sibu-loading-dot" }),
4496
- span({ class: "sibu-loading-dot" })
4497
- ]
4498
- }),
4499
- text2 ? span({ class: "sibu-loading-text", nodes: text2 }) : null
4500
- ].filter(Boolean)
4501
- });
4502
- }
4503
- return div({
4504
- class: `sibu-loading${sizeClass}`,
4505
- role: "status",
4506
- "aria-live": "polite",
4507
- "aria-label": text2 ? void 0 : "Loading",
4508
- nodes: [
4509
- div({ class: "sibu-loading-spinner" }),
4510
- text2 ? span({ class: "sibu-loading-text", nodes: text2 }) : null
4511
- ].filter(Boolean)
4512
- });
5368
+ "aria-label": text2 ? void 0 : "Loading"
5369
+ },
5370
+ [div({ class: "sibu-loading-spinner" }), text2 ? span({ class: "sibu-loading-text" }, text2) : null]
5371
+ );
4513
5372
  }
4514
5373
 
4515
5374
  // src/core/rendering/action.ts
@@ -4615,13 +5474,35 @@ registerAction("copyOnClick", copyOnClick);
4615
5474
  registerAction("autoResize", autoResize);
4616
5475
  registerAction("trapFocus", trapFocus);
4617
5476
 
5477
+ // src/utils/adoptThenable.ts
5478
+ function adoptThenable(value) {
5479
+ if (value === null || typeof value !== "object" && typeof value !== "function") return null;
5480
+ let then;
5481
+ try {
5482
+ then = value.then;
5483
+ } catch (error) {
5484
+ return Promise.reject(error);
5485
+ }
5486
+ if (typeof then !== "function") return null;
5487
+ return new Promise((resolve, reject) => {
5488
+ queueMicrotask(() => {
5489
+ try {
5490
+ Reflect.apply(then, value, [resolve, reject]);
5491
+ } catch (error) {
5492
+ reject(error);
5493
+ }
5494
+ });
5495
+ });
5496
+ }
5497
+
4618
5498
  // src/core/rendering/catch.ts
4619
5499
  var globalErrorHandler = null;
4620
5500
  function catchError(fn, onError) {
4621
5501
  try {
4622
5502
  const result = fn();
4623
- if (result && typeof result.then === "function") {
4624
- result.catch((err) => {
5503
+ const adopted = adoptThenable(result);
5504
+ if (adopted) {
5505
+ adopted.then(void 0, (err) => {
4625
5506
  if (onError) {
4626
5507
  onError(err, "async");
4627
5508
  } else if (globalErrorHandler) {
@@ -4761,7 +5642,7 @@ function when(condition, thenBranch, elseBranch) {
4761
5642
  const show2 = condition();
4762
5643
  const parent = anchor.parentNode;
4763
5644
  if (!parent) return;
4764
- if (initialized && show2 === lastCondition) return;
5645
+ if (initialized && Object.is(show2, lastCondition)) return;
4765
5646
  lastCondition = show2;
4766
5647
  const focused = currentNode ? captureFocusWithin([currentNode]) : null;
4767
5648
  if (currentNode?.parentNode) {
@@ -4770,7 +5651,7 @@ function when(condition, thenBranch, elseBranch) {
4770
5651
  currentNode = null;
4771
5652
  }
4772
5653
  const branch = show2 ? thenBranch : elseBranch !== void 0 ? elseBranch : null;
4773
- const attached = attachBranch(parent, anchor, branch, attachedOnce, "when");
5654
+ const attached = untracked2(() => attachBranch(parent, anchor, branch, attachedOnce, "when"));
4774
5655
  currentNode = attached.node;
4775
5656
  currentNodeOwned = attached.owned;
4776
5657
  restoreFocusWithin(focused, currentNode ? [currentNode] : [], "when");
@@ -4810,7 +5691,7 @@ function match(value, cases, fallback) {
4810
5691
  currentNode = null;
4811
5692
  }
4812
5693
  const branch = Object.hasOwn(cases, key) ? cases[key] : fallback;
4813
- const attached = attachBranch(parent, anchor, branch ?? null, attachedOnce, "match");
5694
+ const attached = untracked2(() => attachBranch(parent, anchor, branch ?? null, attachedOnce, "match"));
4814
5695
  currentNode = attached.node;
4815
5696
  currentNodeOwned = attached.owned;
4816
5697
  restoreFocusWithin(focused, currentNode ? [currentNode] : [], "match");
@@ -4840,11 +5721,17 @@ var componentRegistry = globalSingleton(
4840
5721
  /* @__PURE__ */ Symbol.for("sibujs.components.registry.v1"),
4841
5722
  () => /* @__PURE__ */ new Map()
4842
5723
  );
5724
+ var registryVersion = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.components.registryVersion.v1"), () => signal(0));
5725
+ function bumpRegistry() {
5726
+ const [read, write] = registryVersion;
5727
+ write(untracked2(read) + 1);
5728
+ }
4843
5729
  function registerComponent(name, component) {
4844
5730
  componentRegistry.set(name, component);
5731
+ bumpRegistry();
4845
5732
  }
4846
5733
  function unregisterComponent(name) {
4847
- componentRegistry.delete(name);
5734
+ if (componentRegistry.delete(name)) bumpRegistry();
4848
5735
  }
4849
5736
  function resolveComponent(name) {
4850
5737
  const component = componentRegistry.get(name);
@@ -4855,14 +5742,21 @@ function resolveComponent(name) {
4855
5742
  }
4856
5743
  function DynamicComponent(is) {
4857
5744
  const container = div({ class: "sibu-dynamic" });
5745
+ const NONE = {};
5746
+ let shown = NONE;
4858
5747
  function render() {
4859
5748
  const target = is();
4860
- let el;
5749
+ let component;
4861
5750
  if (typeof target === "function") {
4862
- el = target();
5751
+ component = target;
4863
5752
  } else {
4864
- el = resolveComponent(target);
5753
+ registryVersion[0]();
5754
+ component = componentRegistry.get(target);
4865
5755
  }
5756
+ const next = component ?? `missing:${target}`;
5757
+ if (next === shown) return;
5758
+ const el = untracked2(() => component ? component() : div(`[Component "${target}" not found]`));
5759
+ shown = next;
4866
5760
  replaceChildrenSafely(container, el);
4867
5761
  }
4868
5762
  const untrack = track2(render);
@@ -5126,31 +6020,30 @@ function Fragment(nodes) {
5126
6020
  if (Array.isArray(child)) {
5127
6021
  for (const nested of child) {
5128
6022
  if (nested == null || typeof nested === "boolean") continue;
5129
- frag.appendChild(resolveChild(nested));
6023
+ appendChild(frag, nested);
5130
6024
  }
5131
6025
  } else {
5132
- frag.appendChild(resolveChild(child));
6026
+ appendChild(frag, child);
5133
6027
  }
5134
6028
  }
5135
6029
  return frag;
5136
6030
  }
5137
- function resolveChild(child) {
5138
- if (child == null) {
5139
- return document.createTextNode("");
5140
- }
6031
+ function appendChild(frag, child) {
5141
6032
  if (child instanceof Node) {
5142
- return child;
6033
+ frag.appendChild(child);
6034
+ return;
5143
6035
  }
5144
6036
  if (typeof child === "function") {
5145
- const result = child();
5146
- if (result instanceof Node) return result;
5147
- return document.createTextNode(String(result ?? ""));
6037
+ const placeholder = document.createComment("");
6038
+ frag.appendChild(placeholder);
6039
+ registerDisposer(placeholder, bindChildNode(placeholder, child));
6040
+ return;
5148
6041
  }
5149
- return document.createTextNode(String(child));
6042
+ frag.appendChild(document.createTextNode(String(child)));
5150
6043
  }
5151
6044
 
5152
6045
  // src/core/rendering/htm.ts
5153
- var RAW_TEXT_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
6046
+ var RAW_TEXT_TAGS2 = /* @__PURE__ */ new Set(["script", "style"]);
5154
6047
  var VOID_ELEMENTS3 = /* @__PURE__ */ new Set([
5155
6048
  "area",
5156
6049
  "base",
@@ -5191,7 +6084,7 @@ var SVG_TAGS2 = /* @__PURE__ */ new Set([
5191
6084
  "marker"
5192
6085
  ]);
5193
6086
  var cache = /* @__PURE__ */ new WeakMap();
5194
- function parseTemplate(strings) {
6087
+ function parseTemplate2(strings) {
5195
6088
  const exprCount = strings.length - 1;
5196
6089
  let template2 = strings[0];
5197
6090
  for (let i2 = 0; i2 < exprCount; i2++) {
@@ -5373,7 +6266,7 @@ function parseTemplate(strings) {
5373
6266
  children.push({ t: 0, el: { tag, svg: SVG_TAGS2.has(tag), attrs, children: [] } });
5374
6267
  } else {
5375
6268
  const inner = parseChildren();
5376
- if (RAW_TEXT_TAGS.has(tag.toLowerCase())) {
6269
+ if (RAW_TEXT_TAGS2.has(tag.toLowerCase())) {
5377
6270
  for (let i2 = 0; i2 < inner.length; i2++) {
5378
6271
  if (inner[i2].t === 2) {
5379
6272
  throw new Error(
@@ -5399,7 +6292,7 @@ function parseTemplate(strings) {
5399
6292
  return parseChildren();
5400
6293
  }
5401
6294
  function executeElement(tmpl, values) {
5402
- const el = tmpl.svg ? document.createElementNS(SVG_NS2, tmpl.tag) : document.createElement(tmpl.tag);
6295
+ const el = tmpl.svg ? document.createElementNS(SVG_NS3, tmpl.tag) : document.createElement(tmpl.tag);
5403
6296
  for (let i2 = 0; i2 < tmpl.attrs.length; i2++) {
5404
6297
  const attr = tmpl.attrs[i2];
5405
6298
  switch (attr.t) {
@@ -5479,7 +6372,7 @@ function executeElement(tmpl, values) {
5479
6372
  function html2(strings, ...values) {
5480
6373
  let tmpl = cache.get(strings);
5481
6374
  if (!tmpl) {
5482
- tmpl = parseTemplate(strings);
6375
+ tmpl = parseTemplate2(strings);
5483
6376
  cache.set(strings, tmpl);
5484
6377
  }
5485
6378
  if (tmpl.length === 1 && tmpl[0].t === 0) {
@@ -5616,25 +6509,77 @@ function mount(component, container) {
5616
6509
  typeof component === "function" || component instanceof Node,
5617
6510
  "mount: first argument must be a component function or a DOM Node."
5618
6511
  );
5619
- const startTime = typeof performance !== "undefined" ? performance.now() : 0;
5620
- const node = typeof component === "function" ? component() : component;
5621
- const duration = typeof performance !== "undefined" ? performance.now() - startTime : 0;
5622
- container.appendChild(node);
5623
- const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
5624
- if (hook) {
5625
- hook.emit("app:init", { rootElement: node, container, duration });
6512
+ const now = () => typeof performance !== "undefined" ? performance.now() : 0;
6513
+ const startTime = now();
6514
+ let range = null;
6515
+ const attach = (built) => {
6516
+ if (built instanceof DocumentFragment) {
6517
+ const start = document.createComment("");
6518
+ const end = document.createComment("");
6519
+ built.insertBefore(start, built.firstChild);
6520
+ built.appendChild(end);
6521
+ range = { start, end };
6522
+ }
6523
+ container.appendChild(built);
6524
+ };
6525
+ let node;
6526
+ if (typeof component === "function") {
6527
+ node = withDisposerRollback(() => {
6528
+ const built = component();
6529
+ attach(built);
6530
+ return built;
6531
+ });
6532
+ } else {
6533
+ node = component;
6534
+ attach(node);
5626
6535
  }
6536
+ const duration = now() - startTime;
6537
+ const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
6538
+ if (hook) emitDevtools(hook, "app:init", { rootElement: node, container, duration });
6539
+ const mounted = range;
5627
6540
  return {
5628
6541
  node,
5629
6542
  unmount() {
5630
- if (hook) hook.emit("app:unmount", { rootElement: node });
5631
- dispose(node);
5632
- if (node.parentNode) {
5633
- node.parentNode.removeChild(node);
6543
+ if (hook) emitDevtools(hook, "app:unmount", { rootElement: node });
6544
+ if (mounted) {
6545
+ unmountRange(mounted.start, mounted.end);
6546
+ return;
5634
6547
  }
6548
+ dispose(node);
6549
+ node.parentNode?.removeChild(node);
5635
6550
  }
5636
6551
  };
5637
6552
  }
6553
+ function unmountRange(start, end) {
6554
+ const parent = start.parentNode;
6555
+ if (!parent && !end.parentNode) return;
6556
+ let budget = MAX_DRAIN_TEARDOWNS;
6557
+ for (let n = start.nextSibling; n && n !== end; n = n.nextSibling) budget++;
6558
+ let before = start;
6559
+ let after = null;
6560
+ let intact = true;
6561
+ for (; ; ) {
6562
+ if (start.parentNode !== parent || end.parentNode !== parent) intact = false;
6563
+ else if (start.previousSibling !== before || end.nextSibling !== after) {
6564
+ before = start.previousSibling;
6565
+ after = end.nextSibling;
6566
+ intact = !!(start.compareDocumentPosition(end) & 4);
6567
+ }
6568
+ const n = start.nextSibling;
6569
+ if (!intact || n === end) break;
6570
+ if (budget-- <= 0) {
6571
+ reportDrainRunaway("mount", MAX_DRAIN_TEARDOWNS, 1);
6572
+ return;
6573
+ }
6574
+ dispose(n);
6575
+ if (n.parentNode === parent) parent.removeChild(n);
6576
+ }
6577
+ if (!intact) {
6578
+ reportError(new Error("[SibuJS mount] fragment markers lost"), { phase: "cleanup", name: "mount" });
6579
+ }
6580
+ start.parentNode?.removeChild(start);
6581
+ end.parentNode?.removeChild(end);
6582
+ }
5638
6583
 
5639
6584
  // src/core/rendering/portal.ts
5640
6585
  function Portal(nodes, target) {
@@ -6070,17 +7015,17 @@ function derived(getter, options) {
6070
7015
  cs._d = false;
6071
7016
  cs._init = true;
6072
7017
  };
6073
- track2(() => {
6074
- let threw = true;
6075
- try {
7018
+ try {
7019
+ track2(() => {
6076
7020
  cs._v = getter();
6077
7021
  cs._d = false;
6078
7022
  cs._init = true;
6079
- threw = false;
6080
- } finally {
6081
- if (threw) cs._d = true;
6082
- }
6083
- }, markDirty);
7023
+ }, markDirty);
7024
+ } catch (err) {
7025
+ disposed = true;
7026
+ cleanup2(markDirty);
7027
+ throw err;
7028
+ }
6084
7029
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
6085
7030
  const validate = () => {
6086
7031
  if (!cs._d || disposed || pendingError !== void 0) return;
@@ -6099,7 +7044,7 @@ function derived(getter, options) {
6099
7044
  if (disposed) cleanup2(markDirty);
6100
7045
  }
6101
7046
  if (hook && !disposed && !Object.is(oldValue, cs._v)) {
6102
- hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
7047
+ emitDevtools(hook, "computed:update", { signal: cs, oldValue, newValue: cs._v });
6103
7048
  }
6104
7049
  };
6105
7050
  cs._validate = validate;
@@ -6150,7 +7095,7 @@ function derived(getter, options) {
6150
7095
  }
6151
7096
  }
6152
7097
  };
6153
- if (hook) hook.emit("computed:create", { signal: cs, name: debugName, getter: computedGetter });
7098
+ if (hook) emitDevtools(hook, "computed:create", { signal: cs, name: debugName, getter: computedGetter });
6154
7099
  return computedGetter;
6155
7100
  }
6156
7101
 
@@ -6790,27 +7735,6 @@ function trustHTML(html3) {
6790
7735
  }
6791
7736
  var DEFAULT_MAX_SSR_BYTES = 1024 * 1024;
6792
7737
 
6793
- // src/utils/adoptThenable.ts
6794
- function adoptThenable(value) {
6795
- if (value === null || typeof value !== "object" && typeof value !== "function") return null;
6796
- let then;
6797
- try {
6798
- then = value.then;
6799
- } catch (error) {
6800
- return Promise.reject(error);
6801
- }
6802
- if (typeof then !== "function") return null;
6803
- return new Promise((resolve, reject) => {
6804
- queueMicrotask(() => {
6805
- try {
6806
- Reflect.apply(then, value, [resolve, reject]);
6807
- } catch (error) {
6808
- reject(error);
6809
- }
6810
- });
6811
- });
6812
- }
6813
-
6814
7738
  // src/reactivity/concurrent.ts
6815
7739
  function defer(getter) {
6816
7740
  const [value, setValue] = signal(getter());
@@ -6901,7 +7825,23 @@ function nextTick() {
6901
7825
  }
6902
7826
 
6903
7827
  // src/build/cdn.ts
6904
- var PACKAGE_NAME = "sibu";
7828
+ var PACKAGE_NAME = "sibujs";
7829
+ var SUBPATH_ENTRIES = [
7830
+ "data",
7831
+ "browser",
7832
+ "patterns",
7833
+ "motion",
7834
+ "ui",
7835
+ "widgets",
7836
+ "ssr",
7837
+ "devtools",
7838
+ "performance",
7839
+ "ecosystem",
7840
+ "plugins",
7841
+ "build",
7842
+ "testing",
7843
+ "extras"
7844
+ ];
6905
7845
  function registerGlobal() {
6906
7846
  if (typeof window === "undefined") return;
6907
7847
  window.Sibu = { ...index_exports };
@@ -6947,7 +7887,7 @@ var cdnUrls = {
6947
7887
  * @example
6948
7888
  * ```ts
6949
7889
  * cdnUrls.scriptTag('jsdelivr', '1.0.0')
6950
- * // => '<script src="https://cdn.jsdelivr.net/npm/sibu@1.0.0/dist/cdn.global.js"></script>'
7890
+ * // => '<script src="https://cdn.jsdelivr.net/npm/sibujs@1.0.0/dist/cdn.global.js"></script>'
6951
7891
  * ```
6952
7892
  */
6953
7893
  scriptTag: (provider = "jsdelivr", version) => {
@@ -6965,15 +7905,11 @@ function generateImportMap(baseUrl) {
6965
7905
  const base2 = baseUrl || `https://cdn.jsdelivr.net/npm/${PACKAGE_NAME}@latest`;
6966
7906
  const imports = {
6967
7907
  // Main package entry
6968
- [PACKAGE_NAME]: `${base2}/dist/index.js`,
6969
- // Sub-path imports for tree-shaking in browsers
6970
- [`${PACKAGE_NAME}/core`]: `${base2}/dist/core/index.js`,
6971
- [`${PACKAGE_NAME}/reactivity`]: `${base2}/dist/reactivity/index.js`,
6972
- [`${PACKAGE_NAME}/plugins`]: `${base2}/dist/plugins/index.js`,
6973
- [`${PACKAGE_NAME}/components`]: `${base2}/dist/components/index.js`,
6974
- [`${PACKAGE_NAME}/testing`]: `${base2}/dist/testing/index.js`,
6975
- [`${PACKAGE_NAME}/build`]: `${base2}/dist/build/index.js`
7908
+ [PACKAGE_NAME]: `${base2}/dist/index.js`
6976
7909
  };
7910
+ for (const entry of SUBPATH_ENTRIES) {
7911
+ imports[`${PACKAGE_NAME}/${entry}`] = `${base2}/dist/${entry}.js`;
7912
+ }
6977
7913
  return {
6978
7914
  imports,
6979
7915
  /**
@@ -7056,9 +7992,9 @@ function generateTsConfig(options) {
7056
7992
  if (paths && Object.keys(paths).length > 0) {
7057
7993
  compilerOptions.baseUrl = ".";
7058
7994
  compilerOptions.paths = {
7059
- // Default sibu alias
7060
- sibu: ["node_modules/sibu/dist/index.d.ts"],
7061
- "sibu/*": ["node_modules/sibu/dist/*"],
7995
+ // Default sibujs alias
7996
+ sibujs: ["node_modules/sibujs/dist/index.d.ts"],
7997
+ "sibujs/*": ["node_modules/sibujs/dist/*"],
7062
7998
  // User-provided paths
7063
7999
  ...paths
7064
8000
  };
@@ -8172,6 +9108,19 @@ function generateTypeStubs() {
8172
9108
  };
8173
9109
  }
8174
9110
 
9111
+ // src/build/compileTemplates.ts
9112
+ function compileHtmlTemplates(code2) {
9113
+ const plan = planHtmlTemplates(code2);
9114
+ const edits = plan.compiledCount > 0 ? normalizeEdits(plan.edits) : null;
9115
+ return {
9116
+ code: edits ? applyEdits(code2, edits, plan.append) : null,
9117
+ usedTags: plan.usedTags,
9118
+ usesSvg: plan.usesSvg,
9119
+ compiledCount: edits ? plan.compiledCount : 0,
9120
+ skippedCount: plan.skippedCount
9121
+ };
9122
+ }
9123
+
8175
9124
  // src/build/routeSplitting.ts
8176
9125
  function fileToRoute(relativePath) {
8177
9126
  const basename = relativePath.split("/").pop() || "";
@@ -8227,7 +9176,7 @@ function generateRouteModule(entries, routesDir) {
8227
9176
  (e, i2) => `const route${i2} = () => import(/* webpackChunkName: "${e.chunkName}" */ "${routesDir}/${e.importPath}");`
8228
9177
  ).join("\n");
8229
9178
  const routes = entries.map((e, i2) => ` { path: "${e.path}", component: lazy(route${i2}) }`).join(",\n");
8230
- return `import { lazy } from "sibu";
9179
+ return `import { lazy } from "sibujs";
8231
9180
 
8232
9181
  ${imports}
8233
9182