@vune-ui/compiler 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,678 @@
1
+ function syntaxError(message, offset) {
2
+ const error = new SyntaxError(message);
3
+ error.offset = offset;
4
+ return error;
5
+ }
6
+ function skipQuoted(source, index) {
7
+ const quote = source[index];
8
+ for (let cursor = index + 1; cursor < source.length; cursor += 1) {
9
+ if (source[cursor] === "\\") {
10
+ cursor += 1;
11
+ continue;
12
+ }
13
+ if (source[cursor] === quote)
14
+ return cursor + 1;
15
+ }
16
+ throw syntaxError(`Unclosed ${quote} string in Vune source`, index);
17
+ }
18
+ function skipTemplate(source, index) {
19
+ for (let cursor = index + 1; cursor < source.length; cursor += 1) {
20
+ if (source[cursor] === "\\") {
21
+ cursor += 1;
22
+ continue;
23
+ }
24
+ if (source[cursor] === "`")
25
+ return cursor + 1;
26
+ if (source[cursor] !== "$" || source[cursor + 1] !== "{")
27
+ continue;
28
+ cursor = matching(source, cursor + 1, "{", "}");
29
+ }
30
+ throw syntaxError("Unclosed template literal in Vune source", index);
31
+ }
32
+ function skipString(source, index) {
33
+ return source[index] === "`" ? skipTemplate(source, index) : skipQuoted(source, index);
34
+ }
35
+ function skipComment(source, index) {
36
+ if (source.startsWith("//", index)) {
37
+ const end = source.indexOf("\n", index + 2);
38
+ return end < 0 ? source.length : end;
39
+ }
40
+ const end = source.indexOf("*/", index + 2);
41
+ if (end < 0)
42
+ throw syntaxError("Unclosed block comment in Vune source", index);
43
+ return end + 2;
44
+ }
45
+ function regexCanStart(source, index) {
46
+ for (let cursor = index - 1; cursor >= 0; cursor -= 1) {
47
+ if (/\s/.test(source[cursor]))
48
+ continue;
49
+ if ("([{=,:;!?&|+-*%^~<>".includes(source[cursor]))
50
+ return true;
51
+ if (/[A-Za-z_$]/.test(source[cursor])) {
52
+ const end = cursor + 1;
53
+ while (cursor >= 0 && /[A-Za-z0-9_$]/.test(source[cursor]))
54
+ cursor -= 1;
55
+ const word = source.slice(cursor + 1, end);
56
+ return new Set(["case", "delete", "do", "else", "in", "instanceof", "of", "return", "throw", "typeof", "void", "yield", "await"]).has(word);
57
+ }
58
+ return false;
59
+ }
60
+ return true;
61
+ }
62
+ function skipRegex(source, index) {
63
+ let inClass = false;
64
+ for (let cursor = index + 1; cursor < source.length; cursor += 1) {
65
+ if (source[cursor] === "\\") {
66
+ cursor += 1;
67
+ continue;
68
+ }
69
+ if (source[cursor] === "[")
70
+ inClass = true;
71
+ if (source[cursor] === "]")
72
+ inClass = false;
73
+ if (source[cursor] === "/" && !inClass) {
74
+ cursor += 1;
75
+ while (/[A-Za-z]/.test(source[cursor] ?? ""))
76
+ cursor += 1;
77
+ return cursor;
78
+ }
79
+ if (source[cursor] === "\n")
80
+ return index + 1;
81
+ }
82
+ return index + 1;
83
+ }
84
+ function skipTrivia(source, index) {
85
+ let cursor = index;
86
+ while (cursor < source.length) {
87
+ if (/\s/.test(source[cursor])) {
88
+ cursor += 1;
89
+ continue;
90
+ }
91
+ if (source.startsWith("//", cursor) || source.startsWith("/*", cursor)) {
92
+ cursor = skipComment(source, cursor);
93
+ continue;
94
+ }
95
+ break;
96
+ }
97
+ return cursor;
98
+ }
99
+ function matching(source, open, left, right) {
100
+ let depth = 0;
101
+ let steps = 0;
102
+ for (let cursor = open; cursor < source.length; cursor += 1) {
103
+ if (++steps > source.length + 1)
104
+ throw syntaxError(`Unable to scan ${left} block in Vune source`, open);
105
+ const character = source[cursor];
106
+ if (character === "\"" || character === "'" || character === "`") {
107
+ cursor = skipString(source, cursor) - 1;
108
+ continue;
109
+ }
110
+ if (character === "/" && (source[cursor + 1] === "/" || source[cursor + 1] === "*")) {
111
+ cursor = skipComment(source, cursor) - 1;
112
+ continue;
113
+ }
114
+ if (character === "/" && regexCanStart(source, cursor)) {
115
+ cursor = skipRegex(source, cursor) - 1;
116
+ continue;
117
+ }
118
+ if (character === left)
119
+ depth += 1;
120
+ if (character === right) {
121
+ depth -= 1;
122
+ if (depth === 0)
123
+ return cursor;
124
+ }
125
+ }
126
+ throw syntaxError(`Unclosed ${left} block in Vune source`, open);
127
+ }
128
+ function identifierAt(source, start) {
129
+ const match = /^[A-Za-z_$][A-Za-z0-9_$]*/.exec(source.slice(start));
130
+ return match ? { name: match[0], end: start + match[0].length } : undefined;
131
+ }
132
+ /**
133
+ * A less-than token is only a raw HTML opener in an expression-start
134
+ * position. Keeping this decision here prevents generic calls and ordinary
135
+ * comparisons from leaking into the HTML parser.
136
+ */
137
+ function isRawHtmlCandidate(source, start) {
138
+ if (source[start] !== "<" || source[start + 1] === "/" || source[start + 1] === "!")
139
+ return false;
140
+ if (!/^[A-Za-z]/.test(source[start + 1] ?? ""))
141
+ return false;
142
+ // `<T>(value)` and `<T extends U>(value)` are TypeScript generic calls or
143
+ // assertions. A real opening element continues with attributes/text or a
144
+ // closing tag, not an immediately following call parenthesis.
145
+ const possibleGenericClose = source.indexOf(">", start + 1);
146
+ if (possibleGenericClose >= 0) {
147
+ let afterClose = possibleGenericClose + 1;
148
+ while (afterClose < source.length && /\s/.test(source[afterClose]))
149
+ afterClose += 1;
150
+ if (source[afterClose] === "(")
151
+ return false;
152
+ const tagName = /^[A-Za-z][A-Za-z0-9:._-]*/.exec(source.slice(start + 1))?.[0];
153
+ const hasMatchingClosingTag = tagName ? source.slice(possibleGenericClose + 1).includes(`</${tagName}`) : false;
154
+ // TypeScript's angle-bracket assertion `<Foo>value` is still valid in
155
+ // `.ts` files. Prefer that interpretation for an uppercase type name
156
+ // when no matching closing tag exists. Actual `<Foo>...</Foo>` raw HTML
157
+ // remains unambiguous.
158
+ if (tagName && /^[A-Z]/.test(tagName)
159
+ && !hasMatchingClosingTag
160
+ && /[A-Za-z_$0-9('"`[!+~-]/.test(source[afterClose] ?? ""))
161
+ return false;
162
+ }
163
+ let cursor = start - 1;
164
+ while (cursor >= 0 && /\s/.test(source[cursor]))
165
+ cursor -= 1;
166
+ if (cursor < 0)
167
+ return true;
168
+ const openingName = /^[A-Za-z][A-Za-z0-9:._-]*/.exec(source.slice(start + 1))?.[0];
169
+ const openingClose = source.indexOf(">", start + 1);
170
+ if (source.slice(cursor + 1, start).includes("\n") && openingName && openingClose >= 0
171
+ && source.slice(openingClose + 1).includes(`</${openingName}`))
172
+ return true;
173
+ // A tag at the beginning of a new statement may follow a completed call,
174
+ // array, or object expression on the previous line.
175
+ if (source.slice(cursor + 1, start).includes("\n") && new Set([")", "]", "}"]).has(source[cursor]))
176
+ return true;
177
+ if ("([{=,:;!?&|+-*%^~;".includes(source[cursor]))
178
+ return true;
179
+ if (!/[A-Za-z0-9_$.)\]]/.test(source[cursor]))
180
+ return true;
181
+ const end = cursor + 1;
182
+ while (cursor >= 0 && /[A-Za-z0-9_$]/.test(source[cursor]))
183
+ cursor -= 1;
184
+ const word = source.slice(cursor + 1, end);
185
+ return new Set(["await", "case", "else", "return", "throw", "yield"]).has(word);
186
+ }
187
+ function previousSignificantCharacter(source, index) {
188
+ for (let cursor = index - 1; cursor >= 0; cursor -= 1) {
189
+ if (!/\s/.test(source[cursor]))
190
+ return source[cursor];
191
+ }
192
+ return undefined;
193
+ }
194
+ function braceContext(source, index) {
195
+ const prefix = source.slice(Math.max(0, index - 160), index).trimEnd();
196
+ if (/\b(?:class|interface|enum|namespace)\s+[A-Za-z_$][A-Za-z0-9_$]*(?:\s+extends[^{]+)?$/.test(prefix))
197
+ return "class";
198
+ const previous = previousSignificantCharacter(source, index);
199
+ if (previous && "=(:,[".includes(previous))
200
+ return "object";
201
+ if (/\b(?:return|yield)\s*$/.test(prefix))
202
+ return "object";
203
+ if (/\btype\s+[A-Za-z_$][A-Za-z0-9_$]*(?:<[^>]*>)?\s*=\s*$/.test(prefix))
204
+ return "object";
205
+ return "block";
206
+ }
207
+ function findBuilder(source, from = 0, uppercaseOnly = false) {
208
+ const excluded = new Set(["if", "for", "while", "switch", "catch", "function"]);
209
+ const braces = [];
210
+ let parenDepth = 0;
211
+ let bracketDepth = 0;
212
+ let steps = 0;
213
+ for (let cursor = 0; cursor < source.length; cursor += 1) {
214
+ if (++steps > source.length + 1)
215
+ throw syntaxError("Unable to scan builder expressions in Vune source", from);
216
+ const character = source[cursor];
217
+ if (character === "\"" || character === "'" || character === "`") {
218
+ cursor = skipString(source, cursor) - 1;
219
+ continue;
220
+ }
221
+ if (character === "/" && (source[cursor + 1] === "/" || source[cursor + 1] === "*")) {
222
+ cursor = skipComment(source, cursor) - 1;
223
+ continue;
224
+ }
225
+ if (character === "/" && regexCanStart(source, cursor)) {
226
+ cursor = skipRegex(source, cursor) - 1;
227
+ continue;
228
+ }
229
+ if (character === "(") {
230
+ parenDepth += 1;
231
+ continue;
232
+ }
233
+ if (character === ")") {
234
+ parenDepth = Math.max(0, parenDepth - 1);
235
+ continue;
236
+ }
237
+ if (character === "[") {
238
+ bracketDepth += 1;
239
+ continue;
240
+ }
241
+ if (character === "]") {
242
+ bracketDepth = Math.max(0, bracketDepth - 1);
243
+ continue;
244
+ }
245
+ if (character === "{") {
246
+ braces.push({ context: braceContext(source, cursor), parenDepth, bracketDepth });
247
+ continue;
248
+ }
249
+ if (character === "}") {
250
+ braces.pop();
251
+ continue;
252
+ }
253
+ if (cursor < from)
254
+ continue;
255
+ const identifier = identifierAt(source, cursor);
256
+ if (!identifier)
257
+ continue;
258
+ const start = cursor;
259
+ cursor = identifier.end - 1;
260
+ if (excluded.has(identifier.name))
261
+ continue;
262
+ if (uppercaseOnly && !/^[A-Z]/.test(identifier.name))
263
+ continue;
264
+ const preceding = source.slice(0, start).trimEnd();
265
+ if (/\bfunction\s*\*?$/.test(preceding))
266
+ continue;
267
+ const open = skipTrivia(source, identifier.end);
268
+ if (source[open] !== "(")
269
+ continue;
270
+ const close = matching(source, open, "(", ")");
271
+ const brace = skipTrivia(source, close + 1);
272
+ if (source[brace] !== "{")
273
+ continue;
274
+ // A call-shaped token at member position inside a class/object is a
275
+ // JavaScript/TypeScript method declaration, not Vune trailing-closure
276
+ // syntax. Property initializers (`field = Card() { ... }`) remain valid
277
+ // Vune expressions because their preceding token is `=`/`:`.
278
+ const frame = braces.at(-1);
279
+ const container = frame?.context;
280
+ const atMemberLevel = frame !== undefined
281
+ && parenDepth === frame.parenDepth
282
+ && bracketDepth === frame.bracketDepth;
283
+ const before = previousSignificantCharacter(source, start);
284
+ const memberPrefix = source.slice(Math.max(0, start - 120), start);
285
+ const followsMemberModifier = /\b(?:public|private|protected|static|abstract|override|async|get|set|readonly|declare|accessor)\s+$/.test(memberPrefix);
286
+ if (atMemberLevel && (container === "class" || container === "object")
287
+ && (before === undefined || "{,;}*".includes(before) || followsMemberModifier))
288
+ continue;
289
+ const braceClose = matching(source, brace, "{", "}");
290
+ return {
291
+ start,
292
+ open,
293
+ close,
294
+ brace,
295
+ end: braceClose + 1,
296
+ name: identifier.name,
297
+ argumentSource: source.slice(open + 1, close),
298
+ bodySource: source.slice(brace + 1, braceClose),
299
+ };
300
+ }
301
+ return undefined;
302
+ }
303
+ function skipHtmlTrivia(source, index) {
304
+ let cursor = index;
305
+ while (cursor < source.length && /\s/.test(source[cursor]))
306
+ cursor += 1;
307
+ return cursor;
308
+ }
309
+ function htmlNameAt(source, start) {
310
+ const match = /^[A-Za-z][A-Za-z0-9:._-]*/.exec(source.slice(start));
311
+ return match ? { name: match[0], end: start + match[0].length } : undefined;
312
+ }
313
+ function htmlAttributeNameAt(source, start) {
314
+ const match = /^[^\s=/>]+/.exec(source.slice(start));
315
+ return match ? { name: match[0], end: start + match[0].length } : undefined;
316
+ }
317
+ const identityExpression = source => source;
318
+ function decodeHtmlEntities(value) {
319
+ const named = {
320
+ amp: "&",
321
+ apos: "'",
322
+ gt: ">",
323
+ lt: "<",
324
+ quot: '"',
325
+ };
326
+ return value.replace(/&(#(?:x[0-9A-Fa-f]+|[0-9]+)|[A-Za-z][A-Za-z0-9]+);/g, (match, entity) => {
327
+ if (entity[0] === "#") {
328
+ const hexadecimal = entity[1]?.toLowerCase() === "x";
329
+ const digits = entity.slice(hexadecimal ? 2 : 1);
330
+ const codePoint = Number.parseInt(digits, hexadecimal ? 16 : 10);
331
+ if (!Number.isFinite(codePoint) || codePoint < 0 || codePoint > 0x10ffff)
332
+ return match;
333
+ try {
334
+ return String.fromCodePoint(codePoint);
335
+ }
336
+ catch {
337
+ return match;
338
+ }
339
+ }
340
+ return named[entity] ?? match;
341
+ });
342
+ }
343
+ function htmlAttributes(source, baseOffset = 0, lower = identityExpression) {
344
+ const attributes = [];
345
+ let cursor = 0;
346
+ while (cursor < source.length) {
347
+ cursor = skipHtmlTrivia(source, cursor);
348
+ if (cursor >= source.length)
349
+ break;
350
+ if (source[cursor] === "{") {
351
+ const end = matching(source, cursor, "{", "}");
352
+ const expression = source.slice(cursor + 1, end).trim();
353
+ if (!expression.startsWith("...") || expression.slice(3).trim().length === 0) {
354
+ throw syntaxError("Raw HTML attribute expressions must use {...value}", baseOffset + cursor);
355
+ }
356
+ attributes.push(`...(${lower(expression.slice(3).trim())})`);
357
+ cursor = end + 1;
358
+ continue;
359
+ }
360
+ const name = htmlAttributeNameAt(source, cursor);
361
+ if (!name)
362
+ throw syntaxError("Invalid raw HTML attribute", baseOffset + cursor);
363
+ cursor = skipHtmlTrivia(source, name.end);
364
+ let value = "true";
365
+ if (source[cursor] === "=") {
366
+ cursor = skipHtmlTrivia(source, cursor + 1);
367
+ if (source[cursor] === "\"" || source[cursor] === "'") {
368
+ const end = skipQuoted(source, cursor);
369
+ value = JSON.stringify(decodeHtmlEntities(source.slice(cursor + 1, end - 1)));
370
+ cursor = end;
371
+ }
372
+ else if (source[cursor] === "{") {
373
+ const end = matching(source, cursor, "{", "}");
374
+ value = lower(source.slice(cursor + 1, end));
375
+ cursor = end + 1;
376
+ }
377
+ else {
378
+ const match = /^[^\s/>]+/.exec(source.slice(cursor));
379
+ if (!match)
380
+ throw syntaxError(`Invalid value for raw HTML attribute ${name.name}`, baseOffset + cursor);
381
+ value = JSON.stringify(decodeHtmlEntities(match[0]));
382
+ cursor += match[0].length;
383
+ }
384
+ }
385
+ attributes.push(`${JSON.stringify(name.name)}: ${value}`);
386
+ }
387
+ return attributes.length === 0 ? "null" : `{ ${attributes.join(", ")} }`;
388
+ }
389
+ const voidHtmlElements = new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
390
+ function rawHtmlAt(source, start, lower = identityExpression, nested = false) {
391
+ if (!nested && !isRawHtmlCandidate(source, start))
392
+ return undefined;
393
+ const openingName = htmlNameAt(source, start + 1);
394
+ if (!openingName)
395
+ return undefined;
396
+ let cursor = openingName.end;
397
+ let braceDepth = 0;
398
+ let quote;
399
+ let close = -1;
400
+ for (; cursor < source.length; cursor += 1) {
401
+ const character = source[cursor];
402
+ if (quote) {
403
+ if (character === "\\") {
404
+ cursor += 1;
405
+ continue;
406
+ }
407
+ if (character === quote)
408
+ quote = undefined;
409
+ continue;
410
+ }
411
+ if (character === "\"" || character === "'") {
412
+ quote = character;
413
+ continue;
414
+ }
415
+ if (character === "{") {
416
+ braceDepth += 1;
417
+ continue;
418
+ }
419
+ if (character === "}") {
420
+ braceDepth -= 1;
421
+ continue;
422
+ }
423
+ if (character === ">" && braceDepth === 0) {
424
+ close = cursor;
425
+ break;
426
+ }
427
+ }
428
+ if (close < 0 || quote || braceDepth !== 0)
429
+ return undefined;
430
+ const opening = source.slice(openingName.end, close);
431
+ const trimmedOpening = opening.trimEnd();
432
+ const selfClosing = trimmedOpening.endsWith("/");
433
+ const attributeSource = selfClosing ? trimmedOpening.slice(0, -1) : opening;
434
+ const attributes = htmlAttributes(attributeSource, openingName.end, lower);
435
+ if (selfClosing || voidHtmlElements.has(openingName.name.toLowerCase()))
436
+ return { start, end: close + 1, code: `Element(${JSON.stringify(openingName.name)}, ${attributes})` };
437
+ const children = [];
438
+ cursor = close + 1;
439
+ while (cursor < source.length) {
440
+ if (source.startsWith("<!--", cursor)) {
441
+ const commentEnd = source.indexOf("-->", cursor + 4);
442
+ if (commentEnd < 0)
443
+ throw syntaxError("Unclosed raw HTML comment in Vune source", cursor);
444
+ cursor = commentEnd + 3;
445
+ continue;
446
+ }
447
+ if (source[cursor] === "<" && source[cursor + 1] === "/") {
448
+ const closing = /^<\/([A-Za-z][A-Za-z0-9:._-]*)([^>]*)>/.exec(source.slice(cursor));
449
+ if (!closing)
450
+ throw syntaxError("Unclosed raw HTML closing tag in Vune source", cursor);
451
+ if (closing[1] !== openingName.name)
452
+ throw syntaxError(`Mismatched raw HTML closing tag </${closing[1]}>; expected </${openingName.name}>`, cursor);
453
+ if (closing[2].trim().length > 0)
454
+ throw syntaxError("Raw HTML closing tags cannot have attributes", cursor);
455
+ const end = cursor + closing[0].length - 1;
456
+ return {
457
+ start,
458
+ end: end + 1,
459
+ code: `Element(${JSON.stringify(openingName.name)}, ${attributes}${children.length ? `, ${children.join(", ")}` : ""})`,
460
+ };
461
+ }
462
+ if (source[cursor] === "<") {
463
+ const nestedHtml = rawHtmlAt(source, cursor, lower, true);
464
+ if (!nestedHtml)
465
+ return undefined;
466
+ children.push(nestedHtml.code);
467
+ cursor = nestedHtml.end;
468
+ continue;
469
+ }
470
+ if (source[cursor] === "{") {
471
+ const end = matching(source, cursor, "{", "}");
472
+ const expression = source.slice(cursor + 1, end).trim();
473
+ if (expression)
474
+ children.push(lower(expression));
475
+ cursor = end + 1;
476
+ continue;
477
+ }
478
+ const nextTag = source.indexOf("<", cursor);
479
+ const nextExpression = source.indexOf("{", cursor);
480
+ const end = [nextTag, nextExpression].filter(value => value >= 0).sort((left, right) => left - right)[0] ?? source.length;
481
+ const text = source.slice(cursor, end);
482
+ if (text.length > 0)
483
+ children.push(JSON.stringify(decodeHtmlEntities(text)));
484
+ cursor = end;
485
+ }
486
+ return undefined;
487
+ }
488
+ function findRawHtml(source, from = 0, lower = identityExpression) {
489
+ for (let cursor = from; cursor < source.length; cursor += 1) {
490
+ if (source[cursor] === "\"" || source[cursor] === "'" || source[cursor] === "`") {
491
+ cursor = skipString(source, cursor) - 1;
492
+ continue;
493
+ }
494
+ if (source[cursor] === "/" && (source[cursor + 1] === "/" || source[cursor + 1] === "*")) {
495
+ cursor = skipComment(source, cursor) - 1;
496
+ continue;
497
+ }
498
+ if (source[cursor] === "/" && regexCanStart(source, cursor)) {
499
+ cursor = skipRegex(source, cursor) - 1;
500
+ continue;
501
+ }
502
+ if (source[cursor] !== "<")
503
+ continue;
504
+ const call = rawHtmlAt(source, cursor, lower);
505
+ if (call)
506
+ return call;
507
+ }
508
+ return undefined;
509
+ }
510
+ function validateRawHtmlSyntax(source) {
511
+ for (let cursor = 0; cursor < source.length; cursor += 1) {
512
+ if (source[cursor] === "\"" || source[cursor] === "'" || source[cursor] === "`") {
513
+ cursor = skipString(source, cursor) - 1;
514
+ continue;
515
+ }
516
+ if (source[cursor] === "/" && (source[cursor + 1] === "/" || source[cursor + 1] === "*")) {
517
+ cursor = skipComment(source, cursor) - 1;
518
+ continue;
519
+ }
520
+ if (source[cursor] === "/" && regexCanStart(source, cursor)) {
521
+ cursor = skipRegex(source, cursor) - 1;
522
+ continue;
523
+ }
524
+ if (!isRawHtmlCandidate(source, cursor))
525
+ continue;
526
+ const html = rawHtmlAt(source, cursor);
527
+ if (html) {
528
+ cursor = html.end - 1;
529
+ continue;
530
+ }
531
+ throw syntaxError("Unclosed raw HTML element in Vune source", cursor);
532
+ }
533
+ }
534
+ function splitTopLevel(source, separator = ",") {
535
+ const parts = [];
536
+ let start = 0;
537
+ let parens = 0;
538
+ let brackets = 0;
539
+ let braces = 0;
540
+ for (let cursor = 0; cursor < source.length; cursor += 1) {
541
+ const character = source[cursor];
542
+ if (character === "\"" || character === "'" || character === "`") {
543
+ cursor = skipString(source, cursor) - 1;
544
+ continue;
545
+ }
546
+ if (character === "/" && (source[cursor + 1] === "/" || source[cursor + 1] === "*")) {
547
+ cursor = skipComment(source, cursor) - 1;
548
+ continue;
549
+ }
550
+ if (character === "/" && regexCanStart(source, cursor)) {
551
+ cursor = skipRegex(source, cursor) - 1;
552
+ continue;
553
+ }
554
+ if (character === "<") {
555
+ const html = rawHtmlAt(source, cursor);
556
+ if (html) {
557
+ cursor = html.end - 1;
558
+ continue;
559
+ }
560
+ }
561
+ if (character === "(")
562
+ parens += 1;
563
+ else if (character === ")")
564
+ parens -= 1;
565
+ else if (character === "[")
566
+ brackets += 1;
567
+ else if (character === "]")
568
+ brackets -= 1;
569
+ else if (character === "{")
570
+ braces += 1;
571
+ else if (character === "}")
572
+ braces -= 1;
573
+ else if (character === separator && parens === 0 && brackets === 0 && braces === 0) {
574
+ parts.push(source.slice(start, cursor).trim());
575
+ start = cursor + 1;
576
+ }
577
+ }
578
+ const tail = source.slice(start).trim();
579
+ if (tail)
580
+ parts.push(tail);
581
+ return parts;
582
+ }
583
+ function splitStatements(source) {
584
+ const parts = [];
585
+ let start = 0;
586
+ let parens = 0;
587
+ let brackets = 0;
588
+ let braces = 0;
589
+ for (let cursor = 0; cursor < source.length; cursor += 1) {
590
+ const character = source[cursor];
591
+ if (character === "\"" || character === "'" || character === "`") {
592
+ cursor = skipString(source, cursor) - 1;
593
+ continue;
594
+ }
595
+ if (character === "/" && (source[cursor + 1] === "/" || source[cursor + 1] === "*")) {
596
+ cursor = skipComment(source, cursor) - 1;
597
+ continue;
598
+ }
599
+ if (character === "/" && regexCanStart(source, cursor)) {
600
+ cursor = skipRegex(source, cursor) - 1;
601
+ continue;
602
+ }
603
+ if (character === "<") {
604
+ const html = rawHtmlAt(source, cursor);
605
+ if (html) {
606
+ cursor = html.end - 1;
607
+ continue;
608
+ }
609
+ }
610
+ if (character === "(")
611
+ parens += 1;
612
+ else if (character === ")")
613
+ parens -= 1;
614
+ else if (character === "[")
615
+ brackets += 1;
616
+ else if (character === "]")
617
+ brackets -= 1;
618
+ else if (character === "{")
619
+ braces += 1;
620
+ else if (character === "}")
621
+ braces -= 1;
622
+ const boundary = character === ";" || (character === "\n" && parens === 0 && brackets === 0 && braces === 0);
623
+ if (boundary && parens === 0 && brackets === 0 && braces === 0) {
624
+ const part = source.slice(start, cursor).trim();
625
+ if (part)
626
+ parts.push(part);
627
+ start = cursor + 1;
628
+ }
629
+ }
630
+ const tail = source.slice(start).trim();
631
+ if (tail)
632
+ parts.push(tail);
633
+ return parts;
634
+ }
635
+ function topLevelColon(source) {
636
+ let parens = 0;
637
+ let brackets = 0;
638
+ let braces = 0;
639
+ let ternary = 0;
640
+ for (let cursor = 0; cursor < source.length; cursor += 1) {
641
+ const character = source[cursor];
642
+ if (character === "\"" || character === "'" || character === "`") {
643
+ cursor = skipString(source, cursor) - 1;
644
+ continue;
645
+ }
646
+ if (character === "/" && (source[cursor + 1] === "/" || source[cursor + 1] === "*")) {
647
+ cursor = skipComment(source, cursor) - 1;
648
+ continue;
649
+ }
650
+ if (character === "/" && regexCanStart(source, cursor)) {
651
+ cursor = skipRegex(source, cursor) - 1;
652
+ continue;
653
+ }
654
+ if (character === "(")
655
+ parens += 1;
656
+ else if (character === ")")
657
+ parens -= 1;
658
+ else if (character === "[")
659
+ brackets += 1;
660
+ else if (character === "]")
661
+ brackets -= 1;
662
+ else if (character === "{")
663
+ braces += 1;
664
+ else if (character === "}")
665
+ braces -= 1;
666
+ else if (parens === 0 && brackets === 0 && braces === 0 && character === "?" && source[cursor + 1] !== ".")
667
+ ternary += 1;
668
+ else if (character === ":" && parens === 0 && brackets === 0 && braces === 0) {
669
+ if (ternary > 0)
670
+ ternary -= 1;
671
+ else
672
+ return cursor;
673
+ }
674
+ }
675
+ return -1;
676
+ }
677
+ export { syntaxError, skipString, skipComment, regexCanStart, skipRegex, skipTrivia, matching, identifierAt, isRawHtmlCandidate, findBuilder, skipHtmlTrivia, htmlNameAt, htmlAttributeNameAt, htmlAttributes, rawHtmlAt, findRawHtml, validateRawHtmlSyntax, splitTopLevel, splitStatements, topLevelColon, };
678
+ //# sourceMappingURL=scanner.js.map