olova 2.0.61 → 2.0.63

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 (80) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +42 -61
  3. package/dist/compiler.d.ts +44 -0
  4. package/dist/compiler.js +2139 -0
  5. package/dist/compiler.js.map +1 -0
  6. package/dist/core.d.ts +4 -0
  7. package/dist/core.js +859 -0
  8. package/dist/core.js.map +1 -0
  9. package/dist/global.d.ts +15 -0
  10. package/dist/global.js +226 -0
  11. package/dist/global.js.map +1 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +2302 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/runtime.d.ts +89 -0
  16. package/dist/runtime.js +633 -0
  17. package/dist/runtime.js.map +1 -0
  18. package/dist/signals-core-BdfWh1Yt.d.ts +43 -0
  19. package/dist/vite.d.ts +5 -0
  20. package/dist/vite.js +2302 -0
  21. package/dist/vite.js.map +1 -0
  22. package/package.json +83 -65
  23. package/dist/chunk-D7SIC5TC.js +0 -367
  24. package/dist/chunk-D7SIC5TC.js.map +0 -1
  25. package/dist/entry-server.cjs +0 -120
  26. package/dist/entry-server.cjs.map +0 -1
  27. package/dist/entry-server.js +0 -115
  28. package/dist/entry-server.js.map +0 -1
  29. package/dist/entry-worker.cjs +0 -133
  30. package/dist/entry-worker.cjs.map +0 -1
  31. package/dist/entry-worker.js +0 -127
  32. package/dist/entry-worker.js.map +0 -1
  33. package/dist/main.cjs +0 -18
  34. package/dist/main.cjs.map +0 -1
  35. package/dist/main.js +0 -16
  36. package/dist/main.js.map +0 -1
  37. package/dist/olova.cjs +0 -1680
  38. package/dist/olova.cjs.map +0 -1
  39. package/dist/olova.d.cts +0 -72
  40. package/dist/olova.d.ts +0 -72
  41. package/dist/olova.js +0 -1321
  42. package/dist/olova.js.map +0 -1
  43. package/dist/performance.cjs +0 -386
  44. package/dist/performance.cjs.map +0 -1
  45. package/dist/performance.js +0 -3
  46. package/dist/performance.js.map +0 -1
  47. package/dist/router.cjs +0 -646
  48. package/dist/router.cjs.map +0 -1
  49. package/dist/router.d.cts +0 -113
  50. package/dist/router.d.ts +0 -113
  51. package/dist/router.js +0 -632
  52. package/dist/router.js.map +0 -1
  53. package/main.tsx +0 -76
  54. package/olova.ts +0 -619
  55. package/src/entry-server.tsx +0 -165
  56. package/src/entry-worker.tsx +0 -201
  57. package/src/generator/index.ts +0 -409
  58. package/src/hydration/flight.ts +0 -320
  59. package/src/hydration/index.ts +0 -12
  60. package/src/hydration/types.ts +0 -225
  61. package/src/logger.ts +0 -182
  62. package/src/main.tsx +0 -24
  63. package/src/performance.ts +0 -488
  64. package/src/plugin/index.ts +0 -204
  65. package/src/router/ErrorBoundary.tsx +0 -145
  66. package/src/router/Link.tsx +0 -117
  67. package/src/router/OlovaRouter.tsx +0 -354
  68. package/src/router/Outlet.tsx +0 -8
  69. package/src/router/context.ts +0 -117
  70. package/src/router/index.ts +0 -29
  71. package/src/router/matching.ts +0 -63
  72. package/src/router/router.tsx +0 -23
  73. package/src/router/search-params.ts +0 -29
  74. package/src/scanner/index.ts +0 -114
  75. package/src/types/index.ts +0 -190
  76. package/src/utils/export.ts +0 -85
  77. package/src/utils/index.ts +0 -4
  78. package/src/utils/naming.ts +0 -54
  79. package/src/utils/path.ts +0 -45
  80. package/tsup.config.ts +0 -35
