@torpor/view 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3683 @@
1
+
2
+ //#region src/compile/utils/endOfTemplateString.ts
3
+ function endOfTemplateString(content, start) {
4
+ let level = 0;
5
+ for (let j = start + 1; j < content.length; j++) if (content[j] === "`" && content[j - 1] !== "\\" && level === 0) return j;
6
+ else if (content[j] === "{" && (level > 0 || content[j - 1] === "$")) level += 1;
7
+ else if (content[j] === "}" && level > 0) level -= 1;
8
+ return content.length;
9
+ }
10
+
11
+ //#endregion
12
+ //#region src/compile/utils/Builder.ts
13
+ var Builder = class {
14
+ #text = "";
15
+ #space = 0;
16
+ #mapped = false;
17
+ lineMap = [];
18
+ constructor(mapped) {
19
+ this.#mapped = mapped === true;
20
+ }
21
+ prepend(text) {
22
+ this.#text = text + "\n" + this.#text;
23
+ }
24
+ append(text) {
25
+ if (this.#mapped) {
26
+ this.lineMap.push(this.#text.length);
27
+ for (let i$1 = 0; i$1 < text.length; i$1++) if (text[i$1] === "\n") this.lineMap.push(this.#text.length + i$1);
28
+ this.#text += "\n" + text;
29
+ return;
30
+ }
31
+ const start = this.#text.length;
32
+ text = text.trim();
33
+ let i = 0;
34
+ while (i < text.length) {
35
+ while (text[i] === " " || text[i] === " ") i++;
36
+ if (text[i] === ";") i++;
37
+ else if (text[i] === "\n") {
38
+ this.lineMap.push(start + i);
39
+ this.#text += "\n";
40
+ } else {
41
+ if (text[i] === "}" || text[i] === ")" || text[i] === "]") this.#space -= 1;
42
+ this.#text += " ".repeat(Math.max(0, this.#space));
43
+ const start$1 = i;
44
+ while (i < text.length && text[i] !== "\n") {
45
+ if (text[i] === "`") i = endOfTemplateString(text, i);
46
+ i++;
47
+ }
48
+ const line = text.substring(start$1, i + 1);
49
+ if (line.startsWith("*")) this.#text += " ";
50
+ this.#text += line;
51
+ if (text[i - 1] === "{" || text[i - 1] === "(" || text[i - 1] === "[") this.#space += 1;
52
+ }
53
+ i++;
54
+ }
55
+ if (!this.#text.endsWith("\n\n")) this.#text += "\n";
56
+ }
57
+ toString() {
58
+ return this.#text;
59
+ }
60
+ };
61
+
62
+ //#endregion
63
+ //#region src/compile/build/client/addMappedText.ts
64
+ function addMappedText(preText, text, postText, range, status, b) {
65
+ if (status.options?.mapped) {
66
+ let start = b.toString().length + preText.length;
67
+ b.append(preText + text + postText);
68
+ let end = start + text.length - postText.length;
69
+ status.map.push({
70
+ script: text,
71
+ source: range,
72
+ compiled: {
73
+ start,
74
+ end
75
+ }
76
+ });
77
+ } else b.append(preText + text + postText);
78
+ }
79
+
80
+ //#endregion
81
+ //#region src/compile/types/comments.ts
82
+ const ANCHOR_COMMENT = "<!>";
83
+ const HYDRATION_START_COMMENT = "<![>";
84
+ const HYDRATION_END_COMMENT = "<!]>";
85
+ const HYDRATION_BREAK_COMMENT = "<!^>";
86
+
87
+ //#endregion
88
+ //#region src/compile/utils/isReactive.ts
89
+ /**
90
+ * Checks whether the supplied string is fully or partially reactive (i.e. contains braces)
91
+ *
92
+ * @param text The string to check
93
+ *
94
+ * @returns True if the supplied string is fully or partially reactive
95
+ */
96
+ function isReactive(text) {
97
+ return text.includes("{") && text.includes("}");
98
+ }
99
+
100
+ //#endregion
101
+ //#region src/compile/utils/trimQuotes.ts
102
+ /**
103
+ * Trims matching quotes from the start and end of the supplied text
104
+ *
105
+ * @param text The text to trim
106
+ *
107
+ * @returns The text without the quotes at the start or the end
108
+ */
109
+ function trimQuotes(text) {
110
+ let start = 0;
111
+ let end = text.length;
112
+ while (text[start] === "'" && text[end - 1] === "'" || text[start] === "\"" && text[end - 1] === "\"" || text[start] === "`" && text[end - 1] === "`") {
113
+ start += 1;
114
+ end -= 1;
115
+ }
116
+ return text.substring(start, end);
117
+ }
118
+
119
+ //#endregion
120
+ //#region src/compile/build/client/buildFragmentText.ts
121
+ /**
122
+ * Builds the HTML template text for all of the fragments in a component
123
+ */
124
+ function buildFragmentText(node, status, b) {
125
+ const fragments = [];
126
+ buildNodeFragmentText(node, status, fragments);
127
+ if (fragments.length) b.append(`const t_fragments: DocumentFragment[] = [];`);
128
+ }
129
+ function buildNodeFragmentText(node, status, fragments, currentFragment) {
130
+ switch (node.type) {
131
+ case "root":
132
+ buildRootFragmentText(node, status, fragments);
133
+ break;
134
+ case "control":
135
+ buildControlFragmentText(node, status, fragments, currentFragment);
136
+ break;
137
+ case "component":
138
+ buildComponentFragmentText(node, status, fragments, currentFragment);
139
+ break;
140
+ case "element": {
141
+ let ns = node.tagName === "svg";
142
+ if (ns) status.ns = true;
143
+ buildElementFragmentText(node, status, fragments, currentFragment);
144
+ if (ns) status.ns = false;
145
+ break;
146
+ }
147
+ case "text": {
148
+ const content = node.content;
149
+ if (currentFragment) currentFragment.text += isReactive(content) ? "#" : content;
150
+ break;
151
+ }
152
+ case "special":
153
+ buildSpecialFragmentText(node, status, fragments, currentFragment);
154
+ break;
155
+ }
156
+ }
157
+ function buildRootFragmentText(node, status, fragments) {
158
+ node.fragment = {
159
+ number: fragments.length,
160
+ text: "",
161
+ ns: status.ns,
162
+ effects: [],
163
+ events: [],
164
+ animations: []
165
+ };
166
+ fragments.push(node.fragment);
167
+ for (let child of node.children) buildNodeFragmentText(child, status, fragments, node.fragment);
168
+ }
169
+ function buildControlFragmentText(node, status, fragments, currentFragment) {
170
+ switch (node.operation) {
171
+ case "@if group":
172
+ case "@switch group":
173
+ case "@for group":
174
+ case "@await group":
175
+ case "@replace group":
176
+ case "@html group":
177
+ currentFragment.text += ANCHOR_COMMENT;
178
+ for (let child of node.children) buildNodeFragmentText(child, status, fragments, currentFragment);
179
+ break;
180
+ default:
181
+ node.fragment = {
182
+ number: fragments.length,
183
+ text: "",
184
+ ns: status.ns,
185
+ effects: [],
186
+ events: [],
187
+ animations: []
188
+ };
189
+ fragments.push(node.fragment);
190
+ for (let child of node.children) buildNodeFragmentText(child, status, fragments, node.fragment);
191
+ break;
192
+ }
193
+ }
194
+ function buildComponentFragmentText(node, status, fragments, currentFragment) {
195
+ currentFragment.text += ANCHOR_COMMENT;
196
+ if (node.children.length) {
197
+ node.fragment = {
198
+ number: fragments.length,
199
+ text: "",
200
+ ns: status.ns,
201
+ effects: [],
202
+ events: [],
203
+ animations: []
204
+ };
205
+ fragments.push(node.fragment);
206
+ for (let child of node.children) buildNodeFragmentText(child, status, fragments, node.fragment);
207
+ }
208
+ }
209
+ function buildElementFragmentText(node, status, fragments, currentFragment) {
210
+ let needsClass = node.scopeStyles;
211
+ currentFragment.text += `<${node.tagName}`;
212
+ let attributesText = node.attributes.filter((a) => !a.name.startsWith("on")).map((a) => {
213
+ if (a.value && a.reactive) {
214
+ if (a.name.startsWith("data-")) return `${a.name}=""`;
215
+ } else if (a.name === "class" && node.scopeStyles) {
216
+ needsClass = false;
217
+ let value = a.value ? `"${trimQuotes(a.value)} torp-${status.styleHash}"` : `"torp-${status.styleHash}"`;
218
+ return `${a.name}=${value}`;
219
+ } else return `${a.name}${a.value != null ? `=${a.value}` : ""}`;
220
+ }).filter(Boolean).join(" ");
221
+ if (attributesText) currentFragment.text += " " + attributesText;
222
+ if (needsClass) currentFragment.text += ` class="torp-${status.styleHash}"`;
223
+ currentFragment.text += ">";
224
+ for (let child of node.children) buildNodeFragmentText(child, status, fragments, currentFragment);
225
+ currentFragment.text += `</${node.tagName}>`;
226
+ }
227
+ function buildSpecialFragmentText(node, status, fragments, currentFragment) {
228
+ switch (node.tagName) {
229
+ case "slot":
230
+ currentFragment.text += ANCHOR_COMMENT;
231
+ for (let child of node.children) buildNodeFragmentText(child, status, fragments, node.fragment);
232
+ break;
233
+ case "fill":
234
+ node.fragment = {
235
+ number: fragments.length,
236
+ text: "",
237
+ ns: status.ns,
238
+ effects: [],
239
+ events: [],
240
+ animations: []
241
+ };
242
+ fragments.push(node.fragment);
243
+ for (let child of node.children) buildNodeFragmentText(child, status, fragments, node.fragment);
244
+ break;
245
+ case "@element":
246
+ buildComponentFragmentText(node, status, fragments, currentFragment);
247
+ break;
248
+ case "@component":
249
+ buildComponentFragmentText(node, status, fragments, currentFragment);
250
+ break;
251
+ default: throw new Error(`Invalid special node: ${node.tagName}`);
252
+ }
253
+ }
254
+
255
+ //#endregion
256
+ //#region src/compile/utils/isSpecialNode.ts
257
+ function isSpecialNode(node) {
258
+ return node.type === "special";
259
+ }
260
+
261
+ //#endregion
262
+ //#region src/compile/build/utils/nextVarName.ts
263
+ /**
264
+ * Makes a supplied variable name (hopefully!) unique by prepending `t_` and
265
+ * appending a sequence number
266
+ *
267
+ * @param name The variable name
268
+ * @param status The build status
269
+ *
270
+ * @returns A unique variable name
271
+ */
272
+ function nextVarName(name, status) {
273
+ if (!status.varNames[name]) status.varNames[name] = 1;
274
+ let varName = `t_${name}_${status.varNames[name]}`;
275
+ status.varNames[name] += 1;
276
+ return varName;
277
+ }
278
+
279
+ //#endregion
280
+ //#region src/compile/build/client/buildAddFragment.ts
281
+ function buildAddFragment(node, status, b, parentName, anchorName) {
282
+ if (node.fragment) {
283
+ const fragment = node.fragment;
284
+ const fragmentName = `t_fragment_${fragment.number}`;
285
+ status.imports.add("t_add_fragment");
286
+ if (fragment.effects.length > 0) {
287
+ status.imports.add("$run");
288
+ b.append(`$run(function setAttributes() {`);
289
+ if (status.options?.mapped) for (let effect of fragment.effects) for (let i = 0; i < effect.ranges.length; i++) {
290
+ let start = b.toString().length + effect.offsets[i];
291
+ b.append(effect.functionBody);
292
+ let end = start + effect.lengths[i];
293
+ status.map.push({
294
+ script: effect.functionBody,
295
+ source: effect.ranges[i],
296
+ compiled: {
297
+ start,
298
+ end
299
+ }
300
+ });
301
+ }
302
+ else b.append(fragment.effects.map((e) => e.functionBody).join("\n"));
303
+ b.append("});");
304
+ }
305
+ let params = [
306
+ fragmentName,
307
+ parentName,
308
+ anchorName
309
+ ];
310
+ if (fragment.endVarName) params.push(fragment.endVarName);
311
+ b.append(`t_add_fragment(${params.join(", ")});`);
312
+ if (fragment.endVarName) {
313
+ status.imports.add("t_next");
314
+ b.append(`t_next(${fragment.endVarName});`);
315
+ }
316
+ }
317
+ }
318
+
319
+ //#endregion
320
+ //#region src/compile/parse/utils/isSpaceChar.ts
321
+ /**
322
+ * Checks whether the supplied character is a space
323
+ *
324
+ * @param text The string to check
325
+ * @param index The index of the character in the string
326
+ *
327
+ * @returns True if the supplied character is a space
328
+ */
329
+ function isSpaceChar(text, index) {
330
+ let code = text.charCodeAt(index);
331
+ return code === 32 || code >= 9 && code <= 13;
332
+ }
333
+
334
+ //#endregion
335
+ //#region src/compile/parse/utils/isSpace.ts
336
+ /**
337
+ * Checks whether the supplied string contains only space characters
338
+ *
339
+ * @param text The string to check
340
+ *
341
+ * @returns True if the supplied string contains only space characters
342
+ */
343
+ function isSpace(text) {
344
+ for (let i = 0; i < text.length; i++) if (!isSpaceChar(text, i)) return false;
345
+ return true;
346
+ }
347
+
348
+ //#endregion
349
+ //#region src/compile/utils/isControlNode.ts
350
+ function isControlNode(node) {
351
+ return node.type === "control";
352
+ }
353
+
354
+ //#endregion
355
+ //#region src/compile/utils/isElementNode.ts
356
+ function isElementNode(node) {
357
+ return node.type === "element";
358
+ }
359
+
360
+ //#endregion
361
+ //#region src/compile/utils/isTextNode.ts
362
+ function isTextNode(node) {
363
+ return node.type === "text";
364
+ }
365
+
366
+ //#endregion
367
+ //#region src/compile/build/client/buildFragment.ts
368
+ /**
369
+ * Builds the variables and code in a fragment
370
+ */
371
+ function buildFragment(node, status, b, parentName, anchorName) {
372
+ if (node.fragment) {
373
+ const fragment = node.fragment;
374
+ const fragmentName = `t_fragment_${fragment.number}`;
375
+ if (status.options?.useCreateElement) {
376
+ let fragmentPath = {
377
+ parent: null,
378
+ type: "fragment",
379
+ children: []
380
+ };
381
+ let varPaths = /* @__PURE__ */ new Map();
382
+ declareFragmentVars(node.fragment, node, fragmentPath, status, b, parentName, anchorName, varPaths, false, true);
383
+ fragmentPath.children.length = 0;
384
+ varPaths.clear();
385
+ status.imports.add("t_frg");
386
+ b.append(`const ${fragmentName} = t_frg([`);
387
+ declareFragmentVars(node.fragment, node, fragmentPath, status, b, parentName, anchorName, varPaths, false, false);
388
+ b.append(`]);`);
389
+ } else {
390
+ status.imports.add("t_fragment");
391
+ const fragmentText = fragment.text.replaceAll("`", "\\`").replaceAll(/\s+/g, " ");
392
+ b.append(`const ${fragmentName} = t_fragment($parent.ownerDocument!, t_fragments, ${fragment.number}, \`${fragmentText}\`${fragment.ns ? ", true" : ""});`);
393
+ let fragmentPath = {
394
+ parent: null,
395
+ type: "fragment",
396
+ children: []
397
+ };
398
+ let varPaths = /* @__PURE__ */ new Map();
399
+ maybeAddRootNodeDeclaration(node, fragment, fragmentName, status, b, varPaths);
400
+ declareFragmentVars(node.fragment, node, fragmentPath, status, b, parentName, anchorName, varPaths, false, true);
401
+ }
402
+ }
403
+ }
404
+ function maybeAddRootNodeDeclaration(node, fragment, fragmentName, status, b, varPaths) {
405
+ if (!node.children.length) return;
406
+ let firstNode = node.children[0];
407
+ let firstNodeIsLastNode = node.children.length === 1;
408
+ if (isControlNode(firstNode) && firstNode.operation.endsWith(" group") || isElementNode(firstNode) && elementNodeNeedsDeclaration(firstNode, true, firstNodeIsLastNode) || isTextNode(firstNode) && textNodeNeedsDeclaration(firstNode, true, firstNodeIsLastNode)) {} else {
409
+ status.imports.add("t_root");
410
+ const rootName = `t_root_${fragment.number}`;
411
+ const params = [fragmentName];
412
+ if (firstNode.type === "text") params.push("true");
413
+ const rootPath = `t_root(${params.join(", ")})`;
414
+ b.append(`const ${rootName} = ${rootPath};`);
415
+ varPaths.set(`t_root(${fragmentName})`, rootName);
416
+ printDebug(rootName, status, b);
417
+ }
418
+ }
419
+ function declareFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, lastChild, declare) {
420
+ switch (node.type) {
421
+ case "root":
422
+ declareRootFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, declare);
423
+ break;
424
+ case "control":
425
+ declareControlFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, declare);
426
+ break;
427
+ case "component":
428
+ declareComponentFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, declare);
429
+ break;
430
+ case "element":
431
+ declareElementFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, lastChild, declare);
432
+ break;
433
+ case "text":
434
+ declareTextFragmentVars(fragment, node, path, status, b, varPaths, lastChild, declare);
435
+ break;
436
+ case "special":
437
+ declareSpecialFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, lastChild, declare);
438
+ break;
439
+ default: throw new Error(`Invalid node type: ${node.type}`);
440
+ }
441
+ }
442
+ function declareRootFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, declare) {
443
+ for (let [i, child] of node.children.entries()) declareFragmentVars(fragment, child, path, status, b, parentName, anchorName, varPaths, i === node.children.length - 1, declare);
444
+ }
445
+ function declareControlFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, declare) {
446
+ switch (node.operation) {
447
+ case "@if group":
448
+ case "@switch group":
449
+ case "@for group":
450
+ case "@await group":
451
+ case "@replace group":
452
+ case "@html group": {
453
+ const operation = node.operation.substring(1).replace(" group", "");
454
+ declareParentAndAnchorFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, operation, declare);
455
+ break;
456
+ }
457
+ default:
458
+ for (let [i, child] of node.children.entries()) declareFragmentVars(fragment, child, path, status, b, parentName, anchorName, varPaths, i === node.children.length - 1, declare);
459
+ break;
460
+ }
461
+ }
462
+ function declareComponentFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, declare) {
463
+ declareParentAndAnchorFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, "comp", declare);
464
+ }
465
+ function elementTypeName(node) {
466
+ switch (node.tagName.toLowerCase()) {
467
+ case "div":
468
+ case "input": return `HTML${node.tagName.substring(0, 1).toUpperCase() + node.tagName.substring(1)}Element`;
469
+ case "textarea": return "HTMLTextAreaElement";
470
+ }
471
+ return "HTMLElement";
472
+ }
473
+ function declareElementFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, lastChild, declare) {
474
+ const tagName = node.tagName === "@element" ? "element" : node.tagName;
475
+ let elementPath = {
476
+ parent: path,
477
+ type: tagName,
478
+ children: []
479
+ };
480
+ path.children.push(elementPath);
481
+ const topLevel = !path.parent;
482
+ const declareVariable = elementNodeNeedsDeclaration(node, topLevel, lastChild);
483
+ if (declare) {
484
+ if (declareVariable) {
485
+ node.varName = nextVarName(tagName, status);
486
+ if (topLevel) fragment.endVarName = node.varName;
487
+ const varPath = getFragmentVarPath(fragment, status, node.varName, elementPath, varPaths);
488
+ if (status.options?.useCreateElement) b.append(`let ${node.varName};`);
489
+ else if (node.tagName === "@element") b.append(`let ${node.varName} = ${varPath} as ${elementTypeName(node)};`);
490
+ else {
491
+ b.append(`const ${node.varName} = ${varPath} as ${elementTypeName(node)};`);
492
+ printDebug(node.varName, status, b);
493
+ }
494
+ }
495
+ }
496
+ let childParentName = node.children.find((c) => !!c.parentName)?.parentName;
497
+ if (!declare) {
498
+ let attributes = node.attributes.filter((a) => !a.name.startsWith("on")).map((a) => {
499
+ if (a.value && a.reactive) return `"${a.name}": "#"`;
500
+ else if (a.value) return `"${a.name}": "${trimQuotes(a.value) || a.name}"`;
501
+ else return `"${a.name}": true"`;
502
+ }).join(", ");
503
+ status.imports.add("t_elm");
504
+ if (declareVariable) b.append(`(${childParentName ? `${childParentName} = ` : ""}${node.varName} = t_elm("${node.tagName}", {${attributes}}, [`);
505
+ else if (childParentName) b.append(`(${childParentName} = t_elm("${node.tagName}", {${attributes}}, [`);
506
+ else b.append(`t_elm("${node.tagName}", {${attributes}}, [`);
507
+ }
508
+ for (let [i, child] of node.children.entries()) declareFragmentVars(fragment, child, elementPath, status, b, parentName, anchorName, varPaths, i === node.children.length - 1, declare);
509
+ if (!declare) if (declareVariable || childParentName) b.append("])),");
510
+ else b.append("]),");
511
+ }
512
+ function elementNodeNeedsDeclaration(node, topLevel, lastChild) {
513
+ const hasReactiveAttribute = node.attributes.some((a) => a.value && a.reactive);
514
+ const isDynamicElement = node.tagName === "@element" && node.attributes.find((a) => a.name === "self");
515
+ return hasReactiveAttribute || isDynamicElement || topLevel && lastChild;
516
+ }
517
+ function declareTextFragmentVars(fragment, node, path, status, b, varPaths, lastChild, declare) {
518
+ const lastType = path.children[path.children.length - 1]?.type;
519
+ if (lastType === "text" || lastType === "space") return;
520
+ let textPath = {
521
+ parent: path,
522
+ type: isSpace(node.content) ? "space" : "text",
523
+ children: []
524
+ };
525
+ path.children.push(textPath);
526
+ const topLevel = !path.parent;
527
+ const declareVariable = textNodeNeedsDeclaration(node, topLevel, lastChild);
528
+ if (declare) {
529
+ if (declareVariable) {
530
+ node.varName = nextVarName("text", status);
531
+ if (topLevel) fragment.endVarName = node.varName;
532
+ const varPath = getFragmentVarPath(fragment, status, node.varName, textPath, varPaths);
533
+ if (status.options?.useCreateElement) b.append(`let ${node.varName};`);
534
+ else {
535
+ b.append(`const ${node.varName} = ${varPath};`);
536
+ printDebug(node.varName, status, b);
537
+ }
538
+ }
539
+ } else {
540
+ status.imports.add("t_txt");
541
+ if (declareVariable) b.append(`(${node.varName} = t_txt(" ")),`);
542
+ else b.append(`t_txt("${node.content}"),`);
543
+ }
544
+ }
545
+ function textNodeNeedsDeclaration(node, topLevel, lastChild) {
546
+ return isReactive(node.content) || topLevel && lastChild;
547
+ }
548
+ function declareSpecialFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, lastChild, declare) {
549
+ if (node.tagName === "slot") declareParentAndAnchorFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, "slot", declare);
550
+ switch (node.tagName) {
551
+ case "slot":
552
+ case "fill":
553
+ for (let [i, child] of node.children.entries()) {
554
+ if (node.tagName === "slot" && isSpecialNode(child) && child.tagName === "fill") continue;
555
+ declareFragmentVars(fragment, child, path, status, b, parentName, anchorName, varPaths, i === node.children.length - 1, declare);
556
+ }
557
+ break;
558
+ case "@element":
559
+ declareElementFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, lastChild, declare);
560
+ break;
561
+ case "@component":
562
+ declareComponentFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, declare);
563
+ break;
564
+ default: throw new Error(`Invalid special node: ${node.tagName}`);
565
+ }
566
+ }
567
+ function declareParentAndAnchorFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, name, declare) {
568
+ if (!path.parent) node.parentName = `t_fragment_${fragment.number}`;
569
+ else {
570
+ const parentPath = path;
571
+ const oldChildren = parentPath.children;
572
+ parentPath.children = [];
573
+ const parentVarPath = getFragmentVarPath(fragment, status, "?", parentPath, varPaths);
574
+ parentPath.children = oldChildren;
575
+ if (varPaths.has(parentVarPath)) node.parentName = varPaths.get(parentVarPath);
576
+ else if (declare) {
577
+ node.parentName = nextVarName(`${name}_parent`, status);
578
+ if (status.options?.useCreateElement) b.append(`let ${node.parentName} as HTMLElement;`);
579
+ else {
580
+ b.append(`const ${node.parentName} = ${parentVarPath} as HTMLElement;`);
581
+ printDebug(node.parentName, status, b);
582
+ }
583
+ varPaths.set(parentVarPath, node.parentName);
584
+ }
585
+ }
586
+ if (declare) {
587
+ const anchorPath = {
588
+ parent: path,
589
+ type: "#",
590
+ children: []
591
+ };
592
+ path.children.push(anchorPath);
593
+ node.varName = nextVarName(`${name}_anchor`, status);
594
+ const anchorVarPath = getFragmentVarPath(fragment, status, node.varName, anchorPath, varPaths);
595
+ if (status.options?.useCreateElement) b.append(`let ${node.varName};`);
596
+ else {
597
+ status.imports.add("t_anchor");
598
+ b.append(`let ${node.varName} = t_anchor(${anchorVarPath}) as HTMLElement;`);
599
+ printDebug(node.varName, status, b);
600
+ }
601
+ } else {
602
+ status.imports.add("t_cmt");
603
+ b.append(`(${node.varName} = t_cmt()),`);
604
+ }
605
+ buildNode(node, status, b, parentName, anchorName);
606
+ node.handled = true;
607
+ }
608
+ function getFragmentVarPath(fragment, status, name, path, varPaths) {
609
+ let node = path;
610
+ while (node.parent) node = node.parent;
611
+ let varName = `t_fragment_${fragment.number}`;
612
+ let varPath = getFragmentVarPathPart(node, varName, status, true);
613
+ for (let [existingPath, existingName] of varPaths) if (varPath.includes(existingPath)) varPath = varPath.replace(existingPath, existingName);
614
+ if (name !== "?") {
615
+ varPaths.set(varPath, name);
616
+ varPaths.set(name, name);
617
+ }
618
+ while (varPath.includes("t_next(t_next(")) {
619
+ status.imports.add("t_skip");
620
+ const match = varPath.match(/(t_next\(){2,}/)[0];
621
+ const count = match.length / 7;
622
+ const pos = varPath.indexOf("t_next(t_next(");
623
+ const head = varPath.substring(0, pos);
624
+ let level = 0;
625
+ let end = 0;
626
+ for (let i = pos + match.length; i < varPath.length; i++) if (varPath[i] === "(") level++;
627
+ else if (varPath[i] === ")") if (level === 0) {
628
+ end = i;
629
+ break;
630
+ } else level--;
631
+ const tail = varPath.substring(end + count);
632
+ const param = varPath.substring(pos + match.length, end);
633
+ varPath = `${head}t_skip(${param}, ${count})${tail}`;
634
+ }
635
+ varPath = varPath.replaceAll("t_next_text", "t_next");
636
+ return varPath;
637
+ }
638
+ function getFragmentVarPathPart(path, varPath, status, root = false) {
639
+ if (root) {
640
+ status.imports.add("t_root");
641
+ varPath = `t_root(${varPath})`;
642
+ } else {
643
+ status.imports.add("t_child");
644
+ varPath = `t_child(${varPath})`;
645
+ }
646
+ for (let [i, child] of path.children.entries()) {
647
+ if (i > 0) if (child.type === "text" || child.type === "space") varPath = `t_next_text(${varPath}, true)`;
648
+ else {
649
+ status.imports.add("t_next");
650
+ varPath = `t_next(${varPath})`;
651
+ }
652
+ if (i === path.children.length - 1 && child.children.length) varPath = getFragmentVarPathPart(child, varPath, status);
653
+ }
654
+ return varPath;
655
+ }
656
+ function printDebug(varName, status, b) {}
657
+
658
+ //#endregion
659
+ //#region src/compile/build/client/replaceForVarNames.ts
660
+ /**
661
+ * Variables used within a for loop need to be retrieved from the loop data.
662
+ */
663
+ function replaceForVarNames(value, status) {
664
+ for (let varName of status.forVarNames) value = value.replaceAll(new RegExp(`(^|\\s|\\(|\\[|\\{)${varName}($|\\s|\\.|\\(|\\)|\\[|\\]|\\}|;)`, "g"), `$1t_item.data.${varName}$2`);
665
+ return value;
666
+ }
667
+
668
+ //#endregion
669
+ //#region src/compile/build/client/buildRun.ts
670
+ function buildRun(functionName, functionBody, status, b) {
671
+ functionBody = replaceForVarNames(functionBody, status);
672
+ status.imports.add("$run");
673
+ b.append(`$run(function ${functionName}() {`);
674
+ b.append(functionBody);
675
+ b.append("});");
676
+ }
677
+
678
+ //#endregion
679
+ //#region src/compile/build/client/buildComponentNode.ts
680
+ function buildComponentNode(node, status, b, root = false) {
681
+ b.append("");
682
+ b.append("/* @component */");
683
+ const componentHasProps = node.attributes.length || root;
684
+ const propsName = componentHasProps ? nextVarName("props", status) : "undefined";
685
+ if (componentHasProps) {
686
+ status.imports.add("$watch");
687
+ b.append(`const ${propsName}: any = $watch({});`);
688
+ for (let { name, value, reactive, fullyReactive } of node.attributes) if (name === "self" && node.tagName === "@component") {} else if (name.startsWith("&") && value != null && fullyReactive) {
689
+ name = name.substring(1);
690
+ buildRun("setProp", `${propsName}["${name}"] = ${value};`, status, b);
691
+ buildRun("setBinding", `${value} = ${propsName}["${name}"];`, status, b);
692
+ } else if (name === "class" && value != null) {
693
+ status.imports.add("t_class");
694
+ const params = [value];
695
+ if (node.scopeStyles) params.push(`"torp-${status.styleHash}"`);
696
+ buildRun("setClasses", `${propsName}["class"] = t_class(${params.join(", ")});`, status, b);
697
+ } else if (name === "style" && value != null) {
698
+ status.imports.add("t_style");
699
+ buildRun("setStyles", `${propsName}["style"] = t_style(${value});`, status, b);
700
+ } else if (value != null) {
701
+ const setProp = `${propsName}["${name}"] = ${value};`;
702
+ if (reactive) buildRun("setProp", setProp, status, b);
703
+ else b.append(setProp);
704
+ } else b.append(`${propsName}["${name}"] = true;`);
705
+ if (root) {
706
+ status.imports.add("$run");
707
+ b.append(`
708
+ if ($props) {
709
+ const propNames = [${status.props.map((p) => `'${p}'`).join(", ")}];
710
+ for (let name of Object.keys($props)) {
711
+ if (!name.startsWith("$") && !propNames.includes(name)) {
712
+ $run(() => ${propsName}[name] = $props[name]);
713
+ }
714
+ }
715
+ }`);
716
+ }
717
+ }
718
+ const componentHasSlots = node.children.length;
719
+ const slotsName = componentHasSlots ? nextVarName("slots", status) : "undefined";
720
+ if (componentHasSlots) {
721
+ b.append(`const ${slotsName}: Record<string, SlotRender> = {};`);
722
+ for (let slot of node.children) if (isSpecialNode(slot) && slot.tagName === "fill") {
723
+ const nameAttribute = slot.attributes.find((a) => a.name === "name");
724
+ const slotName = nameAttribute?.value ? trimQuotes(nameAttribute.value) : "_";
725
+ const slotParams = [
726
+ "$sparent: ParentNode",
727
+ "$sanchor: Node | null",
728
+ `${slot.hasSlotProps ? "$sprops" : "_$sprops?"}: Record<PropertyKey, any>`,
729
+ "// @ts-ignore\n$context?: Record<PropertyKey, any>"
730
+ ];
731
+ b.append(`${slotsName}["${slotName}"] = (\n${slotParams.join(",\n")}\n) => {`);
732
+ buildFragment(slot, status, b, "$sparent", "$sanchor");
733
+ status.fragmentStack.push({
734
+ fragment: slot.fragment,
735
+ path: ""
736
+ });
737
+ for (let child of slot.children) buildNode(child, status, b, "$sparent", "$sanchor");
738
+ status.fragmentStack.pop();
739
+ buildAddFragment(slot, status, b, "$sparent", "$sanchor");
740
+ b.append(`}`);
741
+ }
742
+ }
743
+ const compParentName = node.parentName;
744
+ const compAnchorName = node.varName;
745
+ let renderParams = `${compParentName}, ${compAnchorName}, ${propsName}, $context`;
746
+ if (slotsName !== "undefined") renderParams += `, ${slotsName}`;
747
+ let componentName = node.tagName;
748
+ if (componentName === "@component") {
749
+ let selfAttribute = node.attributes.find((a) => a.name === "self");
750
+ if (selfAttribute && selfAttribute.value && selfAttribute.fullyReactive) componentName = selfAttribute.value;
751
+ }
752
+ addMappedText("", `${componentName}(${renderParams})`, ";", node.range, status, b);
753
+ b.append("");
754
+ }
755
+
756
+ //#endregion
757
+ //#region src/compile/utils/trimMatched.ts
758
+ /**
759
+ * Trims chars from the start and end of the supplied text when both the start and end match
760
+ *
761
+ * @param text The text to trim
762
+ *
763
+ * @returns The text without the chars at the start or the end
764
+ */
765
+ function trimMatched(text, startChar, endChar) {
766
+ let start = 0;
767
+ let end = text.length;
768
+ while (text[start] === startChar && text[end - 1] === endChar) {
769
+ start += 1;
770
+ end -= 1;
771
+ }
772
+ return text.substring(start, end);
773
+ }
774
+
775
+ //#endregion
776
+ //#region src/compile/build/client/buildAwaitNode.ts
777
+ function buildAwaitNode(node, status, b) {
778
+ const anchorName = node.varName;
779
+ const parentName = node.parentName;
780
+ const rangeName = nextVarName("await_range", status);
781
+ const stateName = "$" + nextVarName("await_state", status);
782
+ const awaitVarsName = nextVarName("await_vars", status);
783
+ const branches = node.children.filter((n) => isControlNode(n));
784
+ let awaitBranch = branches.find((n) => n.operation === "@await");
785
+ if (!awaitBranch) {}
786
+ let thenBranch = branches.find((n) => n.operation === "@then");
787
+ if (!thenBranch) thenBranch = {
788
+ type: "control",
789
+ operation: "@then",
790
+ statement: "then",
791
+ children: [],
792
+ range: {
793
+ start: 0,
794
+ end: 0
795
+ }
796
+ };
797
+ let catchBranch = branches.find((n) => n.operation === "@catch");
798
+ if (!catchBranch) catchBranch = {
799
+ type: "control",
800
+ operation: "@catch",
801
+ statement: "catch",
802
+ children: [],
803
+ range: {
804
+ start: 0,
805
+ end: 0
806
+ }
807
+ };
808
+ const awaiterName = trimMatched(awaitBranch.statement.substring(5).trim(), "(", ")");
809
+ const thenVar = trimMatched(thenBranch.statement.substring(4).trim(), "(", ")");
810
+ const catchVar = trimMatched(catchBranch.statement.substring(5).trim(), "(", ")");
811
+ status.imports.add("$watch");
812
+ status.imports.add("$run");
813
+ status.imports.add("t_range");
814
+ status.imports.add("t_run_control");
815
+ status.imports.add("t_run_branch");
816
+ b.append("");
817
+ b.append(`
818
+ /* @await */
819
+ const ${rangeName} = t_range();
820
+ let ${stateName} = $watch({ creator: (_: Node | null) => {} });`);
821
+ b.append(`
822
+ let ${awaitVarsName} = { t_token: 0 };
823
+ $run(function runAwait() {`);
824
+ buildAwaitBranch(awaitBranch, status, b, parentName, stateName);
825
+ b.append(`${awaitVarsName}.t_token++;
826
+ ((t_token) => {`);
827
+ awaitBranch.range.start += 7;
828
+ addMappedText("", replaceForVarNames(awaiterName, status), "", awaitBranch.range, status, b);
829
+ thenBranch.range.start -= 1;
830
+ addMappedText(".then((", replaceForVarNames(thenVar, status), ") => {", thenBranch.range, status, b);
831
+ b.append(`if (t_token === ${awaitVarsName}.t_token) {`);
832
+ buildAwaitBranch(thenBranch, status, b, parentName, stateName);
833
+ b.append(`}
834
+ })`);
835
+ catchBranch.range.start -= 1;
836
+ addMappedText(".catch((", replaceForVarNames(catchVar, status), ") => {", catchBranch.range, status, b);
837
+ b.append(`
838
+ if (t_token === ${awaitVarsName}.t_token) {`);
839
+ buildAwaitBranch(catchBranch, status, b, parentName, stateName);
840
+ b.append(`}
841
+ });
842
+ })(${awaitVarsName}.t_token);
843
+ });`);
844
+ b.append(`
845
+ t_run_control(${rangeName}, ${anchorName}, (t_before) => {
846
+ t_run_branch(${rangeName}, () => ${stateName}.creator(t_before));
847
+ });`);
848
+ b.append("");
849
+ }
850
+ function buildAwaitBranch(node, status, b, parentName, stateName) {
851
+ if (node.children.length > 0) {
852
+ b.append(`${stateName}.creator = (t_before) => {`);
853
+ buildFragment(node, status, b, parentName, "t_before");
854
+ status.fragmentStack.push({
855
+ fragment: node.fragment,
856
+ path: ""
857
+ });
858
+ for (let child of node.children) buildNode(child, status, b, parentName, "t_before");
859
+ status.fragmentStack.pop();
860
+ buildAddFragment(node, status, b, parentName, "t_before");
861
+ b.append("};");
862
+ } else b.append(`${stateName}.creator = (_) => {};`);
863
+ }
864
+
865
+ //#endregion
866
+ //#region src/compile/utils/trimEnd.ts
867
+ /**
868
+ * Trims a char from the end of the supplied text
869
+ *
870
+ * @param text The text to trim
871
+ * @param char The char to remove from the end of the text
872
+ *
873
+ * @returns The text without the char on the end
874
+ */
875
+ function trimEnd(text, char) {
876
+ let end = text.length;
877
+ while (text[end - 1] === char) end -= 1;
878
+ return text.substring(0, end);
879
+ }
880
+
881
+ //#endregion
882
+ //#region src/compile/build/client/buildForNode.ts
883
+ const forLoopRegex = /for\s*\((.+?);.*?;.*?\)/;
884
+ const forLoopVarsRegex = /(?:let\s+|var\s+){0,1}([^\s,;+=]+)(?:\s*=\s*[^,;]+){0,1}/g;
885
+ const forOfRegex = /for\s*\(\s*(?:let\s*|var\s*){0,1}(.+?)\s+(?:of|in).*?\)/;
886
+ function buildForNode(node, status, b) {
887
+ const forParentName = node.parentName;
888
+ const forAnchorName = node.varName;
889
+ node = node.children[0];
890
+ const forVarNames = [];
891
+ const forIndexMatch = node.statement.match(forLoopRegex);
892
+ if (forIndexMatch) {
893
+ const forVarMatches = forIndexMatch[1].matchAll(forLoopVarsRegex);
894
+ for (let match of forVarMatches) forVarNames.push(match[1]);
895
+ } else {
896
+ const forOfMatch = node.statement.match(forOfRegex);
897
+ if (forOfMatch) {
898
+ const match = forOfMatch[1];
899
+ if (match.startsWith("{") && match.endsWith("}") || match.startsWith("[") && match.endsWith("]")) forVarNames.push(...trimMatched(trimMatched(match, "{", "}"), "[", "]").split(",").map((m) => m.trim()));
900
+ else forVarNames.push(match);
901
+ }
902
+ }
903
+ const forRangeName = nextVarName("for_range", status);
904
+ const key = node.children.find((n) => n.type === "control" && n.operation === "@key");
905
+ let keyStatement = key ? key.statement : "";
906
+ if (keyStatement) keyStatement = trimEnd(keyStatement.substring(keyStatement.indexOf("=") + 1).trim(), ";");
907
+ status.imports.add("t_range");
908
+ status.imports.add("t_run_list");
909
+ status.imports.add("t_list_item");
910
+ status.imports.add("ListItem");
911
+ b.append("");
912
+ b.append(`
913
+ /* @for */
914
+ let ${forRangeName} = t_range();
915
+ t_run_list(
916
+ ${forRangeName},
917
+ ${forParentName},
918
+ ${forAnchorName},
919
+ function createNewItems() {
920
+ let t_new_items: ListItem[] = [];
921
+ let t_previous_item = ${forRangeName};
922
+ let t_next_item = ${forRangeName}.nextRange;`);
923
+ addMappedText("", `${node.statement}`, " {", node.range, status, b);
924
+ b.append(`
925
+ let t_new_item = t_list_item({ ${forVarNames.join(",\n")} }${keyStatement ? `, ${keyStatement}` : ""});
926
+ t_new_item.previousRange = t_previous_item;
927
+ t_previous_item.nextRange = t_new_item;
928
+ t_previous_item = t_new_item;
929
+ t_new_items.push(t_new_item);
930
+ }
931
+ ${forRangeName}.nextRange = t_next_item;
932
+ return t_new_items;
933
+ },
934
+ function createListItem(t_item, t_before) {`);
935
+ status.forVarNames = forVarNames;
936
+ buildForItem(node, status, b, forParentName);
937
+ status.forVarNames = [];
938
+ b.append(`},
939
+ function updateListItem(t_old_item, t_new_item) {`);
940
+ for (let varName of forVarNames) b.append(`t_old_item.data.${varName} = t_new_item.data.${varName};`);
941
+ b.append(`}
942
+ );`);
943
+ b.append("");
944
+ }
945
+ function buildForItem(node, status, b, parentName) {
946
+ const oldRangeName = nextVarName("old_range", status);
947
+ status.imports.add("t_push_range");
948
+ b.append(`let ${oldRangeName} = t_push_range(t_item);`);
949
+ buildFragment(node, status, b, parentName, "t_before");
950
+ status.fragmentStack.push({
951
+ fragment: node.fragment,
952
+ path: ""
953
+ });
954
+ for (let child of node.children) {
955
+ if (isControlNode(child) && child.operation === "@key") continue;
956
+ buildNode(child, status, b, parentName, "t_before");
957
+ }
958
+ status.fragmentStack.pop();
959
+ buildAddFragment(node, status, b, parentName, "t_before");
960
+ status.imports.add("t_pop_range");
961
+ b.append(`t_pop_range(${oldRangeName});`);
962
+ }
963
+
964
+ //#endregion
965
+ //#region src/compile/build/client/buildHtmlNode.ts
966
+ function buildHtmlNode(node, status, b) {
967
+ const htmlAnchorName = node.varName;
968
+ const htmlParentName = node.parentName || htmlAnchorName + ".parentNode";
969
+ const htmlRangeName = nextVarName("html_range", status);
970
+ node = node.children[0];
971
+ status.imports.add("t_range");
972
+ status.imports.add("t_run_control");
973
+ b.append("");
974
+ b.append(`
975
+ /* @html */
976
+ const ${htmlRangeName} = t_range();
977
+ t_run_control(${htmlRangeName}, ${htmlAnchorName}, (t_before) => {`);
978
+ buildHtmlBranch(node, status, b, htmlParentName, htmlRangeName);
979
+ b.append("});");
980
+ b.append("");
981
+ }
982
+ function buildHtmlBranch(node, status, b, parentName, rangeName) {
983
+ status.imports.add("t_run_branch");
984
+ b.append(`${node.statement};`);
985
+ b.append(`t_run_branch(${rangeName}, () => {`);
986
+ const templateName = nextVarName("template", status);
987
+ const fragmentName = `t_fragment_${node.fragment.number}`;
988
+ b.append(`const ${templateName} = document.createElement("template");`);
989
+ b.append(`${templateName}.innerHTML = ${node.statement};`);
990
+ b.append(`let ${fragmentName} = ${templateName}.content.cloneNode(true) as DocumentFragment;`);
991
+ buildAddFragment(node, status, b, parentName, "t_before");
992
+ b.append(`});`);
993
+ }
994
+
995
+ //#endregion
996
+ //#region src/compile/build/client/buildIfNode.ts
997
+ function buildIfNode(node, status, b) {
998
+ const anchorName = node.varName;
999
+ const parentName = node.parentName || anchorName + ".parentNode";
1000
+ const rangeName = nextVarName("if_range", status);
1001
+ const stateName = "$" + nextVarName("if_state", status);
1002
+ const branches = node.children.filter((n) => isControlNode(n));
1003
+ if (branches.findIndex((n) => n.operation === "@else") === -1) branches.push({
1004
+ type: "control",
1005
+ operation: "@else",
1006
+ statement: "else",
1007
+ children: [],
1008
+ range: {
1009
+ start: 0,
1010
+ end: 0
1011
+ }
1012
+ });
1013
+ status.imports.add("$watch");
1014
+ status.imports.add("$run");
1015
+ status.imports.add("t_range");
1016
+ status.imports.add("t_run_control");
1017
+ status.imports.add("t_run_branch");
1018
+ b.append("");
1019
+ b.append(`
1020
+ /* @if */
1021
+ const ${rangeName} = t_range();
1022
+ let ${stateName} = $watch({ creator: (_: Node | null) => {} });`);
1023
+ b.append(`$run(function runIf() {`);
1024
+ for (let branch of branches) buildIfBranch(branch, status, b, parentName, stateName);
1025
+ b.append(`});`);
1026
+ b.append(`
1027
+ t_run_control(${rangeName}, ${anchorName}, (t_before) => {
1028
+ t_run_branch(${rangeName}, () => ${stateName}.creator(t_before));
1029
+ });`);
1030
+ b.append("");
1031
+ }
1032
+ function buildIfBranch(node, status, b, parentName, stateName) {
1033
+ addMappedText("", `${replaceForVarNames(node.statement, status)}`, " {", node.range, status, b);
1034
+ if (node.children.length > 0) {
1035
+ b.append(`${stateName}.creator = (t_before) => {`);
1036
+ buildFragment(node, status, b, parentName, "t_before");
1037
+ status.fragmentStack.push({
1038
+ fragment: node.fragment,
1039
+ path: ""
1040
+ });
1041
+ for (let child of node.children) buildNode(child, status, b, parentName, "t_before");
1042
+ status.fragmentStack.pop();
1043
+ buildAddFragment(node, status, b, parentName, "t_before");
1044
+ b.append("};");
1045
+ } else b.append(`${stateName}.creator = (_) => {};`);
1046
+ b.append("}");
1047
+ }
1048
+
1049
+ //#endregion
1050
+ //#region src/compile/build/client/buildReplaceNode.ts
1051
+ function buildReplaceNode(node, status, b) {
1052
+ const replaceAnchorName = node.varName;
1053
+ const replaceParentName = node.parentName || replaceAnchorName + ".parentNode";
1054
+ const replaceRangeName = nextVarName("replace_range", status);
1055
+ node = node.children[0];
1056
+ status.imports.add("t_range");
1057
+ status.imports.add("t_run_control");
1058
+ b.append("");
1059
+ b.append(`
1060
+ /* @replace */
1061
+ const ${replaceRangeName} = t_range();
1062
+ t_run_control(${replaceRangeName}, ${replaceAnchorName}, (t_before) => {`);
1063
+ buildReplaceBranch(node, status, b, replaceParentName, replaceRangeName);
1064
+ b.append("});");
1065
+ b.append("");
1066
+ }
1067
+ function buildReplaceBranch(node, status, b, parentName, rangeName) {
1068
+ status.imports.add("t_run_branch");
1069
+ b.append(`${replaceForVarNames(node.statement, status)};`);
1070
+ b.append(`t_run_branch(${rangeName}, () => {`);
1071
+ buildFragment(node, status, b, parentName, "t_before");
1072
+ status.fragmentStack.push({
1073
+ fragment: node.fragment,
1074
+ path: ""
1075
+ });
1076
+ for (let child of node.children) buildNode(child, status, b, parentName, "t_before");
1077
+ status.fragmentStack.pop();
1078
+ buildAddFragment(node, status, b, parentName, "t_before");
1079
+ b.append(`});`);
1080
+ }
1081
+
1082
+ //#endregion
1083
+ //#region src/compile/build/client/buildScriptNode.ts
1084
+ function buildScriptNode(node, status, b) {
1085
+ b.append(`/* ${node.operation} */`);
1086
+ addMappedText("", `${maybeAppend$1(node.statement, ";")}`, "", node.range, status, b);
1087
+ }
1088
+ function maybeAppend$1(text, end) {
1089
+ if (!text.endsWith(end)) text += end;
1090
+ return text;
1091
+ }
1092
+
1093
+ //#endregion
1094
+ //#region src/compile/build/client/buildSwitchNode.ts
1095
+ function buildSwitchNode(node, status, b) {
1096
+ const anchorName = node.varName;
1097
+ const parentName = node.parentName;
1098
+ const rangeName = nextVarName("switch_range", status);
1099
+ const stateName = "$" + nextVarName("switch_state", status);
1100
+ const branches = node.children.filter((n) => isControlNode(n));
1101
+ if (branches.findIndex((n) => n.operation === "@default") === -1) branches.push({
1102
+ type: "control",
1103
+ operation: "@default",
1104
+ statement: "default",
1105
+ children: [],
1106
+ range: {
1107
+ start: 0,
1108
+ end: 0
1109
+ }
1110
+ });
1111
+ status.imports.add("$watch");
1112
+ status.imports.add("$run");
1113
+ status.imports.add("t_range");
1114
+ status.imports.add("t_run_control");
1115
+ status.imports.add("t_run_branch");
1116
+ b.append("");
1117
+ b.append(`
1118
+ /* @switch */
1119
+ const ${rangeName} = t_range();
1120
+ let ${stateName} = $watch({ creator: (_: Node | null) => {} });`);
1121
+ b.append(`
1122
+ $run(function runSwitch() {`);
1123
+ addMappedText("", `${replaceForVarNames(node.statement, status)}`, " {", node.range, status, b);
1124
+ for (let branch of branches) buildSwitchBranch(branch, status, b, parentName, stateName);
1125
+ b.append(`
1126
+ }
1127
+ });`);
1128
+ b.append(`
1129
+ t_run_control(${rangeName}, ${anchorName}, (t_before) => {
1130
+ t_run_branch(${rangeName}, () => ${stateName}.creator(t_before));
1131
+ });`);
1132
+ b.append("");
1133
+ }
1134
+ function buildSwitchBranch(node, status, b, parentName, stateName) {
1135
+ addMappedText("", `${replaceForVarNames(node.statement, status)}`, " {", node.range, status, b);
1136
+ if (node.children.length > 0) {
1137
+ b.append(`${stateName}.creator = (t_before) => {`);
1138
+ buildFragment(node, status, b, parentName, "t_before");
1139
+ status.fragmentStack.push({
1140
+ fragment: node.fragment,
1141
+ path: ""
1142
+ });
1143
+ for (let child of node.children) buildNode(child, status, b, parentName, "t_before");
1144
+ status.fragmentStack.pop();
1145
+ buildAddFragment(node, status, b, parentName, "t_before");
1146
+ b.append("};");
1147
+ } else b.append(`${stateName}.creator = (_) => {};`);
1148
+ b.append(`
1149
+ break;
1150
+ }`);
1151
+ }
1152
+
1153
+ //#endregion
1154
+ //#region src/compile/build/client/buildControlNode.ts
1155
+ function buildControlNode(node, status, b) {
1156
+ switch (node.operation) {
1157
+ case "@if group":
1158
+ buildIfNode(node, status, b);
1159
+ break;
1160
+ case "@if":
1161
+ case "@else if":
1162
+ case "@else": break;
1163
+ case "@switch group":
1164
+ buildSwitchNode(node, status, b);
1165
+ break;
1166
+ case "@case":
1167
+ case "@default": break;
1168
+ case "@for group":
1169
+ buildForNode(node, status, b);
1170
+ break;
1171
+ case "@for":
1172
+ case "@key": break;
1173
+ case "@await group":
1174
+ buildAwaitNode(node, status, b);
1175
+ break;
1176
+ case "@await":
1177
+ case "@then":
1178
+ case "@catch": break;
1179
+ case "@replace group":
1180
+ buildReplaceNode(node, status, b);
1181
+ break;
1182
+ case "@replace": break;
1183
+ case "@html group":
1184
+ buildHtmlNode(node, status, b);
1185
+ break;
1186
+ case "@html": break;
1187
+ case "@const":
1188
+ case "@console":
1189
+ case "@debugger":
1190
+ buildScriptNode(node, status, b);
1191
+ break;
1192
+ case "@function":
1193
+ case "@async function":
1194
+ b.append("");
1195
+ buildScriptNode(node, status, b);
1196
+ b.append("");
1197
+ break;
1198
+ default: throw new Error(`Invalid operation: ${node.operation}`);
1199
+ }
1200
+ }
1201
+
1202
+ //#endregion
1203
+ //#region src/compile/build/client/buildMount.ts
1204
+ function buildMount(functionName, functionBody, status, b) {
1205
+ functionBody = replaceForVarNames(functionBody, status);
1206
+ status.imports.add("$mount");
1207
+ b.append(`$mount(function ${functionName}() {`);
1208
+ b.append(functionBody);
1209
+ b.append("});");
1210
+ }
1211
+
1212
+ //#endregion
1213
+ //#region src/compile/build/client/stashRun.ts
1214
+ function stashRun(fragment, functionStart, value, functionEnd, range, status) {
1215
+ value = replaceForVarNames(value, status);
1216
+ let functionBody = functionStart + value + functionEnd;
1217
+ fragment.effects.push({
1218
+ functionBody,
1219
+ ranges: [range],
1220
+ offsets: [functionStart.length],
1221
+ lengths: [value.length]
1222
+ });
1223
+ }
1224
+
1225
+ //#endregion
1226
+ //#region src/compile/build/client/buildElementNode.ts
1227
+ function buildElementNode(node, status, b, parentName, root = false) {
1228
+ if (status.inHead) {
1229
+ if (node.tagName === "title") buildTitleNode(node, status, b);
1230
+ else buildHeadNode(node, status, b);
1231
+ return;
1232
+ }
1233
+ const svgElement = node.tagName === "svg";
1234
+ const oldns = status.ns;
1235
+ if (svgElement) status.ns = true;
1236
+ const varName = node.varName;
1237
+ if (varName) {
1238
+ if (node.tagName === "@element") buildDynamicElementNode(node, status, b);
1239
+ if (root) {}
1240
+ buildElementAttributes$1(node, varName, status, b);
1241
+ }
1242
+ const oldPreserveWhitespace = status.preserveWhitespace;
1243
+ status.preserveWhitespace = node.tagName === "code" || node.tagName === "pre";
1244
+ for (let child of node.children) buildNode(child, status, b, parentName, "null");
1245
+ status.preserveWhitespace = oldPreserveWhitespace;
1246
+ if (svgElement) status.ns = oldns;
1247
+ }
1248
+ function buildDynamicElementNode(node, status, b) {
1249
+ let selfAttribute = node.attributes.find((a) => a.name === "self");
1250
+ if (selfAttribute && selfAttribute.value && selfAttribute.fullyReactive) {
1251
+ status.imports.add("$run");
1252
+ status.imports.add("t_dynamic");
1253
+ let selfValue = selfAttribute.value;
1254
+ b.append(`$run(function setDynamic() {`);
1255
+ b.append(`${node.varName} = t_dynamic(${node.varName}, ${selfValue});`);
1256
+ let parentName = node.varName;
1257
+ buildFragment(node, status, b, parentName, "null");
1258
+ status.fragmentStack.push({
1259
+ fragment: node.fragment,
1260
+ path: ""
1261
+ });
1262
+ for (let child of node.children) buildNode(child, status, b, parentName, "null");
1263
+ status.fragmentStack.pop();
1264
+ buildAddFragment(node, status, b, parentName, "null");
1265
+ b.append(`});`);
1266
+ }
1267
+ }
1268
+ function buildTitleNode(node, status, b) {
1269
+ let content = node.children[0].content || "";
1270
+ content = content.replace(/\s+/g, " ");
1271
+ let reactiveStarted = false;
1272
+ let reactiveCount = 0;
1273
+ for (let i = 0; i < content.length; i++) if (content[i] === "{") reactiveStarted = true;
1274
+ else if (content[i] === "}") {
1275
+ if (reactiveStarted) {
1276
+ reactiveCount += 1;
1277
+ reactiveStarted = false;
1278
+ }
1279
+ }
1280
+ if (reactiveCount) {
1281
+ status.imports.add("t_fmt");
1282
+ if (reactiveCount === 1 && content.startsWith("{") && content.endsWith("}")) content = `t_fmt(${content.substring(1, content.length - 1)})`;
1283
+ else content = `\`${content.replaceAll("{", "${t_fmt(").replaceAll("}", ")}")}\``;
1284
+ } else content = `"${content}"`;
1285
+ status.imports.add("$run");
1286
+ b.append(`
1287
+ $run(function runTitle() {
1288
+ const t_old_title = document.title;
1289
+ document.title = ${content};
1290
+ return () => document.title = t_old_title;
1291
+ });`);
1292
+ }
1293
+ function buildHeadNode(node, status, b) {
1294
+ status.imports.add("$run");
1295
+ b.append(`
1296
+ $run(function runHead() {
1297
+ const t_headel = document.createElement("${node.tagName}");
1298
+ document.getElementsByTagName("head")[0].appendChild(t_headel);`);
1299
+ for (let { name, value } of node.attributes) if (value != null) {
1300
+ status.imports.add("t_attribute");
1301
+ b.append(`t_attribute(t_headel, "${name}", ${value});`);
1302
+ }
1303
+ b.append(`
1304
+ });`);
1305
+ }
1306
+ function buildElementAttributes$1(node, varName, status, b) {
1307
+ let fragment = status.fragmentStack.at(-1)?.fragment;
1308
+ if (!fragment) throw new Error("No fragment on stack");
1309
+ const refAttribute = node.attributes.find((a) => a.name === "&ref");
1310
+ if (refAttribute) {
1311
+ let { value, fullyReactive } = refAttribute;
1312
+ if (value != null && fullyReactive) b.append(`${value} = ${varName};`);
1313
+ }
1314
+ for (let { name, value, reactive, range } of node.attributes) if (name === "self" && node.tagName === "@element") {} else if (name === "&ref") {} else if (value != null && reactive) if (name === "&group") buildBindGroupAttribute(node, varName, value, status, b);
1315
+ else if (name === "&value" || name === "&checked") buildBindAttribute(node, varName, name, value, status, b);
1316
+ else if (name === "onmount") buildMount("elMount", `return (${trimEnd(value.trim(), ";")})(${varName});`, status, b);
1317
+ else if (name.startsWith("on")) buildEventAttribute(varName, name, value, range, status, b);
1318
+ else if (name.startsWith("transition")) buildTransitionAttribute(node, varName, name, value, status, b);
1319
+ else if (name === "class") {
1320
+ status.imports.add("t_class");
1321
+ const params = [value];
1322
+ if (node.scopeStyles) params.push(`"torp-${status.styleHash}"`);
1323
+ const propName = status.ns ? "className.baseVal" : "className";
1324
+ stashRun(fragment, `${varName}.${propName} = t_class(`, params.join(", "), ");", range, status);
1325
+ } else if (name === "style") {
1326
+ status.imports.add("t_style");
1327
+ stashRun(fragment, `${varName}.setAttribute("style", t_style(`, value, "));", range, status);
1328
+ } else if (name.includes("-")) {
1329
+ status.imports.add("t_attribute");
1330
+ stashRun(fragment, `t_attribute(${varName}, "${name}", `, value, ");", range, status);
1331
+ } else {
1332
+ status.imports.add("t_attribute");
1333
+ stashRun(fragment, `t_attribute(${varName}, "${name}", `, value, ");", range, status);
1334
+ }
1335
+ }
1336
+ function buildBindGroupAttribute(node, varName, value, status, b) {
1337
+ value = replaceForVarNames(value, status);
1338
+ let eventName = "change";
1339
+ let inputValue = node.attributes.find((a) => a.name === "value")?.value;
1340
+ let set = `${value} == ${inputValue}`;
1341
+ let propName = "checked";
1342
+ const setAttribute = `${varName}.${propName} = ${set}`;
1343
+ buildRun("setBinding", `${setAttribute};`, status, b);
1344
+ status.imports.add("t_event");
1345
+ b.append(`t_event(${varName}, "${eventName}", (e) => {
1346
+ if (e.target.${propName}) ${value} = ${inputValue};
1347
+ });`);
1348
+ }
1349
+ function buildBindAttribute(node, varName, name, value, status, b) {
1350
+ value = replaceForVarNames(value, status);
1351
+ let eventName = "input";
1352
+ let defaultValue = "\"\"";
1353
+ let inputValue = "e.target.value";
1354
+ if (node.tagName === "input") {
1355
+ let typeAttribute = node.attributes.find((a) => a.name === "type");
1356
+ if (typeAttribute && typeAttribute.value) switch (trimQuotes(typeAttribute.value)) {
1357
+ case "number":
1358
+ defaultValue = "0";
1359
+ inputValue = "Number(e.target.value)";
1360
+ break;
1361
+ case "checkbox":
1362
+ defaultValue = "false";
1363
+ inputValue = "e.target.checked";
1364
+ break;
1365
+ case "radio":
1366
+ eventName = "change";
1367
+ inputValue = "e.target.value";
1368
+ break;
1369
+ }
1370
+ } else if (node.tagName === "select") eventName = "change";
1371
+ let set = `${value} || ${defaultValue}`;
1372
+ const propName = name.substring(1);
1373
+ const setAttribute = `${varName}.${propName} = ${set}`;
1374
+ buildRun("setBinding", `${setAttribute};`, status, b);
1375
+ status.imports.add("t_event");
1376
+ b.append(`t_event(${varName}, "${eventName}", (e) => ${value} = ${inputValue});`);
1377
+ }
1378
+ function buildEventAttribute(varName, name, value, range, status, b) {
1379
+ value = replaceForVarNames(value, status);
1380
+ const eventName = name.substring(2);
1381
+ status.imports.add("t_event");
1382
+ addMappedText(`t_event(${varName}, "${eventName}", `, value, `);`, range, status, b);
1383
+ }
1384
+ function buildTransitionAttribute(node, varName, name, value, status, b) {
1385
+ value = replaceForVarNames(value, status);
1386
+ status.imports.add("t_animate");
1387
+ let entryVarName = nextVarName("trans_in", status);
1388
+ let exitVarName = nextVarName("trans_out", status);
1389
+ if (name === "transition") {
1390
+ b.append(`const ${entryVarName} = ${getAnimationDetails(value)};`);
1391
+ b.append(`const ${exitVarName} = ${entryVarName};`);
1392
+ b.append(`t_animate(${varName}, ${entryVarName}, ${exitVarName});`);
1393
+ } else if (name === "transition-in") {
1394
+ let outAttribute = node.attributes.find((a) => a.name === "transition-out");
1395
+ if (outAttribute && outAttribute.value && outAttribute.fullyReactive) {
1396
+ let outValue = outAttribute.value;
1397
+ b.append(`const ${entryVarName} = ${getAnimationDetails(value)};`);
1398
+ b.append(`const ${exitVarName} = ${getAnimationDetails(outValue)};`);
1399
+ b.append(`t_animate(${varName}, ${entryVarName}, ${exitVarName});`);
1400
+ } else {
1401
+ b.append(`const ${entryVarName} = ${getAnimationDetails(value)};`);
1402
+ b.append(`t_animate(${varName}, ${entryVarName});`);
1403
+ }
1404
+ } else if (name === "transition-out") if (node.attributes.find((a) => a.name === "transition-in")) return;
1405
+ else {
1406
+ b.append(`const ${exitVarName} = ${getAnimationDetails(value)};`);
1407
+ b.append(`t_animate(${varName}, null, ${entryVarName});`);
1408
+ }
1409
+ }
1410
+ function getAnimationDetails(value) {
1411
+ let parts = [];
1412
+ let start = 0;
1413
+ let squareCount = 0;
1414
+ let curlyCount = 0;
1415
+ for (let i = 0; i < value.length; i++) switch (value[i]) {
1416
+ case ",":
1417
+ if (squareCount === 0 && curlyCount === 0) {
1418
+ parts.push(value.substring(start, i));
1419
+ start = i + 1;
1420
+ }
1421
+ break;
1422
+ case "[":
1423
+ squareCount += 1;
1424
+ break;
1425
+ case "]":
1426
+ squareCount -= 1;
1427
+ break;
1428
+ case "{":
1429
+ curlyCount += 1;
1430
+ break;
1431
+ case "}":
1432
+ curlyCount -= 1;
1433
+ break;
1434
+ }
1435
+ parts.push(value.substring(start));
1436
+ let keyframes = parts[0]?.trim();
1437
+ let options = parts[1]?.trim();
1438
+ return `{ ${[`keyframes: ${keyframes}`, options ? `options: ${options}` : null].filter(Boolean).join(", ")} }`;
1439
+ }
1440
+
1441
+ //#endregion
1442
+ //#region src/compile/build/client/buildRootNode.ts
1443
+ function buildRootNode(node, status, b, parentName, anchorName) {
1444
+ buildFragment(node, status, b, parentName, anchorName);
1445
+ status.fragmentStack.push({
1446
+ fragment: node.fragment,
1447
+ path: "0:ch/"
1448
+ });
1449
+ for (let child of node.children) buildNode(child, status, b, parentName, anchorName, true);
1450
+ status.fragmentStack.pop();
1451
+ buildAddFragment(node, status, b, parentName, anchorName);
1452
+ }
1453
+
1454
+ //#endregion
1455
+ //#region src/compile/build/client/buildSlotNode.ts
1456
+ function buildSlotNode(node, status, b) {
1457
+ let slotName = node.attributes.find((a) => a.name === "name")?.value;
1458
+ slotName = slotName ? trimQuotes(slotName) : "_";
1459
+ const propsName = nextVarName("sprops", status);
1460
+ const slotAttributes = node.attributes.filter((a) => a.name !== "name");
1461
+ const slotHasProps = slotAttributes.length;
1462
+ if (slotHasProps) {
1463
+ status.imports.add("$watch");
1464
+ b.append(`const ${propsName} = $watch({});`);
1465
+ for (let { name, value, reactive } of slotAttributes) if (value != null) {
1466
+ const setProp = `${propsName}["${name}"] = ${value}`;
1467
+ if (reactive) buildRun("setProp", `${setProp};`, status, b);
1468
+ else b.append(`${setProp};`);
1469
+ } else b.append(`${propsName}["${name}"] = true;`);
1470
+ }
1471
+ const slotParentName = node.parentName;
1472
+ const slotAnchorName = node.varName;
1473
+ b.append(`if ($slots && $slots["${slotName}"]) {`);
1474
+ let params = [
1475
+ slotParentName,
1476
+ slotAnchorName,
1477
+ slotHasProps ? propsName : "undefined",
1478
+ "$context"
1479
+ ];
1480
+ b.append(`$slots["${slotName}"](${params.join(", ")})`);
1481
+ const fill = node.children.find((c) => isSpecialNode(c) && c.tagName === "fill");
1482
+ if (fill && isSpecialNode(fill) && fill.children.length) {
1483
+ b.append(`} else {`);
1484
+ buildFragment(fill, status, b, slotParentName, slotAnchorName);
1485
+ status.fragmentStack.push({
1486
+ fragment: fill.fragment,
1487
+ path: "0:ch/"
1488
+ });
1489
+ for (let child of fill.children) buildNode(child, status, b, slotParentName, slotAnchorName);
1490
+ status.fragmentStack.pop();
1491
+ buildAddFragment(fill, status, b, slotParentName, slotAnchorName);
1492
+ }
1493
+ b.append(`}`);
1494
+ }
1495
+
1496
+ //#endregion
1497
+ //#region src/compile/build/client/buildSpecialNode.ts
1498
+ function buildSpecialNode(node, status, b, parentName, root = false) {
1499
+ switch (node.tagName) {
1500
+ case "slot":
1501
+ buildSlotNode(node, status, b);
1502
+ break;
1503
+ case "fill": break;
1504
+ case "@element":
1505
+ buildElementNode(node, status, b, parentName, root);
1506
+ break;
1507
+ case "@component":
1508
+ buildComponentNode(node, status, b, root);
1509
+ break;
1510
+ default: throw new Error(`Invalid special node: ${node.tagName}`);
1511
+ }
1512
+ return "";
1513
+ }
1514
+
1515
+ //#endregion
1516
+ //#region src/compile/utils/endOfString.ts
1517
+ function endOfString(char, content, start) {
1518
+ for (let j = start + 1; j < content.length; j++) if (content[j] === char && content[j - 1] !== "\\") return j;
1519
+ return content.length;
1520
+ }
1521
+
1522
+ //#endregion
1523
+ //#region src/compile/build/client/stashRunWithOffsets.ts
1524
+ function stashRunWithOffsets(fragment, functionBody, ranges, offsets, lengths, status) {
1525
+ for (let i = 0; i < offsets.length; i++) {
1526
+ const oldLength = functionBody.length;
1527
+ functionBody = functionBody.substring(0, offsets[i]) + replaceForVarNames(functionBody.substring(offsets[i], offsets[i] + lengths[i]), status) + functionBody.substring(offsets[i] + lengths[i]);
1528
+ const changedLength = functionBody.length - oldLength;
1529
+ lengths[i] += changedLength;
1530
+ for (let j = i + 1; j < offsets.length; j++) offsets[j] += changedLength;
1531
+ }
1532
+ fragment.effects.push({
1533
+ functionBody,
1534
+ ranges,
1535
+ offsets,
1536
+ lengths
1537
+ });
1538
+ }
1539
+
1540
+ //#endregion
1541
+ //#region src/compile/build/client/buildTextNode.ts
1542
+ function buildTextNode(node, status) {
1543
+ let fragment = status.fragmentStack.at(-1)?.fragment;
1544
+ if (!fragment) return;
1545
+ let content = node.content || "";
1546
+ if (!status.preserveWhitespace) content = content.replaceAll(/\s+/g, " ");
1547
+ let textContent = "";
1548
+ let reactiveCount = 0;
1549
+ let reactiveStart = 0;
1550
+ let offsets = [];
1551
+ let lengths = [];
1552
+ let level = 0;
1553
+ let maxLevel = 0;
1554
+ for (let i = 0; i < content.length; i++) {
1555
+ const char = content[i];
1556
+ const nextChar = content[i + 1];
1557
+ if (char === "{") {
1558
+ level++;
1559
+ maxLevel = Math.max(level, maxLevel);
1560
+ if (level === 1) {
1561
+ reactiveCount += 1;
1562
+ textContent += "${t_fmt(";
1563
+ reactiveStart = textContent.length;
1564
+ offsets.push(reactiveStart);
1565
+ continue;
1566
+ }
1567
+ } else if (char === "}") {
1568
+ level--;
1569
+ if (level === 0) {
1570
+ lengths.push(textContent.length - reactiveStart);
1571
+ textContent += ")";
1572
+ }
1573
+ } else if (char === "`" && level === 0) textContent += "\\";
1574
+ else if (level > 0) {
1575
+ if (char === "/" && nextChar === "/") {
1576
+ let start = i;
1577
+ i = content.indexOf("\n", i);
1578
+ textContent += content.substring(start, i + 1);
1579
+ continue;
1580
+ } else if (char === "/" && nextChar === "*") {
1581
+ let start = i;
1582
+ i = content.indexOf("*/", i) + 1;
1583
+ textContent += content.substring(start, i + 1);
1584
+ continue;
1585
+ } else if (char === "\"" || char === "'") {
1586
+ let start = i;
1587
+ i = endOfString(char, content, i);
1588
+ textContent += content.substring(start, i + 1);
1589
+ continue;
1590
+ } else if (char === "`") {
1591
+ let start = i;
1592
+ i = endOfTemplateString(content, i);
1593
+ textContent += content.substring(start, i + 1);
1594
+ continue;
1595
+ }
1596
+ }
1597
+ textContent += content[i];
1598
+ }
1599
+ if (maxLevel) {
1600
+ let offsetChange = 0;
1601
+ if (reactiveCount === 1 && content.startsWith("{") && content.endsWith("}")) {
1602
+ textContent = textContent.substring(2, textContent.length - 1);
1603
+ offsetChange = -2;
1604
+ } else {
1605
+ textContent = `\`${textContent}\``;
1606
+ offsetChange = 1;
1607
+ }
1608
+ status.imports.add("t_fmt");
1609
+ let functionBody = `${node.varName}.textContent = `;
1610
+ offsetChange += functionBody.length;
1611
+ offsets.map((_, i) => offsets[i] += offsetChange);
1612
+ functionBody += `${textContent};`;
1613
+ stashRunWithOffsets(fragment, functionBody, node.ranges, offsets, lengths, status);
1614
+ }
1615
+ }
1616
+
1617
+ //#endregion
1618
+ //#region src/compile/build/client/buildNode.ts
1619
+ function buildNode(node, status, b, parentName, anchorName, root = false) {
1620
+ if (node.handled) {
1621
+ node.handled = false;
1622
+ return;
1623
+ }
1624
+ switch (node.type) {
1625
+ case "root":
1626
+ buildRootNode(node, status, b, parentName, anchorName);
1627
+ break;
1628
+ case "control":
1629
+ buildControlNode(node, status, b);
1630
+ break;
1631
+ case "component":
1632
+ buildComponentNode(node, status, b, root);
1633
+ break;
1634
+ case "element":
1635
+ buildElementNode(node, status, b, parentName, root);
1636
+ break;
1637
+ case "text":
1638
+ buildTextNode(node, status);
1639
+ break;
1640
+ case "special":
1641
+ buildSpecialNode(node, status, b, parentName, root);
1642
+ break;
1643
+ default: throw new Error(`Invalid node type: ${node.type}`);
1644
+ }
1645
+ }
1646
+
1647
+ //#endregion
1648
+ //#region src/compile/build/client/buildCode.ts
1649
+ const importsMap$1 = {
1650
+ $watch: "import { $watch } from \"${folder}\";",
1651
+ $cache: "import { $cache } from \"${folder}\";",
1652
+ $run: "import { $run } from \"${folder}\";",
1653
+ $mount: "import { $mount } from \"${folder}\";",
1654
+ $unwrap: "import { $unwrap } from \"${folder}\";",
1655
+ $peek: "import { $peek } from \"${folder}\";",
1656
+ $batch: "import { $batch } from \"${folder}\";",
1657
+ t_range: "import { t_range } from \"${folder}\";",
1658
+ t_push_range: "import { t_push_range } from \"${folder}\";",
1659
+ t_pop_range: "import { t_pop_range } from \"${folder}\";",
1660
+ t_run_control: "import { t_run_control } from \"${folder}\";",
1661
+ t_run_branch: "import { t_run_branch } from \"${folder}\";",
1662
+ t_list_item: "import { t_list_item } from \"${folder}\";",
1663
+ t_run_list: "import { t_run_list } from \"${folder}\";",
1664
+ t_add_fragment: "import { t_add_fragment } from \"${folder}\";",
1665
+ t_apply_props: "import { t_apply_props } from \"${folder}\";",
1666
+ t_class: "import { t_class } from \"${folder}\";",
1667
+ t_style: "import { t_style } from \"${folder}\";",
1668
+ t_attribute: "import { t_attribute } from \"${folder}\";",
1669
+ t_dynamic: "import { t_dynamic } from \"${folder}\";",
1670
+ t_fmt: "import { t_fmt } from \"${folder}\";",
1671
+ t_fragment: "import { t_fragment } from \"${folder}\";",
1672
+ t_event: "import { t_event } from \"${folder}\";",
1673
+ t_animate: "import { t_animate } from \"${folder}\";",
1674
+ t_root: "import { t_root } from \"${folder}\";",
1675
+ t_anchor: "import { t_anchor } from \"${folder}\";",
1676
+ t_child: "import { t_child } from \"${folder}\";",
1677
+ t_next: "import { t_next } from \"${folder}\";",
1678
+ t_skip: "import { t_skip } from \"${folder}\";",
1679
+ t_frg: "import { t_frg } from \"${folder}\";",
1680
+ t_elm: "import { t_elm } from \"${folder}\";",
1681
+ t_txt: "import { t_txt } from \"${folder}\";",
1682
+ t_cmt: "import { t_cmt } from \"${folder}\";",
1683
+ t_print: "import { t_print } from \"${folder}\";",
1684
+ ListItem: "import { type ListItem } from \"${folder}\";",
1685
+ SlotRender: "import { type SlotRender } from \"${folder}\";"
1686
+ };
1687
+ function buildCode(template, map, options) {
1688
+ let b = new Builder(options?.mapped);
1689
+ let imports = /* @__PURE__ */ new Set();
1690
+ imports.add("SlotRender");
1691
+ buildTemplate(template, map, imports, b, options);
1692
+ let startSize = b.toString().length;
1693
+ if (imports.size) {
1694
+ b.prepend("");
1695
+ for (let imp of Array.from(imports).sort().reverse()) {
1696
+ imp = importsMap$1[imp] || imp;
1697
+ b.prepend(imp.replace("${folder}", options?.renderFolder || "@torpor/view"));
1698
+ }
1699
+ }
1700
+ if (options?.mapped) {
1701
+ let diff = b.toString().length - startSize + 1;
1702
+ for (let m of map) {
1703
+ m.compiled.start += diff;
1704
+ m.compiled.end += diff;
1705
+ }
1706
+ }
1707
+ return b.toString();
1708
+ }
1709
+ function buildTemplate(template, map, imports, b, options) {
1710
+ let script = template.script.map((s) => s.script).join("\n");
1711
+ if (/\$watch\b/.test(script)) imports.add("$watch");
1712
+ if (/\$cache\b/.test(script)) imports.add("$cache");
1713
+ if (/\$run\b/.test(script)) imports.add("$run");
1714
+ if (/\$mount\b/.test(script)) imports.add("$mount");
1715
+ if (/\$unwrap\b/.test(script)) imports.add("$unwrap");
1716
+ if (/\$peek\b/.test(script)) imports.add("$peek");
1717
+ if (/\$batch\b/.test(script)) imports.add("$batch");
1718
+ let currentIndex = 0;
1719
+ let current = template.components[0];
1720
+ for (let chunk of template.script) if (chunk.script === "/* @params */") {
1721
+ let params = [
1722
+ `${current.markup ? "$parent" : "_$parent"}: ParentNode`,
1723
+ `${current.markup ? "$anchor" : "_$anchor"}: Node | null`,
1724
+ current.params ?? `${current.props?.length ? "$props: Record<PropertyKey, any>" : "_$props: Record<PropertyKey, any> | undefined"}`,
1725
+ `${current.contextProps?.length || current.needsContext ? "$context" : "_$context"}: Record<PropertyKey, any>`,
1726
+ `${current.slotProps?.length ? "$slots" : "_$slots"}?: Record<string, SlotRender>`
1727
+ ];
1728
+ b.append(params.join(",\n"));
1729
+ } else if (chunk.script === ") /* @return_type */ {") b.append("): void {");
1730
+ else if (chunk.script === "/* @start */") {
1731
+ if (current.contextProps?.length) b.append(`$context = Object.assign({}, $context);`);
1732
+ b.append("");
1733
+ } else if (chunk.script === "/* @render */") {
1734
+ if (current.markup) {
1735
+ let status = makeStatus(imports, map, current, options);
1736
+ b.append("");
1737
+ b.append("/* User interface */");
1738
+ buildFragmentText(current.markup, status, b);
1739
+ b.append("");
1740
+ buildNode(current.markup, status, b, "$parent", "$anchor", true);
1741
+ }
1742
+ } else if (chunk.script === "/* @head */") {
1743
+ if (current.head) {
1744
+ let status = makeStatus(imports, map, current, options);
1745
+ b.append("");
1746
+ b.append("/* Head */");
1747
+ status.inHead = true;
1748
+ buildNode(current.head, status, b, "$parent", "$anchor", true);
1749
+ status.inHead = false;
1750
+ }
1751
+ } else if (chunk.script === "/* @style */") {} else if (chunk.script === "/* @end */") {
1752
+ currentIndex += 1;
1753
+ current = template.components[currentIndex];
1754
+ } else {
1755
+ let status = makeStatus(imports, map, current, options);
1756
+ addMappedText("", chunk.script, "", chunk.range, status, b);
1757
+ }
1758
+ }
1759
+ function makeStatus(imports, map, current, options) {
1760
+ return {
1761
+ imports,
1762
+ props: current?.props ?? [],
1763
+ contextProps: current?.contextProps ?? [],
1764
+ slotProps: current?.slotProps ?? [],
1765
+ styleHash: current?.style?.hash ?? "",
1766
+ map,
1767
+ varNames: {},
1768
+ fragmentStack: [],
1769
+ forVarNames: [],
1770
+ ns: false,
1771
+ preserveWhitespace: false,
1772
+ options
1773
+ };
1774
+ }
1775
+
1776
+ //#endregion
1777
+ //#region src/compile/build/client/buildStyles.ts
1778
+ function buildStyles(style, styleHash) {
1779
+ const b = new Builder();
1780
+ for (let block of style.blocks) buildStyleBlock(block, b, styleHash);
1781
+ return b.toString();
1782
+ }
1783
+ function buildStyleBlock(block, b, styleHash) {
1784
+ if (block.selector.startsWith("@")) b.append(`${block.selector} {`);
1785
+ else {
1786
+ block.selector = block.selector.replaceAll(/\s+/g, " ");
1787
+ let selectors = [];
1788
+ let global = false;
1789
+ let start = 0;
1790
+ for (let i = 0; i <= block.selector.length; i++) if (block.selector.substring(i).startsWith(":global(")) {
1791
+ i += 7;
1792
+ start = i + 1;
1793
+ global = true;
1794
+ } else if (global && block.selector[i] === ")") {
1795
+ addSelector(selectors, block.selector, styleHash, start, i, global);
1796
+ start = i + 1;
1797
+ global = false;
1798
+ } else if ("*,>+~".includes(block.selector[i])) {
1799
+ addSelector(selectors, block.selector, styleHash, start, i, global);
1800
+ selectors.push(block.selector[i]);
1801
+ start = i + 1;
1802
+ } else if (i === block.selector.length) addSelector(selectors, block.selector, styleHash, start, i, global);
1803
+ else if (block.selector[i] === " ") {
1804
+ addSelector(selectors, block.selector, styleHash, start, i, global);
1805
+ start = i + 1;
1806
+ }
1807
+ b.append(`${selectors.join(" ").replaceAll(" ,", ",")} {`);
1808
+ }
1809
+ for (let attribute of block.attributes) buildStyleAttribute(attribute, b);
1810
+ for (let child of block.children) buildStyleBlock(child, b, styleHash);
1811
+ b.append(`}`);
1812
+ }
1813
+ function addSelector(selectors, selector, styleHash, start, end, global) {
1814
+ if (end > start) {
1815
+ let s = selector.substring(start, end);
1816
+ if (!global) {
1817
+ let pre = s;
1818
+ let post = "";
1819
+ let colonPosition = s.indexOf(":");
1820
+ if (colonPosition !== -1) {
1821
+ pre = s.substring(0, colonPosition);
1822
+ post = s.substring(colonPosition);
1823
+ }
1824
+ s = `${pre}.torp-${styleHash}${post}`;
1825
+ }
1826
+ selectors.push(s);
1827
+ }
1828
+ }
1829
+ function buildStyleAttribute(attribute, b) {
1830
+ b.append(`${attribute.name}: ${attribute.value};`);
1831
+ }
1832
+
1833
+ //#endregion
1834
+ //#region src/compile/build/server/buildServerComponentNode.ts
1835
+ function buildServerComponentNode(node, status, b) {
1836
+ status.output += HYDRATION_START_COMMENT;
1837
+ if (status.output) {
1838
+ b.append(`t_body += \`${status.output}\`;`);
1839
+ status.output = "";
1840
+ }
1841
+ const componentHasProps = node.attributes.length;
1842
+ const propsName = componentHasProps ? nextVarName("props", status) : "undefined";
1843
+ if (componentHasProps) {
1844
+ b.append(`const ${propsName}: any = {};`);
1845
+ for (let { name, value } of node.attributes) if (name === "class" && value != null) {
1846
+ status.imports.add("t_class");
1847
+ const params = [value];
1848
+ if (node.scopeStyles) params.push(`"torp-${status.styleHash}"`);
1849
+ b.append(`${propsName}["class"] = t_class(${params.join(", ")});`);
1850
+ } else if (name === "style" && value != null) {
1851
+ status.imports.add("t_style");
1852
+ b.append(`${propsName}["style"] = t_style(${value});`);
1853
+ } else if (value != null) b.append(`${propsName}["${name}"] = ${value};`);
1854
+ else b.append(`${propsName}["${name}"] = true;`);
1855
+ }
1856
+ const componentHasSlots = node.children.length;
1857
+ const slotsName = componentHasSlots ? nextVarName("slots", status) : "undefined";
1858
+ if (componentHasSlots) {
1859
+ b.append(`const ${slotsName}: Record<string, ServerSlotRender> = {};`);
1860
+ for (let slot of node.children) if (isSpecialNode(slot) && slot.tagName === "fill") {
1861
+ const nameAttribute = slot.attributes.find((a) => a.name === "name");
1862
+ const slotName = nameAttribute?.value ? trimQuotes(nameAttribute.value) : "_";
1863
+ const slotParams = [`${slot.hasSlotProps ? "$sprops" : "_$sprops?"}: Record<PropertyKey, any>`, "// @ts-ignore\n$context?: Record<PropertyKey, any>"];
1864
+ b.append(`${slotsName}["${slotName}"] = (\n${slotParams.join(",\n")}\n) => {`);
1865
+ b.append(`let t_body = "";`);
1866
+ for (let child of slot.children) buildServerNode(child, status, b);
1867
+ if (status.output) {
1868
+ b.append(`t_body += \`${status.output}\`;`);
1869
+ status.output = "";
1870
+ }
1871
+ b.append("return t_body;");
1872
+ b.append(`}`);
1873
+ }
1874
+ }
1875
+ let componentName = node.tagName;
1876
+ if (componentName === "@component") {
1877
+ let selfAttribute = node.attributes.find((a) => a.name === "self");
1878
+ if (selfAttribute && selfAttribute.value && selfAttribute.fullyReactive) componentName = selfAttribute.value;
1879
+ }
1880
+ let renderParams = `${propsName}, $context`;
1881
+ if (slotsName !== "undefined") renderParams += `, ${slotsName}`;
1882
+ b.append("");
1883
+ const componentResult = nextVarName("comp", status);
1884
+ b.append(`const ${componentResult} = ${componentName}(${renderParams});`);
1885
+ b.append(`t_body += ${componentResult}.body;`);
1886
+ b.append(`t_head += ${componentResult}.head;`);
1887
+ status.output += HYDRATION_END_COMMENT;
1888
+ status.output += ANCHOR_COMMENT;
1889
+ }
1890
+
1891
+ //#endregion
1892
+ //#region src/compile/build/server/buildServerAwaitNode.ts
1893
+ function buildServerAwaitNode(node, status, b) {
1894
+ status.output += HYDRATION_START_COMMENT;
1895
+ if (status.output) {
1896
+ b.append(`t_body += \`${status.output}\`;`);
1897
+ status.output = "";
1898
+ }
1899
+ for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@await") buildServerAwaitBranch(branch, status, b);
1900
+ status.output += HYDRATION_END_COMMENT;
1901
+ status.output += ANCHOR_COMMENT;
1902
+ }
1903
+ function buildServerAwaitBranch(node, status, b) {
1904
+ for (let child of node.children) buildServerNode(child, status, b);
1905
+ if (status.output) {
1906
+ b.append(`t_body += \`${status.output}\`;`);
1907
+ status.output = "";
1908
+ }
1909
+ }
1910
+
1911
+ //#endregion
1912
+ //#region src/compile/build/server/buildServerForNode.ts
1913
+ function buildServerForNode(node, status, b) {
1914
+ status.output += HYDRATION_START_COMMENT;
1915
+ if (status.output) {
1916
+ b.append(`t_body += \`${status.output}\`;`);
1917
+ status.output = "";
1918
+ }
1919
+ for (let branch of node.children) if (isControlNode(branch)) buildServerForBranch(branch, status, b);
1920
+ status.output += HYDRATION_END_COMMENT;
1921
+ status.output += ANCHOR_COMMENT;
1922
+ }
1923
+ function buildServerForBranch(node, status, b) {
1924
+ b.append(`${node.statement} {`);
1925
+ status.output += HYDRATION_BREAK_COMMENT;
1926
+ for (let child of node.children) buildServerNode(child, status, b);
1927
+ if (status.output) {
1928
+ b.append(`t_body += \`${status.output}\`;`);
1929
+ status.output = "";
1930
+ }
1931
+ b.append("}");
1932
+ }
1933
+
1934
+ //#endregion
1935
+ //#region src/compile/build/server/buildServerHtmlNode.ts
1936
+ function buildServerHtmlNode(node, status) {
1937
+ status.output += HYDRATION_START_COMMENT;
1938
+ for (let branch of node.children) if (isControlNode(branch)) status.output += `\${${branch.statement}}`;
1939
+ status.output += HYDRATION_END_COMMENT;
1940
+ status.output += ANCHOR_COMMENT;
1941
+ }
1942
+
1943
+ //#endregion
1944
+ //#region src/compile/build/server/buildServerIfNode.ts
1945
+ function buildServerIfNode(node, status, b) {
1946
+ status.output += HYDRATION_START_COMMENT;
1947
+ if (status.output) {
1948
+ b.append(`t_body += \`${status.output}\`;`);
1949
+ status.output = "";
1950
+ }
1951
+ for (let branch of node.children) if (isControlNode(branch)) buildServerIfBranch(branch, status, b);
1952
+ status.output += HYDRATION_END_COMMENT;
1953
+ status.output += ANCHOR_COMMENT;
1954
+ }
1955
+ function buildServerIfBranch(node, status, b) {
1956
+ b.append(`${node.statement} {`);
1957
+ status.output += HYDRATION_BREAK_COMMENT;
1958
+ for (let child of node.children) buildServerNode(child, status, b);
1959
+ if (status.output) {
1960
+ b.append(`t_body += \`${status.output}\`;`);
1961
+ status.output = "";
1962
+ }
1963
+ b.append("}");
1964
+ }
1965
+
1966
+ //#endregion
1967
+ //#region src/compile/build/server/buildServerReplaceNode.ts
1968
+ function buildServerReplaceNode(node, status, b) {
1969
+ status.output += HYDRATION_START_COMMENT;
1970
+ if (status.output) {
1971
+ b.append(`t_body += \`${status.output}\`;`);
1972
+ status.output = "";
1973
+ }
1974
+ for (let branch of node.children) if (isControlNode(branch)) buildServerReplaceBranch(branch, status, b);
1975
+ status.output += HYDRATION_END_COMMENT;
1976
+ status.output += ANCHOR_COMMENT;
1977
+ }
1978
+ function buildServerReplaceBranch(node, status, b) {
1979
+ b.append(`${node.statement};`);
1980
+ for (let child of node.children) buildServerNode(child, status, b);
1981
+ if (status.output) {
1982
+ b.append(`t_body += \`${status.output}\`;`);
1983
+ status.output = "";
1984
+ }
1985
+ }
1986
+
1987
+ //#endregion
1988
+ //#region src/compile/build/server/buildServerScriptNode.ts
1989
+ function buildServerScriptNode(node, b) {
1990
+ b.append(`${maybeAppend(node.statement, ";")}`);
1991
+ }
1992
+ function maybeAppend(text, end) {
1993
+ if (!text.endsWith(end)) text += end;
1994
+ return text;
1995
+ }
1996
+
1997
+ //#endregion
1998
+ //#region src/compile/build/server/buildServerSwitchNode.ts
1999
+ function buildServerSwitchNode(node, status, b) {
2000
+ status.output += HYDRATION_START_COMMENT;
2001
+ if (status.output) {
2002
+ b.append(`t_body += \`${status.output}\`;`);
2003
+ status.output = "";
2004
+ }
2005
+ b.append(`${node.statement} {`);
2006
+ for (let branch of node.children.filter((c) => isControlNode(c))) if (isControlNode(branch)) buildServerSwitchBranch(branch, status, b);
2007
+ b.append("}");
2008
+ status.output += HYDRATION_END_COMMENT;
2009
+ status.output += ANCHOR_COMMENT;
2010
+ }
2011
+ function buildServerSwitchBranch(node, status, b) {
2012
+ b.append(`${node.statement} {`);
2013
+ status.output += HYDRATION_BREAK_COMMENT;
2014
+ for (let child of node.children) buildServerNode(child, status, b);
2015
+ if (status.output) {
2016
+ b.append(`t_body += \`${status.output}\`;`);
2017
+ status.output = "";
2018
+ }
2019
+ b.append("break;");
2020
+ b.append("}");
2021
+ }
2022
+
2023
+ //#endregion
2024
+ //#region src/compile/build/server/buildServerControlNode.ts
2025
+ function buildServerControlNode(node, status, b) {
2026
+ switch (node.operation) {
2027
+ case "@if group":
2028
+ buildServerIfNode(node, status, b);
2029
+ break;
2030
+ case "@if":
2031
+ case "@else if":
2032
+ case "@else": break;
2033
+ case "@switch group":
2034
+ buildServerSwitchNode(node, status, b);
2035
+ break;
2036
+ case "@case":
2037
+ case "@default": break;
2038
+ case "@for group":
2039
+ buildServerForNode(node, status, b);
2040
+ break;
2041
+ case "@for":
2042
+ case "@key": break;
2043
+ case "@await group":
2044
+ buildServerAwaitNode(node, status, b);
2045
+ break;
2046
+ case "@await":
2047
+ case "@then":
2048
+ case "@catch": break;
2049
+ case "@replace group":
2050
+ buildServerReplaceNode(node, status, b);
2051
+ break;
2052
+ case "@replace": break;
2053
+ case "@html group":
2054
+ buildServerHtmlNode(node, status);
2055
+ break;
2056
+ case "@html": break;
2057
+ case "@const":
2058
+ if (status.output) {
2059
+ b.append(`t_body += \`${status.output}\`;`);
2060
+ status.output = "";
2061
+ }
2062
+ buildServerScriptNode(node, b);
2063
+ break;
2064
+ case "@function":
2065
+ case "@async function":
2066
+ if (status.output) {
2067
+ b.append(`t_body += \`${status.output}\`;`);
2068
+ status.output = "";
2069
+ }
2070
+ b.append("");
2071
+ buildServerScriptNode(node, b);
2072
+ b.append("");
2073
+ break;
2074
+ case "@console":
2075
+ case "@debugger": break;
2076
+ default: throw new Error(`Invalid operation: ${node.operation}`);
2077
+ }
2078
+ }
2079
+
2080
+ //#endregion
2081
+ //#region src/compile/utils/voidTags.ts
2082
+ const voidTags = [
2083
+ "area",
2084
+ "base",
2085
+ "br",
2086
+ "col",
2087
+ "embed",
2088
+ "hr",
2089
+ "img",
2090
+ "input",
2091
+ "link",
2092
+ "meta",
2093
+ "param",
2094
+ "source",
2095
+ "track",
2096
+ "wbr"
2097
+ ];
2098
+ var voidTags_default = voidTags;
2099
+
2100
+ //#endregion
2101
+ //#region src/compile/build/server/buildServerElementNode.ts
2102
+ function buildServerElementNode(node, status, b) {
2103
+ let attributes = buildElementAttributes(node, status);
2104
+ if (attributes.length) attributes = " " + attributes;
2105
+ let tagName = node.tagName;
2106
+ if (tagName === "@element") {
2107
+ let selfAttribute = node.attributes.find((a) => a.name === "self");
2108
+ if (selfAttribute) tagName = `$\{${selfAttribute.value}}`;
2109
+ }
2110
+ status.output += `<${tagName}${attributes}>`;
2111
+ if (!voidTags_default.includes(tagName)) {
2112
+ const oldPreserveWhitespace = status.preserveWhitespace;
2113
+ status.preserveWhitespace = ["pre", "code"].includes(tagName);
2114
+ for (let child of node.children) buildServerNode(child, status, b);
2115
+ status.preserveWhitespace = oldPreserveWhitespace;
2116
+ status.output += `</${tagName}>`;
2117
+ }
2118
+ }
2119
+ function buildElementAttributes(node, status) {
2120
+ let needsClass = node.scopeStyles;
2121
+ let attributes = [];
2122
+ for (let { name, value, reactive, fullyReactive } of node.attributes) if (name === "self" && node.tagName === "@element") {} else if (name === "&ref") {} else if (name.startsWith("on") || name.startsWith(":on")) {} else if (name.startsWith("transition") && value) {
2123
+ value = value.split(",")[0].trim();
2124
+ if (value.startsWith("[")) value = value.substring(1);
2125
+ else value = value + "[0]";
2126
+ attributes.push(`style="\${Object.entries(${value}).map(([k, v]) => \`$\{k}: $\{v}\`).join("; ")}"`);
2127
+ } else if (value != null && fullyReactive) if (name === "&value" || name === "&checked" || name === "&group") {
2128
+ let defaultValue = "";
2129
+ let typeAttribute = node.attributes.find((a) => a.name === "type");
2130
+ if (typeAttribute && typeAttribute.value) switch (trimQuotes(typeAttribute.value)) {
2131
+ case "number":
2132
+ defaultValue = "0";
2133
+ break;
2134
+ case "checkbox":
2135
+ defaultValue = "false";
2136
+ break;
2137
+ }
2138
+ if (!defaultValue) {
2139
+ status.imports.add("t_attr");
2140
+ value = `t_attr(${value})`;
2141
+ defaultValue = "\"\"";
2142
+ }
2143
+ let valueOrDefault = `${value} || ${defaultValue}`;
2144
+ const propName = name.substring(1);
2145
+ attributes.push(`${propName}="\${${valueOrDefault}}"`);
2146
+ } else if (name === "class") {
2147
+ status.imports.add("t_class");
2148
+ let params = [value];
2149
+ if (node.scopeStyles) {
2150
+ params.push(`"torp-${status.styleHash}"`);
2151
+ needsClass = false;
2152
+ }
2153
+ attributes.push(`class="\${t_class(${params.join(", ")})}"`);
2154
+ } else if (name === "style") {
2155
+ status.imports.add("t_style");
2156
+ attributes.push(`style="\${t_style(${value})}"`);
2157
+ } else {
2158
+ status.imports.add("t_attr");
2159
+ attributes.push(`\${${value} ? \`${name}="\${t_attr(${value})}"\` : ''}`);
2160
+ }
2161
+ else if (value != null && reactive) {
2162
+ status.imports.add("t_attr");
2163
+ attributes.push(`${name}="${trimQuotes(value).replaceAll("${", "${t_attr(").replaceAll("}", ")}")}"`);
2164
+ } else if (name === "class" && node.scopeStyles) {
2165
+ value = value ? `"${trimQuotes(value)} torp-${status.styleHash}"` : `"torp-${status.styleHash}"`;
2166
+ attributes.push(`${name}="${trimQuotes(value).replaceAll("\"", "&quot;")}"`);
2167
+ needsClass = false;
2168
+ } else if (value != null) attributes.push(`${name}="${trimQuotes(value).replaceAll("\"", "&quot;")}"`);
2169
+ else attributes.push(`${name}`);
2170
+ if (needsClass) attributes.push(`class="torp-${status.styleHash}"`);
2171
+ return attributes.join(" ");
2172
+ }
2173
+
2174
+ //#endregion
2175
+ //#region src/compile/build/server/buildServerRootNode.ts
2176
+ function buildServerRootNode(node, status, b) {
2177
+ for (let child of node.children) buildServerNode(child, status, b);
2178
+ }
2179
+
2180
+ //#endregion
2181
+ //#region src/compile/build/server/buildServerSlotNode.ts
2182
+ function buildServerSlotNode(node, status, b) {
2183
+ status.output += HYDRATION_START_COMMENT;
2184
+ if (status.output) {
2185
+ b.append(`t_body += \`${status.output}\`;`);
2186
+ status.output = "";
2187
+ }
2188
+ let slotName = node.attributes.find((a) => a.name === "name")?.value;
2189
+ slotName = slotName ? trimQuotes(slotName) : "_";
2190
+ const propsName = nextVarName("sprops", status);
2191
+ const slotAttributes = node.attributes.filter((a) => a.name !== "name");
2192
+ const slotHasProps = slotAttributes.length;
2193
+ if (slotHasProps) {
2194
+ b.append(`const ${propsName}: any = {};`);
2195
+ for (let { name, value } of slotAttributes) if (value != null) b.append(`${propsName}["${name}"] = ${value};`);
2196
+ else b.append(`${propsName}["${name}"] = true;`);
2197
+ }
2198
+ b.append(`if ($slots && $slots["${slotName}"]) {`);
2199
+ b.append(`t_body += $slots["${slotName}"](${slotHasProps ? propsName : "undefined"}, $context);`);
2200
+ const fill = node.children.find((c) => isSpecialNode(c) && c.tagName === "fill");
2201
+ if (fill && isSpecialNode(fill) && fill.children.length) {
2202
+ b.append(`} else {`);
2203
+ for (let child of fill.children) buildServerNode(child, status, b);
2204
+ if (status.output) {
2205
+ b.append(`t_body += \`${status.output}\`;`);
2206
+ status.output = "";
2207
+ }
2208
+ }
2209
+ b.append(`}`);
2210
+ status.output += HYDRATION_END_COMMENT;
2211
+ status.output += ANCHOR_COMMENT;
2212
+ }
2213
+
2214
+ //#endregion
2215
+ //#region src/compile/build/server/buildServerSpecialNode.ts
2216
+ function buildServerSpecialNode(node, status, b) {
2217
+ switch (node.tagName) {
2218
+ case "slot":
2219
+ buildServerSlotNode(node, status, b);
2220
+ break;
2221
+ case "fill": break;
2222
+ case "@element":
2223
+ buildServerElementNode(node, status, b);
2224
+ break;
2225
+ case "@component":
2226
+ buildServerComponentNode(node, status, b);
2227
+ break;
2228
+ default: throw new Error(`Invalid special node: ${node.tagName}`);
2229
+ }
2230
+ }
2231
+
2232
+ //#endregion
2233
+ //#region src/compile/build/server/buildServerTextNode.ts
2234
+ function buildServerTextNode(node, status) {
2235
+ let content = node.content || "";
2236
+ if (!status.preserveWhitespace) content = content.replaceAll(/\s+/g, " ");
2237
+ let level = 0;
2238
+ for (let i = 0; i < content.length; i++) {
2239
+ const char = content[i];
2240
+ const nextChar = content[i + 1];
2241
+ if (char === "{") {
2242
+ level++;
2243
+ if (level === 1) {
2244
+ status.imports.add("t_fmt");
2245
+ status.output += "${t_fmt(";
2246
+ continue;
2247
+ }
2248
+ } else if (char === "}") {
2249
+ level--;
2250
+ if (level === 0) status.output += ")";
2251
+ } else if (char === "`" && level === 0) status.output += "\\";
2252
+ else if (level > 0) {
2253
+ if (char === "/" && nextChar === "/") {
2254
+ let start = i;
2255
+ i = content.indexOf("\n", i);
2256
+ status.output += escapeHtml(content.substring(start, i + 1));
2257
+ continue;
2258
+ } else if (char === "/" && nextChar === "*") {
2259
+ let start = i;
2260
+ i = content.indexOf("*/", i) + 1;
2261
+ status.output += escapeHtml(content.substring(start, i + 1));
2262
+ continue;
2263
+ } else if (char === "\"" || char === "'") {
2264
+ let start = i;
2265
+ i = endOfString(char, content, i);
2266
+ status.output += escapeHtml(content.substring(start, i + 1));
2267
+ continue;
2268
+ } else if (char === "`") {
2269
+ let start = i;
2270
+ i = endOfTemplateString(content, i);
2271
+ status.output += escapeHtml(content.substring(start, i + 1));
2272
+ continue;
2273
+ }
2274
+ }
2275
+ status.output += char;
2276
+ }
2277
+ }
2278
+ function escapeHtml(text) {
2279
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
2280
+ }
2281
+
2282
+ //#endregion
2283
+ //#region src/compile/build/server/buildServerNode.ts
2284
+ function buildServerNode(node, status, b) {
2285
+ switch (node.type) {
2286
+ case "root":
2287
+ buildServerRootNode(node, status, b);
2288
+ break;
2289
+ case "control":
2290
+ buildServerControlNode(node, status, b);
2291
+ break;
2292
+ case "component":
2293
+ buildServerComponentNode(node, status, b);
2294
+ break;
2295
+ case "element":
2296
+ buildServerElementNode(node, status, b);
2297
+ break;
2298
+ case "text":
2299
+ buildServerTextNode(node, status);
2300
+ break;
2301
+ case "special":
2302
+ buildServerSpecialNode(node, status, b);
2303
+ break;
2304
+ default: throw new Error(`Invalid node type: ${node.type}`);
2305
+ }
2306
+ }
2307
+
2308
+ //#endregion
2309
+ //#region src/compile/build/server/buildServerCode.ts
2310
+ const importsMap = {
2311
+ $watch: "import { $watch } from \"${folder}\";",
2312
+ $cache: "import { $cache } from \"${folder}\";",
2313
+ $run: "import { $run } from \"${folder}\";",
2314
+ $mount: "import { $mount } from \"${folder}\";",
2315
+ $unwrap: "import { $unwrap } from \"${folder}\";",
2316
+ $peek: "import { $peek } from \"${folder}\";",
2317
+ $batch: "import { $batch } from \"${folder}\";",
2318
+ t_fmt: "import { t_fmt } from \"${folder}\";",
2319
+ t_attr: "import { t_attr } from \"${folder}\";",
2320
+ t_class: "import { t_class } from \"${folder}\";",
2321
+ t_style: "import { t_style } from \"${folder}\";",
2322
+ ServerSlotRender: "import { type ServerSlotRender } from \"${folder}\";"
2323
+ };
2324
+ function buildServerCode(template, options) {
2325
+ let b = new Builder(options?.mapped);
2326
+ let imports = /* @__PURE__ */ new Set();
2327
+ imports.add("ServerSlotRender");
2328
+ buildServerTemplate(template, imports, b, options);
2329
+ if (imports.size) {
2330
+ b.prepend("");
2331
+ for (let imp of Array.from(imports).sort().reverse()) {
2332
+ imp = importsMap[imp] || imp;
2333
+ b.prepend(imp.replace("${folder}", options?.renderFolder || "@torpor/view/ssr"));
2334
+ }
2335
+ }
2336
+ return b.toString();
2337
+ }
2338
+ function buildServerTemplate(template, imports, b, options) {
2339
+ let script = template.script.map((s) => s.script).join("\n");
2340
+ if (/\$watch\b/.test(script)) imports.add("$watch");
2341
+ if (/\$cache\b/.test(script)) imports.add("$cache");
2342
+ if (/\$run\b/.test(script)) imports.add("$run");
2343
+ if (/\$mount\b/.test(script)) imports.add("$mount");
2344
+ if (/\$unwrap\b/.test(script)) imports.add("$unwrap");
2345
+ if (/\$peek\b/.test(script)) imports.add("$peek");
2346
+ if (/\$batch\b/.test(script)) imports.add("$batch");
2347
+ let currentIndex = 0;
2348
+ let current = template.components[0];
2349
+ for (let chunk of template.script) if (chunk.script === "/* @params */") {
2350
+ let params = [
2351
+ current.params ?? `${current.props?.length ? "$props: Record<PropertyKey, any>" : "_$props: Record<PropertyKey, any> | undefined"}`,
2352
+ `${current.contextProps?.length || current.needsContext ? "$context" : "_$context"}: Record<PropertyKey, any>`,
2353
+ `${current.slotProps?.length ? "$slots" : "_$slots"}?: Record<string, ServerSlotRender>`
2354
+ ];
2355
+ b.append(params.join(",\n"));
2356
+ } else if (chunk.script === ") /* @return_type */ {") b.append("): { body: string, head: string } {");
2357
+ else if (chunk.script === "/* @start */") {
2358
+ if (current.contextProps?.length) b.append(`$context = Object.assign({}, $context);`);
2359
+ b.append(`let t_body = "";`);
2360
+ b.append(`let t_head = "";`);
2361
+ } else if (chunk.script === "/* @render */") {
2362
+ if (current.markup) {
2363
+ const status = {
2364
+ imports,
2365
+ output: "",
2366
+ styleHash: current.style?.hash || "",
2367
+ varNames: {},
2368
+ preserveWhitespace: false,
2369
+ options
2370
+ };
2371
+ b.append("");
2372
+ b.append("/* User interface */");
2373
+ buildServerNode(current.markup, status, b);
2374
+ if (status.output) {
2375
+ b.append(`t_body += \`${status.output}\`;`);
2376
+ status.output = "";
2377
+ }
2378
+ }
2379
+ } else if (chunk.script === "/* @head */") {} else if (chunk.script === "/* @style */") {
2380
+ if (current.style) {
2381
+ b.append("");
2382
+ b.append("/* Style */");
2383
+ const styles = buildStyles(current.style, current.style.hash).replaceAll("\"", "\\\"").replaceAll(/\s+/g, " ");
2384
+ b.append(`t_head += "<style id='${current.style.hash}'>${styles}</style>";\n`);
2385
+ }
2386
+ } else if (chunk.script === "/* @end */") {
2387
+ b.append(`return { body: t_body, head: t_head };`);
2388
+ currentIndex += 1;
2389
+ current = template.components[currentIndex];
2390
+ } else b.append(chunk.script);
2391
+ }
2392
+
2393
+ //#endregion
2394
+ //#region src/compile/build.ts
2395
+ /**
2396
+ * Builds a component template into code and styles for rendering
2397
+ *
2398
+ * @param template The component template, possibly including script, markup and styles
2399
+ *
2400
+ * @returns The component's code and styles
2401
+ */
2402
+ function build(template, options) {
2403
+ let map = [];
2404
+ let code = options?.server ? buildServerCode(template, options) : buildCode(template, map, options);
2405
+ let styles = template.components.map((c) => c.style ? {
2406
+ style: buildStyles(c.style, c.style.hash),
2407
+ hash: c.style.hash
2408
+ } : void 0).filter((c) => c !== void 0);
2409
+ return {
2410
+ code,
2411
+ styles,
2412
+ map
2413
+ };
2414
+ }
2415
+
2416
+ //#endregion
2417
+ //#region src/compile/buildType.ts
2418
+ /**
2419
+ * Builds a component template into a d.ts file
2420
+ *
2421
+ * @param name The name of the component
2422
+ * @param template The component template, possibly including script, markup and styles
2423
+ *
2424
+ * @returns The d.ts file content
2425
+ */
2426
+ function buildType(template, options) {
2427
+ let b = new Builder();
2428
+ b.append(`import { type SlotRender } from "${options?.renderFolder || "@torpor/view"}";`);
2429
+ for (let component of template.components) {
2430
+ b.append("");
2431
+ b.append(`
2432
+ /**
2433
+ * Mounts or hydrates the component into the supplied parent node.
2434
+ * @param $parent -- The parent node.
2435
+ * @param $anchor -- The node to mount the component before.
2436
+ * @param $props -- The values that have been passed into the component as properties.
2437
+ * @param $context -- Values that have been passed into the component from its ancestors.
2438
+ * @param $slots -- Functions for rendering children into slot nodes within the component.
2439
+ */
2440
+ ${component.default ? "declare" : "export declare"} function ${component.name}(
2441
+ $parent: ParentNode,
2442
+ $anchor: Node | null,
2443
+ ${component.params || "$props?: Record<PropertyKey, any>"},
2444
+ $context?: Record<PropertyKey, any>,
2445
+ $slots?: Record<string, SlotRender>,
2446
+ ): void;
2447
+ `);
2448
+ if (component.default) b.append(`export default ${component.name};`);
2449
+ }
2450
+ return b.toString();
2451
+ }
2452
+
2453
+ //#endregion
2454
+ //#region src/compile/utils/isSpaceNode.ts
2455
+ function isSpaceNode(node) {
2456
+ return isTextNode(node) && isSpace(node.content);
2457
+ }
2458
+
2459
+ //#endregion
2460
+ //#region src/compile/utils/trimStart.ts
2461
+ /**
2462
+ * Trims a char from the start of the supplied text
2463
+ *
2464
+ * @param text The text to trim
2465
+ * @param char The char to remove from the start of the text
2466
+ *
2467
+ * @returns The text without the char on the start
2468
+ */
2469
+ function trimStart(text, char) {
2470
+ let start = 0;
2471
+ while (text[start] === char) start += 1;
2472
+ return text.substring(start);
2473
+ }
2474
+
2475
+ //#endregion
2476
+ //#region src/compile/parse/utils/consumeSpace.ts
2477
+ /**
2478
+ * Consumes space characters and advances the current parse position to before
2479
+ * the next non-space character
2480
+ *
2481
+ * @param status The parse status
2482
+ *
2483
+ * @returns The string of space characters from the current parse position to
2484
+ * the next non-space character
2485
+ */
2486
+ function consumeSpace(status) {
2487
+ const start = status.i;
2488
+ for (status.i; status.i < status.source.length; status.i++) if (!isSpaceChar(status.source, status.i)) return status.source.substring(start, status.i);
2489
+ return "";
2490
+ }
2491
+
2492
+ //#endregion
2493
+ //#region src/compile/parse/addSpaceElement.ts
2494
+ function addSpaceElement(parent, status) {
2495
+ const content = consumeSpace(status);
2496
+ if (content) {
2497
+ const previousNode = parent.children[parent.children.length - 1];
2498
+ if (previousNode && isTextNode(previousNode)) previousNode.content += content;
2499
+ else {
2500
+ const space = {
2501
+ type: "text",
2502
+ content,
2503
+ ranges: []
2504
+ };
2505
+ parent.children.push(space);
2506
+ }
2507
+ }
2508
+ }
2509
+
2510
+ //#endregion
2511
+ //#region src/compile/utils/isFullyReactive.ts
2512
+ /**
2513
+ * Checks whether the supplied string is fully reactive (i.e. starts and ends with braces)
2514
+ *
2515
+ * @param text The string to check
2516
+ *
2517
+ * @returns True if the supplied string is fully reactive
2518
+ */
2519
+ function isFullyReactive(text) {
2520
+ return text.trim().startsWith("{") && text.trim().endsWith("}");
2521
+ }
2522
+
2523
+ //#endregion
2524
+ //#region src/compile/parse/utils/accept.ts
2525
+ /**
2526
+ * Checks whether the source at the current parse position matches the supplied
2527
+ * string
2528
+ *
2529
+ * @param value The string to check
2530
+ * @param status The parse status
2531
+ * @param advance (Optional) If true, advances the parse position past the
2532
+ * checked string (defaults to true)
2533
+ *
2534
+ * @returns True if the supplied string was found at the current parse position
2535
+ */
2536
+ function accept(value, status, advance = true) {
2537
+ if (status.source.substring(status.i, status.i + value.length) == value) {
2538
+ status.i += advance ? value.length : 0;
2539
+ return true;
2540
+ }
2541
+ return false;
2542
+ }
2543
+
2544
+ //#endregion
2545
+ //#region src/compile/parse/parseInlineScript.ts
2546
+ function parseInlineScript(status) {
2547
+ const start = status.i;
2548
+ let braceCount = 0;
2549
+ while (status.i < status.source.length) if (accept("{", status)) braceCount += 1;
2550
+ else if (accept("}", status)) if (braceCount > 0) braceCount -= 1;
2551
+ else return status.source.substring(start, status.i - 1);
2552
+ else if (accept("//", status)) status.i = status.source.indexOf("\n", status.i) + 1;
2553
+ else if (accept("/*", status)) status.i = status.source.indexOf("*/", status.i) + 2;
2554
+ else if (accept("\"", status) || accept("'", status)) {
2555
+ const char = status.source[status.i - 1];
2556
+ status.i = endOfString(char, status.source, status.i - 1) + 1;
2557
+ } else if (accept("`", status)) status.i = endOfTemplateString(status.source, status.i - 1) + 1;
2558
+ else status.i += 1;
2559
+ return "";
2560
+ }
2561
+
2562
+ //#endregion
2563
+ //#region src/compile/parse/utils/isAlphaNumericChar.ts
2564
+ /**
2565
+ * Checks whether the supplied character is a alpha-numeric
2566
+ *
2567
+ * @param text The string to check
2568
+ * @param index The index of the character in the string
2569
+ *
2570
+ * @returns True if the supplied character is alpha-numeric
2571
+ */
2572
+ function isAlphaNumericChar(text, index) {
2573
+ let code = text.charCodeAt(index);
2574
+ return code > 47 && code < 58 || code > 64 && code < 91 || code > 96 && code < 123;
2575
+ }
2576
+
2577
+ //#endregion
2578
+ //#region src/compile/parse/utils/consumeAlphaNumeric.ts
2579
+ /**
2580
+ * Consumes alpha-numeric characters and advances the current parse position to
2581
+ * before the next non-alpha character
2582
+ *
2583
+ * @param status The parse status
2584
+ *
2585
+ * @returns The string of alpha-numeric characters from the current parse position
2586
+ * to the next non-alpha character
2587
+ */
2588
+ function consumeAlphaNumeric(status) {
2589
+ const start = status.i;
2590
+ for (status.i; status.i < status.source.length; status.i++) if (!isAlphaNumericChar(status.source, status.i)) return status.source.substring(start, status.i);
2591
+ return "";
2592
+ }
2593
+
2594
+ //#endregion
2595
+ //#region src/compile/parse/utils/consumeUntil.ts
2596
+ /**
2597
+ * Consumes characters until one of the characters in the supplied string is
2598
+ * found. The parse position will be advanced to before the found character
2599
+ *
2600
+ * @param chars The characters to match
2601
+ * @param status The parse status
2602
+ *
2603
+ * @returns The string of characters from the current parse position to the
2604
+ * found character
2605
+ */
2606
+ function consumeUntil(chars, status) {
2607
+ const start = status.i;
2608
+ for (status.i; status.i < status.source.length; status.i++) if (chars.includes(status.source[status.i])) return status.source.substring(start, status.i);
2609
+ return "";
2610
+ }
2611
+
2612
+ //#endregion
2613
+ //#region src/compile/parse/parseTag.ts
2614
+ function parseTag(status) {
2615
+ accept("<", status);
2616
+ let closeTag = accept("/", status);
2617
+ consumeSpace(status);
2618
+ let tagStart = status.i;
2619
+ let snailed = accept("@", status);
2620
+ let tagName = consumeAlphaNumeric(status);
2621
+ let tagEnd = status.i;
2622
+ if (snailed) tagName = "@" + tagName;
2623
+ if (closeTag) tagName = "/" + tagName;
2624
+ consumeSpace(status);
2625
+ let attributes = parseTagAttributes(status);
2626
+ let selfClosed = false;
2627
+ if (accept("/>", status)) selfClosed = true;
2628
+ else accept(">", status);
2629
+ if (closeTag) selfClosed = true;
2630
+ return {
2631
+ type: [
2632
+ "slot",
2633
+ "fill",
2634
+ "@component",
2635
+ "@element"
2636
+ ].includes(tagName) ? "special" : "element",
2637
+ tagName,
2638
+ selfClosed: selfClosed || void 0,
2639
+ closed: selfClosed || void 0,
2640
+ attributes,
2641
+ children: [],
2642
+ range: {
2643
+ start: tagStart,
2644
+ end: tagEnd
2645
+ }
2646
+ };
2647
+ }
2648
+ function parseTagAttributes(status) {
2649
+ let attributes = [];
2650
+ while (status.i < status.source.length) {
2651
+ consumeSpace(status);
2652
+ if (accept("@//", status)) status.i = status.source.indexOf("\n", status.i) + 1;
2653
+ else if (accept("@/*", status)) status.i = status.source.indexOf("*/", status.i) + 2;
2654
+ else if (accept("/>", status, false) || accept(">", status, false)) break;
2655
+ else {
2656
+ const attribute = parseAttribute(status);
2657
+ attributes.push(attribute);
2658
+ }
2659
+ }
2660
+ return attributes;
2661
+ }
2662
+ function parseAttribute(status) {
2663
+ let valueStart = status.i;
2664
+ let valueEnd = status.i;
2665
+ let name = consumeUntil("= \r\n/>", status);
2666
+ let value = void 0;
2667
+ let reactive = false;
2668
+ let fullyReactive = false;
2669
+ consumeSpace(status);
2670
+ if (accept("=", status)) {
2671
+ consumeSpace(status);
2672
+ valueStart = status.i;
2673
+ value = parseAttributeValue(status);
2674
+ valueEnd = status.i;
2675
+ reactive = isReactive(value);
2676
+ fullyReactive = isFullyReactive(value);
2677
+ if (fullyReactive) {
2678
+ value = value.substring(1, value.length - 1);
2679
+ valueStart++;
2680
+ valueEnd--;
2681
+ } else if (reactive) value = `\`${trimQuotes(value).replaceAll("{", "${")}\``;
2682
+ } else if (name.startsWith("{") && name.endsWith("}")) {
2683
+ value = name.substring(1, name.length - 1);
2684
+ valueStart++;
2685
+ valueEnd = valueStart + name.length;
2686
+ name = value.split(".").at(-1);
2687
+ reactive = fullyReactive = true;
2688
+ }
2689
+ return {
2690
+ name,
2691
+ value,
2692
+ reactive,
2693
+ fullyReactive,
2694
+ range: {
2695
+ start: valueStart,
2696
+ end: valueEnd
2697
+ }
2698
+ };
2699
+ }
2700
+ function parseAttributeValue(status) {
2701
+ if (accept("\"", status)) {
2702
+ const value = consumeUntil("\"", status);
2703
+ accept("\"", status);
2704
+ return `"${value}"`;
2705
+ } else if (accept("'", status)) {
2706
+ const value = consumeUntil("'", status);
2707
+ accept("'", status);
2708
+ return `'${value}'`;
2709
+ } else if (accept("{", status)) return `{${parseInlineScript(status)}}`;
2710
+ else {
2711
+ let value = consumeUntil(" \r\n>", status);
2712
+ if (status.source.substring(status.i - 1, status.i + 1) === "/>") {
2713
+ value = value.substring(0, value.length - 1);
2714
+ status.i -= 1;
2715
+ }
2716
+ return value;
2717
+ }
2718
+ }
2719
+
2720
+ //#endregion
2721
+ //#region src/compile/parse/parseText.ts
2722
+ function parseText(status, element) {
2723
+ let ranges = [];
2724
+ const start = status.i;
2725
+ let end = -1;
2726
+ for (let j = status.i; j < status.source.length; j++) {
2727
+ if (status.source[j] === "}") end = j;
2728
+ else if (status.source[j] === "{") {
2729
+ const reactiveStart = j + 1;
2730
+ let level = 0;
2731
+ for (let k = j; k < status.source.length; k++) {
2732
+ const char = status.source[k];
2733
+ const nextChar = status.source[k + 1];
2734
+ if (char === "/" && nextChar === "/") k = status.source.indexOf("\n", k);
2735
+ else if (char === "/" && nextChar === "*") k = status.source.indexOf("*/", k) + 1;
2736
+ else if (char === "\"" || char === "'") k = endOfString(char, status.source, k);
2737
+ else if (char === "`") k = endOfTemplateString(status.source, k);
2738
+ else if (char === "{") level += 1;
2739
+ else if (char === "}") {
2740
+ level -= 1;
2741
+ if (level === 0) {
2742
+ j = k;
2743
+ break;
2744
+ }
2745
+ }
2746
+ }
2747
+ ranges.push({
2748
+ start: reactiveStart,
2749
+ end: j
2750
+ });
2751
+ } else if (status.source[j] === "<") end = j;
2752
+ else if (status.source[j] === "@") {
2753
+ for (let k = j - 1; k > status.i; k--) if (status.source[k] === "\n") {
2754
+ end = j;
2755
+ break;
2756
+ } else if (!isSpaceChar(status.source, k)) break;
2757
+ }
2758
+ if (end !== -1) break;
2759
+ }
2760
+ if (end !== -1) {
2761
+ const content = status.source.substring(start, end).trimEnd();
2762
+ const spaceContent = status.source.substring(start + content.length, end);
2763
+ if (content || spaceContent) {
2764
+ const previousNode = element.children[element.children.length - 1];
2765
+ if (previousNode && isTextNode(previousNode)) {
2766
+ previousNode.content += content + spaceContent;
2767
+ previousNode.ranges.push(...ranges);
2768
+ } else {
2769
+ const text = {
2770
+ type: "text",
2771
+ content: content + spaceContent,
2772
+ ranges
2773
+ };
2774
+ element.children.push(text);
2775
+ }
2776
+ }
2777
+ status.i = end;
2778
+ } else status.i += 1;
2779
+ }
2780
+
2781
+ //#endregion
2782
+ //#region src/compile/parse/slottifyChildNodes.ts
2783
+ /**
2784
+ * Moves any child nodes that aren't already in a <fill> node into a default
2785
+ * <fill> node
2786
+ */
2787
+ function slottifyChildNodes(node) {
2788
+ const nonFillNodes = node.children.filter((c) => !isFillNode(c));
2789
+ if (nonFillNodes.length) {
2790
+ let defaultFillNode = node.children.find((n) => isFillNode(n) && !n.attributes.find((a) => a.name === "name"));
2791
+ if (!defaultFillNode) {
2792
+ defaultFillNode = {
2793
+ type: "special",
2794
+ tagName: "fill",
2795
+ attributes: [],
2796
+ children: nonFillNodes,
2797
+ range: {
2798
+ start: 0,
2799
+ end: 0
2800
+ }
2801
+ };
2802
+ node.children.unshift(defaultFillNode);
2803
+ }
2804
+ }
2805
+ node.children = node.children.filter((c) => isFillNode(c));
2806
+ }
2807
+ function isFillNode(n) {
2808
+ return isSpecialNode(n) && n.tagName === "fill";
2809
+ }
2810
+
2811
+ //#endregion
2812
+ //#region src/compile/parse/utils/addError.ts
2813
+ /**
2814
+ * Adds an error to the parse status
2815
+ *
2816
+ * @param status The parse status
2817
+ * @param message The error message to add
2818
+ * @param start (Optional) The start position of the error (defaults to the
2819
+ * current parse position)
2820
+ */
2821
+ function addError(status, message, start = status.i, end = status.i) {
2822
+ let startLine = 0;
2823
+ let lastLineStart = 0;
2824
+ let i = 0;
2825
+ for (; i < start; i++) if (status.source[i] === "\n") {
2826
+ startLine += 1;
2827
+ lastLineStart = i + 1;
2828
+ }
2829
+ let startChar = start - lastLineStart;
2830
+ let endLine = startLine;
2831
+ for (; i < end; i++) if (status.source[i] === "\n") {
2832
+ endLine += 1;
2833
+ lastLineStart = i + 1;
2834
+ }
2835
+ let endChar = end - lastLineStart;
2836
+ status.errors.push({
2837
+ message,
2838
+ startIndex: start,
2839
+ startLine,
2840
+ startChar,
2841
+ endIndex: end,
2842
+ endLine,
2843
+ endChar
2844
+ });
2845
+ }
2846
+
2847
+ //#endregion
2848
+ //#region src/compile/parse/parseElement.ts
2849
+ function parseElement(status) {
2850
+ const current = status.components.at(-1);
2851
+ if (!current) throw new Error("No component in stack");
2852
+ const start = status.i;
2853
+ const element = parseTag(status);
2854
+ if (element.tagName.startsWith("/")) {
2855
+ addError(status, `Non-matching close tag: ${element.tagName.substring(1)}`, start, start + element.tagName.length);
2856
+ return element;
2857
+ }
2858
+ if (voidTags_default.includes(element.tagName)) checkVoidElement(status, element);
2859
+ if (!element.selfClosed && !voidTags_default.includes(element.tagName)) {
2860
+ current.stack ??= [];
2861
+ current.stack.push(element);
2862
+ while (status.i < status.source.length) {
2863
+ addSpaceElement(element, status);
2864
+ if (accept("}", status, false)) break;
2865
+ else if (accept("<!--", status)) status.i = status.source.indexOf("-->", status.i) + 3;
2866
+ else if (accept("@//", status)) status.i = status.source.indexOf("\n", status.i) + 1;
2867
+ else if (accept("@/*", status)) status.i = status.source.indexOf("*/", status.i) + 2;
2868
+ else if (accept("</", status, false)) {
2869
+ if (checkCloseTag(status, element, current)) break;
2870
+ } else if (accept("<", status, false)) {
2871
+ const child = parseElement(status);
2872
+ element.children.push(child);
2873
+ } else if (accept("@", status, false)) parseControl(status, element);
2874
+ else parseText(status, element);
2875
+ }
2876
+ current.stack.pop();
2877
+ }
2878
+ if (/[A-Z]/.test(element.tagName[0])) {
2879
+ element.type = "component";
2880
+ slottifyChildNodes(element);
2881
+ current.needsContext = true;
2882
+ }
2883
+ if (isSpecialNode(element)) {
2884
+ if (element.tagName === "slot") {
2885
+ slottifyChildNodes(element);
2886
+ current.needsContext = true;
2887
+ current.slotProps ??= [];
2888
+ current.slotProps.push(element.attributes.find((a) => a.name === "name")?.name ?? "default");
2889
+ } else if (element.tagName === "fill") {
2890
+ const fillSource = status.source.substring(start, status.i);
2891
+ element.hasSlotProps = /\$sprops\b/.test(fillSource);
2892
+ } else if (element.tagName === "@component") {
2893
+ const selfAttribute = element.attributes.find((a) => a.name === "self");
2894
+ if (selfAttribute && selfAttribute.value && selfAttribute.fullyReactive) {
2895
+ element.type = "component";
2896
+ slottifyChildNodes(element);
2897
+ current.needsContext = true;
2898
+ const selfValue = selfAttribute.value;
2899
+ return {
2900
+ type: "control",
2901
+ operation: "@replace group",
2902
+ statement: selfValue,
2903
+ children: [{
2904
+ type: "control",
2905
+ operation: "@replace",
2906
+ statement: selfValue,
2907
+ children: [element],
2908
+ range: {
2909
+ start: 0,
2910
+ end: 0
2911
+ }
2912
+ }],
2913
+ range: {
2914
+ start: 0,
2915
+ end: 0
2916
+ }
2917
+ };
2918
+ } else addError(status, "@component element must have a self attribute", start, start + element.tagName.length + 1);
2919
+ }
2920
+ }
2921
+ if (!element.closed && !voidTags_default.includes(element.tagName)) addError(status, `Unclosed non-void element: ${element.tagName}`, start, start + element.tagName.length + 1);
2922
+ return element;
2923
+ }
2924
+ function checkVoidElement(status, element) {
2925
+ let i = status.i;
2926
+ consumeSpace(status);
2927
+ if (accept("</", status)) {
2928
+ consumeSpace(status);
2929
+ if (accept(element.tagName, status)) {
2930
+ addError(status, `Closed void element: ${element.tagName}`, i, i + element.tagName.length + 1);
2931
+ consumeSpace(status);
2932
+ accept(">", status);
2933
+ return;
2934
+ }
2935
+ }
2936
+ status.i = i;
2937
+ }
2938
+ function checkCloseTag(status, element, current) {
2939
+ const start = status.i;
2940
+ accept("</", status);
2941
+ consumeSpace(status);
2942
+ let snailed = accept("@", status);
2943
+ let closingTagName = consumeAlphaNumeric(status);
2944
+ if (snailed) closingTagName = "@" + closingTagName;
2945
+ consumeSpace(status);
2946
+ accept(">", status);
2947
+ let found = false;
2948
+ if (current.stack) {
2949
+ for (let i = current.stack.length - 1; i >= 0; i--) if (closingTagName === current.stack[i].tagName) {
2950
+ current.stack[i].closed = true;
2951
+ found = true;
2952
+ break;
2953
+ }
2954
+ }
2955
+ if (!found) addError(status, `Non-matching close tag: ${closingTagName} (expected ${element.tagName})`, start, start + element.tagName.length + 1);
2956
+ return found;
2957
+ }
2958
+
2959
+ //#endregion
2960
+ //#region src/compile/parse/parseControl.ts
2961
+ const controlOperations = [
2962
+ "@if",
2963
+ "@else",
2964
+ "@for",
2965
+ "@key",
2966
+ "@switch",
2967
+ "@case",
2968
+ "@default",
2969
+ "@await",
2970
+ "@then",
2971
+ "@catch",
2972
+ "@replace",
2973
+ "@html",
2974
+ "@const",
2975
+ "@console",
2976
+ "@debugger",
2977
+ "@function",
2978
+ "@async function"
2979
+ ];
2980
+ const standaloneOperations = [
2981
+ "@key",
2982
+ "@html",
2983
+ "@const",
2984
+ "@console",
2985
+ "@debugger",
2986
+ "@function",
2987
+ "@async function"
2988
+ ];
2989
+ function parseControl(status, parentNode) {
2990
+ const node = parseControlOpen(status);
2991
+ if (!node) return;
2992
+ if (standaloneOperations.includes(node.operation)) {
2993
+ wrangleControlNode(node, parentNode);
2994
+ return;
2995
+ }
2996
+ while (status.i < status.source.length) {
2997
+ addSpaceElement(node, status);
2998
+ if (accept("}", status)) break;
2999
+ else if (accept("<", status, false)) {
3000
+ const child = parseElement(status);
3001
+ node.children.push(child);
3002
+ } else if (accept("@//", status)) status.i = status.source.indexOf("\n", status.i) + 1;
3003
+ else if (accept("@/*", status)) status.i = status.source.indexOf("*/", status.i) + 2;
3004
+ else if (accept("@", status, false)) parseControl(status, node);
3005
+ else if (accept("case", status, false) || accept("key", status, false)) parseControl(status, node);
3006
+ else {
3007
+ const char = status.source[status.i];
3008
+ addError(status, `Unexpected token in control block: ${char}`, status.i, status.i + 1);
3009
+ break;
3010
+ }
3011
+ }
3012
+ wrangleControlNode(node, parentNode);
3013
+ parseControlBranches(status, parentNode);
3014
+ }
3015
+ function parseControlOpen(status) {
3016
+ const start = status.i;
3017
+ let statementStart = start;
3018
+ let operation = "";
3019
+ for (status.i; status.i < status.source.length; status.i++) {
3020
+ const char = status.source[status.i];
3021
+ if (isSpaceChar(status.source, status.i) && operation !== "@async" || accept(":", status) && operation === "default" || accept(".", status) && operation === "@console" || accept("(", status, false) && operation === "@html") break;
3022
+ operation += char;
3023
+ }
3024
+ if (!operation.startsWith("@")) operation = "@" + operation;
3025
+ else statementStart++;
3026
+ let statement = "";
3027
+ if (controlOperations.includes(operation)) {
3028
+ statement = parseControlStatement(start, operation, status);
3029
+ if (operation === "@function" || operation === "@async function") {
3030
+ statement += ` {${parseInlineScript(status)}}`;
3031
+ accept("}", status);
3032
+ }
3033
+ if (operation === "@replace" || operation === "@html") statement = trimMatched(statement.substring(statement.indexOf("(")).trim(), "(", ")");
3034
+ } else {
3035
+ addError(status, `Unknown operation: ${operation}`, status.i, status.i + operation.length);
3036
+ return null;
3037
+ }
3038
+ return {
3039
+ type: "control",
3040
+ operation,
3041
+ statement,
3042
+ children: [],
3043
+ range: {
3044
+ start: statementStart,
3045
+ end: status.i
3046
+ }
3047
+ };
3048
+ }
3049
+ function parseControlStatement(start, operation, status) {
3050
+ let parenCount = 0;
3051
+ while (status.i < status.source.length) if (accept("(", status)) parenCount += 1;
3052
+ else if (accept(")", status)) parenCount -= 1;
3053
+ else if (accept("{", status) || accept("\n", status) && standaloneOperations.includes(operation)) {
3054
+ if (parenCount === 0) return trimStart(status.source.substring(start, status.i - 1).trim(), "@");
3055
+ } else if (accept("//", status)) status.i = status.source.indexOf("\n", status.i) + 1;
3056
+ else if (accept("/*", status)) status.i = status.source.indexOf("*/", status.i) + 2;
3057
+ else if (accept("\"", status) || accept("'", status)) {
3058
+ let char = status.source[status.i - 1];
3059
+ status.i = endOfString(char, status.source, status.i - 1) + 1;
3060
+ } else if (accept("`", status)) status.i = endOfTemplateString(status.source, status.i - 1) + 1;
3061
+ else status.i += 1;
3062
+ return "";
3063
+ }
3064
+ /**
3065
+ * Groups the control node into the correct parent e.g. if/else into an if group
3066
+ * @param node The control node
3067
+ * @param parentNode The parent node
3068
+ */
3069
+ function wrangleControlNode(node, parentNode) {
3070
+ if (node.operation === "@if") {
3071
+ const ifGroup = {
3072
+ type: "control",
3073
+ operation: "@if group",
3074
+ statement: "",
3075
+ children: [node],
3076
+ range: {
3077
+ start: 0,
3078
+ end: 0
3079
+ }
3080
+ };
3081
+ parentNode.children.push(ifGroup);
3082
+ } else if (node.operation === "@else") {
3083
+ if (/^else\s+if/.test(node.statement)) node.operation = "@else if";
3084
+ for (let i = parentNode.children.length - 1; i >= 0; i--) {
3085
+ const lastChild = parentNode.children[i];
3086
+ if (isControlNode(lastChild) && lastChild.operation === "@if group") {
3087
+ lastChild.children.push(node);
3088
+ break;
3089
+ }
3090
+ }
3091
+ } else if (node.operation === "@for") {
3092
+ const forGroup = {
3093
+ type: "control",
3094
+ operation: "@for group",
3095
+ statement: "",
3096
+ children: [node],
3097
+ range: {
3098
+ start: 0,
3099
+ end: 0
3100
+ }
3101
+ };
3102
+ parentNode.children.push(forGroup);
3103
+ } else if (node.operation === "@switch") {
3104
+ node.operation = "@switch group";
3105
+ parentNode.children.push(node);
3106
+ } else if (node.operation === "@await") {
3107
+ const awaitGroup = {
3108
+ type: "control",
3109
+ operation: "@await group",
3110
+ statement: "",
3111
+ children: [node],
3112
+ range: {
3113
+ start: 0,
3114
+ end: 0
3115
+ }
3116
+ };
3117
+ parentNode.children.push(awaitGroup);
3118
+ } else if (node.operation === "@then" || node.operation === "@catch") for (let i = parentNode.children.length - 1; i >= 0; i--) {
3119
+ const lastChild = parentNode.children[i];
3120
+ if (isControlNode(lastChild) && lastChild.operation === "@await group") {
3121
+ lastChild.children.push(node);
3122
+ break;
3123
+ }
3124
+ }
3125
+ else if (node.operation === "@replace") {
3126
+ const replaceGroup = {
3127
+ type: "control",
3128
+ operation: "@replace group",
3129
+ statement: "",
3130
+ children: [node],
3131
+ range: {
3132
+ start: 0,
3133
+ end: 0
3134
+ }
3135
+ };
3136
+ parentNode.children.push(replaceGroup);
3137
+ } else if (node.operation === "@html") {
3138
+ const htmlGroup = {
3139
+ type: "control",
3140
+ operation: "@html group",
3141
+ statement: "",
3142
+ children: [node],
3143
+ range: {
3144
+ start: 0,
3145
+ end: 0
3146
+ }
3147
+ };
3148
+ parentNode.children.push(htmlGroup);
3149
+ } else parentNode.children.push(node);
3150
+ }
3151
+ /**
3152
+ * Parses control branches that don't start with an @ symbol e.g. else or case
3153
+ * @param status The parse status
3154
+ * @param parentNode The parent node
3155
+ */
3156
+ function parseControlBranches(status, parentNode) {
3157
+ let gotAnotherBranch = true;
3158
+ while (gotAnotherBranch) {
3159
+ let start = status.i;
3160
+ status.i += 1;
3161
+ consumeSpace(status);
3162
+ if (accept("else", status, false) || accept("case", status, false) || accept("default", status, false) || accept("then", status, false) || accept("catch", status, false)) parseControl(status, parentNode);
3163
+ else {
3164
+ gotAnotherBranch = false;
3165
+ status.i = start;
3166
+ }
3167
+ }
3168
+ }
3169
+
3170
+ //#endregion
3171
+ //#region src/compile/parse/parseMarkup.ts
3172
+ function parseMarkup(status, source) {
3173
+ const current = status.components.at(-1);
3174
+ if (!current) return;
3175
+ while (status.i < source.length) if (accept("}", status, false)) return;
3176
+ else if (accept("<!--", status)) status.i = status.source.indexOf("-->", status.i) + 3;
3177
+ else if (accept("@//", status)) status.i = status.source.indexOf("\n", status.i) + 1;
3178
+ else if (accept("@/*", status)) status.i = status.source.indexOf("*/", status.i) + 2;
3179
+ else if (isSpaceChar(status.source, status.i)) {
3180
+ current.markup ??= {
3181
+ type: "root",
3182
+ children: []
3183
+ };
3184
+ const text = {
3185
+ type: "text",
3186
+ content: consumeSpace(status),
3187
+ ranges: []
3188
+ };
3189
+ current.markup.children.push(text);
3190
+ } else if (accept("<", status, false)) {
3191
+ current.markup ??= {
3192
+ type: "root",
3193
+ children: []
3194
+ };
3195
+ const element = parseElement(status);
3196
+ current.markup.children.push(element);
3197
+ } else if (accept("@", status, false)) {
3198
+ current.markup ??= {
3199
+ type: "root",
3200
+ children: []
3201
+ };
3202
+ parseControl(status, current.markup);
3203
+ } else status.i += 1;
3204
+ }
3205
+
3206
+ //#endregion
3207
+ //#region src/compile/utils/hash.ts
3208
+ const returnRegex = /\r/g;
3209
+ function hash(input) {
3210
+ input = input.replace(returnRegex, "");
3211
+ let hash$1 = 5381;
3212
+ let i = input.length;
3213
+ while (i--) hash$1 = (hash$1 << 5) - hash$1 ^ input.charCodeAt(i);
3214
+ return (hash$1 >>> 0).toString(36);
3215
+ }
3216
+
3217
+ //#endregion
3218
+ //#region src/compile/parse/utils/expect.ts
3219
+ /**
3220
+ * Checks whether the source at the current parse position matches the supplied
3221
+ * string and if not, adds an error to the parse status
3222
+ *
3223
+ * @param value The string to check
3224
+ * @param status The parse status
3225
+ * @param advance (Optional) If true, advances the parse position past the
3226
+ * checked string (defaults to true)
3227
+ *
3228
+ * @returns True if the supplied string was found at the current parse position
3229
+ */
3230
+ function expect(value, status, advance = true) {
3231
+ if (status.i < status.source.length) if (status.source.substring(status.i, status.i + value.length) == value) {
3232
+ status.i += advance ? value.length : 0;
3233
+ return true;
3234
+ } else addError(status, `Expected ${value}`);
3235
+ else addError(status, "Expected token");
3236
+ return false;
3237
+ }
3238
+
3239
+ //#endregion
3240
+ //#region src/compile/parse/parseStyles.ts
3241
+ function parseStyles(source, status) {
3242
+ const current = status.components.at(-1);
3243
+ if (!current) return;
3244
+ current.style = {
3245
+ global: false,
3246
+ blocks: [],
3247
+ hash: hash(source)
3248
+ };
3249
+ const styleStatus = {
3250
+ source,
3251
+ i: 0,
3252
+ marker: 0,
3253
+ level: 0,
3254
+ imports: [],
3255
+ script: [],
3256
+ components: [],
3257
+ errors: []
3258
+ };
3259
+ while (styleStatus.i < styleStatus.source.length) if (isSpaceChar(styleStatus.source, styleStatus.i)) consumeSpace(styleStatus);
3260
+ else {
3261
+ const block = parseStyleBlock(styleStatus);
3262
+ if (block) current.style.blocks.push(block);
3263
+ else break;
3264
+ }
3265
+ status.errors = status.errors.concat(styleStatus.errors);
3266
+ }
3267
+ function parseStyleBlock(status) {
3268
+ const selector = consumeUntil("{", status).trim();
3269
+ if (expect("{", status)) {
3270
+ const block = {
3271
+ selector,
3272
+ attributes: [],
3273
+ children: []
3274
+ };
3275
+ consumeSpace(status);
3276
+ while (status.source[status.i] !== "}") {
3277
+ let nextColon = status.source.indexOf(":", status.i);
3278
+ if (nextColon === -1) nextColon = status.source.length;
3279
+ let nextOpenBrace = status.source.indexOf("{", status.i);
3280
+ if (nextOpenBrace === -1) nextOpenBrace = status.source.length;
3281
+ if (nextColon < nextOpenBrace) {
3282
+ const attribute = parseStyleAttribute(status);
3283
+ block.attributes.push(attribute);
3284
+ } else if (nextOpenBrace < nextColon) {
3285
+ const child = parseStyleBlock(status);
3286
+ if (child) block.children.push(child);
3287
+ else break;
3288
+ } else return block;
3289
+ consumeSpace(status);
3290
+ }
3291
+ accept("}", status);
3292
+ return block;
3293
+ }
3294
+ }
3295
+ function parseStyleAttribute(status) {
3296
+ const name = consumeUntil(":", status).trim();
3297
+ accept(":", status);
3298
+ const value = consumeUntil(";", status).trim();
3299
+ accept(";", status);
3300
+ return {
3301
+ name,
3302
+ value
3303
+ };
3304
+ }
3305
+
3306
+ //#endregion
3307
+ //#region src/compile/utils/isComponentNode.ts
3308
+ function isComponentNode(node) {
3309
+ return node.type === "component";
3310
+ }
3311
+
3312
+ //#endregion
3313
+ //#region src/compile/utils/isParentNode.ts
3314
+ function isParentNode(node) {
3315
+ return "children" in node;
3316
+ }
3317
+
3318
+ //#endregion
3319
+ //#region src/compile/parse/scopeStyles.ts
3320
+ function scopeStyles(status) {
3321
+ let selectors = [];
3322
+ for (let component of status.components) if (component.style && component.markup) {
3323
+ for (let block of component.style.blocks) collectStyleSelectors(block, selectors);
3324
+ scopeStylesOnNode(component.markup, selectors);
3325
+ }
3326
+ }
3327
+ function scopeStylesOnNode(node, selectors) {
3328
+ if (isElementNode(node) || isComponentNode(node)) {
3329
+ let scopeStyles$1 = selectors.includes(node.tagName);
3330
+ if (!scopeStyles$1) for (let a of node.attributes) {
3331
+ if (a.name === "class" && a.reactive) scopeStyles$1 = true;
3332
+ else if (a.name === "id" && a.value) scopeStyles$1 = selectors.includes(`#${trimQuotes(a.value)}`);
3333
+ else if (a.name === "class" && a.value) scopeStyles$1 = selectors.includes(`.${trimQuotes(a.value)}`);
3334
+ if (scopeStyles$1) break;
3335
+ }
3336
+ node.scopeStyles = scopeStyles$1;
3337
+ }
3338
+ if (isParentNode(node)) for (let child of node.children) scopeStylesOnNode(child, selectors);
3339
+ }
3340
+ function collectStyleSelectors(block, selectors) {
3341
+ for (let s of block.selector.split(/[\s*,>+~]/)) selectors.push(s);
3342
+ }
3343
+
3344
+ //#endregion
3345
+ //#region src/compile/parse/parseCode.ts
3346
+ function parseCode(source) {
3347
+ const status = {
3348
+ source,
3349
+ i: 0,
3350
+ marker: 0,
3351
+ level: 0,
3352
+ imports: [],
3353
+ script: [],
3354
+ components: [],
3355
+ errors: []
3356
+ };
3357
+ for (status.i; status.i < source.length; status.i++) if (consumeScriptComments(status)) {} else if (status.source[status.i] === "{") status.level += 1;
3358
+ else if (status.source[status.i] === "}") {
3359
+ status.level -= 1;
3360
+ if (status.level === 0) parseComponentEnd(status);
3361
+ } else if (accept("function", status)) {
3362
+ if (consumeSpace(status)) if (/[A-Z]/.test(source[status.i])) parseComponentStart(status);
3363
+ else status.i--;
3364
+ } else if (accept("const", status)) {
3365
+ if (consumeSpace(status)) if (/[A-Z]/.test(source[status.i])) {
3366
+ const start = status.i;
3367
+ consumeAlphaNumeric(status);
3368
+ consumeSpace(status);
3369
+ if (accept("=", status)) {
3370
+ consumeSpace(status);
3371
+ if (accept("(", status, false)) {
3372
+ status.i = start;
3373
+ parseComponentStart(status);
3374
+ }
3375
+ }
3376
+ } else status.i--;
3377
+ } else if (accept("export", status)) {
3378
+ const start = status.i;
3379
+ if (consumeSpace(status) && accept("default", status) && consumeSpace(status) && accept("(", status, false)) parseComponentStart(status);
3380
+ else status.i = start;
3381
+ } else if (accept("@render", status, false)) parseComponentRender(status);
3382
+ else if (accept("@head", status, false)) parseComponentHead(status);
3383
+ else if (accept("@style", status, false)) parseComponentStyle(status);
3384
+ status.script.push({
3385
+ script: source.substring(status.marker, source.length),
3386
+ range: {
3387
+ start: status.marker,
3388
+ end: source.length
3389
+ }
3390
+ });
3391
+ scopeStyles(status);
3392
+ const ok = !status.errors.length;
3393
+ if (!ok) status.errors = status.errors.sort((a, b) => a.startIndex - b.startIndex);
3394
+ return {
3395
+ ok,
3396
+ errors: status.errors,
3397
+ template: ok ? {
3398
+ imports: status.imports,
3399
+ script: status.script,
3400
+ components: status.components.map((c) => {
3401
+ return {
3402
+ start: c.start,
3403
+ name: c.name,
3404
+ default: c.default,
3405
+ params: c.params,
3406
+ markup: c.markup,
3407
+ head: c.head,
3408
+ style: c.style,
3409
+ props: c.props,
3410
+ contextProps: c.contextProps,
3411
+ slotProps: c.slotProps,
3412
+ needsContext: c.needsContext
3413
+ };
3414
+ })
3415
+ } : void 0
3416
+ };
3417
+ }
3418
+ function parseComponentStart(status) {
3419
+ let source = status.source.substring(0, status.i);
3420
+ let def = /export\s+default\s+(function\s+|const\s+)*$/.test(source);
3421
+ let isConst = /const\s+$/.test(source);
3422
+ let isAnon = /export default\s+$/.test(source);
3423
+ status.components.push({
3424
+ start: status.i,
3425
+ name: consumeAlphaNumeric(status),
3426
+ default: def
3427
+ });
3428
+ const current = status.components.at(-1);
3429
+ if (!current) return;
3430
+ let start = -1;
3431
+ let end = -1;
3432
+ let level = 0;
3433
+ for (status.i; status.i < status.source.length; status.i++) {
3434
+ const char = status.source[status.i];
3435
+ if (consumeScriptComments(status)) {} else if (char === "(") {
3436
+ level += 1;
3437
+ if (start === -1) start = status.i + 1;
3438
+ } else if (char === ")") {
3439
+ level -= 1;
3440
+ if (level === 0) {
3441
+ end = status.i;
3442
+ break;
3443
+ }
3444
+ }
3445
+ }
3446
+ status.script.push({
3447
+ script: status.source.substring(status.marker, start),
3448
+ range: {
3449
+ start: status.marker,
3450
+ end: start
3451
+ }
3452
+ });
3453
+ status.script.push({
3454
+ script: "/* @params */",
3455
+ range: {
3456
+ start: 0,
3457
+ end: 0
3458
+ }
3459
+ });
3460
+ current.params = status.source.substring(start, end).trim() || void 0;
3461
+ status.marker = end;
3462
+ status.i = status.marker;
3463
+ accept(")", status);
3464
+ consumeSpace(status);
3465
+ if (isConst || isAnon) {
3466
+ accept("=>", status);
3467
+ consumeSpace(status);
3468
+ }
3469
+ accept("{", status);
3470
+ status.level += 1;
3471
+ const postFunctionChunk = {
3472
+ script: status.source.substring(status.marker, status.i),
3473
+ range: {
3474
+ start: status.marker,
3475
+ end: status.i
3476
+ }
3477
+ };
3478
+ if (/\)\s+\{/.test(postFunctionChunk.script)) postFunctionChunk.script = ") /* @return_type */ {";
3479
+ status.script.push(postFunctionChunk);
3480
+ status.script.push({
3481
+ script: "/* @start */",
3482
+ range: {
3483
+ start: 0,
3484
+ end: 0
3485
+ }
3486
+ });
3487
+ status.marker = status.i;
3488
+ const space = consumeSpace(status);
3489
+ status.script.push({
3490
+ script: space,
3491
+ range: {
3492
+ start: status.marker,
3493
+ end: status.i
3494
+ }
3495
+ });
3496
+ status.marker = status.i;
3497
+ status.i -= 1;
3498
+ }
3499
+ function parseComponentRender(status) {
3500
+ const current = status.components.at(-1);
3501
+ if (!current) return;
3502
+ if (current.markup) addError(status, `Multiple @render sections`, status.i, status.i + 7);
3503
+ status.script.push({
3504
+ script: status.source.substring(status.marker, status.i),
3505
+ range: {
3506
+ start: status.marker,
3507
+ end: status.i
3508
+ }
3509
+ });
3510
+ status.script.push({
3511
+ script: "/* @render */",
3512
+ range: {
3513
+ start: 0,
3514
+ end: 0
3515
+ }
3516
+ });
3517
+ accept("@render", status);
3518
+ consumeSpace(status);
3519
+ if (accept("{", status)) {
3520
+ parseMarkup(status, status.source);
3521
+ accept("}", status);
3522
+ }
3523
+ status.marker = status.i;
3524
+ }
3525
+ function parseComponentHead(status) {
3526
+ const current = status.components.at(-1);
3527
+ if (!current) return;
3528
+ if (current.head) addError(status, `Multiple @head sections`, status.i, status.i + 5);
3529
+ status.script.push({
3530
+ script: status.source.substring(status.marker, status.i),
3531
+ range: {
3532
+ start: status.marker,
3533
+ end: status.i
3534
+ }
3535
+ });
3536
+ status.script.push({
3537
+ script: "/* @head */",
3538
+ range: {
3539
+ start: 0,
3540
+ end: 0
3541
+ }
3542
+ });
3543
+ status.components.push({ start: status.i });
3544
+ accept("@head", status);
3545
+ consumeSpace(status);
3546
+ if (accept("{", status)) {
3547
+ parseMarkup(status, status.source);
3548
+ accept("}", status);
3549
+ }
3550
+ current.head = status.components.pop()?.markup;
3551
+ status.marker = status.i;
3552
+ }
3553
+ function parseComponentStyle(status) {
3554
+ const current = status.components.at(-1);
3555
+ if (!current) return;
3556
+ if (current.style) addError(status, `Multiple @style sections`, status.i, status.i + 6);
3557
+ status.script.push({
3558
+ script: status.source.substring(status.marker, status.i),
3559
+ range: {
3560
+ start: status.marker,
3561
+ end: status.i
3562
+ }
3563
+ });
3564
+ status.script.push({
3565
+ script: "/* @style */",
3566
+ range: {
3567
+ start: 0,
3568
+ end: 0
3569
+ }
3570
+ });
3571
+ let start = -1;
3572
+ let end = status.source.length;
3573
+ let level = 0;
3574
+ let styleSource = "";
3575
+ for (; status.i < status.source.length; status.i++) {
3576
+ const char = status.source[status.i];
3577
+ const nextChar = status.source[status.i + 1];
3578
+ if (char === "/" && nextChar === "/") status.i = status.source.indexOf("\n", status.i);
3579
+ else if (char === "/" && nextChar === "*") status.i = status.source.indexOf("*/", status.i) + 1;
3580
+ else if (char === "\"" || char === "'") {
3581
+ let start$1 = status.i;
3582
+ status.i = endOfString(char, status.source, start$1);
3583
+ styleSource += status.source.substring(start$1, status.i + 1);
3584
+ } else if (char === "`") {
3585
+ let start$1 = status.i;
3586
+ status.i = endOfTemplateString(status.source, start$1);
3587
+ styleSource += status.source.substring(start$1, status.i + 1);
3588
+ } else if (char === "{") {
3589
+ level += 1;
3590
+ if (start === -1) start = status.i + 1;
3591
+ else styleSource += char;
3592
+ } else if (char === "}") {
3593
+ level -= 1;
3594
+ if (level === 0) {
3595
+ end = status.i - 1;
3596
+ break;
3597
+ } else styleSource += char;
3598
+ } else if (start !== -1) styleSource += char;
3599
+ }
3600
+ status.i = start;
3601
+ parseStyles(styleSource.trim(), status);
3602
+ status.i = end + 2;
3603
+ status.marker = status.i;
3604
+ }
3605
+ function parseComponentEnd(status) {
3606
+ const current = status.components.at(-1);
3607
+ if (!current) return;
3608
+ status.script.push({
3609
+ script: status.source.substring(status.marker, status.i),
3610
+ range: {
3611
+ start: status.marker,
3612
+ end: status.i
3613
+ }
3614
+ });
3615
+ status.script.push({
3616
+ script: "/* @end */",
3617
+ range: {
3618
+ start: 0,
3619
+ end: 0
3620
+ }
3621
+ });
3622
+ const componentSource = status.source.substring(current.start || 0, status.i);
3623
+ current.props = getPropsUsage(componentSource);
3624
+ current.contextProps = getContextUsage(componentSource);
3625
+ if (current.markup && isElementNode(current.markup.children[0]) && current.markup.children[0].tagName === "html" && isSpaceNode(current.markup.children[0].children[0])) current.markup.children.splice(1, 1);
3626
+ status.marker = status.i;
3627
+ }
3628
+ function getPropsUsage(source) {
3629
+ const propsMatches = source.matchAll(/\$props\s*(?:\.([\d\w]+)|\[([^\]]+)\])/g);
3630
+ const props = [];
3631
+ for (let match of propsMatches) {
3632
+ const name = trimQuotes(match[1] || match[2]);
3633
+ if (!props.includes(name)) props.push(name);
3634
+ }
3635
+ return props.length ? props : void 0;
3636
+ }
3637
+ function getContextUsage(source) {
3638
+ const contextsMatches = source.matchAll(/\$context\s*(?:\.([\d\w]+)|\[([^\]]+)\])/g);
3639
+ const contexts = [];
3640
+ for (let match of contextsMatches) {
3641
+ const name = trimQuotes(match[1] || match[2]);
3642
+ if (!contexts.includes(name)) contexts.push(name);
3643
+ }
3644
+ return contexts.length ? contexts : void 0;
3645
+ }
3646
+ function consumeScriptComments(status) {
3647
+ const char = status.source[status.i];
3648
+ const nextChar = status.source[status.i + 1];
3649
+ if (char === "/" && nextChar === "/") {
3650
+ status.i = status.source.indexOf("\n", status.i);
3651
+ return true;
3652
+ } else if (char === "/" && nextChar === "*") {
3653
+ status.i = status.source.indexOf("*/", status.i) + 1;
3654
+ return true;
3655
+ } else if (char === "\"" || char === "'") {
3656
+ status.i = endOfString(char, status.source, status.i);
3657
+ return true;
3658
+ } else if (char === "`") {
3659
+ status.i = endOfTemplateString(status.source, status.i);
3660
+ return true;
3661
+ }
3662
+ return false;
3663
+ }
3664
+
3665
+ //#endregion
3666
+ //#region src/compile/parse.ts
3667
+ /**
3668
+ * Parses source code into a component template
3669
+ *
3670
+ * @param source The source code
3671
+ *
3672
+ * @returns A result indicating whether parsing was ok, and if so, containing
3673
+ * the component template's parts such as script, markup and styles
3674
+ */
3675
+ function parse(source) {
3676
+ return parseCode(source);
3677
+ }
3678
+
3679
+ //#endregion
3680
+ exports.build = build;
3681
+ exports.buildType = buildType;
3682
+ exports.parse = parse;
3683
+ //# sourceMappingURL=compile.cjs.map