package/dist/vite.js ADDED
@@ -0,0 +1,2302 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { normalizePath } from 'vite';
5
+ import { parse } from '@babel/parser';
6
+ import generatorModule from '@babel/generator';
7
+ import traverseModule from '@babel/traverse';
8
+ import * as t from '@babel/types';
9
+ import ts from 'typescript';
10
+
11
+ // vite/olova-plugin.ts
12
+
13
+ // compiler/parse.ts
14
+ function isWhitespace(char) {
15
+ return /\s/.test(char);
16
+ }
17
+ function skipQuoted(source, start, quote) {
18
+ let index = start + 1;
19
+ while (index < source.length) {
20
+ if (source[index] === "\\" && index + 1 < source.length) {
21
+ index += 2;
22
+ continue;
23
+ }
24
+ if (source[index] === quote) {
25
+ return index + 1;
26
+ }
27
+ index += 1;
28
+ }
29
+ return source.length;
30
+ }
31
+ function findTagEnd(source, start) {
32
+ let index = start;
33
+ while (index < source.length) {
34
+ const char = source[index];
35
+ if (char === '"' || char === "'") {
36
+ index = skipQuoted(source, index, char);
37
+ continue;
38
+ }
39
+ if (char === ">") {
40
+ return index;
41
+ }
42
+ index += 1;
43
+ }
44
+ return -1;
45
+ }
46
+ function parseAttributes(source) {
47
+ const attrs = {};
48
+ const attrRegex = /([:@A-Za-z_][-A-Za-z0-9_:.@]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g;
49
+ let match;
50
+ while ((match = attrRegex.exec(source)) !== null) {
51
+ const [, name, doubleQuoted, singleQuoted, bare] = match;
52
+ const value = doubleQuoted ?? singleQuoted ?? bare ?? true;
53
+ attrs[name] = value;
54
+ }
55
+ return attrs;
56
+ }
57
+ function readOpeningTag(source, start) {
58
+ if (source[start] !== "<") {
59
+ return null;
60
+ }
61
+ const tagEnd = findTagEnd(source, start + 1);
62
+ if (tagEnd < 0) {
63
+ return null;
64
+ }
65
+ const raw = source.slice(start + 1, tagEnd).trim();
66
+ if (!raw || raw.startsWith("/") || raw.startsWith("!")) {
67
+ return null;
68
+ }
69
+ let splitIndex = 0;
70
+ while (splitIndex < raw.length && !isWhitespace(raw[splitIndex]) && raw[splitIndex] !== "/") {
71
+ splitIndex += 1;
72
+ }
73
+ const tag = raw.slice(0, splitIndex);
74
+ const attrsSource = raw.slice(splitIndex).replace(/\/\s*$/, "").trim();
75
+ return {
76
+ tag,
77
+ openEnd: tagEnd + 1,
78
+ attrs: parseAttributes(attrsSource)
79
+ };
80
+ }
81
+ function findMatchingTag(source, start, tagName) {
82
+ const opening = readOpeningTag(source, start);
83
+ if (!opening || opening.tag.toLowerCase() !== tagName.toLowerCase()) {
84
+ return null;
85
+ }
86
+ const selfClosing = /\/\s*>$/.test(source.slice(start, opening.openEnd));
87
+ if (selfClosing) {
88
+ return {
89
+ fullStart: start,
90
+ openEnd: opening.openEnd,
91
+ closeStart: opening.openEnd,
92
+ closeEnd: opening.openEnd,
93
+ inner: "",
94
+ attrs: opening.attrs
95
+ };
96
+ }
97
+ let depth = 1;
98
+ let index = opening.openEnd;
99
+ while (index < source.length) {
100
+ const next = source.indexOf("<", index);
101
+ if (next < 0) {
102
+ break;
103
+ }
104
+ if (source.startsWith("<!--", next)) {
105
+ const commentEnd = source.indexOf("-->", next + 4);
106
+ index = commentEnd >= 0 ? commentEnd + 3 : source.length;
107
+ continue;
108
+ }
109
+ if (source[next + 1] === "/") {
110
+ const closeEnd = findTagEnd(source, next + 2);
111
+ if (closeEnd < 0) {
112
+ break;
113
+ }
114
+ const closeTag = source.slice(next + 2, closeEnd).trim();
115
+ if (closeTag.toLowerCase() === tagName.toLowerCase()) {
116
+ depth -= 1;
117
+ if (depth === 0) {
118
+ return {
119
+ fullStart: start,
120
+ openEnd: opening.openEnd,
121
+ closeStart: next,
122
+ closeEnd: closeEnd + 1,
123
+ inner: source.slice(opening.openEnd, next),
124
+ attrs: opening.attrs
125
+ };
126
+ }
127
+ }
128
+ index = closeEnd + 1;
129
+ continue;
130
+ }
131
+ const nested = readOpeningTag(source, next);
132
+ if (!nested) {
133
+ index = next + 1;
134
+ continue;
135
+ }
136
+ if (nested.tag.toLowerCase() === tagName.toLowerCase()) {
137
+ const nestedSelfClosing = /\/\s*>$/.test(source.slice(next, nested.openEnd));
138
+ if (!nestedSelfClosing) {
139
+ depth += 1;
140
+ }
141
+ }
142
+ index = nested.openEnd;
143
+ }
144
+ return null;
145
+ }
146
+ function collectTopLevelBlocks(source, tagName) {
147
+ const blocks = [];
148
+ let index = 0;
149
+ while (index < source.length) {
150
+ const next = source.indexOf(`<${tagName}`, index);
151
+ if (next < 0) {
152
+ break;
153
+ }
154
+ const previous = next > 0 ? source[next - 1] : "";
155
+ if (previous && /[A-Za-z0-9_:-]/.test(previous)) {
156
+ index = next + tagName.length + 1;
157
+ continue;
158
+ }
159
+ const block = findMatchingTag(source, next, tagName);
160
+ if (!block) {
161
+ index = next + tagName.length + 1;
162
+ continue;
163
+ }
164
+ blocks.push(block);
165
+ index = block.closeEnd;
166
+ }
167
+ return blocks;
168
+ }
169
+ function removeRanges(source, ranges) {
170
+ if (ranges.length === 0) {
171
+ return source.trim();
172
+ }
173
+ const ordered = [...ranges].sort((a, b) => a.start - b.start);
174
+ let cursor = 0;
175
+ let output = "";
176
+ for (const range of ordered) {
177
+ output += source.slice(cursor, range.start);
178
+ cursor = range.end;
179
+ }
180
+ output += source.slice(cursor);
181
+ return output.trim();
182
+ }
183
+ function parseOlovaFile(source) {
184
+ const scriptBlock = collectTopLevelBlocks(source, "script")[0] ?? null;
185
+ const styleBlocks = collectTopLevelBlocks(source, "style");
186
+ const ranges = styleBlocks.map((block) => ({
187
+ start: block.fullStart,
188
+ end: block.closeEnd
189
+ }));
190
+ if (scriptBlock) {
191
+ ranges.push({ start: scriptBlock.fullStart, end: scriptBlock.closeEnd });
192
+ }
193
+ return {
194
+ script: scriptBlock?.inner.trim() ?? "",
195
+ template: removeRanges(source, ranges),
196
+ styles: styleBlocks.map((block) => ({
197
+ content: block.inner.trim(),
198
+ attrs: block.attrs
199
+ }))
200
+ };
201
+ }
202
+
203
+ // compiler/html-parser.ts
204
+ function isWhitespace2(char) {
205
+ return /\s/.test(char);
206
+ }
207
+ function findTagEnd2(source, start) {
208
+ let index = start;
209
+ while (index < source.length) {
210
+ const char = source[index];
211
+ if (char === '"' || char === "'") {
212
+ const quoted = readQuotedValue(source, index, char);
213
+ index = quoted.end;
214
+ continue;
215
+ }
216
+ if (char === "{") {
217
+ const braced = readBraceValue(source, index);
218
+ index = braced.end;
219
+ continue;
220
+ }
221
+ if (char === ">") {
222
+ return index;
223
+ }
224
+ index += 1;
225
+ }
226
+ return -1;
227
+ }
228
+ function readQuotedValue(source, start, quote) {
229
+ let index = start + 1;
230
+ let value = "";
231
+ while (index < source.length) {
232
+ const char = source[index];
233
+ if (char === "\\" && index + 1 < source.length) {
234
+ value += source.slice(index, index + 2);
235
+ index += 2;
236
+ continue;
237
+ }
238
+ if (char === quote) {
239
+ return { value, end: index + 1 };
240
+ }
241
+ value += char;
242
+ index += 1;
243
+ }
244
+ return { value, end: source.length };
245
+ }
246
+ function readBraceValue(source, start) {
247
+ let index = start + 1;
248
+ let depth = 1;
249
+ let value = "{";
250
+ let quote = null;
251
+ while (index < source.length && depth > 0) {
252
+ const char = source[index];
253
+ const prev = source[index - 1];
254
+ value += char;
255
+ if (quote) {
256
+ if (char === quote && prev !== "\\") {
257
+ quote = null;
258
+ }
259
+ index += 1;
260
+ continue;
261
+ }
262
+ if (char === "'" || char === '"' || char === "`") {
263
+ quote = char;
264
+ index += 1;
265
+ continue;
266
+ }
267
+ if (char === "{") {
268
+ depth += 1;
269
+ } else if (char === "}") {
270
+ depth -= 1;
271
+ }
272
+ index += 1;
273
+ }
274
+ return { value, end: index };
275
+ }
276
+ function parseAttributes2(source) {
277
+ const attrs = {};
278
+ let index = 0;
279
+ while (index < source.length) {
280
+ while (index < source.length && isWhitespace2(source[index])) {
281
+ index += 1;
282
+ }
283
+ if (index >= source.length) {
284
+ break;
285
+ }
286
+ let nameEnd = index;
287
+ while (nameEnd < source.length && !isWhitespace2(source[nameEnd]) && source[nameEnd] !== "=") {
288
+ nameEnd += 1;
289
+ }
290
+ const name = source.slice(index, nameEnd).trim();
291
+ index = nameEnd;
292
+ if (!name) {
293
+ index += 1;
294
+ continue;
295
+ }
296
+ while (index < source.length && isWhitespace2(source[index])) {
297
+ index += 1;
298
+ }
299
+ if (source[index] !== "=") {
300
+ attrs[name] = true;
301
+ continue;
302
+ }
303
+ index += 1;
304
+ while (index < source.length && isWhitespace2(source[index])) {
305
+ index += 1;
306
+ }
307
+ if (index >= source.length) {
308
+ attrs[name] = true;
309
+ break;
310
+ }
311
+ const start = index;
312
+ const char = source[index];
313
+ if (char === '"' || char === "'") {
314
+ const quoted = readQuotedValue(source, index, char);
315
+ attrs[name] = quoted.value;
316
+ index = quoted.end;
317
+ continue;
318
+ }
319
+ if (char === "{") {
320
+ const braced = readBraceValue(source, index);
321
+ attrs[name] = braced.value;
322
+ index = braced.end;
323
+ continue;
324
+ }
325
+ while (index < source.length && !isWhitespace2(source[index])) {
326
+ index += 1;
327
+ }
328
+ attrs[name] = source.slice(start, index);
329
+ }
330
+ return attrs;
331
+ }
332
+ function parseHTML(html) {
333
+ const root = [];
334
+ const stack = [
335
+ { node: null, children: root }
336
+ ];
337
+ let i = 0;
338
+ while (i < html.length) {
339
+ const textStart = i;
340
+ const tagStart = html.indexOf("<", i);
341
+ if (tagStart === -1) {
342
+ if (textStart < html.length) {
343
+ stack[stack.length - 1].children.push({
344
+ type: "text",
345
+ content: html.slice(textStart)
346
+ });
347
+ }
348
+ break;
349
+ }
350
+ if (tagStart > textStart) {
351
+ stack[stack.length - 1].children.push({
352
+ type: "text",
353
+ content: html.slice(textStart, tagStart)
354
+ });
355
+ }
356
+ i = tagStart;
357
+ if (html.startsWith("<!--", i)) {
358
+ const commentEnd = html.indexOf("-->", i + 4);
359
+ if (commentEnd === -1) {
360
+ stack[stack.length - 1].children.push({
361
+ type: "comment",
362
+ content: html.slice(i + 4)
363
+ });
364
+ break;
365
+ }
366
+ stack[stack.length - 1].children.push({
367
+ type: "comment",
368
+ content: html.slice(i + 4, commentEnd)
369
+ });
370
+ i = commentEnd + 3;
371
+ continue;
372
+ }
373
+ if (html.startsWith("</", i)) {
374
+ const tagEnd2 = html.indexOf(">", i + 2);
375
+ if (tagEnd2 === -1) {
376
+ i += 2;
377
+ continue;
378
+ }
379
+ const tag2 = html.slice(i + 2, tagEnd2).trim().toLowerCase();
380
+ let closeIdx = stack.length - 1;
381
+ while (closeIdx > 0 && stack[closeIdx].node?.tag.toLowerCase() !== tag2) {
382
+ closeIdx--;
383
+ }
384
+ if (closeIdx > 0) {
385
+ stack.length = closeIdx;
386
+ }
387
+ i = tagEnd2 + 1;
388
+ continue;
389
+ }
390
+ const tagEnd = findTagEnd2(html, i + 1);
391
+ if (tagEnd === -1) {
392
+ stack[stack.length - 1].children.push({ type: "text", content: "<" });
393
+ i++;
394
+ continue;
395
+ }
396
+ const rawTag = html.slice(i + 1, tagEnd).trim();
397
+ let tagNameEnd = 0;
398
+ while (tagNameEnd < rawTag.length && !isWhitespace2(rawTag[tagNameEnd]) && rawTag[tagNameEnd] !== "/") {
399
+ tagNameEnd += 1;
400
+ }
401
+ const tag = rawTag.slice(0, tagNameEnd);
402
+ if (!tag) {
403
+ stack[stack.length - 1].children.push({ type: "text", content: "<" });
404
+ i++;
405
+ continue;
406
+ }
407
+ const attrsStr = rawTag.slice(tagNameEnd).replace(/\/\s*$/, "").trim();
408
+ const selfClose = /\/\s*$/.test(rawTag);
409
+ const attrs = parseAttributes2(attrsStr);
410
+ const isSelfClosing = !!selfClose || /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/i.test(
411
+ tag
412
+ );
413
+ const element = {
414
+ type: "element",
415
+ tag,
416
+ attrs,
417
+ children: [],
418
+ isSelfClosing
419
+ };
420
+ stack[stack.length - 1].children.push(element);
421
+ if (!isSelfClosing) {
422
+ stack.push({ node: element, children: element.children });
423
+ }
424
+ i = tagEnd + 1;
425
+ }
426
+ return root;
427
+ }
428
+
429
+ // compiler/dom-generator.ts
430
+ var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
431
+ function splitTextFragments(text) {
432
+ const parts = [];
433
+ const tokenRe = /__O_(TEXT|HTML|SLOT|COMP|IF)_([a-zA-Z0-9_]+)__/g;
434
+ let lastIndex = 0;
435
+ let match;
436
+ while ((match = tokenRe.exec(text)) !== null) {
437
+ if (match.index > lastIndex) {
438
+ parts.push({ type: "static", value: text.slice(lastIndex, match.index) });
439
+ }
440
+ parts.push({ type: "dynamic", tokenType: match[1], id: match[2] });
441
+ lastIndex = match.index + match[0].length;
442
+ }
443
+ if (lastIndex < text.length) {
444
+ parts.push({ type: "static", value: text.slice(lastIndex) });
445
+ }
446
+ return parts;
447
+ }
448
+ function isSvgContext(tag, inSvg) {
449
+ if (tag === "foreignObject") {
450
+ return false;
451
+ }
452
+ return inSvg || tag === "svg";
453
+ }
454
+ function processNode(node, parentVar, stateNames, transformExpr, counters, buildCtx, componentNames, inSvg = false) {
455
+ if (node.type === "comment") {
456
+ return;
457
+ }
458
+ if (node.type === "text") {
459
+ const parts = splitTextFragments(node.content);
460
+ for (const part of parts) {
461
+ if (part.type === "static") {
462
+ if (!part.value) continue;
463
+ const varName = `_t${buildCtx.localCount++}`;
464
+ buildCtx.statements.push(
465
+ `const ${varName} = document.createTextNode(${JSON.stringify(part.value)});`
466
+ );
467
+ buildCtx.statements.push(`${parentVar}.appendChild(${varName});`);
468
+ } else {
469
+ const varName = `_d${buildCtx.localCount++}`;
470
+ buildCtx.statements.push(
471
+ `const ${varName} = document.createTextNode('');`
472
+ );
473
+ buildCtx.nodes.push(`'${part.id}': ${varName}`);
474
+ buildCtx.statements.push(`${parentVar}.appendChild(${varName});`);
475
+ }
476
+ }
477
+ return;
478
+ }
479
+ if (node.type === "element") {
480
+ const tag = node.tag;
481
+ const varName = `_el${buildCtx.localCount++}`;
482
+ const useSvgNamespace = isSvgContext(tag, inSvg);
483
+ buildCtx.statements.push(
484
+ useSvgNamespace ? `const ${varName} = document.createElementNS(${JSON.stringify(SVG_NAMESPACE)}, "${tag}");` : `const ${varName} = document.createElement("${tag}");`
485
+ );
486
+ for (const [attr, val] of Object.entries(node.attrs)) {
487
+ if (attr.startsWith("data-o-on-")) {
488
+ if (typeof val === "string") {
489
+ buildCtx.nodes.push(`'${val}': ${varName}`);
490
+ }
491
+ } else if (typeof val === "string" && val.includes("__O_ATTR_")) {
492
+ const match = val.match(/__O_ATTR_([a-zA-Z0-9_]+)__/);
493
+ if (match) {
494
+ buildCtx.nodes.push(`'${match[1]}': ${varName}`);
495
+ }
496
+ } else {
497
+ if (val === true) {
498
+ buildCtx.statements.push(`${varName}.setAttribute("${attr}", "");`);
499
+ } else {
500
+ buildCtx.statements.push(
501
+ `${varName}.setAttribute("${attr}", ${JSON.stringify(val)});`
502
+ );
503
+ }
504
+ }
505
+ }
506
+ for (const child of node.children) {
507
+ processNode(
508
+ child,
509
+ varName,
510
+ stateNames,
511
+ transformExpr,
512
+ counters,
513
+ buildCtx,
514
+ componentNames,
515
+ useSvgNamespace
516
+ );
517
+ }
518
+ buildCtx.statements.push(`${parentVar}.appendChild(${varName});`);
519
+ }
520
+ }
521
+ function generateBuildFunction(html, stateNames, transformExpr, counters, componentNames) {
522
+ const ast = parseHTML(html);
523
+ const buildCtx = {
524
+ statements: [],
525
+ nodes: [],
526
+ localCount: 0
527
+ };
528
+ buildCtx.statements.push("const _f = document.createDocumentFragment();");
529
+ for (const child of ast) {
530
+ processNode(
531
+ child,
532
+ "_f",
533
+ stateNames,
534
+ transformExpr,
535
+ counters,
536
+ buildCtx,
537
+ componentNames
538
+ );
539
+ }
540
+ return `function() {
541
+ ${buildCtx.statements.join("\n ")}
542
+ return { fragment: _f, nodes: { ${buildCtx.nodes.join(", ")} } };
543
+ }`;
544
+ }
545
+
546
+ // compiler/compile.ts
547
+ var generate = generatorModule.default ?? generatorModule;
548
+ var traverse = traverseModule.default ?? traverseModule;
549
+ var CORE_KNOWN_IMPORTS = /* @__PURE__ */ new Set([
550
+ "Signal",
551
+ "computed",
552
+ "createApp",
553
+ "defineComponent",
554
+ "derived",
555
+ "effect",
556
+ "getContext",
557
+ "global",
558
+ "hasContext",
559
+ "mount",
560
+ "setContext",
561
+ "state"
562
+ ]);
563
+ function escapeTemplate(source) {
564
+ return source.replace(/\\/g, "\\\\").replace(/`/g, "\\`");
565
+ }
566
+ function indexToLocation(source, index) {
567
+ const safeIndex = Math.max(0, Math.min(index, source.length));
568
+ const lines = source.slice(0, safeIndex).split("\n");
569
+ return {
570
+ line: lines.length,
571
+ column: lines[lines.length - 1]?.length ?? 0,
572
+ index: safeIndex
573
+ };
574
+ }
575
+ function createCodeFrame(source, line, column, contextLines = 2) {
576
+ const lines = source.split("\n");
577
+ const start = Math.max(0, line - 1 - contextLines);
578
+ const end = Math.min(lines.length, line + contextLines);
579
+ return lines.slice(start, end).map((content, offset) => {
580
+ const lineNumber = start + offset + 1;
581
+ const prefix = `${String(lineNumber).padStart(4, " ")} | `;
582
+ if (lineNumber !== line) {
583
+ return `${prefix}${content}`;
584
+ }
585
+ return `${prefix}${content}
586
+ | ${" ".repeat(Math.max(column, 0))}^`;
587
+ }).join("\n");
588
+ }
589
+ function createCompileError(message, source, index) {
590
+ const loc = indexToLocation(source, index);
591
+ const error = new Error(message);
592
+ error.loc = { start: loc };
593
+ error.frame = createCodeFrame(source, loc.line, loc.column);
594
+ return error;
595
+ }
596
+ function normalizeCompileError(error, source, filename = "component.olova") {
597
+ const compileError = error instanceof Error ? error : new Error(String(error));
598
+ const start = compileError.loc?.start ?? compileError.loc;
599
+ const line = start?.line;
600
+ const column = start?.column;
601
+ const locationSuffix = line === void 0 || column === void 0 ? "" : ` (${filename}:${line}:${column + 1})`;
602
+ compileError.message = `[olova] ${compileError.message.replace(/^\[olova\]\s*/, "")}${locationSuffix}`;
603
+ if (line !== void 0 && column !== void 0 && !compileError.frame) {
604
+ compileError.frame = createCodeFrame(source, line, column);
605
+ }
606
+ throw compileError;
607
+ }
608
+ function parseModule(code) {
609
+ return parse(code, {
610
+ sourceType: "module",
611
+ plugins: ["typescript", "jsx"]
612
+ });
613
+ }
614
+ function transpileTypeScript(code, filename = "component.ts") {
615
+ const result = ts.transpileModule(code, {
616
+ fileName: filename,
617
+ compilerOptions: {
618
+ target: ts.ScriptTarget.ES2022,
619
+ module: ts.ModuleKind.ESNext,
620
+ jsx: ts.JsxEmit.Preserve,
621
+ importsNotUsedAsValues: ts.ImportsNotUsedAsValues.Remove,
622
+ verbatimModuleSyntax: false,
623
+ sourceMap: true,
624
+ inlineSources: true
625
+ }
626
+ });
627
+ return {
628
+ code: result.outputText.trim(),
629
+ map: result.sourceMapText ? JSON.parse(result.sourceMapText) : null
630
+ };
631
+ }
632
+ function compileModuleScript(script, filename = "module.olova.ts") {
633
+ const ast = parseModule(script || "");
634
+ return transpileTypeScript(
635
+ ast.program.body.map((node) => generate(node).code).join("\n"),
636
+ filename
637
+ );
638
+ }
639
+ function isModuleOnlyScript(script) {
640
+ if (!script.trim()) {
641
+ return false;
642
+ }
643
+ const ast = parseModule(script);
644
+ return ast.program.body.every(
645
+ (node) => t.isImportDeclaration(node) || t.isExportNamedDeclaration(node) || t.isExportAllDeclaration(node) || t.isExportDefaultDeclaration(node)
646
+ );
647
+ }
648
+ function isStateInit(node) {
649
+ return !!(node && t.isCallExpression(node) && (t.isIdentifier(node.callee) && node.callee.name === "state" || t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object) && node.callee.object.name === "__olovaGlobal" && t.isIdentifier(node.callee.property) && (node.callee.property.name === "state" || node.callee.property.name === "get")));
650
+ }
651
+ function isComputedInit(node) {
652
+ return !!(node && t.isCallExpression(node) && t.isIdentifier(node.callee) && (node.callee.name === "computed" || node.callee.name === "derived"));
653
+ }
654
+ function appendHmrStateKey(node, key) {
655
+ if (!node) {
656
+ return;
657
+ }
658
+ if (t.isCallExpression(node) && t.isIdentifier(node.callee) && node.callee.name === "state") {
659
+ const existingKey = node.arguments[1];
660
+ if (!existingKey || !t.isStringLiteral(existingKey)) {
661
+ node.arguments = [node.arguments[0] ?? t.identifier("undefined"), t.stringLiteral(key)];
662
+ }
663
+ return;
664
+ }
665
+ if (t.isConditionalExpression(node)) {
666
+ appendHmrStateKey(node.consequent, `${key}:then`);
667
+ appendHmrStateKey(node.alternate, `${key}:else`);
668
+ return;
669
+ }
670
+ if (t.isLogicalExpression(node)) {
671
+ appendHmrStateKey(node.left, `${key}:left`);
672
+ appendHmrStateKey(node.right, `${key}:right`);
673
+ }
674
+ }
675
+ function containsStateInit(node) {
676
+ if (!node) {
677
+ return false;
678
+ }
679
+ if (isStateInit(node)) {
680
+ return true;
681
+ }
682
+ if (t.isConditionalExpression(node)) {
683
+ return containsStateInit(node.consequent) || containsStateInit(node.alternate);
684
+ }
685
+ if (t.isLogicalExpression(node)) {
686
+ return containsStateInit(node.left) || containsStateInit(node.right);
687
+ }
688
+ return false;
689
+ }
690
+ function containsSignalInit(node) {
691
+ if (!node) {
692
+ return false;
693
+ }
694
+ if (isStateInit(node) || isComputedInit(node)) {
695
+ return true;
696
+ }
697
+ if (t.isConditionalExpression(node)) {
698
+ return containsSignalInit(node.consequent) || containsSignalInit(node.alternate);
699
+ }
700
+ if (t.isLogicalExpression(node)) {
701
+ return containsSignalInit(node.left) || containsSignalInit(node.right);
702
+ }
703
+ return false;
704
+ }
705
+ function isGlobalStateCall(node) {
706
+ return t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object) && (node.callee.object.name === "global" || node.callee.object.name === "__olovaGlobal") && t.isIdentifier(node.callee.property) && node.callee.property.name === "state";
707
+ }
708
+ function shouldSkipIdentifierTransform(path2) {
709
+ if (path2.parentPath?.isVariableDeclarator() && path2.parentPath.node.id === path2.node) {
710
+ return true;
711
+ }
712
+ if (path2.parentPath?.isAssignmentExpression() && path2.parentPath.node.left === path2.node) {
713
+ return true;
714
+ }
715
+ if (path2.parentPath?.isUpdateExpression()) {
716
+ return true;
717
+ }
718
+ if (path2.parentPath?.isCallExpression() && t.isIdentifier(path2.parentPath.node.callee) && path2.parentPath.node.callee.name === "setContext" && path2.parentPath.node.arguments.includes(path2.node)) {
719
+ return true;
720
+ }
721
+ if (path2.parentPath?.isMemberExpression() && path2.parentPath.node.property === path2.node && !path2.parentPath.node.computed) {
722
+ return true;
723
+ }
724
+ if (path2.parentPath?.isMemberExpression() && path2.parentPath.node.object === path2.node && t.isIdentifier(path2.parentPath.node.property) && (path2.parentPath.node.property.name === "value" || path2.parentPath.node.property.name === "notify")) {
725
+ return true;
726
+ }
727
+ if (path2.parentPath?.isImportSpecifier() || path2.parentPath?.isExportSpecifier()) {
728
+ return true;
729
+ }
730
+ if (path2.parentPath?.isObjectProperty() && path2.parentPath.node.key === path2.node && !path2.parentPath.node.computed && !path2.parentPath.node.shorthand) {
731
+ return true;
732
+ }
733
+ if (path2.parentPath?.isFunctionDeclaration() && path2.parentPath.node.id === path2.node) {
734
+ return true;
735
+ }
736
+ if (path2.parentPath?.isClassDeclaration() && path2.parentPath.node.id === path2.node) {
737
+ return true;
738
+ }
739
+ return false;
740
+ }
741
+ function addNestedMutationNotify(ast, stateNames) {
742
+ traverse(ast, {
743
+ AssignmentExpression(path2) {
744
+ const left = path2.node.left;
745
+ if (!t.isMemberExpression(left)) {
746
+ return;
747
+ }
748
+ let root = left;
749
+ while (t.isMemberExpression(root.object)) {
750
+ root = root.object;
751
+ }
752
+ if (!t.isIdentifier(root.object)) {
753
+ return;
754
+ }
755
+ if (!t.isIdentifier(root.property) || root.property.name !== "value") {
756
+ return;
757
+ }
758
+ const stateName = root.object.name;
759
+ if (!stateNames.has(stateName)) {
760
+ return;
761
+ }
762
+ if (left === root) {
763
+ return;
764
+ }
765
+ if (path2.parentPath?.isSequenceExpression()) {
766
+ return;
767
+ }
768
+ path2.replaceWith(
769
+ t.sequenceExpression([
770
+ path2.node,
771
+ t.callExpression(
772
+ t.memberExpression(t.identifier(stateName), t.identifier("notify")),
773
+ []
774
+ )
775
+ ])
776
+ );
777
+ path2.skip();
778
+ }
779
+ });
780
+ }
781
+ function collectScriptInfo(script) {
782
+ const ast = parseModule(script || "");
783
+ const stateNames = /* @__PURE__ */ new Set();
784
+ const allSignalNames = /* @__PURE__ */ new Set();
785
+ const componentNames = /* @__PURE__ */ new Set();
786
+ const jsxFunctionNames = /* @__PURE__ */ new Set();
787
+ let needsGlobalRuntime = false;
788
+ for (const node of ast.program.body) {
789
+ if (!t.isImportDeclaration(node)) {
790
+ continue;
791
+ }
792
+ for (const specifier of node.specifiers) {
793
+ componentNames.add(specifier.local.name);
794
+ }
795
+ }
796
+ const rewrittenBody = [];
797
+ for (const node of ast.program.body) {
798
+ if (t.isImportDeclaration(node) && t.isStringLiteral(node.source) && (node.source.value === "olova/global" || node.source.value === "olova/core")) {
799
+ if (node.source.value === "olova/global") {
800
+ needsGlobalRuntime = true;
801
+ const declarators = [];
802
+ for (const specifier of node.specifiers) {
803
+ if (!t.isImportSpecifier(specifier)) {
804
+ throw new Error(
805
+ "[olova] Use named imports with 'olova/global' (e.g. import { user } from 'olova/global')."
806
+ );
807
+ }
808
+ const imported = t.isIdentifier(specifier.imported) ? specifier.imported.name : specifier.imported.value;
809
+ declarators.push(
810
+ t.variableDeclarator(
811
+ t.identifier(specifier.local.name),
812
+ t.callExpression(
813
+ t.memberExpression(
814
+ t.identifier("__olovaGlobal"),
815
+ t.identifier("get")
816
+ ),
817
+ [t.stringLiteral(imported)]
818
+ )
819
+ )
820
+ );
821
+ }
822
+ if (declarators.length > 0) {
823
+ rewrittenBody.push(t.variableDeclaration("const", declarators));
824
+ }
825
+ continue;
826
+ }
827
+ const keptSpecifiers = [];
828
+ const globalDeclarators = [];
829
+ for (const specifier of node.specifiers) {
830
+ if (!t.isImportSpecifier(specifier)) {
831
+ keptSpecifiers.push(specifier);
832
+ continue;
833
+ }
834
+ const imported = t.isIdentifier(specifier.imported) ? specifier.imported.name : specifier.imported.value;
835
+ if (CORE_KNOWN_IMPORTS.has(imported)) {
836
+ keptSpecifiers.push(specifier);
837
+ continue;
838
+ }
839
+ needsGlobalRuntime = true;
840
+ globalDeclarators.push(
841
+ t.variableDeclarator(
842
+ t.identifier(specifier.local.name),
843
+ t.callExpression(
844
+ t.memberExpression(
845
+ t.identifier("__olovaGlobal"),
846
+ t.identifier("get")
847
+ ),
848
+ [t.stringLiteral(imported)]
849
+ )
850
+ )
851
+ );
852
+ }
853
+ if (keptSpecifiers.length > 0) {
854
+ rewrittenBody.push(
855
+ t.importDeclaration(keptSpecifiers, t.stringLiteral("olova/core"))
856
+ );
857
+ }
858
+ if (globalDeclarators.length > 0) {
859
+ rewrittenBody.push(t.variableDeclaration("const", globalDeclarators));
860
+ }
861
+ continue;
862
+ }
863
+ rewrittenBody.push(node);
864
+ }
865
+ ast.program.body = rewrittenBody;
866
+ traverse(ast, {
867
+ CallExpression(path2) {
868
+ if (!isGlobalStateCall(path2.node)) {
869
+ return;
870
+ }
871
+ needsGlobalRuntime = true;
872
+ path2.node.callee = t.memberExpression(
873
+ t.identifier("__olovaGlobal"),
874
+ t.identifier("state")
875
+ );
876
+ if (path2.parentPath && path2.parentPath.isVariableDeclarator() && t.isIdentifier(path2.parentPath.node.id)) {
877
+ const firstArg = path2.node.arguments[0];
878
+ const alreadyNamed = !!firstArg && t.isStringLiteral(firstArg) && firstArg.value.length > 0;
879
+ if (!alreadyNamed) {
880
+ path2.node.arguments = [
881
+ t.stringLiteral(path2.parentPath.node.id.name),
882
+ ...path2.node.arguments
883
+ ];
884
+ }
885
+ }
886
+ }
887
+ });
888
+ traverse(ast, {
889
+ VariableDeclarator(path2) {
890
+ if (!t.isIdentifier(path2.node.id)) {
891
+ return;
892
+ }
893
+ if (containsStateInit(path2.node.init)) {
894
+ stateNames.add(path2.node.id.name);
895
+ appendHmrStateKey(path2.node.init, path2.node.id.name);
896
+ }
897
+ if (containsSignalInit(path2.node.init)) {
898
+ allSignalNames.add(path2.node.id.name);
899
+ }
900
+ }
901
+ });
902
+ traverse(ast, {
903
+ AssignmentExpression(path2) {
904
+ if (!t.isIdentifier(path2.node.left)) {
905
+ return;
906
+ }
907
+ const name = path2.node.left.name;
908
+ if (!stateNames.has(name)) {
909
+ return;
910
+ }
911
+ const binding = path2.scope.getBinding(name);
912
+ if (!binding || !t.isVariableDeclarator(binding.path.node)) {
913
+ return;
914
+ }
915
+ if (!t.isIdentifier(binding.path.node.id) || !containsStateInit(binding.path.node.init)) {
916
+ return;
917
+ }
918
+ path2.node.left = t.memberExpression(
919
+ t.identifier(name),
920
+ t.identifier("value")
921
+ );
922
+ },
923
+ UpdateExpression(path2) {
924
+ if (!t.isIdentifier(path2.node.argument)) {
925
+ return;
926
+ }
927
+ const name = path2.node.argument.name;
928
+ if (!stateNames.has(name)) {
929
+ return;
930
+ }
931
+ const binding = path2.scope.getBinding(name);
932
+ if (!binding || !t.isVariableDeclarator(binding.path.node)) {
933
+ return;
934
+ }
935
+ if (!t.isIdentifier(binding.path.node.id) || !containsStateInit(binding.path.node.init)) {
936
+ return;
937
+ }
938
+ path2.node.argument = t.memberExpression(
939
+ t.identifier(name),
940
+ t.identifier("value")
941
+ );
942
+ }
943
+ });
944
+ traverse(ast, {
945
+ Identifier(path2) {
946
+ const name = path2.node.name;
947
+ if (!allSignalNames.has(name)) {
948
+ return;
949
+ }
950
+ if (shouldSkipIdentifierTransform(path2)) {
951
+ return;
952
+ }
953
+ const binding = path2.scope.getBinding(name);
954
+ if (!binding || !t.isVariableDeclarator(binding.path.node)) {
955
+ return;
956
+ }
957
+ if (!t.isIdentifier(binding.path.node.id) || !containsSignalInit(binding.path.node.init)) {
958
+ return;
959
+ }
960
+ path2.replaceWith(
961
+ t.memberExpression(t.identifier(name), t.identifier("value"))
962
+ );
963
+ path2.skip();
964
+ }
965
+ });
966
+ addNestedMutationNotify(ast, stateNames);
967
+ const nodeContainsJsx = (node) => {
968
+ if (!node) {
969
+ return false;
970
+ }
971
+ let found = false;
972
+ traverse(parseModule(`(${generate(node).code})`), {
973
+ JSXElement() {
974
+ found = true;
975
+ },
976
+ JSXFragment() {
977
+ found = true;
978
+ }
979
+ });
980
+ return found;
981
+ };
982
+ traverse(ast, {
983
+ FunctionDeclaration(path2) {
984
+ if (path2.node.id && nodeContainsJsx(path2.node.body)) {
985
+ jsxFunctionNames.add(path2.node.id.name);
986
+ }
987
+ },
988
+ VariableDeclarator(path2) {
989
+ if (t.isIdentifier(path2.node.id) && nodeContainsJsx(path2.node.init)) {
990
+ jsxFunctionNames.add(path2.node.id.name);
991
+ }
992
+ }
993
+ });
994
+ traverse(ast, {
995
+ JSXElement(path2) {
996
+ const transformed = transformExpression(
997
+ generate(path2.node).code,
998
+ stateNames,
999
+ allSignalNames,
1000
+ jsxFunctionNames
1001
+ );
1002
+ const parsed = parseModule(`(${transformed.code})`);
1003
+ const statement = parsed.program.body[0];
1004
+ if (statement && t.isExpressionStatement(statement)) {
1005
+ path2.replaceWith(statement.expression);
1006
+ path2.skip();
1007
+ }
1008
+ },
1009
+ JSXFragment(path2) {
1010
+ const transformed = transformExpression(
1011
+ generate(path2.node).code,
1012
+ stateNames,
1013
+ allSignalNames,
1014
+ jsxFunctionNames
1015
+ );
1016
+ const parsed = parseModule(`(${transformed.code})`);
1017
+ const statement = parsed.program.body[0];
1018
+ if (statement && t.isExpressionStatement(statement)) {
1019
+ path2.replaceWith(statement.expression);
1020
+ path2.skip();
1021
+ }
1022
+ }
1023
+ });
1024
+ if (needsGlobalRuntime) {
1025
+ ast.program.body.unshift(
1026
+ t.importDeclaration(
1027
+ [
1028
+ t.importSpecifier(
1029
+ t.identifier("__olovaGlobal"),
1030
+ t.identifier("global")
1031
+ )
1032
+ ],
1033
+ t.stringLiteral("olova/global")
1034
+ )
1035
+ );
1036
+ }
1037
+ const importNodes = ast.program.body.filter(
1038
+ (node) => t.isImportDeclaration(node)
1039
+ );
1040
+ const bodyNodes = ast.program.body.filter(
1041
+ (node) => !t.isImportDeclaration(node)
1042
+ );
1043
+ const importsCode = importNodes.map((node) => generate(node).code).join("\n");
1044
+ const scriptBodyCode = bodyNodes.map((node) => generate(node).code).join("\n");
1045
+ return {
1046
+ importsCode,
1047
+ scriptBodyCode,
1048
+ stateNames,
1049
+ allSignalNames,
1050
+ componentNames,
1051
+ jsxFunctionNames
1052
+ };
1053
+ }
1054
+ function transformExpression(expr, stateNames, allSignalNames, jsxFunctionNames = /* @__PURE__ */ new Set(), scopeId) {
1055
+ const ast = parseModule(`(${expr})`);
1056
+ let containsJsx = false;
1057
+ const jsxScopeAttr = createScopeAttr(scopeId);
1058
+ traverse(ast, {
1059
+ AssignmentExpression(path2) {
1060
+ if (!t.isIdentifier(path2.node.left)) {
1061
+ return;
1062
+ }
1063
+ const name = path2.node.left.name;
1064
+ if (!stateNames.has(name)) {
1065
+ return;
1066
+ }
1067
+ const binding = path2.scope.getBinding(name);
1068
+ if (binding) {
1069
+ return;
1070
+ }
1071
+ path2.node.left = t.memberExpression(
1072
+ t.identifier(name),
1073
+ t.identifier("value")
1074
+ );
1075
+ },
1076
+ UpdateExpression(path2) {
1077
+ if (!t.isIdentifier(path2.node.argument)) {
1078
+ return;
1079
+ }
1080
+ const name = path2.node.argument.name;
1081
+ if (!stateNames.has(name)) {
1082
+ return;
1083
+ }
1084
+ const binding = path2.scope.getBinding(name);
1085
+ if (binding) {
1086
+ return;
1087
+ }
1088
+ path2.node.argument = t.memberExpression(
1089
+ t.identifier(name),
1090
+ t.identifier("value")
1091
+ );
1092
+ }
1093
+ });
1094
+ traverse(ast, {
1095
+ Identifier(path2) {
1096
+ const name = path2.node.name;
1097
+ if (!allSignalNames.has(name)) {
1098
+ return;
1099
+ }
1100
+ if (shouldSkipIdentifierTransform(path2)) {
1101
+ return;
1102
+ }
1103
+ const binding = path2.scope.getBinding(name);
1104
+ if (binding) {
1105
+ return;
1106
+ }
1107
+ path2.replaceWith(
1108
+ t.memberExpression(t.identifier(name), t.identifier("value"))
1109
+ );
1110
+ path2.skip();
1111
+ }
1112
+ });
1113
+ addNestedMutationNotify(ast, stateNames);
1114
+ const toStringCall = (input) => t.callExpression(t.identifier("__olovaToString"), [input]);
1115
+ const escapeCall = (input) => t.callExpression(t.identifier("__olovaEscape"), [input]);
1116
+ const expressionContainsJsx = (input) => {
1117
+ let found = false;
1118
+ traverse(parseModule(`(${generate(input).code})`), {
1119
+ JSXElement() {
1120
+ found = true;
1121
+ },
1122
+ JSXFragment() {
1123
+ found = true;
1124
+ }
1125
+ });
1126
+ return found;
1127
+ };
1128
+ const expressionProducesHtml = (input) => {
1129
+ if (!input) {
1130
+ return false;
1131
+ }
1132
+ if (expressionContainsJsx(input)) {
1133
+ return true;
1134
+ }
1135
+ let found = false;
1136
+ traverse(parseModule(`(${generate(input).code})`), {
1137
+ CallExpression(path2) {
1138
+ if (t.isIdentifier(path2.node.callee) && jsxFunctionNames.has(path2.node.callee.name)) {
1139
+ found = true;
1140
+ path2.stop();
1141
+ return;
1142
+ }
1143
+ if (t.isMemberExpression(path2.node.callee) && t.isIdentifier(path2.node.callee.property) && (path2.node.callee.property.name === "map" || path2.node.callee.property.name === "flatMap")) {
1144
+ const [firstArg] = path2.node.arguments;
1145
+ if (t.isIdentifier(firstArg) && jsxFunctionNames.has(firstArg.name) || nodeContainsRenderableFunction(firstArg, jsxFunctionNames)) {
1146
+ found = true;
1147
+ path2.stop();
1148
+ }
1149
+ }
1150
+ }
1151
+ });
1152
+ return found;
1153
+ };
1154
+ const nodeContainsRenderableFunction = (node, knownJsxFunctions) => {
1155
+ if (!node) {
1156
+ return false;
1157
+ }
1158
+ if (t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)) {
1159
+ return expressionContainsJsx(node.body) || expressionProducesHtml(node.body);
1160
+ }
1161
+ if (t.isIdentifier(node)) {
1162
+ return knownJsxFunctions.has(node.name);
1163
+ }
1164
+ return false;
1165
+ };
1166
+ const concatExpressions = (parts) => {
1167
+ if (parts.length === 0) {
1168
+ return t.stringLiteral("");
1169
+ }
1170
+ return parts.slice(1).reduce(
1171
+ (acc, current) => t.binaryExpression("+", acc, current),
1172
+ parts[0]
1173
+ );
1174
+ };
1175
+ const jsxNameToString = (name) => {
1176
+ if (t.isJSXIdentifier(name)) {
1177
+ return name.name;
1178
+ }
1179
+ if (t.isJSXNamespacedName(name)) {
1180
+ return `${name.namespace.name}:${name.name.name}`;
1181
+ }
1182
+ const walkMember = (node) => {
1183
+ if (t.isJSXIdentifier(node)) {
1184
+ return node.name;
1185
+ }
1186
+ return `${walkMember(node.object)}.${node.property.name}`;
1187
+ };
1188
+ return walkMember(name);
1189
+ };
1190
+ const transformEmbeddedJsxExpression = (input) => {
1191
+ const wrapped = parseModule(`(${generate(input).code})`);
1192
+ let containsNestedJsx = false;
1193
+ traverse(wrapped, {
1194
+ JSXElement(path2) {
1195
+ containsNestedJsx = true;
1196
+ path2.replaceWith(jsxElementToExpression(path2.node));
1197
+ path2.skip();
1198
+ },
1199
+ JSXFragment(path2) {
1200
+ containsNestedJsx = true;
1201
+ path2.replaceWith(jsxFragmentToExpression(path2.node));
1202
+ path2.skip();
1203
+ }
1204
+ });
1205
+ const statement2 = wrapped.program.body[0];
1206
+ if (!statement2 || !t.isExpressionStatement(statement2)) {
1207
+ return { expression: input, containsJsx: containsNestedJsx };
1208
+ }
1209
+ return {
1210
+ expression: statement2.expression,
1211
+ containsJsx: containsNestedJsx
1212
+ };
1213
+ };
1214
+ const jsxAttrToExpression = (attr) => {
1215
+ if (t.isJSXSpreadAttribute(attr)) {
1216
+ const transformed = transformEmbeddedJsxExpression(
1217
+ attr.argument
1218
+ );
1219
+ return t.callExpression(t.identifier("__olovaSpreadAttrs"), [
1220
+ transformed.expression
1221
+ ]);
1222
+ }
1223
+ const name = jsxNameToString(attr.name);
1224
+ if (name === "key") {
1225
+ return t.stringLiteral("");
1226
+ }
1227
+ const normalizedName = name === "className" ? "class" : name;
1228
+ const eventMatch = name.match(/^(?:on:|on)([A-Za-z]+)$/);
1229
+ if (!attr.value) {
1230
+ return t.stringLiteral(` ${normalizedName}`);
1231
+ }
1232
+ if (t.isStringLiteral(attr.value)) {
1233
+ return t.stringLiteral(` ${normalizedName}="${attr.value.value}"`);
1234
+ }
1235
+ if (t.isJSXExpressionContainer(attr.value)) {
1236
+ const transformed = transformEmbeddedJsxExpression(
1237
+ attr.value.expression ?? t.stringLiteral("")
1238
+ );
1239
+ if (eventMatch) {
1240
+ return t.callExpression(t.identifier("__olovaRegisterJsxEvent"), [
1241
+ t.stringLiteral(eventMatch[1].toLowerCase()),
1242
+ transformed.expression
1243
+ ]);
1244
+ }
1245
+ const valueExpression = normalizedName === "class" ? t.callExpression(t.identifier("__olovaNormalizeClassValue"), [
1246
+ transformed.expression
1247
+ ]) : transformed.expression;
1248
+ return concatExpressions([
1249
+ t.stringLiteral(` ${normalizedName}="`),
1250
+ escapeCall(valueExpression),
1251
+ t.stringLiteral('"')
1252
+ ]);
1253
+ }
1254
+ return t.stringLiteral("");
1255
+ };
1256
+ const jsxChildToExpression = (child) => {
1257
+ if (t.isJSXText(child)) {
1258
+ return t.stringLiteral(child.value);
1259
+ }
1260
+ if (t.isJSXExpressionContainer(child)) {
1261
+ if (t.isJSXEmptyExpression(child.expression)) {
1262
+ return t.stringLiteral("");
1263
+ }
1264
+ const rawExpression = child.expression;
1265
+ const transformed = transformEmbeddedJsxExpression(rawExpression);
1266
+ return transformed.containsJsx || expressionProducesHtml(rawExpression) ? toStringCall(transformed.expression) : escapeCall(transformed.expression);
1267
+ }
1268
+ if (t.isJSXSpreadChild(child)) {
1269
+ const transformed = transformEmbeddedJsxExpression(child.expression);
1270
+ return transformed.containsJsx || expressionProducesHtml(child.expression) ? toStringCall(transformed.expression) : escapeCall(transformed.expression);
1271
+ }
1272
+ if (t.isJSXElement(child)) {
1273
+ return jsxElementToExpression(child);
1274
+ }
1275
+ return jsxFragmentToExpression(child);
1276
+ };
1277
+ const jsxFragmentToExpression = (fragment) => {
1278
+ const parts = fragment.children.map((child) => jsxChildToExpression(child));
1279
+ return concatExpressions(parts);
1280
+ };
1281
+ const jsxElementToExpression = (element) => {
1282
+ const opening = element.openingElement;
1283
+ const tag = jsxNameToString(opening.name);
1284
+ if (tag === "For") {
1285
+ let eachExpr = t.arrayExpression([]);
1286
+ for (const attr of opening.attributes) {
1287
+ if (t.isJSXAttribute(attr) && jsxNameToString(attr.name) === "each" && attr.value && t.isJSXExpressionContainer(attr.value) && !t.isJSXEmptyExpression(attr.value.expression)) {
1288
+ eachExpr = transformEmbeddedJsxExpression(
1289
+ attr.value.expression
1290
+ ).expression;
1291
+ }
1292
+ }
1293
+ const childRenderer = element.children.find(
1294
+ (child) => t.isJSXExpressionContainer(child) && !t.isJSXEmptyExpression(child.expression)
1295
+ );
1296
+ if (!childRenderer) {
1297
+ return t.stringLiteral("");
1298
+ }
1299
+ return t.callExpression(t.identifier("__olovaFor"), [
1300
+ eachExpr,
1301
+ transformEmbeddedJsxExpression(
1302
+ childRenderer.expression
1303
+ ).expression
1304
+ ]);
1305
+ }
1306
+ if (tag === "Show") {
1307
+ let whenExpr = t.booleanLiteral(false);
1308
+ let fallbackExpr = t.stringLiteral("");
1309
+ for (const attr of opening.attributes) {
1310
+ if (!t.isJSXAttribute(attr)) {
1311
+ continue;
1312
+ }
1313
+ const attrName = jsxNameToString(attr.name);
1314
+ if (attrName === "when" && attr.value && t.isJSXExpressionContainer(attr.value) && !t.isJSXEmptyExpression(attr.value.expression)) {
1315
+ whenExpr = transformEmbeddedJsxExpression(
1316
+ attr.value.expression
1317
+ ).expression;
1318
+ }
1319
+ if (attrName === "fallback" && attr.value && t.isJSXExpressionContainer(attr.value) && !t.isJSXEmptyExpression(attr.value.expression)) {
1320
+ fallbackExpr = transformEmbeddedJsxExpression(
1321
+ attr.value.expression
1322
+ ).expression;
1323
+ }
1324
+ }
1325
+ const childExpr2 = concatExpressions(
1326
+ element.children.map((child) => jsxChildToExpression(child)).filter(Boolean)
1327
+ );
1328
+ return t.callExpression(t.identifier("__olovaShow"), [
1329
+ whenExpr,
1330
+ t.arrowFunctionExpression(
1331
+ [t.identifier("__value")],
1332
+ childExpr2
1333
+ ),
1334
+ fallbackExpr
1335
+ ]);
1336
+ }
1337
+ const attrExpr = concatExpressions(
1338
+ [
1339
+ ...jsxScopeAttr ? [t.stringLiteral(` ${jsxScopeAttr}`)] : [],
1340
+ ...opening.attributes.map((attr) => jsxAttrToExpression(attr))
1341
+ ]
1342
+ );
1343
+ const childExpr = concatExpressions(
1344
+ element.children.map((child) => jsxChildToExpression(child)).filter(Boolean)
1345
+ );
1346
+ if (opening.selfClosing) {
1347
+ return concatExpressions([
1348
+ t.stringLiteral(`<${tag}`),
1349
+ attrExpr,
1350
+ t.stringLiteral("/>")
1351
+ ]);
1352
+ }
1353
+ return concatExpressions([
1354
+ t.stringLiteral(`<${tag}`),
1355
+ attrExpr,
1356
+ t.stringLiteral(">"),
1357
+ childExpr,
1358
+ t.stringLiteral(`</${tag}>`)
1359
+ ]);
1360
+ };
1361
+ traverse(ast, {
1362
+ JSXElement(path2) {
1363
+ containsJsx = true;
1364
+ path2.replaceWith(jsxElementToExpression(path2.node));
1365
+ path2.skip();
1366
+ },
1367
+ JSXFragment(path2) {
1368
+ containsJsx = true;
1369
+ path2.replaceWith(jsxFragmentToExpression(path2.node));
1370
+ path2.skip();
1371
+ }
1372
+ });
1373
+ const statement = ast.program.body[0];
1374
+ if (!statement || !t.isExpressionStatement(statement)) {
1375
+ return { code: expr.trim(), containsJsx };
1376
+ }
1377
+ containsJsx = containsJsx || expressionProducesHtml(statement.expression);
1378
+ return { code: generate(statement.expression).code, containsJsx };
1379
+ }
1380
+ function jsxToTemplateHtml(node) {
1381
+ if (t.isJSXElement(node)) {
1382
+ const opening = node.openingElement;
1383
+ let tag = "";
1384
+ if (t.isJSXIdentifier(opening.name)) {
1385
+ tag = opening.name.name;
1386
+ } else if (t.isJSXMemberExpression(opening.name)) {
1387
+ const walk = (n) => t.isJSXIdentifier(n) ? n.name : `${walk(n.object)}.${n.property.name}`;
1388
+ tag = walk(opening.name);
1389
+ } else if (t.isJSXNamespacedName(opening.name)) {
1390
+ tag = `${opening.name.namespace.name}:${opening.name.name.name}`;
1391
+ }
1392
+ let attrsStr = "";
1393
+ for (const attr of opening.attributes) {
1394
+ if (t.isJSXSpreadAttribute(attr)) {
1395
+ attrsStr += ` {...${generate(attr.argument).code}}`;
1396
+ } else {
1397
+ let attrName = "";
1398
+ if (t.isJSXIdentifier(attr.name)) attrName = attr.name.name;
1399
+ else attrName = `${attr.name.namespace.name}:${attr.name.name.name}`;
1400
+ if (!attr.value) attrsStr += ` ${attrName}`;
1401
+ else if (t.isStringLiteral(attr.value))
1402
+ attrsStr += ` ${attrName}="${attr.value.value}"`;
1403
+ else if (t.isJSXExpressionContainer(attr.value)) {
1404
+ if (!t.isJSXEmptyExpression(attr.value.expression)) {
1405
+ attrsStr += ` ${attrName}={${generate(attr.value.expression).code}}`;
1406
+ }
1407
+ }
1408
+ }
1409
+ }
1410
+ const childrenStr = node.children.map(jsxToTemplateHtml).join("");
1411
+ if (opening.selfClosing) {
1412
+ return `<${tag}${attrsStr} />`;
1413
+ }
1414
+ return `<${tag}${attrsStr}>${childrenStr}</${tag}>`;
1415
+ }
1416
+ if (t.isJSXText(node)) {
1417
+ return node.value;
1418
+ }
1419
+ if (t.isJSXExpressionContainer(node)) {
1420
+ if (t.isJSXEmptyExpression(node.expression)) return "";
1421
+ return `{${generate(node.expression).code}}`;
1422
+ }
1423
+ if (t.isJSXFragment(node)) {
1424
+ return node.children.map(jsxToTemplateHtml).join("");
1425
+ }
1426
+ return "";
1427
+ }
1428
+ function replaceTemplateExpressions(input, replacer) {
1429
+ let output = "";
1430
+ let index = 0;
1431
+ while (index < input.length) {
1432
+ const start = input.indexOf("{", index);
1433
+ if (start < 0) {
1434
+ output += input.slice(index);
1435
+ break;
1436
+ }
1437
+ let prevIndex = start - 1;
1438
+ while (prevIndex >= 0 && /\s/.test(input[prevIndex])) {
1439
+ prevIndex -= 1;
1440
+ }
1441
+ if (prevIndex >= 0 && input[prevIndex] === "=") {
1442
+ output += input.slice(index, start + 1);
1443
+ index = start + 1;
1444
+ continue;
1445
+ }
1446
+ if (start > 0 && input[start - 1] === "$") {
1447
+ output += input.slice(index, start + 1);
1448
+ index = start + 1;
1449
+ continue;
1450
+ }
1451
+ output += input.slice(index, start);
1452
+ let depth = 1;
1453
+ let cursor = start + 1;
1454
+ let quote = null;
1455
+ while (cursor < input.length && depth > 0) {
1456
+ const char = input[cursor];
1457
+ const prev = input[cursor - 1];
1458
+ if (quote) {
1459
+ if (char === quote && prev !== "\\") {
1460
+ quote = null;
1461
+ }
1462
+ cursor += 1;
1463
+ continue;
1464
+ }
1465
+ if (char === "'" || char === '"' || char === "`") {
1466
+ quote = char;
1467
+ cursor += 1;
1468
+ continue;
1469
+ }
1470
+ if (char === "{") {
1471
+ depth += 1;
1472
+ } else if (char === "}") {
1473
+ depth -= 1;
1474
+ }
1475
+ cursor += 1;
1476
+ }
1477
+ if (depth !== 0) {
1478
+ throw createCompileError("[olova] Unclosed template expression block.", input, start);
1479
+ }
1480
+ const expression = input.slice(start + 1, cursor - 1);
1481
+ output += replacer(expression);
1482
+ index = cursor;
1483
+ }
1484
+ return output;
1485
+ }
1486
+ function parsePropsFromAttrs(attrs, stateNames, allSignalNames, jsxFunctionNames) {
1487
+ const props = [];
1488
+ for (const [key, rawValue] of Object.entries(attrs)) {
1489
+ if (key === "slot" || key === "key") {
1490
+ continue;
1491
+ }
1492
+ const propName = key === "className" ? "class" : key;
1493
+ if (rawValue === true) {
1494
+ props.push(`${JSON.stringify(propName)}: true`);
1495
+ continue;
1496
+ }
1497
+ if (rawValue.startsWith("{") && rawValue.endsWith("}")) {
1498
+ const expr = rawValue.slice(1, -1);
1499
+ const valueExpr = transformExpression(
1500
+ expr,
1501
+ stateNames,
1502
+ allSignalNames,
1503
+ jsxFunctionNames
1504
+ ).code;
1505
+ props.push(
1506
+ `${JSON.stringify(propName)}: (${propName === "class" ? `__olovaNormalizeClassValue(${valueExpr})` : valueExpr})`
1507
+ );
1508
+ continue;
1509
+ }
1510
+ props.push(`${JSON.stringify(propName)}: ${JSON.stringify(rawValue)}`);
1511
+ }
1512
+ return props.length === 0 ? "{}" : `{ ${props.join(", ")} }`;
1513
+ }
1514
+ function transformTemplate(template, stateNames, allSignalNames, jsxFunctionNames, componentNames, counters, scopeId) {
1515
+ let html = template;
1516
+ const textBindings = [];
1517
+ const htmlBindings = [];
1518
+ const attrBindings = [];
1519
+ const eventBindings = [];
1520
+ const slotBindings = [];
1521
+ const componentBindings = [];
1522
+ const ifBindings = [];
1523
+ html = replaceTemplateExpressions(html, (expr) => {
1524
+ try {
1525
+ const ast = parseModule(`(${expr})`);
1526
+ const exprNode = ast.program.body[0];
1527
+ if (t.isExpressionStatement(exprNode)) {
1528
+ const node = exprNode.expression;
1529
+ const containsJsxNode = (n) => {
1530
+ let found = false;
1531
+ traverse(parseModule(`(${generate(n).code})`), {
1532
+ JSXElement() {
1533
+ found = true;
1534
+ },
1535
+ JSXFragment() {
1536
+ found = true;
1537
+ }
1538
+ });
1539
+ return found;
1540
+ };
1541
+ if (t.isLogicalExpression(node) && node.operator === "&&") {
1542
+ if (containsJsxNode(node.right)) {
1543
+ const id = `i${counters.if++}`;
1544
+ const condition = generate(node.left).code;
1545
+ const trueBranchHtml = jsxToTemplateHtml(node.right);
1546
+ ifBindings.push({
1547
+ id,
1548
+ expr: transformExpression(
1549
+ condition,
1550
+ stateNames,
1551
+ allSignalNames,
1552
+ jsxFunctionNames,
1553
+ scopeId
1554
+ ).code,
1555
+ trueBranch: transformTemplate(
1556
+ trueBranchHtml,
1557
+ stateNames,
1558
+ allSignalNames,
1559
+ jsxFunctionNames,
1560
+ componentNames,
1561
+ counters,
1562
+ scopeId
1563
+ )
1564
+ });
1565
+ return `__O_IF_${id}__`;
1566
+ }
1567
+ } else if (t.isConditionalExpression(node)) {
1568
+ if (containsJsxNode(node.consequent) || containsJsxNode(node.alternate)) {
1569
+ const id = `i${counters.if++}`;
1570
+ const condition = generate(node.test).code;
1571
+ const trueBranchHtml = containsJsxNode(node.consequent) ? jsxToTemplateHtml(node.consequent) : `{${generate(node.consequent).code}}`;
1572
+ const falseBranchHtml = containsJsxNode(node.alternate) ? jsxToTemplateHtml(node.alternate) : `{${generate(node.alternate).code}}`;
1573
+ ifBindings.push({
1574
+ id,
1575
+ expr: transformExpression(
1576
+ condition,
1577
+ stateNames,
1578
+ allSignalNames,
1579
+ jsxFunctionNames,
1580
+ scopeId
1581
+ ).code,
1582
+ trueBranch: transformTemplate(
1583
+ trueBranchHtml,
1584
+ stateNames,
1585
+ allSignalNames,
1586
+ jsxFunctionNames,
1587
+ componentNames,
1588
+ counters,
1589
+ scopeId
1590
+ ),
1591
+ falseBranch: transformTemplate(
1592
+ falseBranchHtml,
1593
+ stateNames,
1594
+ allSignalNames,
1595
+ jsxFunctionNames,
1596
+ componentNames,
1597
+ counters,
1598
+ scopeId
1599
+ )
1600
+ });
1601
+ return `__O_IF_${id}__`;
1602
+ }
1603
+ }
1604
+ }
1605
+ } catch {
1606
+ }
1607
+ return `{${expr}}`;
1608
+ });
1609
+ const rawExpressions = [];
1610
+ html = replaceTemplateExpressions(html, (expr) => {
1611
+ const id = `raw_${rawExpressions.length}`;
1612
+ rawExpressions.push({ id, expression: expr });
1613
+ return `__O_RAW_EXPR_${id}__`;
1614
+ });
1615
+ const renderTextContent = (content) => {
1616
+ return content.replace(
1617
+ /__O_RAW_EXPR_([a-zA-Z0-9_]+)__/g,
1618
+ (_full, exprId) => {
1619
+ const found = rawExpressions.find((entry) => entry.id === exprId);
1620
+ const expr = found ? found.expression : "";
1621
+ const transformed = transformExpression(
1622
+ expr,
1623
+ stateNames,
1624
+ allSignalNames,
1625
+ jsxFunctionNames,
1626
+ scopeId
1627
+ );
1628
+ if (transformed.containsJsx) {
1629
+ const id2 = `h${counters.text++}`;
1630
+ htmlBindings.push({
1631
+ id: id2,
1632
+ expr: `__olovaDangerouslySetHtml(${transformed.code})`
1633
+ });
1634
+ return `__O_HTML_${id2}__`;
1635
+ }
1636
+ const id = `t${counters.text++}`;
1637
+ textBindings.push({ id, expr: transformed.code });
1638
+ return `__O_TEXT_${id}__`;
1639
+ }
1640
+ );
1641
+ };
1642
+ const renderAttrs = (element) => {
1643
+ let output = "";
1644
+ const scopeAttr = createScopeAttr(scopeId);
1645
+ if (scopeAttr) {
1646
+ output += ` ${scopeAttr}`;
1647
+ }
1648
+ for (const [attrName, rawValue] of Object.entries(element.attrs)) {
1649
+ if (attrName === "key") {
1650
+ continue;
1651
+ }
1652
+ const normalizedAttrName = attrName === "className" ? "class" : attrName;
1653
+ if (rawValue === true) {
1654
+ output += ` ${normalizedAttrName}`;
1655
+ continue;
1656
+ }
1657
+ if (rawValue.startsWith("{") && rawValue.endsWith("}")) {
1658
+ const expr = rawValue.slice(1, -1);
1659
+ const eventMatch = normalizedAttrName.match(/^(?:on:|on)([A-Za-z]+)$/);
1660
+ if (eventMatch) {
1661
+ const id2 = `e${counters.event++}`;
1662
+ const event = eventMatch[1].toLowerCase();
1663
+ eventBindings.push({
1664
+ id: id2,
1665
+ event,
1666
+ expr: transformExpression(
1667
+ expr,
1668
+ stateNames,
1669
+ allSignalNames,
1670
+ jsxFunctionNames
1671
+ ).code
1672
+ });
1673
+ output += ` data-o-on-${event}="${id2}"`;
1674
+ continue;
1675
+ }
1676
+ const id = `a${counters.attr++}`;
1677
+ attrBindings.push({
1678
+ id,
1679
+ attr: normalizedAttrName,
1680
+ expr: normalizedAttrName === "class" ? `__olovaNormalizeClassValue(${transformExpression(expr, stateNames, allSignalNames, jsxFunctionNames).code})` : transformExpression(expr, stateNames, allSignalNames, jsxFunctionNames).code
1681
+ });
1682
+ output += ` ${normalizedAttrName}="__O_ATTR_${id}__"`;
1683
+ continue;
1684
+ }
1685
+ output += ` ${normalizedAttrName}="${rawValue.replace(/"/g, "&quot;")}"`;
1686
+ }
1687
+ return output;
1688
+ };
1689
+ const serializeRawNodes = (nodes) => nodes.map((node) => {
1690
+ if (node.type === "comment") {
1691
+ return `<!--${node.content}-->`;
1692
+ }
1693
+ if (node.type === "text") {
1694
+ return node.content;
1695
+ }
1696
+ const attrs = Object.entries(node.attrs).map(([name, value]) => {
1697
+ if (value === true) {
1698
+ return ` ${name}`;
1699
+ }
1700
+ return ` ${name}="${String(value).replace(/"/g, "&quot;")}"`;
1701
+ }).join("");
1702
+ const children = serializeRawNodes(node.children);
1703
+ if (node.isSelfClosing) {
1704
+ return `<${node.tag}${attrs} />`;
1705
+ }
1706
+ return `<${node.tag}${attrs}>${children}</${node.tag}>`;
1707
+ }).join("");
1708
+ const renderNodes = (nodes) => nodes.map((node) => {
1709
+ if (node.type === "comment") {
1710
+ return `<!--${node.content}-->`;
1711
+ }
1712
+ if (node.type === "text") {
1713
+ return renderTextContent(node.content);
1714
+ }
1715
+ if (node.tag === "slot") {
1716
+ const id = `s${counters.slot++}`;
1717
+ const slotName = typeof node.attrs.name === "string" ? node.attrs.name : "default";
1718
+ slotBindings.push({ id, name: slotName });
1719
+ return `__O_SLOT_${id}__`;
1720
+ }
1721
+ if (componentNames.has(node.tag)) {
1722
+ const id = `c${counters.comp++}`;
1723
+ const namedSlotNodes = /* @__PURE__ */ new Map();
1724
+ const defaultNodes = [];
1725
+ for (const child of node.children) {
1726
+ if (child.type === "element" && child.tag === "template" && typeof child.attrs.slot === "string") {
1727
+ namedSlotNodes.set(child.attrs.slot, child.children);
1728
+ continue;
1729
+ }
1730
+ defaultNodes.push(child);
1731
+ }
1732
+ const slotFactories = [];
1733
+ if (defaultNodes.length > 0) {
1734
+ slotFactories.push({
1735
+ varName: `__slot_${id}_default`,
1736
+ name: "default",
1737
+ descriptor: transformTemplate(
1738
+ serializeRawNodes(defaultNodes),
1739
+ stateNames,
1740
+ allSignalNames,
1741
+ jsxFunctionNames,
1742
+ componentNames,
1743
+ counters,
1744
+ scopeId
1745
+ )
1746
+ });
1747
+ }
1748
+ for (const [slotName, slotChildren] of namedSlotNodes.entries()) {
1749
+ slotFactories.push({
1750
+ varName: `__slot_${id}_${slotName.replace(/[^A-Za-z0-9_$]/g, "_")}`,
1751
+ name: slotName,
1752
+ descriptor: transformTemplate(
1753
+ serializeRawNodes(slotChildren),
1754
+ stateNames,
1755
+ allSignalNames,
1756
+ jsxFunctionNames,
1757
+ componentNames,
1758
+ counters,
1759
+ scopeId
1760
+ )
1761
+ });
1762
+ }
1763
+ componentBindings.push({
1764
+ id,
1765
+ component: node.tag,
1766
+ propsExpr: parsePropsFromAttrs(
1767
+ node.attrs,
1768
+ stateNames,
1769
+ allSignalNames,
1770
+ jsxFunctionNames
1771
+ ),
1772
+ slotFactories
1773
+ });
1774
+ return `__O_COMP_${id}__`;
1775
+ }
1776
+ const attrs = renderAttrs(node);
1777
+ const children = renderNodes(node.children);
1778
+ if (node.isSelfClosing) {
1779
+ return `<${node.tag}${attrs} />`;
1780
+ }
1781
+ return `<${node.tag}${attrs}>${children}</${node.tag}>`;
1782
+ }).join("");
1783
+ html = renderNodes(parseHTML(html));
1784
+ const resolveExpr = (expr) => transformExpression(
1785
+ expr,
1786
+ stateNames,
1787
+ allSignalNames,
1788
+ jsxFunctionNames,
1789
+ scopeId
1790
+ );
1791
+ const buildFnStr = generateBuildFunction(
1792
+ escapeTemplate(html),
1793
+ stateNames,
1794
+ resolveExpr,
1795
+ counters,
1796
+ componentNames
1797
+ );
1798
+ return {
1799
+ html: escapeTemplate(html),
1800
+ textBindings,
1801
+ htmlBindings,
1802
+ attrBindings,
1803
+ eventBindings,
1804
+ slotBindings,
1805
+ componentBindings,
1806
+ ifBindings,
1807
+ buildFnStr
1808
+ };
1809
+ }
1810
+ function indentBlock(code, spaces = 2) {
1811
+ const pad = " ".repeat(spaces);
1812
+ if (!code.trim()) {
1813
+ return "";
1814
+ }
1815
+ return code.split("\n").map((line) => `${pad}${line}`).join("\n");
1816
+ }
1817
+ function renderDescriptorLiteral(descriptor, indent = " ") {
1818
+ const slotFactoryDecls = descriptor.componentBindings.flatMap(
1819
+ (binding) => binding.slotFactories.map(
1820
+ (slot) => `${indent}const ${slot.varName} = () => (${renderDescriptorLiteral(slot.descriptor, indent + " ")});`
1821
+ )
1822
+ );
1823
+ const componentBindingsCode = descriptor.componentBindings.map((binding) => {
1824
+ const slotsObject = binding.slotFactories.length === 0 ? "{}" : `{ ${binding.slotFactories.map((slot) => `${JSON.stringify(slot.name)}: ${slot.varName}`).join(", ")} }`;
1825
+ return `${indent} { id: ${JSON.stringify(binding.id)}, getComponent: () => ${binding.component}, getProps: () => (${binding.propsExpr}), getSlots: () => (${slotsObject}) }`;
1826
+ }).join(",\n");
1827
+ const buildLines = descriptor.buildFnStr ? `${indent}build: ${descriptor.buildFnStr.replace(/\n /g, "\n" + indent)},` : "";
1828
+ const objectLiteral = `{
1829
+ ${buildLines}
1830
+ ${indent}textBindings: [
1831
+ ${descriptor.textBindings.map((binding) => `${indent} { id: ${JSON.stringify(binding.id)}, get: () => (${binding.expr}) }`).join(",\n")}
1832
+ ${indent}],
1833
+ ${indent}htmlBindings: [
1834
+ ${descriptor.htmlBindings.map((binding) => `${indent} { id: ${JSON.stringify(binding.id)}, get: () => (${binding.expr}) }`).join(",\n")}
1835
+ ${indent}],
1836
+ ${indent}attrBindings: [
1837
+ ${descriptor.attrBindings.map((binding) => `${indent} { id: ${JSON.stringify(binding.id)}, attr: ${JSON.stringify(binding.attr)}, get: () => (${binding.expr}) }`).join(",\n")}
1838
+ ${indent}],
1839
+ ${indent}eventBindings: [
1840
+ ${descriptor.eventBindings.map((binding) => `${indent} { id: ${JSON.stringify(binding.id)}, event: ${JSON.stringify(binding.event)}, get: () => (${binding.expr}) }`).join(",\n")}
1841
+ ${indent}],
1842
+ ${indent}slotBindings: [
1843
+ ${descriptor.slotBindings.map((binding) => `${indent} { id: ${JSON.stringify(binding.id)}, name: ${JSON.stringify(binding.name)} }`).join(",\n")}
1844
+ ${indent}],
1845
+ ${indent}componentBindings: [
1846
+ ${componentBindingsCode}
1847
+ ${indent}],
1848
+ ${indent}ifBindings: [
1849
+ ${descriptor.ifBindings.map((binding) => {
1850
+ const trueFactory = `() => (${renderDescriptorLiteral(binding.trueBranch, indent + " ")})`;
1851
+ const falseFactory = binding.falseBranch ? `() => (${renderDescriptorLiteral(binding.falseBranch, indent + " ")})` : "undefined";
1852
+ return `${indent} { id: ${JSON.stringify(binding.id)}, get: () => (${binding.expr}), trueBranch: ${trueFactory}, falseBranch: ${falseFactory} }`;
1853
+ }).join(",\n")}
1854
+ ${indent}]
1855
+ ${indent}}`;
1856
+ if (slotFactoryDecls.length === 0) {
1857
+ return objectLiteral;
1858
+ }
1859
+ const outerIndent = indent.length >= 2 ? indent.slice(2) : "";
1860
+ return `(() => {
1861
+ ${slotFactoryDecls.join("\n")}
1862
+ ${indent}return ${objectLiteral};
1863
+ ${outerIndent}})()`;
1864
+ }
1865
+ function buildComponentCode(parsed, options) {
1866
+ const scriptInfo = collectScriptInfo(parsed.script);
1867
+ const counters = {
1868
+ text: 0,
1869
+ attr: 0,
1870
+ event: 0,
1871
+ comp: 0,
1872
+ slot: 0,
1873
+ if: 0
1874
+ };
1875
+ const transformed = transformTemplate(
1876
+ parsed.template,
1877
+ scriptInfo.stateNames,
1878
+ scriptInfo.allSignalNames,
1879
+ scriptInfo.jsxFunctionNames,
1880
+ scriptInfo.componentNames,
1881
+ counters,
1882
+ options?.scopeId
1883
+ );
1884
+ const exportHead = options?.exportName ? `export const ${options.exportName} = __defineComponent((__props, __slots) => {` : `const __olovaComponent = __defineComponent((__props, __slots) => {`;
1885
+ const exportTail = options?.exportName ? `}, ${JSON.stringify(options?.hmrId)});` : `}, ${JSON.stringify(options?.hmrId)});
1886
+ export default __olovaComponent;`;
1887
+ const hmrCode = options?.dev && !options?.exportName && options?.hmrId ? `
1888
+ if (import.meta.hot) {
1889
+ import.meta.hot.accept((nextModule) => {
1890
+ if (nextModule?.default) {
1891
+ __replaceComponent(__olovaComponent, nextModule.default);
1892
+ }
1893
+ });
1894
+ }
1895
+ ` : "";
1896
+ const code = `
1897
+ ${exportHead}
1898
+ const props = __props;
1899
+ const slots = __slots;
1900
+ const __olovaGlobalObj = globalThis;
1901
+ const __olovaJsxHandlers =
1902
+ __olovaGlobalObj.__olovaJsxHandlers ?? (__olovaGlobalObj.__olovaJsxHandlers = new Map());
1903
+ const __olovaNextJsxEventId = () => {
1904
+ __olovaGlobalObj.__olovaJsxEventId = (__olovaGlobalObj.__olovaJsxEventId ?? 0) + 1;
1905
+ return String(__olovaGlobalObj.__olovaJsxEventId);
1906
+ };
1907
+ if (typeof __olovaGlobalObj.__olovaDispatchJsxEvent !== 'function') {
1908
+ __olovaGlobalObj.__olovaDispatchJsxEvent = (event, element) => {
1909
+ const key = element?.getAttribute?.('data-o-jsx-' + event.type);
1910
+ if (!key) {
1911
+ return;
1912
+ }
1913
+
1914
+ const handler = __olovaJsxHandlers.get(key);
1915
+ if (typeof handler === 'function') {
1916
+ handler(event);
1917
+ }
1918
+ };
1919
+ }
1920
+ const __olovaRegisterJsxEvent = (eventName, handler) => {
1921
+ if (typeof handler !== 'function') {
1922
+ return '';
1923
+ }
1924
+
1925
+ const id = __olovaNextJsxEventId();
1926
+ __olovaJsxHandlers.set(id, handler);
1927
+ return ' data-o-jsx-' + eventName + '="' + id + '" on' + eventName + '="window.__olovaDispatchJsxEvent(event, this)"';
1928
+ };
1929
+ const __olovaToString = (value) => Array.isArray(value) ? value.map(__olovaToString).join('') : value == null ? '' : String(value);
1930
+ const __olovaDangerouslySetHtml = (value) => ({ __dangerousHtml: true, value: __olovaToString(value) });
1931
+ const __olovaNormalizeClassValue = (value) => {
1932
+ if (value == null || value === false) {
1933
+ return '';
1934
+ }
1935
+ if (Array.isArray(value)) {
1936
+ return value.map(__olovaNormalizeClassValue).filter(Boolean).join(' ');
1937
+ }
1938
+ if (typeof value === 'object') {
1939
+ return Object.entries(value)
1940
+ .filter(([, enabled]) => !!enabled)
1941
+ .map(([key]) => key)
1942
+ .join(' ');
1943
+ }
1944
+ return String(value);
1945
+ };
1946
+ const __olovaEscape = (value) =>
1947
+ __olovaToString(value)
1948
+ .replace(/&/g, '&amp;')
1949
+ .replace(/</g, '&lt;')
1950
+ .replace(/>/g, '&gt;')
1951
+ .replace(/"/g, '&quot;')
1952
+ .replace(/'/g, '&#39;');
1953
+ const __olovaSpreadAttrs = (input) => {
1954
+ if (input == null || input === false) {
1955
+ return '';
1956
+ }
1957
+ if (Array.isArray(input)) {
1958
+ return input.map(__olovaSpreadAttrs).join('');
1959
+ }
1960
+ if (typeof input !== 'object') {
1961
+ return '';
1962
+ }
1963
+
1964
+ return Object.entries(input).reduce((acc, [key, value]) => {
1965
+ if (value == null || value === false) {
1966
+ return acc;
1967
+ }
1968
+ const attrKey = key === 'className' ? 'class' : key;
1969
+ const normalizedValue = attrKey === 'class'
1970
+ ? __olovaNormalizeClassValue(value)
1971
+ : value;
1972
+ if (attrKey === 'key') {
1973
+ return acc;
1974
+ }
1975
+ if (normalizedValue == null || normalizedValue === false || normalizedValue === '') {
1976
+ return acc;
1977
+ }
1978
+ if (normalizedValue === true) {
1979
+ return acc + ' ' + attrKey;
1980
+ }
1981
+ return acc + ' ' + attrKey + '="' + __olovaEscape(normalizedValue) + '"';
1982
+ }, '');
1983
+ };
1984
+ const __olovaFor = (each, render) => {
1985
+ if (!Array.isArray(each) || typeof render !== 'function') {
1986
+ return '';
1987
+ }
1988
+ return each.map((item, index) => render(item, index));
1989
+ };
1990
+ const __olovaShow = (when, render, fallback = '') => {
1991
+ if (!when) {
1992
+ return fallback;
1993
+ }
1994
+ return typeof render === 'function' ? render(when) : render;
1995
+ };
1996
+ ${indentBlock(scriptInfo.scriptBodyCode, 2)}
1997
+
1998
+ return ${renderDescriptorLiteral(transformed, " ")};
1999
+ ${exportTail}
2000
+ ${hmrCode}
2001
+ `;
2002
+ return { importsCode: scriptInfo.importsCode, code };
2003
+ }
2004
+ function hashScopeSeed(input) {
2005
+ let hash = 2166136261;
2006
+ for (let index = 0; index < input.length; index += 1) {
2007
+ hash ^= input.charCodeAt(index);
2008
+ hash = Math.imul(hash, 16777619);
2009
+ }
2010
+ return Math.abs(hash >>> 0).toString(36);
2011
+ }
2012
+ function createScopeAttr(scopeId) {
2013
+ return scopeId ? `data-o-scope-${scopeId}` : null;
2014
+ }
2015
+ function scopeSelectorList(selectorList, scopeAttr) {
2016
+ const applyScopeToSegment = (segment) => {
2017
+ const trimmed = segment.trim();
2018
+ if (!trimmed) {
2019
+ return trimmed;
2020
+ }
2021
+ if (trimmed.includes(scopeAttr)) {
2022
+ return trimmed;
2023
+ }
2024
+ const globalPattern = /:global\(([^()]+)\)/g;
2025
+ const localCandidate = trimmed.replace(globalPattern, "").trim();
2026
+ const normalized = trimmed.replace(globalPattern, "$1");
2027
+ if (!localCandidate) {
2028
+ return normalized;
2029
+ }
2030
+ if (normalized === ":root") {
2031
+ return `[${scopeAttr}]`;
2032
+ }
2033
+ const pseudoIndex = normalized.search(/(?<!:):(?!:)/);
2034
+ if (pseudoIndex === -1) {
2035
+ return `${normalized}[${scopeAttr}]`;
2036
+ }
2037
+ return `${normalized.slice(0, pseudoIndex)}[${scopeAttr}]${normalized.slice(pseudoIndex)}`;
2038
+ };
2039
+ return selectorList.split(",").map((selector) => selector.trim()).filter(Boolean).map((selector) => {
2040
+ if (selector.startsWith("@") || selector.includes(scopeAttr)) {
2041
+ return selector;
2042
+ }
2043
+ return selector.split(/(\s+|[>+~])/).map((part) => {
2044
+ if (!part || /^(\s+|[>+~])$/.test(part)) {
2045
+ return part;
2046
+ }
2047
+ return applyScopeToSegment(part);
2048
+ }).join("");
2049
+ }).join(", ");
2050
+ }
2051
+ function scopeCss(css, scopeAttr) {
2052
+ let output = "";
2053
+ let index = 0;
2054
+ while (index < css.length) {
2055
+ const open = css.indexOf("{", index);
2056
+ if (open < 0) {
2057
+ output += css.slice(index);
2058
+ break;
2059
+ }
2060
+ const selector = css.slice(index, open).trim();
2061
+ let depth = 1;
2062
+ let cursor = open + 1;
2063
+ while (cursor < css.length && depth > 0) {
2064
+ if (css[cursor] === "{") {
2065
+ depth += 1;
2066
+ } else if (css[cursor] === "}") {
2067
+ depth -= 1;
2068
+ }
2069
+ cursor += 1;
2070
+ }
2071
+ const body = css.slice(open + 1, cursor - 1);
2072
+ if (/^@(?:media|supports|container|layer)\b/i.test(selector)) {
2073
+ output += `${selector} {${scopeCss(body, scopeAttr)}}`;
2074
+ } else if (/^@(?:keyframes|-webkit-keyframes)\b/i.test(selector)) {
2075
+ output += `${selector} {${body}}`;
2076
+ } else {
2077
+ output += `${scopeSelectorList(selector, scopeAttr)} {${body}}`;
2078
+ }
2079
+ index = cursor;
2080
+ }
2081
+ return output;
2082
+ }
2083
+ function collectCss(blocks, scopeId) {
2084
+ const scopeAttr = createScopeAttr(scopeId);
2085
+ return blocks.map((block) => {
2086
+ const content = block.content.trim();
2087
+ if (!content) {
2088
+ return "";
2089
+ }
2090
+ if (!("global" in block.attrs) && scopeAttr) {
2091
+ return scopeCss(content, scopeAttr);
2092
+ }
2093
+ return content;
2094
+ }).filter(Boolean).join("\n\n");
2095
+ }
2096
+ function compileOlovaScriptModule(source, options) {
2097
+ try {
2098
+ const scriptInfo = collectScriptInfo(source);
2099
+ return transpileTypeScript(
2100
+ [scriptInfo.importsCode, scriptInfo.scriptBodyCode].filter(Boolean).join("\n"),
2101
+ options?.filename ?? "module.ts"
2102
+ );
2103
+ } catch (error) {
2104
+ return normalizeCompileError(error, source, options?.filename ?? "module.ts");
2105
+ }
2106
+ }
2107
+ function compileOlova(source, options) {
2108
+ try {
2109
+ const parsed = parseOlovaFile(source);
2110
+ if (!parsed.template.trim() && isModuleOnlyScript(parsed.script)) {
2111
+ return {
2112
+ ...compileModuleScript(parsed.script, options?.filename ?? "module.olova.ts"),
2113
+ css: collectCss(parsed.styles)
2114
+ };
2115
+ }
2116
+ const scopeId = parsed.styles.some((style) => !("global" in style.attrs)) ? hashScopeSeed(`${options?.filename ?? "component.olova"}:${options?.exportName ?? "default"}`) : void 0;
2117
+ const compiled = buildComponentCode(parsed, {
2118
+ scopeId,
2119
+ hmrId: options?.filename ?? "component.olova",
2120
+ dev: options?.dev
2121
+ });
2122
+ const runtimeImport = options?.dev ? `import { defineComponent as __defineComponent, replaceComponent as __replaceComponent } from 'olova/runtime';` : `import { defineComponent as __defineComponent } from 'olova/runtime';`;
2123
+ const result = transpileTypeScript(`
2124
+ ${runtimeImport}
2125
+ ${compiled.importsCode}
2126
+ ${compiled.code}
2127
+ `, options?.filename ?? "component.olova.ts");
2128
+ return {
2129
+ ...result,
2130
+ css: collectCss(parsed.styles, scopeId)
2131
+ };
2132
+ } catch (error) {
2133
+ return normalizeCompileError(
2134
+ error,
2135
+ source,
2136
+ options?.filename ?? "component.olova"
2137
+ );
2138
+ }
2139
+ }
2140
+
2141
+ // vite/olova-plugin.ts
2142
+ var RUNTIME_MODULE_ID = "\0olova:runtime";
2143
+ var CORE_MODULE_ID = "\0olova:core";
2144
+ var GLOBAL_MODULE_ID = "\0olova:global";
2145
+ var STYLE_QUERY = "?olova&type=style";
2146
+ function findPackageRoot(startDir) {
2147
+ let currentDir = startDir;
2148
+ while (true) {
2149
+ const packageJsonPath = path.join(currentDir, "package.json");
2150
+ if (fs.existsSync(packageJsonPath)) {
2151
+ return currentDir;
2152
+ }
2153
+ const parentDir = path.dirname(currentDir);
2154
+ if (parentDir === currentDir) {
2155
+ return null;
2156
+ }
2157
+ currentDir = parentDir;
2158
+ }
2159
+ }
2160
+ function readPackageJson(packageRoot) {
2161
+ if (!packageRoot) {
2162
+ return null;
2163
+ }
2164
+ const packageJsonPath = path.join(packageRoot, "package.json");
2165
+ if (!fs.existsSync(packageJsonPath)) {
2166
+ return null;
2167
+ }
2168
+ return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
2169
+ }
2170
+ function olovaPlugin() {
2171
+ let config;
2172
+ const cssCache = /* @__PURE__ */ new Map();
2173
+ const pluginDir = path.dirname(fileURLToPath(import.meta.url));
2174
+ const packageRoot = findPackageRoot(pluginDir);
2175
+ const packageJson = readPackageJson(packageRoot);
2176
+ const resolvePackageEntry = (exportKey, relativePath) => {
2177
+ const exportEntry = packageJson?.exports?.[exportKey];
2178
+ const publishedPath = typeof exportEntry === "string" ? exportEntry : exportEntry?.import ?? exportEntry?.default;
2179
+ if (packageRoot && publishedPath) {
2180
+ const publishedCandidate = path.resolve(packageRoot, publishedPath);
2181
+ if (fs.existsSync(publishedCandidate)) {
2182
+ return normalizePath(publishedCandidate);
2183
+ }
2184
+ }
2185
+ const candidates = [
2186
+ path.resolve(pluginDir, relativePath),
2187
+ path.resolve(pluginDir, relativePath.replace(/\.ts$/, ".js"))
2188
+ ];
2189
+ for (const candidate of candidates) {
2190
+ if (fs.existsSync(candidate)) {
2191
+ return normalizePath(candidate);
2192
+ }
2193
+ }
2194
+ return normalizePath(candidates[0]);
2195
+ };
2196
+ const runtimeEntry = resolvePackageEntry("./runtime", "../runtime/index.ts");
2197
+ const coreEntry = resolvePackageEntry("./core", "../core/index.ts");
2198
+ const globalEntry = resolvePackageEntry("./global", "../runtime/global.ts");
2199
+ const shouldRewriteRuntimeScript = (id, code) => {
2200
+ if (id.includes("/node_modules/") || id.endsWith(".d.ts")) {
2201
+ return false;
2202
+ }
2203
+ if (!/\.(?:[cm]?[jt]sx?)$/.test(id)) {
2204
+ return false;
2205
+ }
2206
+ return /from\s+['"]olova\/(?:core|global|state)['"]|import\s+['"]olova\/(?:core|global|state)['"]/.test(code);
2207
+ };
2208
+ return {
2209
+ name: "vite-plugin-olova",
2210
+ enforce: "pre",
2211
+ configResolved(resolvedConfig) {
2212
+ config = resolvedConfig;
2213
+ },
2214
+ resolveId(id) {
2215
+ if (id === "olova/runtime") return RUNTIME_MODULE_ID;
2216
+ if (id === "olova/core" || id === "olova/state") return CORE_MODULE_ID;
2217
+ if (id === "olova/global") return GLOBAL_MODULE_ID;
2218
+ if (id.includes(`.css${STYLE_QUERY}`)) return id;
2219
+ return null;
2220
+ },
2221
+ load(id) {
2222
+ if (id === RUNTIME_MODULE_ID) return `export * from ${JSON.stringify(runtimeEntry)};`;
2223
+ if (id === CORE_MODULE_ID) return `export * from ${JSON.stringify(coreEntry)};`;
2224
+ if (id === GLOBAL_MODULE_ID) return `export * from ${JSON.stringify(globalEntry)};`;
2225
+ if (id.includes(`.css${STYLE_QUERY}`)) {
2226
+ const filename = id.split("?")[0].replace(/\.css$/, "");
2227
+ return cssCache.get(filename) || "";
2228
+ }
2229
+ return null;
2230
+ },
2231
+ transform(code, id) {
2232
+ const [filename, query] = id.split("?", 2);
2233
+ if (!filename.endsWith(".olova")) {
2234
+ if (!query && shouldRewriteRuntimeScript(filename, code)) {
2235
+ const result = compileOlovaScriptModule(code, {
2236
+ filename,
2237
+ dev: !config.isProduction
2238
+ });
2239
+ return {
2240
+ code: result.code,
2241
+ map: result.map ?? null
2242
+ };
2243
+ }
2244
+ return null;
2245
+ }
2246
+ if (query && !query.includes("olova")) return null;
2247
+ if (query && query.includes("type=style")) return null;
2248
+ try {
2249
+ const result = compileOlova(code, { filename, dev: !config.isProduction });
2250
+ let jsCode = result.code;
2251
+ if (result.css && result.css.trim()) {
2252
+ cssCache.set(filename, result.css);
2253
+ jsCode = `import ${JSON.stringify(filename + ".css" + STYLE_QUERY)};
2254
+ ` + jsCode;
2255
+ } else {
2256
+ cssCache.delete(filename);
2257
+ }
2258
+ return {
2259
+ code: jsCode,
2260
+ map: result.map || null
2261
+ };
2262
+ } catch (err) {
2263
+ this.error(err, err.loc?.start?.line);
2264
+ }
2265
+ },
2266
+ async handleHotUpdate(ctx) {
2267
+ if (!ctx.file.endsWith(".olova")) {
2268
+ return;
2269
+ }
2270
+ const filename = normalizePath(ctx.file);
2271
+ const previousCss = (cssCache.get(filename) ?? "").trim();
2272
+ try {
2273
+ const source = await ctx.read();
2274
+ const nextCss = (compileOlova(source, { filename, dev: !config.isProduction }).css ?? "").trim();
2275
+ if (nextCss !== previousCss) {
2276
+ if (nextCss) {
2277
+ cssCache.set(filename, nextCss);
2278
+ } else {
2279
+ cssCache.delete(filename);
2280
+ }
2281
+ ctx.server.ws.send({ type: "full-reload", path: "*" });
2282
+ return [];
2283
+ }
2284
+ } catch {
2285
+ }
2286
+ const styleId = `${filename}.css${STYLE_QUERY}`;
2287
+ const modules = [...ctx.modules];
2288
+ const styleModule = ctx.server.moduleGraph.getModuleById(styleId);
2289
+ if (styleModule) {
2290
+ modules.push(styleModule);
2291
+ }
2292
+ for (const mod of modules) {
2293
+ ctx.server.moduleGraph.invalidateModule(mod);
2294
+ }
2295
+ return modules;
2296
+ }
2297
+ };
2298
+ }
2299
+
2300
+ export { olovaPlugin };
2301
+ //# sourceMappingURL=vite.js.map
2302
+ //# sourceMappingURL=vite.js.map