vsn 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1864 -716
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +194 -53
- package/dist/index.d.ts +194 -53
- package/dist/index.js +1859 -714
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +8 -8
- package/dist/index.min.js.map +1 -1
- package/dist/plugins/microdata.d.ts +481 -0
- package/dist/plugins/microdata.js +178 -0
- package/dist/plugins/microdata.js.map +1 -0
- package/dist/plugins/microdata.min.js +2 -0
- package/dist/plugins/microdata.min.js.map +1 -0
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -22,16 +22,21 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
ArrayExpression: () => ArrayExpression,
|
|
24
24
|
ArrayPattern: () => ArrayPattern,
|
|
25
|
+
AssertError: () => AssertError,
|
|
26
|
+
AssertNode: () => AssertNode,
|
|
25
27
|
AssignmentNode: () => AssignmentNode,
|
|
26
28
|
AwaitExpression: () => AwaitExpression,
|
|
27
29
|
BaseNode: () => BaseNode,
|
|
28
30
|
BehaviorNode: () => BehaviorNode,
|
|
29
31
|
BinaryExpression: () => BinaryExpression,
|
|
30
32
|
BlockNode: () => BlockNode,
|
|
33
|
+
BreakNode: () => BreakNode,
|
|
31
34
|
CallExpression: () => CallExpression,
|
|
35
|
+
ContinueNode: () => ContinueNode,
|
|
32
36
|
DeclarationNode: () => DeclarationNode,
|
|
33
37
|
DirectiveExpression: () => DirectiveExpression,
|
|
34
38
|
Engine: () => Engine,
|
|
39
|
+
ForEachNode: () => ForEachNode,
|
|
35
40
|
ForNode: () => ForNode,
|
|
36
41
|
FunctionDeclarationNode: () => FunctionDeclarationNode,
|
|
37
42
|
FunctionExpression: () => FunctionExpression,
|
|
@@ -51,8 +56,6 @@ __export(index_exports, {
|
|
|
51
56
|
ReturnNode: () => ReturnNode,
|
|
52
57
|
SelectorNode: () => SelectorNode,
|
|
53
58
|
SpreadElement: () => SpreadElement,
|
|
54
|
-
StateBlockNode: () => StateBlockNode,
|
|
55
|
-
StateEntryNode: () => StateEntryNode,
|
|
56
59
|
TemplateExpression: () => TemplateExpression,
|
|
57
60
|
TernaryExpression: () => TernaryExpression,
|
|
58
61
|
TokenType: () => TokenType,
|
|
@@ -88,6 +91,9 @@ var TokenType = /* @__PURE__ */ ((TokenType2) => {
|
|
|
88
91
|
TokenType2["While"] = "While";
|
|
89
92
|
TokenType2["Try"] = "Try";
|
|
90
93
|
TokenType2["Catch"] = "Catch";
|
|
94
|
+
TokenType2["Assert"] = "Assert";
|
|
95
|
+
TokenType2["Break"] = "Break";
|
|
96
|
+
TokenType2["Continue"] = "Continue";
|
|
91
97
|
TokenType2["LBrace"] = "LBrace";
|
|
92
98
|
TokenType2["RBrace"] = "RBrace";
|
|
93
99
|
TokenType2["LParen"] = "LParen";
|
|
@@ -103,7 +109,9 @@ var TokenType = /* @__PURE__ */ ((TokenType2) => {
|
|
|
103
109
|
TokenType2["Greater"] = "Greater";
|
|
104
110
|
TokenType2["Less"] = "Less";
|
|
105
111
|
TokenType2["Plus"] = "Plus";
|
|
112
|
+
TokenType2["PlusPlus"] = "PlusPlus";
|
|
106
113
|
TokenType2["Minus"] = "Minus";
|
|
114
|
+
TokenType2["MinusMinus"] = "MinusMinus";
|
|
107
115
|
TokenType2["Tilde"] = "Tilde";
|
|
108
116
|
TokenType2["Star"] = "Star";
|
|
109
117
|
TokenType2["Slash"] = "Slash";
|
|
@@ -143,6 +151,9 @@ var KEYWORDS = {
|
|
|
143
151
|
while: "While" /* While */,
|
|
144
152
|
try: "Try" /* Try */,
|
|
145
153
|
catch: "Catch" /* Catch */,
|
|
154
|
+
assert: "Assert" /* Assert */,
|
|
155
|
+
break: "Break" /* Break */,
|
|
156
|
+
continue: "Continue" /* Continue */,
|
|
146
157
|
true: "Boolean" /* Boolean */,
|
|
147
158
|
false: "Boolean" /* Boolean */,
|
|
148
159
|
null: "Null" /* Null */
|
|
@@ -244,8 +255,20 @@ var Lexer = class {
|
|
|
244
255
|
readIdentifier() {
|
|
245
256
|
const start = this.position();
|
|
246
257
|
let value = "";
|
|
247
|
-
while (!this.eof()
|
|
248
|
-
|
|
258
|
+
while (!this.eof()) {
|
|
259
|
+
const ch = this.peek();
|
|
260
|
+
if (this.isAlphaNumeric(ch) || ch === "_") {
|
|
261
|
+
value += this.next();
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (ch === "-") {
|
|
265
|
+
if (this.peek(1) === "-") {
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
value += this.next();
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
break;
|
|
249
272
|
}
|
|
250
273
|
const keywordType = KEYWORDS[value];
|
|
251
274
|
if (keywordType) {
|
|
@@ -386,6 +409,16 @@ var Lexer = class {
|
|
|
386
409
|
this.next();
|
|
387
410
|
return this.token("Pipe" /* Pipe */, "|>", start);
|
|
388
411
|
}
|
|
412
|
+
if (ch === "+" && next === "+") {
|
|
413
|
+
this.next();
|
|
414
|
+
this.next();
|
|
415
|
+
return this.token("PlusPlus" /* PlusPlus */, "++", start);
|
|
416
|
+
}
|
|
417
|
+
if (ch === "-" && next === "-") {
|
|
418
|
+
this.next();
|
|
419
|
+
this.next();
|
|
420
|
+
return this.token("MinusMinus" /* MinusMinus */, "--", start);
|
|
421
|
+
}
|
|
389
422
|
if (ch === "." && next === "." && this.peek(2) === ".") {
|
|
390
423
|
this.next();
|
|
391
424
|
this.next();
|
|
@@ -488,11 +521,20 @@ var BaseNode = class {
|
|
|
488
521
|
async prepare(_context) {
|
|
489
522
|
return;
|
|
490
523
|
}
|
|
491
|
-
|
|
524
|
+
evaluate(_context) {
|
|
492
525
|
return void 0;
|
|
493
526
|
}
|
|
494
527
|
};
|
|
495
|
-
|
|
528
|
+
function isPromiseLike(value) {
|
|
529
|
+
return Boolean(value) && typeof value.then === "function";
|
|
530
|
+
}
|
|
531
|
+
function resolveMaybe(value, next) {
|
|
532
|
+
if (isPromiseLike(value)) {
|
|
533
|
+
return value.then(next);
|
|
534
|
+
}
|
|
535
|
+
return next(value);
|
|
536
|
+
}
|
|
537
|
+
function evaluateWithChildScope(context, block) {
|
|
496
538
|
const scope = context.scope;
|
|
497
539
|
if (!scope || !scope.createChild) {
|
|
498
540
|
return block.evaluate(context);
|
|
@@ -500,7 +542,7 @@ async function evaluateWithChildScope(context, block) {
|
|
|
500
542
|
const previousScope = context.scope;
|
|
501
543
|
context.scope = scope.createChild();
|
|
502
544
|
try {
|
|
503
|
-
return
|
|
545
|
+
return block.evaluate(context);
|
|
504
546
|
} finally {
|
|
505
547
|
context.scope = previousScope;
|
|
506
548
|
}
|
|
@@ -526,15 +568,25 @@ var BlockNode = class extends BaseNode {
|
|
|
526
568
|
super("Block");
|
|
527
569
|
this.statements = statements;
|
|
528
570
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
571
|
+
evaluate(context) {
|
|
572
|
+
let index = 0;
|
|
573
|
+
const run = () => {
|
|
574
|
+
while (index < this.statements.length) {
|
|
575
|
+
if (context.returning || context.breaking || context.continuing) {
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
const statement = this.statements[index];
|
|
579
|
+
index += 1;
|
|
580
|
+
if (statement && typeof statement.evaluate === "function") {
|
|
581
|
+
const result = statement.evaluate(context);
|
|
582
|
+
if (isPromiseLike(result)) {
|
|
583
|
+
return result.then(() => run());
|
|
584
|
+
}
|
|
585
|
+
}
|
|
536
586
|
}
|
|
537
|
-
|
|
587
|
+
return void 0;
|
|
588
|
+
};
|
|
589
|
+
return run();
|
|
538
590
|
}
|
|
539
591
|
};
|
|
540
592
|
var SelectorNode = class extends BaseNode {
|
|
@@ -544,87 +596,205 @@ var SelectorNode = class extends BaseNode {
|
|
|
544
596
|
}
|
|
545
597
|
};
|
|
546
598
|
var BehaviorNode = class extends BaseNode {
|
|
547
|
-
constructor(selector, body) {
|
|
599
|
+
constructor(selector, body, flags = {}, flagArgs = {}) {
|
|
548
600
|
super("Behavior");
|
|
549
601
|
this.selector = selector;
|
|
550
602
|
this.body = body;
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
var StateEntryNode = class extends BaseNode {
|
|
554
|
-
constructor(name, value, important) {
|
|
555
|
-
super("StateEntry");
|
|
556
|
-
this.name = name;
|
|
557
|
-
this.value = value;
|
|
558
|
-
this.important = important;
|
|
559
|
-
}
|
|
560
|
-
};
|
|
561
|
-
var StateBlockNode = class extends BaseNode {
|
|
562
|
-
constructor(entries) {
|
|
563
|
-
super("StateBlock");
|
|
564
|
-
this.entries = entries;
|
|
603
|
+
this.flags = flags;
|
|
604
|
+
this.flagArgs = flagArgs;
|
|
565
605
|
}
|
|
566
606
|
};
|
|
567
607
|
var OnBlockNode = class extends BaseNode {
|
|
568
|
-
constructor(eventName, args, body,
|
|
608
|
+
constructor(eventName, args, body, flags = {}, flagArgs = {}) {
|
|
569
609
|
super("OnBlock");
|
|
570
610
|
this.eventName = eventName;
|
|
571
611
|
this.args = args;
|
|
572
612
|
this.body = body;
|
|
573
|
-
this.
|
|
613
|
+
this.flags = flags;
|
|
614
|
+
this.flagArgs = flagArgs;
|
|
574
615
|
}
|
|
575
616
|
};
|
|
576
617
|
var AssignmentNode = class extends BaseNode {
|
|
577
|
-
constructor(target, value, operator = "=") {
|
|
618
|
+
constructor(target, value, operator = "=", prefix = false) {
|
|
578
619
|
super("Assignment");
|
|
579
620
|
this.target = target;
|
|
580
621
|
this.value = value;
|
|
581
622
|
this.operator = operator;
|
|
623
|
+
this.prefix = prefix;
|
|
582
624
|
}
|
|
583
|
-
|
|
625
|
+
evaluate(context) {
|
|
584
626
|
if (!context.scope || !context.scope.setPath) {
|
|
585
627
|
return void 0;
|
|
586
628
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
629
|
+
if (this.operator === "++" || this.operator === "--") {
|
|
630
|
+
return this.applyIncrement(context);
|
|
631
|
+
}
|
|
632
|
+
const value = this.value.evaluate(context);
|
|
633
|
+
return resolveMaybe(value, (resolvedValue) => {
|
|
634
|
+
if (this.operator !== "=") {
|
|
635
|
+
return this.applyCompoundAssignment(context, resolvedValue);
|
|
636
|
+
}
|
|
637
|
+
if (this.target instanceof IdentifierExpression && this.target.name.startsWith("root.") && context.rootScope) {
|
|
638
|
+
const path = this.target.name.slice("root.".length);
|
|
639
|
+
context.rootScope.setPath?.(`self.${path}`, resolvedValue);
|
|
640
|
+
return resolvedValue;
|
|
641
|
+
}
|
|
642
|
+
if (this.target instanceof MemberExpression || this.target instanceof IndexExpression) {
|
|
643
|
+
const resolved = this.resolveAssignmentTarget(context);
|
|
644
|
+
return resolveMaybe(resolved, (resolvedTarget) => {
|
|
645
|
+
if (resolvedTarget?.scope?.setPath) {
|
|
646
|
+
resolvedTarget.scope.setPath(resolvedTarget.path, resolvedValue);
|
|
647
|
+
return resolvedValue;
|
|
648
|
+
}
|
|
649
|
+
this.assignTarget(context, this.target, resolvedValue);
|
|
650
|
+
return resolvedValue;
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
this.assignTarget(context, this.target, resolvedValue);
|
|
654
|
+
return resolvedValue;
|
|
655
|
+
});
|
|
598
656
|
}
|
|
599
657
|
applyCompoundAssignment(context, value) {
|
|
600
658
|
if (!context.scope || !context.scope.setPath) {
|
|
601
659
|
return void 0;
|
|
602
660
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
661
|
+
const resolved = this.resolveAssignmentTarget(context);
|
|
662
|
+
return resolveMaybe(resolved, (resolvedTarget) => {
|
|
663
|
+
if (!resolvedTarget) {
|
|
664
|
+
throw new Error("Compound assignment requires a simple identifier or member path");
|
|
665
|
+
}
|
|
666
|
+
const { scope, path } = resolvedTarget;
|
|
667
|
+
const current = scope?.getPath ? scope.getPath(path) : void 0;
|
|
668
|
+
let result;
|
|
669
|
+
if (this.operator === "+=") {
|
|
670
|
+
result = current + value;
|
|
671
|
+
} else if (this.operator === "-=") {
|
|
672
|
+
result = current - value;
|
|
673
|
+
} else if (this.operator === "*=") {
|
|
674
|
+
result = current * value;
|
|
675
|
+
} else {
|
|
676
|
+
result = current / value;
|
|
677
|
+
}
|
|
678
|
+
scope?.setPath?.(path, result);
|
|
679
|
+
return result;
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
applyIncrement(context) {
|
|
683
|
+
if (!context.scope || !context.scope.setPath) {
|
|
684
|
+
return void 0;
|
|
620
685
|
}
|
|
621
|
-
|
|
622
|
-
return
|
|
686
|
+
const resolved = this.resolveAssignmentTarget(context);
|
|
687
|
+
return resolveMaybe(resolved, (resolvedTarget) => {
|
|
688
|
+
if (!resolvedTarget) {
|
|
689
|
+
throw new Error("Increment/decrement requires a simple identifier or member path");
|
|
690
|
+
}
|
|
691
|
+
const { scope, path } = resolvedTarget;
|
|
692
|
+
const current = scope?.getPath ? scope.getPath(path) : void 0;
|
|
693
|
+
const numeric = typeof current === "number" ? current : Number(current);
|
|
694
|
+
const delta = this.operator === "++" ? 1 : -1;
|
|
695
|
+
const next = (Number.isNaN(numeric) ? 0 : numeric) + delta;
|
|
696
|
+
scope?.setPath?.(path, next);
|
|
697
|
+
return this.prefix ? next : numeric;
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
resolveAssignmentTarget(context) {
|
|
701
|
+
if (this.target instanceof IdentifierExpression) {
|
|
702
|
+
const isRoot = this.target.name.startsWith("root.");
|
|
703
|
+
const rawPath = isRoot ? this.target.name.slice("root.".length) : this.target.name;
|
|
704
|
+
if (isRoot) {
|
|
705
|
+
if (context.rootScope) {
|
|
706
|
+
return { scope: context.rootScope, path: `self.${rawPath}` };
|
|
707
|
+
}
|
|
708
|
+
return { scope: context.scope, path: `root.${rawPath}` };
|
|
709
|
+
}
|
|
710
|
+
return { scope: context.scope, path: rawPath };
|
|
711
|
+
}
|
|
712
|
+
if (this.target instanceof MemberExpression) {
|
|
713
|
+
const resolvedPath = this.target.getIdentifierPath();
|
|
714
|
+
if (resolvedPath) {
|
|
715
|
+
const path = resolvedPath.path;
|
|
716
|
+
const isRoot = path.startsWith("root.");
|
|
717
|
+
const rawPath = isRoot ? path.slice("root.".length) : path;
|
|
718
|
+
if (isRoot) {
|
|
719
|
+
if (context.rootScope) {
|
|
720
|
+
return { scope: context.rootScope, path: `self.${rawPath}` };
|
|
721
|
+
}
|
|
722
|
+
return { scope: context.scope, path: `root.${rawPath}` };
|
|
723
|
+
}
|
|
724
|
+
return { scope: context.scope, path: rawPath };
|
|
725
|
+
}
|
|
726
|
+
const targetExpr = this.target;
|
|
727
|
+
const basePath = this.resolveTargetPath(context, targetExpr.target);
|
|
728
|
+
return resolveMaybe(basePath, (resolvedBase) => {
|
|
729
|
+
if (!resolvedBase) {
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
732
|
+
const path = `${resolvedBase}.${targetExpr.property}`;
|
|
733
|
+
const isRoot = path.startsWith("root.");
|
|
734
|
+
const rawPath = isRoot ? path.slice("root.".length) : path;
|
|
735
|
+
if (isRoot) {
|
|
736
|
+
if (context.rootScope) {
|
|
737
|
+
return { scope: context.rootScope, path: `self.${rawPath}` };
|
|
738
|
+
}
|
|
739
|
+
return { scope: context.scope, path: `root.${rawPath}` };
|
|
740
|
+
}
|
|
741
|
+
return { scope: context.scope, path: rawPath };
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
if (this.target instanceof IndexExpression) {
|
|
745
|
+
const path = this.resolveIndexPath(context, this.target);
|
|
746
|
+
return resolveMaybe(path, (resolvedPath) => {
|
|
747
|
+
if (!resolvedPath) {
|
|
748
|
+
return null;
|
|
749
|
+
}
|
|
750
|
+
const isRoot = resolvedPath.startsWith("root.");
|
|
751
|
+
const rawPath = isRoot ? resolvedPath.slice("root.".length) : resolvedPath;
|
|
752
|
+
if (isRoot) {
|
|
753
|
+
if (context.rootScope) {
|
|
754
|
+
return { scope: context.rootScope, path: `self.${rawPath}` };
|
|
755
|
+
}
|
|
756
|
+
return { scope: context.scope, path: `root.${rawPath}` };
|
|
757
|
+
}
|
|
758
|
+
return { scope: context.scope, path: rawPath };
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
resolveIndexPath(context, expr) {
|
|
764
|
+
const base = this.resolveTargetPath(context, expr.target);
|
|
765
|
+
return resolveMaybe(base, (resolvedBase) => {
|
|
766
|
+
if (!resolvedBase) {
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
const indexValue = expr.index.evaluate(context);
|
|
770
|
+
return resolveMaybe(indexValue, (resolvedIndex) => {
|
|
771
|
+
if (resolvedIndex == null) {
|
|
772
|
+
return null;
|
|
773
|
+
}
|
|
774
|
+
return `${resolvedBase}.${resolvedIndex}`;
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
resolveTargetPath(context, target) {
|
|
779
|
+
if (target instanceof IdentifierExpression) {
|
|
780
|
+
return target.name;
|
|
781
|
+
}
|
|
782
|
+
if (target instanceof MemberExpression) {
|
|
783
|
+
return target.getIdentifierPath()?.path ?? null;
|
|
784
|
+
}
|
|
785
|
+
if (target instanceof IndexExpression) {
|
|
786
|
+
return this.resolveIndexPath(context, target);
|
|
787
|
+
}
|
|
788
|
+
return null;
|
|
623
789
|
}
|
|
624
790
|
assignTarget(context, target, value) {
|
|
625
791
|
if (!context.scope || !context.scope.setPath) {
|
|
626
792
|
return;
|
|
627
793
|
}
|
|
794
|
+
if (target instanceof DirectiveExpression) {
|
|
795
|
+
this.assignDirectiveTarget(context, target, value);
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
628
798
|
if (target instanceof IdentifierExpression) {
|
|
629
799
|
context.scope.setPath(target.name, value);
|
|
630
800
|
return;
|
|
@@ -666,19 +836,99 @@ var AssignmentNode = class extends BaseNode {
|
|
|
666
836
|
return;
|
|
667
837
|
}
|
|
668
838
|
}
|
|
839
|
+
assignDirectiveTarget(context, target, value) {
|
|
840
|
+
const element = context.element;
|
|
841
|
+
if (!element) {
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
if (target.kind === "attr") {
|
|
845
|
+
if (target.name === "value") {
|
|
846
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
|
|
847
|
+
element.value = value == null ? "" : String(value);
|
|
848
|
+
element.setAttribute("value", element.value);
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
if (element instanceof HTMLSelectElement) {
|
|
852
|
+
element.value = value == null ? "" : String(value);
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
if (target.name === "checked" && element instanceof HTMLInputElement) {
|
|
857
|
+
const checked = value === true || value === "true" || value === 1 || value === "1";
|
|
858
|
+
element.checked = checked;
|
|
859
|
+
if (checked) {
|
|
860
|
+
element.setAttribute("checked", "");
|
|
861
|
+
} else {
|
|
862
|
+
element.removeAttribute("checked");
|
|
863
|
+
}
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
if (target.name === "html" && element instanceof HTMLElement) {
|
|
867
|
+
element.innerHTML = value == null ? "" : String(value);
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
element.setAttribute(target.name, value == null ? "" : String(value));
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
if (target.kind === "style" && element instanceof HTMLElement) {
|
|
874
|
+
element.style.setProperty(target.name, value == null ? "" : String(value));
|
|
875
|
+
}
|
|
876
|
+
}
|
|
669
877
|
};
|
|
670
878
|
var ReturnNode = class extends BaseNode {
|
|
671
879
|
constructor(value) {
|
|
672
880
|
super("Return");
|
|
673
881
|
this.value = value;
|
|
674
882
|
}
|
|
675
|
-
|
|
883
|
+
evaluate(context) {
|
|
676
884
|
if (context.returning) {
|
|
677
885
|
return context.returnValue;
|
|
678
886
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
887
|
+
const nextValue = this.value ? this.value.evaluate(context) : void 0;
|
|
888
|
+
return resolveMaybe(nextValue, (resolved) => {
|
|
889
|
+
context.returnValue = resolved;
|
|
890
|
+
context.returning = true;
|
|
891
|
+
return context.returnValue;
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
var BreakNode = class extends BaseNode {
|
|
896
|
+
constructor() {
|
|
897
|
+
super("Break");
|
|
898
|
+
}
|
|
899
|
+
evaluate(context) {
|
|
900
|
+
context.breaking = true;
|
|
901
|
+
return void 0;
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
var ContinueNode = class extends BaseNode {
|
|
905
|
+
constructor() {
|
|
906
|
+
super("Continue");
|
|
907
|
+
}
|
|
908
|
+
evaluate(context) {
|
|
909
|
+
context.continuing = true;
|
|
910
|
+
return void 0;
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
var AssertError = class extends Error {
|
|
914
|
+
constructor(message = "Assertion failed") {
|
|
915
|
+
super(message);
|
|
916
|
+
this.name = "AssertError";
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
var AssertNode = class extends BaseNode {
|
|
920
|
+
constructor(test) {
|
|
921
|
+
super("Assert");
|
|
922
|
+
this.test = test;
|
|
923
|
+
}
|
|
924
|
+
evaluate(context) {
|
|
925
|
+
const value = this.test.evaluate(context);
|
|
926
|
+
return resolveMaybe(value, (resolved) => {
|
|
927
|
+
if (!resolved) {
|
|
928
|
+
throw new AssertError();
|
|
929
|
+
}
|
|
930
|
+
return resolved;
|
|
931
|
+
});
|
|
682
932
|
}
|
|
683
933
|
};
|
|
684
934
|
var IfNode = class extends BaseNode {
|
|
@@ -688,14 +938,17 @@ var IfNode = class extends BaseNode {
|
|
|
688
938
|
this.consequent = consequent;
|
|
689
939
|
this.alternate = alternate;
|
|
690
940
|
}
|
|
691
|
-
|
|
692
|
-
const condition =
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
941
|
+
evaluate(context) {
|
|
942
|
+
const condition = this.test.evaluate(context);
|
|
943
|
+
return resolveMaybe(condition, (resolved) => {
|
|
944
|
+
if (resolved) {
|
|
945
|
+
return evaluateWithChildScope(context, this.consequent);
|
|
946
|
+
}
|
|
947
|
+
if (this.alternate) {
|
|
948
|
+
return evaluateWithChildScope(context, this.alternate);
|
|
949
|
+
}
|
|
950
|
+
return void 0;
|
|
951
|
+
});
|
|
699
952
|
}
|
|
700
953
|
};
|
|
701
954
|
var WhileNode = class extends BaseNode {
|
|
@@ -704,21 +957,104 @@ var WhileNode = class extends BaseNode {
|
|
|
704
957
|
this.test = test;
|
|
705
958
|
this.body = body;
|
|
706
959
|
}
|
|
707
|
-
|
|
960
|
+
evaluate(context) {
|
|
708
961
|
const previousScope = context.scope;
|
|
709
962
|
if (context.scope?.createChild) {
|
|
710
963
|
context.scope = context.scope.createChild();
|
|
711
964
|
}
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
if (context.returning) {
|
|
716
|
-
|
|
965
|
+
const run = () => {
|
|
966
|
+
const condition = this.test.evaluate(context);
|
|
967
|
+
return resolveMaybe(condition, (resolved) => {
|
|
968
|
+
if (!resolved || context.returning) {
|
|
969
|
+
return void 0;
|
|
717
970
|
}
|
|
971
|
+
const bodyResult = this.body.evaluate(context);
|
|
972
|
+
return resolveMaybe(bodyResult, () => {
|
|
973
|
+
if (context.breaking) {
|
|
974
|
+
context.breaking = false;
|
|
975
|
+
return void 0;
|
|
976
|
+
}
|
|
977
|
+
if (context.continuing) {
|
|
978
|
+
context.continuing = false;
|
|
979
|
+
}
|
|
980
|
+
return run();
|
|
981
|
+
});
|
|
982
|
+
});
|
|
983
|
+
};
|
|
984
|
+
const result = run();
|
|
985
|
+
if (isPromiseLike(result)) {
|
|
986
|
+
return result.finally(() => {
|
|
987
|
+
context.scope = previousScope;
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
context.scope = previousScope;
|
|
991
|
+
return result;
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
var ForEachNode = class extends BaseNode {
|
|
995
|
+
constructor(target, iterable, kind, body) {
|
|
996
|
+
super("ForEach");
|
|
997
|
+
this.target = target;
|
|
998
|
+
this.iterable = iterable;
|
|
999
|
+
this.kind = kind;
|
|
1000
|
+
this.body = body;
|
|
1001
|
+
}
|
|
1002
|
+
evaluate(context) {
|
|
1003
|
+
const iterableValue = this.iterable.evaluate(context);
|
|
1004
|
+
return resolveMaybe(iterableValue, (resolved) => {
|
|
1005
|
+
const entries = this.getEntries(resolved);
|
|
1006
|
+
const previousScope = context.scope;
|
|
1007
|
+
let bodyScope = context.scope;
|
|
1008
|
+
if (context.scope?.createChild) {
|
|
1009
|
+
bodyScope = context.scope.createChild();
|
|
718
1010
|
}
|
|
719
|
-
|
|
720
|
-
|
|
1011
|
+
let index = 0;
|
|
1012
|
+
const loop = () => {
|
|
1013
|
+
if (index >= entries.length || context.returning) {
|
|
1014
|
+
context.scope = previousScope;
|
|
1015
|
+
return void 0;
|
|
1016
|
+
}
|
|
1017
|
+
const value = entries[index];
|
|
1018
|
+
index += 1;
|
|
1019
|
+
context.scope = bodyScope;
|
|
1020
|
+
context.scope?.setPath?.(this.target.name, value);
|
|
1021
|
+
const bodyResult = this.body.evaluate(context);
|
|
1022
|
+
return resolveMaybe(bodyResult, () => {
|
|
1023
|
+
if (context.breaking) {
|
|
1024
|
+
context.breaking = false;
|
|
1025
|
+
context.scope = previousScope;
|
|
1026
|
+
return void 0;
|
|
1027
|
+
}
|
|
1028
|
+
if (context.continuing) {
|
|
1029
|
+
context.continuing = false;
|
|
1030
|
+
}
|
|
1031
|
+
context.scope = previousScope;
|
|
1032
|
+
return loop();
|
|
1033
|
+
});
|
|
1034
|
+
};
|
|
1035
|
+
return loop();
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
getEntries(value) {
|
|
1039
|
+
if (value == null) {
|
|
1040
|
+
return [];
|
|
1041
|
+
}
|
|
1042
|
+
if (this.kind === "in") {
|
|
1043
|
+
if (typeof value === "object") {
|
|
1044
|
+
return Object.keys(value);
|
|
1045
|
+
}
|
|
1046
|
+
return [];
|
|
1047
|
+
}
|
|
1048
|
+
if (typeof value === "string") {
|
|
1049
|
+
return Array.from(value);
|
|
1050
|
+
}
|
|
1051
|
+
if (typeof value[Symbol.iterator] === "function") {
|
|
1052
|
+
return Array.from(value);
|
|
1053
|
+
}
|
|
1054
|
+
if (typeof value === "object") {
|
|
1055
|
+
return Object.values(value);
|
|
721
1056
|
}
|
|
1057
|
+
return [];
|
|
722
1058
|
}
|
|
723
1059
|
};
|
|
724
1060
|
var ForNode = class extends BaseNode {
|
|
@@ -729,27 +1065,45 @@ var ForNode = class extends BaseNode {
|
|
|
729
1065
|
this.update = update;
|
|
730
1066
|
this.body = body;
|
|
731
1067
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
1068
|
+
evaluate(context) {
|
|
1069
|
+
const initResult = this.init ? this.init.evaluate(context) : void 0;
|
|
1070
|
+
const run = () => {
|
|
1071
|
+
const previousScope = context.scope;
|
|
1072
|
+
let bodyScope = context.scope;
|
|
1073
|
+
if (context.scope?.createChild) {
|
|
1074
|
+
bodyScope = context.scope.createChild();
|
|
1075
|
+
}
|
|
1076
|
+
const loop = () => {
|
|
1077
|
+
const testResult = this.test ? this.test.evaluate(context) : true;
|
|
1078
|
+
return resolveMaybe(testResult, (passed) => {
|
|
1079
|
+
if (!passed || context.returning) {
|
|
1080
|
+
context.scope = previousScope;
|
|
1081
|
+
return void 0;
|
|
1082
|
+
}
|
|
1083
|
+
context.scope = bodyScope;
|
|
1084
|
+
const bodyResult = this.body.evaluate(context);
|
|
1085
|
+
return resolveMaybe(bodyResult, () => {
|
|
1086
|
+
if (context.returning) {
|
|
1087
|
+
context.scope = previousScope;
|
|
1088
|
+
return void 0;
|
|
1089
|
+
}
|
|
1090
|
+
if (context.breaking) {
|
|
1091
|
+
context.breaking = false;
|
|
1092
|
+
context.scope = previousScope;
|
|
1093
|
+
return void 0;
|
|
1094
|
+
}
|
|
1095
|
+
context.scope = previousScope;
|
|
1096
|
+
if (context.continuing) {
|
|
1097
|
+
context.continuing = false;
|
|
1098
|
+
}
|
|
1099
|
+
const updateResult = this.update ? this.update.evaluate(context) : void 0;
|
|
1100
|
+
return resolveMaybe(updateResult, () => loop());
|
|
1101
|
+
});
|
|
1102
|
+
});
|
|
1103
|
+
};
|
|
1104
|
+
return loop();
|
|
1105
|
+
};
|
|
1106
|
+
return resolveMaybe(initResult, () => run());
|
|
753
1107
|
}
|
|
754
1108
|
};
|
|
755
1109
|
var TryNode = class extends BaseNode {
|
|
@@ -759,10 +1113,8 @@ var TryNode = class extends BaseNode {
|
|
|
759
1113
|
this.errorName = errorName;
|
|
760
1114
|
this.handler = handler;
|
|
761
1115
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
return await evaluateWithChildScope(context, this.body);
|
|
765
|
-
} catch (error) {
|
|
1116
|
+
evaluate(context) {
|
|
1117
|
+
const handleError = (error) => {
|
|
766
1118
|
if (context.returning) {
|
|
767
1119
|
return context.returnValue;
|
|
768
1120
|
}
|
|
@@ -780,11 +1132,23 @@ var TryNode = class extends BaseNode {
|
|
|
780
1132
|
scope.setPath(`self.${this.errorName}`, error);
|
|
781
1133
|
}
|
|
782
1134
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
scope.setPath
|
|
1135
|
+
const handlerResult = this.handler.evaluate(context);
|
|
1136
|
+
return resolveMaybe(handlerResult, () => {
|
|
1137
|
+
if (scope && scope.setPath && handlerScope === previousScope) {
|
|
1138
|
+
scope.setPath(this.errorName, previous);
|
|
1139
|
+
}
|
|
1140
|
+
context.scope = previousScope;
|
|
1141
|
+
return void 0;
|
|
1142
|
+
});
|
|
1143
|
+
};
|
|
1144
|
+
try {
|
|
1145
|
+
const bodyResult = evaluateWithChildScope(context, this.body);
|
|
1146
|
+
if (isPromiseLike(bodyResult)) {
|
|
1147
|
+
return bodyResult.catch((error) => handleError(error));
|
|
786
1148
|
}
|
|
787
|
-
|
|
1149
|
+
return bodyResult;
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
return handleError(error);
|
|
788
1152
|
}
|
|
789
1153
|
}
|
|
790
1154
|
};
|
|
@@ -804,11 +1168,35 @@ var FunctionExpression = class extends BaseNode {
|
|
|
804
1168
|
this.body = body;
|
|
805
1169
|
this.isAsync = isAsync;
|
|
806
1170
|
}
|
|
807
|
-
|
|
1171
|
+
evaluate(context) {
|
|
808
1172
|
const scope = context.scope;
|
|
809
1173
|
const globals = context.globals;
|
|
810
1174
|
const element = context.element;
|
|
811
|
-
|
|
1175
|
+
if (this.isAsync) {
|
|
1176
|
+
return (...args) => {
|
|
1177
|
+
const activeScope = scope?.createChild ? scope.createChild() : scope;
|
|
1178
|
+
const inner = {
|
|
1179
|
+
scope: activeScope,
|
|
1180
|
+
rootScope: context.rootScope,
|
|
1181
|
+
...globals ? { globals } : {},
|
|
1182
|
+
...element ? { element } : {},
|
|
1183
|
+
returnValue: void 0,
|
|
1184
|
+
returning: false,
|
|
1185
|
+
breaking: false,
|
|
1186
|
+
continuing: false
|
|
1187
|
+
};
|
|
1188
|
+
const previousValues = /* @__PURE__ */ new Map();
|
|
1189
|
+
const applyResult = activeScope ? this.applyParams(activeScope, previousValues, inner, args) : void 0;
|
|
1190
|
+
const bodyResult = resolveMaybe(applyResult, () => this.body.evaluate(inner));
|
|
1191
|
+
const finalResult = resolveMaybe(bodyResult, () => inner.returnValue);
|
|
1192
|
+
return Promise.resolve(finalResult).finally(() => {
|
|
1193
|
+
if (activeScope && activeScope === scope) {
|
|
1194
|
+
this.restoreParams(activeScope, previousValues);
|
|
1195
|
+
}
|
|
1196
|
+
});
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
return (...args) => {
|
|
812
1200
|
const activeScope = scope?.createChild ? scope.createChild() : scope;
|
|
813
1201
|
const inner = {
|
|
814
1202
|
scope: activeScope,
|
|
@@ -816,47 +1204,69 @@ var FunctionExpression = class extends BaseNode {
|
|
|
816
1204
|
...globals ? { globals } : {},
|
|
817
1205
|
...element ? { element } : {},
|
|
818
1206
|
returnValue: void 0,
|
|
819
|
-
returning: false
|
|
1207
|
+
returning: false,
|
|
1208
|
+
breaking: false,
|
|
1209
|
+
continuing: false
|
|
820
1210
|
};
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
1211
|
+
const previousValues = /* @__PURE__ */ new Map();
|
|
1212
|
+
const applyResult = activeScope ? this.applyParams(activeScope, previousValues, inner, args) : void 0;
|
|
1213
|
+
const bodyResult = resolveMaybe(applyResult, () => this.body.evaluate(inner));
|
|
1214
|
+
const finalResult = resolveMaybe(bodyResult, () => inner.returnValue);
|
|
1215
|
+
if (isPromiseLike(finalResult)) {
|
|
1216
|
+
return finalResult.finally(() => {
|
|
1217
|
+
if (activeScope && activeScope === scope) {
|
|
1218
|
+
this.restoreParams(activeScope, previousValues);
|
|
1219
|
+
}
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
if (activeScope && activeScope === scope) {
|
|
1223
|
+
this.restoreParams(activeScope, previousValues);
|
|
830
1224
|
}
|
|
831
|
-
return
|
|
1225
|
+
return finalResult;
|
|
832
1226
|
};
|
|
833
1227
|
}
|
|
834
|
-
|
|
835
|
-
if (!scope
|
|
1228
|
+
applyParams(scope, previousValues, context, args) {
|
|
1229
|
+
if (!scope) {
|
|
836
1230
|
return;
|
|
837
1231
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
if (!name) {
|
|
842
|
-
continue;
|
|
843
|
-
}
|
|
844
|
-
previousValues.set(name, scope.getPath(name));
|
|
845
|
-
if (param.rest) {
|
|
846
|
-
scope.setPath(`self.${name}`, args.slice(argIndex));
|
|
847
|
-
argIndex = args.length;
|
|
848
|
-
continue;
|
|
849
|
-
}
|
|
850
|
-
let value = args[argIndex];
|
|
851
|
-
if (value === void 0 && param.defaultValue) {
|
|
852
|
-
value = await param.defaultValue.evaluate(context);
|
|
853
|
-
}
|
|
854
|
-
scope.setPath(`self.${name}`, value);
|
|
855
|
-
argIndex += 1;
|
|
1232
|
+
const setPath = scope.setPath?.bind(scope);
|
|
1233
|
+
if (!setPath) {
|
|
1234
|
+
return;
|
|
856
1235
|
}
|
|
1236
|
+
const params = this.params;
|
|
1237
|
+
const applyAt = (paramIndex, argIndex) => {
|
|
1238
|
+
for (let i = paramIndex; i < params.length; i += 1) {
|
|
1239
|
+
const param = params[i];
|
|
1240
|
+
const name = param.name;
|
|
1241
|
+
if (!name) {
|
|
1242
|
+
continue;
|
|
1243
|
+
}
|
|
1244
|
+
previousValues.set(name, scope.getPath(name));
|
|
1245
|
+
if (param.rest) {
|
|
1246
|
+
setPath(`self.${name}`, args.slice(argIndex));
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
let value = args[argIndex];
|
|
1250
|
+
if (value === void 0 && param.defaultValue) {
|
|
1251
|
+
const defaultValue = param.defaultValue.evaluate(context);
|
|
1252
|
+
return resolveMaybe(defaultValue, (resolvedDefault) => {
|
|
1253
|
+
setPath(`self.${name}`, resolvedDefault);
|
|
1254
|
+
return applyAt(i + 1, argIndex + 1);
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
setPath(`self.${name}`, value);
|
|
1258
|
+
argIndex += 1;
|
|
1259
|
+
}
|
|
1260
|
+
return;
|
|
1261
|
+
};
|
|
1262
|
+
return applyAt(0, 0);
|
|
857
1263
|
}
|
|
858
1264
|
restoreParams(scope, previousValues) {
|
|
859
|
-
if (!scope
|
|
1265
|
+
if (!scope) {
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
const setPath = scope.setPath?.bind(scope);
|
|
1269
|
+
if (!setPath) {
|
|
860
1270
|
return;
|
|
861
1271
|
}
|
|
862
1272
|
for (const param of this.params) {
|
|
@@ -864,7 +1274,7 @@ var FunctionExpression = class extends BaseNode {
|
|
|
864
1274
|
if (!name) {
|
|
865
1275
|
continue;
|
|
866
1276
|
}
|
|
867
|
-
|
|
1277
|
+
setPath(name, previousValues.get(name));
|
|
868
1278
|
}
|
|
869
1279
|
}
|
|
870
1280
|
};
|
|
@@ -883,7 +1293,7 @@ var IdentifierExpression = class extends BaseNode {
|
|
|
883
1293
|
super("Identifier");
|
|
884
1294
|
this.name = name;
|
|
885
1295
|
}
|
|
886
|
-
|
|
1296
|
+
evaluate(context) {
|
|
887
1297
|
if (this.name.startsWith("root.") && context.rootScope) {
|
|
888
1298
|
const path = this.name.slice("root.".length);
|
|
889
1299
|
return context.rootScope.getPath(`self.${path}`);
|
|
@@ -928,7 +1338,7 @@ var LiteralExpression = class extends BaseNode {
|
|
|
928
1338
|
super("Literal");
|
|
929
1339
|
this.value = value;
|
|
930
1340
|
}
|
|
931
|
-
|
|
1341
|
+
evaluate() {
|
|
932
1342
|
return this.value;
|
|
933
1343
|
}
|
|
934
1344
|
};
|
|
@@ -937,13 +1347,22 @@ var TemplateExpression = class extends BaseNode {
|
|
|
937
1347
|
super("TemplateExpression");
|
|
938
1348
|
this.parts = parts;
|
|
939
1349
|
}
|
|
940
|
-
|
|
1350
|
+
evaluate(context) {
|
|
941
1351
|
let result = "";
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1352
|
+
let index = 0;
|
|
1353
|
+
const run = () => {
|
|
1354
|
+
while (index < this.parts.length) {
|
|
1355
|
+
const part = this.parts[index];
|
|
1356
|
+
index += 1;
|
|
1357
|
+
const value = part.evaluate(context);
|
|
1358
|
+
return resolveMaybe(value, (resolved) => {
|
|
1359
|
+
result += resolved == null ? "" : String(resolved);
|
|
1360
|
+
return run();
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
return result;
|
|
1364
|
+
};
|
|
1365
|
+
return run();
|
|
947
1366
|
}
|
|
948
1367
|
};
|
|
949
1368
|
var UnaryExpression = class extends BaseNode {
|
|
@@ -952,15 +1371,17 @@ var UnaryExpression = class extends BaseNode {
|
|
|
952
1371
|
this.operator = operator;
|
|
953
1372
|
this.argument = argument;
|
|
954
1373
|
}
|
|
955
|
-
|
|
956
|
-
const value =
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1374
|
+
evaluate(context) {
|
|
1375
|
+
const value = this.argument.evaluate(context);
|
|
1376
|
+
return resolveMaybe(value, (resolved) => {
|
|
1377
|
+
if (this.operator === "!") {
|
|
1378
|
+
return !resolved;
|
|
1379
|
+
}
|
|
1380
|
+
if (this.operator === "-") {
|
|
1381
|
+
return -resolved;
|
|
1382
|
+
}
|
|
1383
|
+
return resolved;
|
|
1384
|
+
});
|
|
964
1385
|
}
|
|
965
1386
|
};
|
|
966
1387
|
var BinaryExpression = class extends BaseNode {
|
|
@@ -970,61 +1391,71 @@ var BinaryExpression = class extends BaseNode {
|
|
|
970
1391
|
this.left = left;
|
|
971
1392
|
this.right = right;
|
|
972
1393
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
return
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1394
|
+
evaluate(context) {
|
|
1395
|
+
const leftValue = this.left.evaluate(context);
|
|
1396
|
+
return resolveMaybe(leftValue, (resolvedLeft) => {
|
|
1397
|
+
if (this.operator === "&&") {
|
|
1398
|
+
if (!resolvedLeft) {
|
|
1399
|
+
return resolvedLeft;
|
|
1400
|
+
}
|
|
1401
|
+
return this.right.evaluate(context);
|
|
1402
|
+
}
|
|
1403
|
+
if (this.operator === "||") {
|
|
1404
|
+
if (resolvedLeft) {
|
|
1405
|
+
return resolvedLeft;
|
|
1406
|
+
}
|
|
1407
|
+
return this.right.evaluate(context);
|
|
1408
|
+
}
|
|
1409
|
+
if (this.operator === "??") {
|
|
1410
|
+
if (resolvedLeft !== null && resolvedLeft !== void 0) {
|
|
1411
|
+
return resolvedLeft;
|
|
1412
|
+
}
|
|
1413
|
+
return this.right.evaluate(context);
|
|
1414
|
+
}
|
|
1415
|
+
const rightValue = this.right.evaluate(context);
|
|
1416
|
+
return resolveMaybe(rightValue, (resolvedRight) => {
|
|
1417
|
+
if (this.operator === "+") {
|
|
1418
|
+
return resolvedLeft + resolvedRight;
|
|
1419
|
+
}
|
|
1420
|
+
if (this.operator === "-") {
|
|
1421
|
+
return resolvedLeft - resolvedRight;
|
|
1422
|
+
}
|
|
1423
|
+
if (this.operator === "*") {
|
|
1424
|
+
return resolvedLeft * resolvedRight;
|
|
1425
|
+
}
|
|
1426
|
+
if (this.operator === "/") {
|
|
1427
|
+
return resolvedLeft / resolvedRight;
|
|
1428
|
+
}
|
|
1429
|
+
if (this.operator === "%") {
|
|
1430
|
+
return resolvedLeft % resolvedRight;
|
|
1431
|
+
}
|
|
1432
|
+
if (this.operator === "==") {
|
|
1433
|
+
return resolvedLeft == resolvedRight;
|
|
1434
|
+
}
|
|
1435
|
+
if (this.operator === "!=") {
|
|
1436
|
+
return resolvedLeft != resolvedRight;
|
|
1437
|
+
}
|
|
1438
|
+
if (this.operator === "===") {
|
|
1439
|
+
return resolvedLeft === resolvedRight;
|
|
1440
|
+
}
|
|
1441
|
+
if (this.operator === "!==") {
|
|
1442
|
+
return resolvedLeft !== resolvedRight;
|
|
1443
|
+
}
|
|
1444
|
+
if (this.operator === "<") {
|
|
1445
|
+
return resolvedLeft < resolvedRight;
|
|
1446
|
+
}
|
|
1447
|
+
if (this.operator === ">") {
|
|
1448
|
+
return resolvedLeft > resolvedRight;
|
|
1449
|
+
}
|
|
1450
|
+
if (this.operator === "<=") {
|
|
1451
|
+
return resolvedLeft <= resolvedRight;
|
|
1452
|
+
}
|
|
1453
|
+
if (this.operator === ">=") {
|
|
1454
|
+
return resolvedLeft >= resolvedRight;
|
|
1455
|
+
}
|
|
1456
|
+
return void 0;
|
|
1457
|
+
});
|
|
1458
|
+
});
|
|
1028
1459
|
}
|
|
1029
1460
|
};
|
|
1030
1461
|
var TernaryExpression = class extends BaseNode {
|
|
@@ -1034,12 +1465,14 @@ var TernaryExpression = class extends BaseNode {
|
|
|
1034
1465
|
this.consequent = consequent;
|
|
1035
1466
|
this.alternate = alternate;
|
|
1036
1467
|
}
|
|
1037
|
-
|
|
1038
|
-
const condition =
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1468
|
+
evaluate(context) {
|
|
1469
|
+
const condition = this.test.evaluate(context);
|
|
1470
|
+
return resolveMaybe(condition, (resolved) => {
|
|
1471
|
+
if (resolved) {
|
|
1472
|
+
return this.consequent.evaluate(context);
|
|
1473
|
+
}
|
|
1474
|
+
return this.alternate.evaluate(context);
|
|
1475
|
+
});
|
|
1043
1476
|
}
|
|
1044
1477
|
};
|
|
1045
1478
|
var MemberExpression = class _MemberExpression extends BaseNode {
|
|
@@ -1049,11 +1482,11 @@ var MemberExpression = class _MemberExpression extends BaseNode {
|
|
|
1049
1482
|
this.property = property;
|
|
1050
1483
|
this.optional = optional;
|
|
1051
1484
|
}
|
|
1052
|
-
|
|
1053
|
-
const resolved =
|
|
1054
|
-
return resolved?.value;
|
|
1485
|
+
evaluate(context) {
|
|
1486
|
+
const resolved = this.resolve(context);
|
|
1487
|
+
return resolveMaybe(resolved, (resolvedValue) => resolvedValue?.value);
|
|
1055
1488
|
}
|
|
1056
|
-
|
|
1489
|
+
resolve(context) {
|
|
1057
1490
|
const path = this.getIdentifierPath();
|
|
1058
1491
|
if (path) {
|
|
1059
1492
|
const resolved = this.resolveFromScope(context, path);
|
|
@@ -1065,11 +1498,13 @@ var MemberExpression = class _MemberExpression extends BaseNode {
|
|
|
1065
1498
|
return resolvedGlobal;
|
|
1066
1499
|
}
|
|
1067
1500
|
}
|
|
1068
|
-
const target =
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1501
|
+
const target = this.target.evaluate(context);
|
|
1502
|
+
return resolveMaybe(target, (resolvedTarget) => {
|
|
1503
|
+
if (resolvedTarget == null) {
|
|
1504
|
+
return { value: void 0, target: resolvedTarget, optional: this.optional };
|
|
1505
|
+
}
|
|
1506
|
+
return { value: resolvedTarget[this.property], target: resolvedTarget, optional: this.optional };
|
|
1507
|
+
});
|
|
1073
1508
|
}
|
|
1074
1509
|
getIdentifierPath() {
|
|
1075
1510
|
const targetPath = this.getTargetIdentifierPath();
|
|
@@ -1145,25 +1580,39 @@ var CallExpression = class extends BaseNode {
|
|
|
1145
1580
|
this.callee = callee;
|
|
1146
1581
|
this.args = args;
|
|
1147
1582
|
}
|
|
1148
|
-
|
|
1149
|
-
const resolved =
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
return
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1583
|
+
evaluate(context) {
|
|
1584
|
+
const resolved = this.resolveCallee(context);
|
|
1585
|
+
return resolveMaybe(resolved, (resolvedCallee) => {
|
|
1586
|
+
const fnValue = resolvedCallee?.fn ?? this.callee.evaluate(context);
|
|
1587
|
+
return resolveMaybe(fnValue, (resolvedFn) => {
|
|
1588
|
+
if (typeof resolvedFn !== "function") {
|
|
1589
|
+
return void 0;
|
|
1590
|
+
}
|
|
1591
|
+
const values = [];
|
|
1592
|
+
const evalArgs = (index) => {
|
|
1593
|
+
for (let i = index; i < this.args.length; i += 1) {
|
|
1594
|
+
const arg = this.args[i];
|
|
1595
|
+
const argValue = arg.evaluate(context);
|
|
1596
|
+
return resolveMaybe(argValue, (resolvedArg) => {
|
|
1597
|
+
values.push(resolvedArg);
|
|
1598
|
+
return evalArgs(i + 1);
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
return resolvedFn.apply(resolvedCallee?.thisArg, values);
|
|
1602
|
+
};
|
|
1603
|
+
return evalArgs(0);
|
|
1604
|
+
});
|
|
1605
|
+
});
|
|
1159
1606
|
}
|
|
1160
|
-
|
|
1607
|
+
resolveCallee(context) {
|
|
1161
1608
|
if (this.callee instanceof MemberExpression) {
|
|
1162
|
-
const resolved =
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1609
|
+
const resolved = this.callee.resolve(context);
|
|
1610
|
+
return resolveMaybe(resolved, (resolvedValue) => {
|
|
1611
|
+
if (!resolvedValue) {
|
|
1612
|
+
return void 0;
|
|
1613
|
+
}
|
|
1614
|
+
return { fn: resolvedValue.value, thisArg: resolvedValue.target };
|
|
1615
|
+
});
|
|
1167
1616
|
}
|
|
1168
1617
|
if (!(this.callee instanceof IdentifierExpression)) {
|
|
1169
1618
|
return void 0;
|
|
@@ -1205,27 +1654,40 @@ var ArrayExpression = class extends BaseNode {
|
|
|
1205
1654
|
super("ArrayExpression");
|
|
1206
1655
|
this.elements = elements;
|
|
1207
1656
|
}
|
|
1208
|
-
|
|
1657
|
+
evaluate(context) {
|
|
1209
1658
|
const values = [];
|
|
1210
|
-
|
|
1211
|
-
if (
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1659
|
+
const pushElements = (value) => {
|
|
1660
|
+
if (value == null) {
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
const iterator = value[Symbol.iterator];
|
|
1664
|
+
if (typeof iterator === "function") {
|
|
1665
|
+
for (const entry of value) {
|
|
1666
|
+
values.push(entry);
|
|
1215
1667
|
}
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1668
|
+
} else {
|
|
1669
|
+
values.push(value);
|
|
1670
|
+
}
|
|
1671
|
+
};
|
|
1672
|
+
const evalAt = (index) => {
|
|
1673
|
+
for (let i = index; i < this.elements.length; i += 1) {
|
|
1674
|
+
const element = this.elements[i];
|
|
1675
|
+
if (element instanceof SpreadElement) {
|
|
1676
|
+
const spreadValue = element.value.evaluate(context);
|
|
1677
|
+
return resolveMaybe(spreadValue, (resolvedSpread) => {
|
|
1678
|
+
pushElements(resolvedSpread);
|
|
1679
|
+
return evalAt(i + 1);
|
|
1680
|
+
});
|
|
1223
1681
|
}
|
|
1224
|
-
|
|
1682
|
+
const value = element.evaluate(context);
|
|
1683
|
+
return resolveMaybe(value, (resolvedValue) => {
|
|
1684
|
+
values.push(resolvedValue);
|
|
1685
|
+
return evalAt(i + 1);
|
|
1686
|
+
});
|
|
1225
1687
|
}
|
|
1226
|
-
values
|
|
1227
|
-
}
|
|
1228
|
-
return
|
|
1688
|
+
return values;
|
|
1689
|
+
};
|
|
1690
|
+
return evalAt(0);
|
|
1229
1691
|
}
|
|
1230
1692
|
};
|
|
1231
1693
|
var ObjectExpression = class extends BaseNode {
|
|
@@ -1233,24 +1695,39 @@ var ObjectExpression = class extends BaseNode {
|
|
|
1233
1695
|
super("ObjectExpression");
|
|
1234
1696
|
this.entries = entries;
|
|
1235
1697
|
}
|
|
1236
|
-
|
|
1698
|
+
evaluate(context) {
|
|
1237
1699
|
const result = {};
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
const
|
|
1241
|
-
if (
|
|
1242
|
-
|
|
1700
|
+
const evalAt = (index) => {
|
|
1701
|
+
for (let i = index; i < this.entries.length; i += 1) {
|
|
1702
|
+
const entry = this.entries[i];
|
|
1703
|
+
if ("spread" in entry) {
|
|
1704
|
+
const spreadValue = entry.spread.evaluate(context);
|
|
1705
|
+
return resolveMaybe(spreadValue, (resolvedSpread) => {
|
|
1706
|
+
if (resolvedSpread != null) {
|
|
1707
|
+
Object.assign(result, resolvedSpread);
|
|
1708
|
+
}
|
|
1709
|
+
return evalAt(i + 1);
|
|
1710
|
+
});
|
|
1243
1711
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1712
|
+
if ("computed" in entry && entry.computed) {
|
|
1713
|
+
const keyValue = entry.keyExpr.evaluate(context);
|
|
1714
|
+
return resolveMaybe(keyValue, (resolvedKey) => {
|
|
1715
|
+
const entryValue = entry.value.evaluate(context);
|
|
1716
|
+
return resolveMaybe(entryValue, (resolvedValue) => {
|
|
1717
|
+
result[String(resolvedKey)] = resolvedValue;
|
|
1718
|
+
return evalAt(i + 1);
|
|
1719
|
+
});
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
const value = entry.value.evaluate(context);
|
|
1723
|
+
return resolveMaybe(value, (resolvedValue) => {
|
|
1724
|
+
result[entry.key] = resolvedValue;
|
|
1725
|
+
return evalAt(i + 1);
|
|
1726
|
+
});
|
|
1251
1727
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1728
|
+
return result;
|
|
1729
|
+
};
|
|
1730
|
+
return evalAt(0);
|
|
1254
1731
|
}
|
|
1255
1732
|
};
|
|
1256
1733
|
var IndexExpression = class extends BaseNode {
|
|
@@ -1259,16 +1736,30 @@ var IndexExpression = class extends BaseNode {
|
|
|
1259
1736
|
this.target = target;
|
|
1260
1737
|
this.index = index;
|
|
1261
1738
|
}
|
|
1262
|
-
|
|
1263
|
-
const target =
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
return
|
|
1739
|
+
evaluate(context) {
|
|
1740
|
+
const target = this.target.evaluate(context);
|
|
1741
|
+
return resolveMaybe(target, (resolvedTarget) => {
|
|
1742
|
+
if (resolvedTarget == null) {
|
|
1743
|
+
return void 0;
|
|
1744
|
+
}
|
|
1745
|
+
const index = this.index.evaluate(context);
|
|
1746
|
+
return resolveMaybe(index, (resolvedIndex) => {
|
|
1747
|
+
if (resolvedIndex == null) {
|
|
1748
|
+
return void 0;
|
|
1749
|
+
}
|
|
1750
|
+
const key = this.normalizeIndexKey(resolvedTarget, resolvedIndex);
|
|
1751
|
+
return resolvedTarget[key];
|
|
1752
|
+
});
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
normalizeIndexKey(target, index) {
|
|
1756
|
+
if (Array.isArray(target) && typeof index === "string" && index.trim() !== "") {
|
|
1757
|
+
const numeric = Number(index);
|
|
1758
|
+
if (!Number.isNaN(numeric)) {
|
|
1759
|
+
return numeric;
|
|
1760
|
+
}
|
|
1270
1761
|
}
|
|
1271
|
-
return
|
|
1762
|
+
return index;
|
|
1272
1763
|
}
|
|
1273
1764
|
};
|
|
1274
1765
|
var DirectiveExpression = class extends BaseNode {
|
|
@@ -1277,7 +1768,7 @@ var DirectiveExpression = class extends BaseNode {
|
|
|
1277
1768
|
this.kind = kind;
|
|
1278
1769
|
this.name = name;
|
|
1279
1770
|
}
|
|
1280
|
-
|
|
1771
|
+
evaluate(context) {
|
|
1281
1772
|
const element = context.element;
|
|
1282
1773
|
if (!element) {
|
|
1283
1774
|
return `${this.kind}:${this.name}`;
|
|
@@ -1291,6 +1782,12 @@ var DirectiveExpression = class extends BaseNode {
|
|
|
1291
1782
|
return element.value;
|
|
1292
1783
|
}
|
|
1293
1784
|
}
|
|
1785
|
+
if (this.name === "text" && element instanceof HTMLElement) {
|
|
1786
|
+
return element.innerText;
|
|
1787
|
+
}
|
|
1788
|
+
if (this.name === "content" && element instanceof HTMLElement) {
|
|
1789
|
+
return element.textContent ?? "";
|
|
1790
|
+
}
|
|
1294
1791
|
if (this.name === "checked" && element instanceof HTMLInputElement) {
|
|
1295
1792
|
return element.checked;
|
|
1296
1793
|
}
|
|
@@ -1310,9 +1807,9 @@ var AwaitExpression = class extends BaseNode {
|
|
|
1310
1807
|
super("AwaitExpression");
|
|
1311
1808
|
this.argument = argument;
|
|
1312
1809
|
}
|
|
1313
|
-
|
|
1314
|
-
const value =
|
|
1315
|
-
return
|
|
1810
|
+
evaluate(context) {
|
|
1811
|
+
const value = this.argument.evaluate(context);
|
|
1812
|
+
return Promise.resolve(value);
|
|
1316
1813
|
}
|
|
1317
1814
|
};
|
|
1318
1815
|
var QueryExpression = class extends BaseNode {
|
|
@@ -1321,7 +1818,7 @@ var QueryExpression = class extends BaseNode {
|
|
|
1321
1818
|
this.direction = direction;
|
|
1322
1819
|
this.selector = selector;
|
|
1323
1820
|
}
|
|
1324
|
-
|
|
1821
|
+
evaluate(context) {
|
|
1325
1822
|
const selector = this.selector.trim();
|
|
1326
1823
|
if (!selector) {
|
|
1327
1824
|
return [];
|
|
@@ -1387,6 +1884,9 @@ var TokenStream = class {
|
|
|
1387
1884
|
let count = 0;
|
|
1388
1885
|
for (let i = this.index; i < this.tokens.length; i++) {
|
|
1389
1886
|
const token = this.tokens[i];
|
|
1887
|
+
if (!token) {
|
|
1888
|
+
continue;
|
|
1889
|
+
}
|
|
1390
1890
|
if (token.type === "Whitespace" /* Whitespace */) {
|
|
1391
1891
|
continue;
|
|
1392
1892
|
}
|
|
@@ -1397,6 +1897,29 @@ var TokenStream = class {
|
|
|
1397
1897
|
}
|
|
1398
1898
|
return null;
|
|
1399
1899
|
}
|
|
1900
|
+
indexAfterDelimited(openType, closeType, offset = 0) {
|
|
1901
|
+
const first = this.peekNonWhitespace(offset);
|
|
1902
|
+
if (!first || first.type !== openType) {
|
|
1903
|
+
return null;
|
|
1904
|
+
}
|
|
1905
|
+
let index = offset + 1;
|
|
1906
|
+
let depth = 1;
|
|
1907
|
+
while (true) {
|
|
1908
|
+
const token = this.peekNonWhitespace(index);
|
|
1909
|
+
if (!token) {
|
|
1910
|
+
return null;
|
|
1911
|
+
}
|
|
1912
|
+
if (token.type === openType) {
|
|
1913
|
+
depth += 1;
|
|
1914
|
+
} else if (token.type === closeType) {
|
|
1915
|
+
depth -= 1;
|
|
1916
|
+
if (depth === 0) {
|
|
1917
|
+
return index + 1;
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
index += 1;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1400
1923
|
};
|
|
1401
1924
|
|
|
1402
1925
|
// src/parser/parser.ts
|
|
@@ -1404,11 +1927,14 @@ var Parser = class _Parser {
|
|
|
1404
1927
|
stream;
|
|
1405
1928
|
source;
|
|
1406
1929
|
customFlags;
|
|
1930
|
+
behaviorFlags;
|
|
1407
1931
|
allowImplicitSemicolon = false;
|
|
1408
1932
|
awaitStack = [];
|
|
1933
|
+
functionDepth = 0;
|
|
1409
1934
|
constructor(input, options) {
|
|
1410
1935
|
this.source = input;
|
|
1411
|
-
this.customFlags = options?.customFlags ?? /* @__PURE__ */ new Set();
|
|
1936
|
+
this.customFlags = options?.customFlags ?? /* @__PURE__ */ new Set(["important", "trusted", "debounce"]);
|
|
1937
|
+
this.behaviorFlags = options?.behaviorFlags ?? /* @__PURE__ */ new Set();
|
|
1412
1938
|
const lexer = new Lexer(input);
|
|
1413
1939
|
this.stream = new TokenStream(lexer.tokenize());
|
|
1414
1940
|
}
|
|
@@ -1448,8 +1974,9 @@ var Parser = class _Parser {
|
|
|
1448
1974
|
this.stream.skipWhitespace();
|
|
1449
1975
|
this.stream.expect("Behavior" /* Behavior */);
|
|
1450
1976
|
const selector = this.parseSelector();
|
|
1977
|
+
const { flags, flagArgs } = this.parseBehaviorFlags();
|
|
1451
1978
|
const body = this.parseBlock({ allowDeclarations: true });
|
|
1452
|
-
return new BehaviorNode(selector, body);
|
|
1979
|
+
return new BehaviorNode(selector, body, flags, flagArgs);
|
|
1453
1980
|
});
|
|
1454
1981
|
}
|
|
1455
1982
|
parseSelector() {
|
|
@@ -1463,6 +1990,9 @@ var Parser = class _Parser {
|
|
|
1463
1990
|
if (token.type === "LBrace" /* LBrace */) {
|
|
1464
1991
|
break;
|
|
1465
1992
|
}
|
|
1993
|
+
if (token.type === "Bang" /* Bang */) {
|
|
1994
|
+
break;
|
|
1995
|
+
}
|
|
1466
1996
|
if (token.type === "Whitespace" /* Whitespace */) {
|
|
1467
1997
|
this.stream.next();
|
|
1468
1998
|
if (sawNonWhitespace && selectorText[selectorText.length - 1] !== " ") {
|
|
@@ -1478,6 +2008,10 @@ var Parser = class _Parser {
|
|
|
1478
2008
|
}
|
|
1479
2009
|
return new SelectorNode(selectorText.trim());
|
|
1480
2010
|
}
|
|
2011
|
+
parseBehaviorFlags() {
|
|
2012
|
+
const result = this.parseFlags(this.behaviorFlags, "behavior modifier");
|
|
2013
|
+
return { flags: result.flags, flagArgs: result.flagArgs };
|
|
2014
|
+
}
|
|
1481
2015
|
parseUseStatement() {
|
|
1482
2016
|
return this.wrapErrors(() => {
|
|
1483
2017
|
this.stream.expect("Use" /* Use */);
|
|
@@ -1561,6 +2095,7 @@ ${caret}`;
|
|
|
1561
2095
|
}
|
|
1562
2096
|
parseBlock(options) {
|
|
1563
2097
|
const allowDeclarations = options?.allowDeclarations ?? false;
|
|
2098
|
+
const allowReturn = options?.allowReturn ?? this.functionDepth > 0;
|
|
1564
2099
|
this.stream.skipWhitespace();
|
|
1565
2100
|
this.stream.expect("LBrace" /* LBrace */);
|
|
1566
2101
|
const statements = [];
|
|
@@ -1622,7 +2157,7 @@ ${caret}`;
|
|
|
1622
2157
|
}
|
|
1623
2158
|
sawConstruct = true;
|
|
1624
2159
|
}
|
|
1625
|
-
statements.push(this.parseStatement());
|
|
2160
|
+
statements.push(this.parseStatement({ allowReturn }));
|
|
1626
2161
|
}
|
|
1627
2162
|
}
|
|
1628
2163
|
return new BlockNode(statements);
|
|
@@ -1641,6 +2176,15 @@ ${caret}`;
|
|
|
1641
2176
|
}
|
|
1642
2177
|
return this.parseReturnStatement();
|
|
1643
2178
|
}
|
|
2179
|
+
if (next.type === "Assert" /* Assert */) {
|
|
2180
|
+
return this.parseAssertStatement();
|
|
2181
|
+
}
|
|
2182
|
+
if (next.type === "Break" /* Break */) {
|
|
2183
|
+
return this.parseBreakStatement();
|
|
2184
|
+
}
|
|
2185
|
+
if (next.type === "Continue" /* Continue */) {
|
|
2186
|
+
return this.parseContinueStatement();
|
|
2187
|
+
}
|
|
1644
2188
|
if (allowBlocks && next.type === "On" /* On */) {
|
|
1645
2189
|
return this.parseOnBlock();
|
|
1646
2190
|
}
|
|
@@ -1676,44 +2220,6 @@ ${caret}`;
|
|
|
1676
2220
|
}
|
|
1677
2221
|
throw new Error(`Unexpected token ${next.type}`);
|
|
1678
2222
|
}
|
|
1679
|
-
parseStateBlock() {
|
|
1680
|
-
this.stream.expect("State" /* State */);
|
|
1681
|
-
this.stream.skipWhitespace();
|
|
1682
|
-
this.stream.expect("LBrace" /* LBrace */);
|
|
1683
|
-
const entries = [];
|
|
1684
|
-
while (true) {
|
|
1685
|
-
this.stream.skipWhitespace();
|
|
1686
|
-
const next = this.stream.peek();
|
|
1687
|
-
if (!next) {
|
|
1688
|
-
throw new Error("Unterminated state block");
|
|
1689
|
-
}
|
|
1690
|
-
if (next.type === "RBrace" /* RBrace */) {
|
|
1691
|
-
this.stream.next();
|
|
1692
|
-
break;
|
|
1693
|
-
}
|
|
1694
|
-
const nameToken = this.stream.expect("Identifier" /* Identifier */);
|
|
1695
|
-
this.stream.skipWhitespace();
|
|
1696
|
-
this.stream.expect("Colon" /* Colon */);
|
|
1697
|
-
this.stream.skipWhitespace();
|
|
1698
|
-
const value = this.parseExpression();
|
|
1699
|
-
this.stream.skipWhitespace();
|
|
1700
|
-
let important = false;
|
|
1701
|
-
if (this.stream.peek()?.type === "Bang" /* Bang */) {
|
|
1702
|
-
this.stream.next();
|
|
1703
|
-
this.stream.skipWhitespace();
|
|
1704
|
-
const importantToken = this.stream.next();
|
|
1705
|
-
if (importantToken.type === "Identifier" /* Identifier */ && importantToken.value === "important") {
|
|
1706
|
-
important = true;
|
|
1707
|
-
} else {
|
|
1708
|
-
throw new Error("Expected 'important' after '!'");
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
this.stream.skipWhitespace();
|
|
1712
|
-
this.stream.expect("Semicolon" /* Semicolon */);
|
|
1713
|
-
entries.push(new StateEntryNode(nameToken.value, value, important));
|
|
1714
|
-
}
|
|
1715
|
-
return new StateBlockNode(entries);
|
|
1716
|
-
}
|
|
1717
2223
|
parseOnBlock() {
|
|
1718
2224
|
this.stream.expect("On" /* On */);
|
|
1719
2225
|
this.stream.skipWhitespace();
|
|
@@ -1741,22 +2247,9 @@ ${caret}`;
|
|
|
1741
2247
|
}
|
|
1742
2248
|
throw new Error(`Unexpected token in on() args: ${next.type}`);
|
|
1743
2249
|
}
|
|
1744
|
-
const
|
|
2250
|
+
const { flags, flagArgs } = this.parseFlags(this.customFlags, "flag");
|
|
1745
2251
|
const body = this.parseBlock({ allowDeclarations: false });
|
|
1746
|
-
return new OnBlockNode(event, args, body,
|
|
1747
|
-
}
|
|
1748
|
-
parseOnModifiers() {
|
|
1749
|
-
const modifiers = [];
|
|
1750
|
-
while (true) {
|
|
1751
|
-
this.stream.skipWhitespace();
|
|
1752
|
-
if (this.stream.peek()?.type !== "Bang" /* Bang */) {
|
|
1753
|
-
break;
|
|
1754
|
-
}
|
|
1755
|
-
this.stream.next();
|
|
1756
|
-
const name = this.stream.expect("Identifier" /* Identifier */).value;
|
|
1757
|
-
modifiers.push(name);
|
|
1758
|
-
}
|
|
1759
|
-
return modifiers;
|
|
2252
|
+
return new OnBlockNode(event, args, body, flags, flagArgs);
|
|
1760
2253
|
}
|
|
1761
2254
|
parseAssignment() {
|
|
1762
2255
|
const target = this.parseAssignmentTarget();
|
|
@@ -1966,6 +2459,11 @@ ${caret}`;
|
|
|
1966
2459
|
if (!token) {
|
|
1967
2460
|
throw new Error("Expected expression");
|
|
1968
2461
|
}
|
|
2462
|
+
if (token.type === "PlusPlus" /* PlusPlus */ || token.type === "MinusMinus" /* MinusMinus */) {
|
|
2463
|
+
this.stream.next();
|
|
2464
|
+
const argument = this.parseUnaryExpression();
|
|
2465
|
+
return this.createIncrementNode(token, argument, true);
|
|
2466
|
+
}
|
|
1969
2467
|
if (token.type === "Bang" /* Bang */) {
|
|
1970
2468
|
this.stream.next();
|
|
1971
2469
|
const argument = this.parseUnaryExpression();
|
|
@@ -1981,7 +2479,31 @@ ${caret}`;
|
|
|
1981
2479
|
const argument = this.parseUnaryExpression();
|
|
1982
2480
|
return new AwaitExpression(argument);
|
|
1983
2481
|
}
|
|
1984
|
-
return this.
|
|
2482
|
+
return this.parsePostfixExpression();
|
|
2483
|
+
}
|
|
2484
|
+
parsePostfixExpression() {
|
|
2485
|
+
let expr = this.parseCallExpression();
|
|
2486
|
+
while (true) {
|
|
2487
|
+
this.stream.skipWhitespace();
|
|
2488
|
+
const token = this.stream.peek();
|
|
2489
|
+
if (!token) {
|
|
2490
|
+
break;
|
|
2491
|
+
}
|
|
2492
|
+
if (token.type === "PlusPlus" /* PlusPlus */ || token.type === "MinusMinus" /* MinusMinus */) {
|
|
2493
|
+
this.stream.next();
|
|
2494
|
+
expr = this.createIncrementNode(token, expr, false);
|
|
2495
|
+
continue;
|
|
2496
|
+
}
|
|
2497
|
+
break;
|
|
2498
|
+
}
|
|
2499
|
+
return expr;
|
|
2500
|
+
}
|
|
2501
|
+
createIncrementNode(token, argument, prefix) {
|
|
2502
|
+
if (!(argument instanceof IdentifierExpression) && !(argument instanceof MemberExpression) && !(argument instanceof IndexExpression) && !(argument instanceof DirectiveExpression)) {
|
|
2503
|
+
throw new Error("Increment/decrement requires a mutable target");
|
|
2504
|
+
}
|
|
2505
|
+
const operator = token.type === "PlusPlus" /* PlusPlus */ ? "++" : "--";
|
|
2506
|
+
return new AssignmentNode(argument, new LiteralExpression(1), operator, prefix);
|
|
1985
2507
|
}
|
|
1986
2508
|
parseCallExpression() {
|
|
1987
2509
|
let expr = this.parsePrimaryExpression();
|
|
@@ -2293,6 +2815,7 @@ ${caret}`;
|
|
|
2293
2815
|
this.stream.expect("LBrace" /* LBrace */);
|
|
2294
2816
|
const statements = [];
|
|
2295
2817
|
this.awaitStack.push(allowAwait);
|
|
2818
|
+
this.functionDepth += 1;
|
|
2296
2819
|
try {
|
|
2297
2820
|
while (true) {
|
|
2298
2821
|
this.stream.skipWhitespace();
|
|
@@ -2304,9 +2827,10 @@ ${caret}`;
|
|
|
2304
2827
|
this.stream.next();
|
|
2305
2828
|
break;
|
|
2306
2829
|
}
|
|
2307
|
-
statements.push(this.parseStatement({ allowBlocks:
|
|
2830
|
+
statements.push(this.parseStatement({ allowBlocks: true, allowReturn: true }));
|
|
2308
2831
|
}
|
|
2309
2832
|
} finally {
|
|
2833
|
+
this.functionDepth -= 1;
|
|
2310
2834
|
this.awaitStack.pop();
|
|
2311
2835
|
}
|
|
2312
2836
|
return new BlockNode(statements);
|
|
@@ -2347,7 +2871,14 @@ ${caret}`;
|
|
|
2347
2871
|
return this.parseObjectPattern();
|
|
2348
2872
|
}
|
|
2349
2873
|
if (token.type === "Identifier" /* Identifier */) {
|
|
2350
|
-
|
|
2874
|
+
const expr = this.parseCallExpression();
|
|
2875
|
+
if (expr instanceof CallExpression) {
|
|
2876
|
+
throw new Error("Invalid assignment target CallExpression");
|
|
2877
|
+
}
|
|
2878
|
+
if (expr instanceof IdentifierExpression || expr instanceof MemberExpression || expr instanceof IndexExpression) {
|
|
2879
|
+
return expr;
|
|
2880
|
+
}
|
|
2881
|
+
throw new Error("Invalid assignment target");
|
|
2351
2882
|
}
|
|
2352
2883
|
throw new Error(`Invalid assignment target ${token.type}`);
|
|
2353
2884
|
}
|
|
@@ -2495,7 +3026,7 @@ ${caret}`;
|
|
|
2495
3026
|
const operator = this.parseDeclarationOperator();
|
|
2496
3027
|
this.stream.skipWhitespace();
|
|
2497
3028
|
const value = this.parseExpression();
|
|
2498
|
-
const { flags, flagArgs } = this.parseFlags();
|
|
3029
|
+
const { flags, flagArgs } = this.parseFlags(this.customFlags, "flag");
|
|
2499
3030
|
this.stream.skipWhitespace();
|
|
2500
3031
|
this.stream.expect("Semicolon" /* Semicolon */);
|
|
2501
3032
|
return new DeclarationNode(target, operator, value, flags, flagArgs);
|
|
@@ -2536,7 +3067,7 @@ ${caret}`;
|
|
|
2536
3067
|
}
|
|
2537
3068
|
return ":";
|
|
2538
3069
|
}
|
|
2539
|
-
parseFlags() {
|
|
3070
|
+
parseFlags(allowed, errorLabel) {
|
|
2540
3071
|
const flags = {};
|
|
2541
3072
|
const flagArgs = {};
|
|
2542
3073
|
while (true) {
|
|
@@ -2546,59 +3077,107 @@ ${caret}`;
|
|
|
2546
3077
|
}
|
|
2547
3078
|
this.stream.next();
|
|
2548
3079
|
const name = this.stream.expect("Identifier" /* Identifier */).value;
|
|
2549
|
-
if (
|
|
2550
|
-
|
|
2551
|
-
}
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
3080
|
+
if (allowed && !allowed.has(name)) {
|
|
3081
|
+
throw new Error(`Unknown ${errorLabel} ${name}`);
|
|
3082
|
+
}
|
|
3083
|
+
flags[name] = true;
|
|
3084
|
+
const customArg = this.parseCustomFlagArg();
|
|
3085
|
+
if (customArg !== void 0) {
|
|
3086
|
+
flagArgs[name] = customArg;
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
return { flags, flagArgs };
|
|
3090
|
+
}
|
|
3091
|
+
parseCustomFlagArg() {
|
|
3092
|
+
if (this.stream.peek()?.type !== "LParen" /* LParen */) {
|
|
3093
|
+
return void 0;
|
|
3094
|
+
}
|
|
3095
|
+
this.stream.next();
|
|
3096
|
+
this.stream.skipWhitespace();
|
|
3097
|
+
const token = this.stream.peek();
|
|
3098
|
+
if (!token) {
|
|
3099
|
+
throw new Error("Unterminated flag arguments");
|
|
3100
|
+
}
|
|
3101
|
+
const value = this.parseCustomFlagLiteral();
|
|
3102
|
+
this.stream.skipWhitespace();
|
|
3103
|
+
this.stream.expect("RParen" /* RParen */);
|
|
3104
|
+
return value;
|
|
3105
|
+
}
|
|
3106
|
+
parseCustomFlagLiteral() {
|
|
3107
|
+
const token = this.stream.peek();
|
|
3108
|
+
if (!token) {
|
|
3109
|
+
throw new Error("Unterminated flag arguments");
|
|
3110
|
+
}
|
|
3111
|
+
if (token.type === "Number" /* Number */) {
|
|
3112
|
+
return Number(this.stream.next().value);
|
|
3113
|
+
}
|
|
3114
|
+
if (token.type === "String" /* String */) {
|
|
3115
|
+
return this.stream.next().value;
|
|
3116
|
+
}
|
|
3117
|
+
if (token.type === "Boolean" /* Boolean */) {
|
|
3118
|
+
return this.stream.next().value === "true";
|
|
3119
|
+
}
|
|
3120
|
+
if (token.type === "Identifier" /* Identifier */) {
|
|
3121
|
+
return this.stream.next().value;
|
|
3122
|
+
}
|
|
3123
|
+
if (token.type === "LBracket" /* LBracket */) {
|
|
3124
|
+
return this.parseCustomFlagArray();
|
|
3125
|
+
}
|
|
3126
|
+
if (token.type === "LBrace" /* LBrace */) {
|
|
3127
|
+
return this.parseCustomFlagObject();
|
|
3128
|
+
}
|
|
3129
|
+
throw new Error(`Unsupported flag argument ${token.type}`);
|
|
3130
|
+
}
|
|
3131
|
+
parseCustomFlagArray() {
|
|
3132
|
+
this.stream.expect("LBracket" /* LBracket */);
|
|
3133
|
+
const items = [];
|
|
3134
|
+
while (true) {
|
|
3135
|
+
this.stream.skipWhitespace();
|
|
3136
|
+
const next = this.stream.peek();
|
|
3137
|
+
if (!next) {
|
|
3138
|
+
throw new Error("Unterminated flag array");
|
|
3139
|
+
}
|
|
3140
|
+
if (next.type === "RBracket" /* RBracket */) {
|
|
3141
|
+
this.stream.next();
|
|
3142
|
+
break;
|
|
3143
|
+
}
|
|
3144
|
+
items.push(this.parseCustomFlagLiteral());
|
|
3145
|
+
this.stream.skipWhitespace();
|
|
3146
|
+
if (this.stream.peek()?.type === "Comma" /* Comma */) {
|
|
3147
|
+
this.stream.next();
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
return items;
|
|
3151
|
+
}
|
|
3152
|
+
parseCustomFlagObject() {
|
|
3153
|
+
this.stream.expect("LBrace" /* LBrace */);
|
|
3154
|
+
const obj = {};
|
|
3155
|
+
while (true) {
|
|
3156
|
+
this.stream.skipWhitespace();
|
|
3157
|
+
const next = this.stream.peek();
|
|
3158
|
+
if (!next) {
|
|
3159
|
+
throw new Error("Unterminated flag object");
|
|
3160
|
+
}
|
|
3161
|
+
if (next.type === "RBrace" /* RBrace */) {
|
|
3162
|
+
this.stream.next();
|
|
3163
|
+
break;
|
|
3164
|
+
}
|
|
3165
|
+
let key;
|
|
3166
|
+
if (next.type === "Identifier" /* Identifier */ || next.type === "String" /* String */) {
|
|
3167
|
+
key = this.stream.next().value;
|
|
2571
3168
|
} else {
|
|
2572
|
-
throw new Error(`
|
|
3169
|
+
throw new Error(`Unsupported flag object key ${next.type}`);
|
|
3170
|
+
}
|
|
3171
|
+
this.stream.skipWhitespace();
|
|
3172
|
+
this.stream.expect("Colon" /* Colon */);
|
|
3173
|
+
this.stream.skipWhitespace();
|
|
3174
|
+
obj[key] = this.parseCustomFlagLiteral();
|
|
3175
|
+
this.stream.skipWhitespace();
|
|
3176
|
+
if (this.stream.peek()?.type === "Comma" /* Comma */) {
|
|
3177
|
+
this.stream.next();
|
|
2573
3178
|
}
|
|
2574
3179
|
}
|
|
2575
|
-
return
|
|
2576
|
-
}
|
|
2577
|
-
parseCustomFlagArg() {
|
|
2578
|
-
if (this.stream.peek()?.type !== "LParen" /* LParen */) {
|
|
2579
|
-
return void 0;
|
|
2580
|
-
}
|
|
2581
|
-
this.stream.next();
|
|
2582
|
-
this.stream.skipWhitespace();
|
|
2583
|
-
const token = this.stream.peek();
|
|
2584
|
-
if (!token) {
|
|
2585
|
-
throw new Error("Unterminated flag arguments");
|
|
2586
|
-
}
|
|
2587
|
-
let value;
|
|
2588
|
-
if (token.type === "Number" /* Number */) {
|
|
2589
|
-
value = Number(this.stream.next().value);
|
|
2590
|
-
} else if (token.type === "String" /* String */) {
|
|
2591
|
-
value = this.stream.next().value;
|
|
2592
|
-
} else if (token.type === "Boolean" /* Boolean */) {
|
|
2593
|
-
value = this.stream.next().value === "true";
|
|
2594
|
-
} else if (token.type === "Identifier" /* Identifier */) {
|
|
2595
|
-
value = this.stream.next().value;
|
|
2596
|
-
} else {
|
|
2597
|
-
throw new Error(`Unsupported flag argument ${token.type}`);
|
|
2598
|
-
}
|
|
2599
|
-
this.stream.skipWhitespace();
|
|
2600
|
-
this.stream.expect("RParen" /* RParen */);
|
|
2601
|
-
return value;
|
|
3180
|
+
return obj;
|
|
2602
3181
|
}
|
|
2603
3182
|
isDeclarationStart() {
|
|
2604
3183
|
const first = this.stream.peekNonWhitespace(0);
|
|
@@ -2623,14 +3202,29 @@ ${caret}`;
|
|
|
2623
3202
|
}
|
|
2624
3203
|
if (first.type === "Identifier" /* Identifier */) {
|
|
2625
3204
|
let index = 1;
|
|
2626
|
-
while (
|
|
2627
|
-
|
|
3205
|
+
while (true) {
|
|
3206
|
+
const token = this.stream.peekNonWhitespace(index);
|
|
3207
|
+
if (!token) {
|
|
3208
|
+
return false;
|
|
3209
|
+
}
|
|
3210
|
+
if (token.type === "Dot" /* Dot */ && this.stream.peekNonWhitespace(index + 1)?.type === "Identifier" /* Identifier */) {
|
|
3211
|
+
index += 2;
|
|
3212
|
+
continue;
|
|
3213
|
+
}
|
|
3214
|
+
if (token.type === "LBracket" /* LBracket */) {
|
|
3215
|
+
const indexAfter = this.stream.indexAfterDelimited("LBracket" /* LBracket */, "RBracket" /* RBracket */, index);
|
|
3216
|
+
if (indexAfter === null) {
|
|
3217
|
+
return false;
|
|
3218
|
+
}
|
|
3219
|
+
index = indexAfter;
|
|
3220
|
+
continue;
|
|
3221
|
+
}
|
|
3222
|
+
break;
|
|
2628
3223
|
}
|
|
2629
3224
|
return this.isAssignmentOperatorStart(index);
|
|
2630
3225
|
}
|
|
2631
3226
|
if (first.type === "At" /* At */ || first.type === "Dollar" /* Dollar */) {
|
|
2632
3227
|
const second = this.stream.peekNonWhitespace(1);
|
|
2633
|
-
const third = this.stream.peekNonWhitespace(2);
|
|
2634
3228
|
return second?.type === "Identifier" /* Identifier */ && this.isAssignmentOperatorStart(2);
|
|
2635
3229
|
}
|
|
2636
3230
|
if (first.type === "LBrace" /* LBrace */ || first.type === "LBracket" /* LBracket */) {
|
|
@@ -2668,17 +3262,6 @@ ${caret}`;
|
|
|
2668
3262
|
}
|
|
2669
3263
|
return false;
|
|
2670
3264
|
}
|
|
2671
|
-
isCallStart() {
|
|
2672
|
-
const first = this.stream.peekNonWhitespace(0);
|
|
2673
|
-
if (!first || first.type !== "Identifier" /* Identifier */) {
|
|
2674
|
-
return false;
|
|
2675
|
-
}
|
|
2676
|
-
let index = 1;
|
|
2677
|
-
while (this.stream.peekNonWhitespace(index)?.type === "Dot" /* Dot */ && this.stream.peekNonWhitespace(index + 1)?.type === "Identifier" /* Identifier */) {
|
|
2678
|
-
index += 2;
|
|
2679
|
-
}
|
|
2680
|
-
return this.stream.peekNonWhitespace(index)?.type === "LParen" /* LParen */;
|
|
2681
|
-
}
|
|
2682
3265
|
isExpressionStatementStart() {
|
|
2683
3266
|
const first = this.stream.peekNonWhitespace(0);
|
|
2684
3267
|
if (!first) {
|
|
@@ -2708,50 +3291,22 @@ ${caret}`;
|
|
|
2708
3291
|
if (this.stream.peekNonWhitespace(index)?.type !== "LParen" /* LParen */) {
|
|
2709
3292
|
return false;
|
|
2710
3293
|
}
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
const token = this.stream.peekNonWhitespace(index);
|
|
2715
|
-
if (!token) {
|
|
2716
|
-
return false;
|
|
2717
|
-
}
|
|
2718
|
-
if (token.type === "LParen" /* LParen */) {
|
|
2719
|
-
depth += 1;
|
|
2720
|
-
} else if (token.type === "RParen" /* RParen */) {
|
|
2721
|
-
depth -= 1;
|
|
2722
|
-
if (depth === 0) {
|
|
2723
|
-
index += 1;
|
|
2724
|
-
break;
|
|
2725
|
-
}
|
|
2726
|
-
}
|
|
2727
|
-
index += 1;
|
|
3294
|
+
const indexAfterParams = this.stream.indexAfterDelimited("LParen" /* LParen */, "RParen" /* RParen */, index);
|
|
3295
|
+
if (indexAfterParams === null) {
|
|
3296
|
+
return false;
|
|
2728
3297
|
}
|
|
2729
|
-
return this.stream.peekNonWhitespace(
|
|
3298
|
+
return this.stream.peekNonWhitespace(indexAfterParams)?.type === "LBrace" /* LBrace */;
|
|
2730
3299
|
}
|
|
2731
3300
|
isArrowFunctionStart() {
|
|
2732
3301
|
const first = this.stream.peekNonWhitespace(0);
|
|
2733
3302
|
if (!first || first.type !== "LParen" /* LParen */) {
|
|
2734
3303
|
return false;
|
|
2735
3304
|
}
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
const token = this.stream.peekNonWhitespace(index);
|
|
2740
|
-
if (!token) {
|
|
2741
|
-
return false;
|
|
2742
|
-
}
|
|
2743
|
-
if (token.type === "LParen" /* LParen */) {
|
|
2744
|
-
depth += 1;
|
|
2745
|
-
} else if (token.type === "RParen" /* RParen */) {
|
|
2746
|
-
depth -= 1;
|
|
2747
|
-
if (depth === 0) {
|
|
2748
|
-
index += 1;
|
|
2749
|
-
break;
|
|
2750
|
-
}
|
|
2751
|
-
}
|
|
2752
|
-
index += 1;
|
|
3305
|
+
const indexAfterParams = this.stream.indexAfterDelimited("LParen" /* LParen */, "RParen" /* RParen */, 0);
|
|
3306
|
+
if (indexAfterParams === null) {
|
|
3307
|
+
return false;
|
|
2753
3308
|
}
|
|
2754
|
-
return this.stream.peekNonWhitespace(
|
|
3309
|
+
return this.stream.peekNonWhitespace(indexAfterParams)?.type === "Arrow" /* Arrow */;
|
|
2755
3310
|
}
|
|
2756
3311
|
isAsyncArrowFunctionStart() {
|
|
2757
3312
|
const first = this.stream.peekNonWhitespace(0);
|
|
@@ -2761,25 +3316,11 @@ ${caret}`;
|
|
|
2761
3316
|
if (this.stream.peekNonWhitespace(1)?.type !== "LParen" /* LParen */) {
|
|
2762
3317
|
return false;
|
|
2763
3318
|
}
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
const token = this.stream.peekNonWhitespace(index);
|
|
2768
|
-
if (!token) {
|
|
2769
|
-
return false;
|
|
2770
|
-
}
|
|
2771
|
-
if (token.type === "LParen" /* LParen */) {
|
|
2772
|
-
depth += 1;
|
|
2773
|
-
} else if (token.type === "RParen" /* RParen */) {
|
|
2774
|
-
depth -= 1;
|
|
2775
|
-
if (depth === 0) {
|
|
2776
|
-
index += 1;
|
|
2777
|
-
break;
|
|
2778
|
-
}
|
|
2779
|
-
}
|
|
2780
|
-
index += 1;
|
|
3319
|
+
const indexAfterParams = this.stream.indexAfterDelimited("LParen" /* LParen */, "RParen" /* RParen */, 1);
|
|
3320
|
+
if (indexAfterParams === null) {
|
|
3321
|
+
return false;
|
|
2781
3322
|
}
|
|
2782
|
-
return this.stream.peekNonWhitespace(
|
|
3323
|
+
return this.stream.peekNonWhitespace(indexAfterParams)?.type === "Arrow" /* Arrow */;
|
|
2783
3324
|
}
|
|
2784
3325
|
isFunctionExpressionAssignmentStart() {
|
|
2785
3326
|
const first = this.stream.peekNonWhitespace(0);
|
|
@@ -2796,25 +3337,11 @@ ${caret}`;
|
|
|
2796
3337
|
if (this.stream.peekNonWhitespace(index)?.type !== "LParen" /* LParen */) {
|
|
2797
3338
|
return false;
|
|
2798
3339
|
}
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
const token = this.stream.peekNonWhitespace(index);
|
|
2803
|
-
if (!token) {
|
|
2804
|
-
return false;
|
|
2805
|
-
}
|
|
2806
|
-
if (token.type === "LParen" /* LParen */) {
|
|
2807
|
-
depth += 1;
|
|
2808
|
-
} else if (token.type === "RParen" /* RParen */) {
|
|
2809
|
-
depth -= 1;
|
|
2810
|
-
if (depth === 0) {
|
|
2811
|
-
index += 1;
|
|
2812
|
-
break;
|
|
2813
|
-
}
|
|
2814
|
-
}
|
|
2815
|
-
index += 1;
|
|
3340
|
+
const indexAfterParams = this.stream.indexAfterDelimited("LParen" /* LParen */, "RParen" /* RParen */, index);
|
|
3341
|
+
if (indexAfterParams === null) {
|
|
3342
|
+
return false;
|
|
2816
3343
|
}
|
|
2817
|
-
return this.stream.peekNonWhitespace(
|
|
3344
|
+
return this.stream.peekNonWhitespace(indexAfterParams)?.type === "Arrow" /* Arrow */;
|
|
2818
3345
|
}
|
|
2819
3346
|
parseExpressionStatement() {
|
|
2820
3347
|
const expr = this.parseExpression();
|
|
@@ -2829,7 +3356,7 @@ ${caret}`;
|
|
|
2829
3356
|
const test = this.parseExpression();
|
|
2830
3357
|
this.stream.skipWhitespace();
|
|
2831
3358
|
this.stream.expect("RParen" /* RParen */);
|
|
2832
|
-
const consequent = this.
|
|
3359
|
+
const consequent = this.parseConditionalBody();
|
|
2833
3360
|
this.stream.skipWhitespace();
|
|
2834
3361
|
let alternate;
|
|
2835
3362
|
if (this.stream.peek()?.type === "Else" /* Else */) {
|
|
@@ -2839,11 +3366,19 @@ ${caret}`;
|
|
|
2839
3366
|
const nested = this.parseIfBlock();
|
|
2840
3367
|
alternate = new BlockNode([nested]);
|
|
2841
3368
|
} else {
|
|
2842
|
-
alternate = this.
|
|
3369
|
+
alternate = this.parseConditionalBody();
|
|
2843
3370
|
}
|
|
2844
3371
|
}
|
|
2845
3372
|
return new IfNode(test, consequent, alternate);
|
|
2846
3373
|
}
|
|
3374
|
+
parseConditionalBody() {
|
|
3375
|
+
this.stream.skipWhitespace();
|
|
3376
|
+
if (this.stream.peek()?.type === "LBrace" /* LBrace */) {
|
|
3377
|
+
return this.parseBlock({ allowDeclarations: false });
|
|
3378
|
+
}
|
|
3379
|
+
const statement = this.parseStatement({ allowBlocks: false, allowReturn: this.functionDepth > 0 });
|
|
3380
|
+
return new BlockNode([statement]);
|
|
3381
|
+
}
|
|
2847
3382
|
parseWhileBlock() {
|
|
2848
3383
|
this.stream.expect("While" /* While */);
|
|
2849
3384
|
this.stream.skipWhitespace();
|
|
@@ -2860,6 +3395,21 @@ ${caret}`;
|
|
|
2860
3395
|
this.stream.skipWhitespace();
|
|
2861
3396
|
this.stream.expect("LParen" /* LParen */);
|
|
2862
3397
|
this.stream.skipWhitespace();
|
|
3398
|
+
const eachKind = this.detectForEachKind();
|
|
3399
|
+
if (eachKind) {
|
|
3400
|
+
const target = this.parseForEachTarget();
|
|
3401
|
+
this.stream.skipWhitespace();
|
|
3402
|
+
const keyword = this.stream.expect("Identifier" /* Identifier */);
|
|
3403
|
+
if (keyword.value !== eachKind) {
|
|
3404
|
+
throw new Error(`Expected '${eachKind}' but got '${keyword.value}'`);
|
|
3405
|
+
}
|
|
3406
|
+
this.stream.skipWhitespace();
|
|
3407
|
+
const iterable = this.parseExpression();
|
|
3408
|
+
this.stream.skipWhitespace();
|
|
3409
|
+
this.stream.expect("RParen" /* RParen */);
|
|
3410
|
+
const body2 = this.parseBlock({ allowDeclarations: false });
|
|
3411
|
+
return new ForEachNode(target, iterable, eachKind, body2);
|
|
3412
|
+
}
|
|
2863
3413
|
let init;
|
|
2864
3414
|
if (this.stream.peek()?.type !== "Semicolon" /* Semicolon */) {
|
|
2865
3415
|
init = this.parseForClause();
|
|
@@ -2883,6 +3433,43 @@ ${caret}`;
|
|
|
2883
3433
|
const body = this.parseBlock({ allowDeclarations: false });
|
|
2884
3434
|
return new ForNode(init, test, update, body);
|
|
2885
3435
|
}
|
|
3436
|
+
detectForEachKind() {
|
|
3437
|
+
let offset = 0;
|
|
3438
|
+
let depth = 0;
|
|
3439
|
+
while (true) {
|
|
3440
|
+
const token = this.stream.peekNonWhitespace(offset);
|
|
3441
|
+
if (!token) {
|
|
3442
|
+
return null;
|
|
3443
|
+
}
|
|
3444
|
+
if (token.type === "LParen" /* LParen */ || token.type === "LBracket" /* LBracket */ || token.type === "LBrace" /* LBrace */) {
|
|
3445
|
+
depth += 1;
|
|
3446
|
+
} else if (token.type === "RParen" /* RParen */ || token.type === "RBracket" /* RBracket */ || token.type === "RBrace" /* RBrace */) {
|
|
3447
|
+
if (depth === 0) {
|
|
3448
|
+
return null;
|
|
3449
|
+
}
|
|
3450
|
+
depth -= 1;
|
|
3451
|
+
}
|
|
3452
|
+
if (depth === 0) {
|
|
3453
|
+
if (token.type === "Semicolon" /* Semicolon */) {
|
|
3454
|
+
return null;
|
|
3455
|
+
}
|
|
3456
|
+
if (token.type === "Identifier" /* Identifier */ && (token.value === "in" || token.value === "of")) {
|
|
3457
|
+
return token.value;
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
offset += 1;
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
parseForEachTarget() {
|
|
3464
|
+
const token = this.stream.peek();
|
|
3465
|
+
if (!token) {
|
|
3466
|
+
throw new Error("Expected for-each target");
|
|
3467
|
+
}
|
|
3468
|
+
if (token.type !== "Identifier" /* Identifier */) {
|
|
3469
|
+
throw new Error("for-in/of target must be an identifier");
|
|
3470
|
+
}
|
|
3471
|
+
return new IdentifierExpression(this.stream.next().value);
|
|
3472
|
+
}
|
|
2886
3473
|
parseForClause() {
|
|
2887
3474
|
if (this.isAssignmentStart()) {
|
|
2888
3475
|
return this.parseAssignmentExpression();
|
|
@@ -2978,9 +3565,6 @@ ${caret}`;
|
|
|
2978
3565
|
const body = this.parseFunctionBlockWithAwait(isAsync);
|
|
2979
3566
|
return new FunctionDeclarationNode(name, params, body, isAsync);
|
|
2980
3567
|
}
|
|
2981
|
-
parseFunctionBlock() {
|
|
2982
|
-
return this.parseFunctionBlockWithAwait(false);
|
|
2983
|
-
}
|
|
2984
3568
|
parseReturnStatement() {
|
|
2985
3569
|
this.stream.expect("Return" /* Return */);
|
|
2986
3570
|
this.stream.skipWhitespace();
|
|
@@ -2993,6 +3577,23 @@ ${caret}`;
|
|
|
2993
3577
|
this.stream.expect("Semicolon" /* Semicolon */);
|
|
2994
3578
|
return new ReturnNode(value);
|
|
2995
3579
|
}
|
|
3580
|
+
parseAssertStatement() {
|
|
3581
|
+
this.stream.expect("Assert" /* Assert */);
|
|
3582
|
+
this.stream.skipWhitespace();
|
|
3583
|
+
const test = this.parseExpression();
|
|
3584
|
+
this.consumeStatementTerminator();
|
|
3585
|
+
return new AssertNode(test);
|
|
3586
|
+
}
|
|
3587
|
+
parseBreakStatement() {
|
|
3588
|
+
this.stream.expect("Break" /* Break */);
|
|
3589
|
+
this.consumeStatementTerminator();
|
|
3590
|
+
return new BreakNode();
|
|
3591
|
+
}
|
|
3592
|
+
parseContinueStatement() {
|
|
3593
|
+
this.stream.expect("Continue" /* Continue */);
|
|
3594
|
+
this.consumeStatementTerminator();
|
|
3595
|
+
return new ContinueNode();
|
|
3596
|
+
}
|
|
2996
3597
|
parseArrowFunctionExpression(isAsync = false) {
|
|
2997
3598
|
const params = this.parseFunctionParams();
|
|
2998
3599
|
this.stream.skipWhitespace();
|
|
@@ -3102,6 +3703,7 @@ var Scope = class _Scope {
|
|
|
3102
3703
|
root;
|
|
3103
3704
|
listeners = /* @__PURE__ */ new Map();
|
|
3104
3705
|
anyListeners = /* @__PURE__ */ new Set();
|
|
3706
|
+
isEachItem = false;
|
|
3105
3707
|
createChild() {
|
|
3106
3708
|
return new _Scope(this);
|
|
3107
3709
|
}
|
|
@@ -3404,18 +4006,19 @@ var Engine = class _Engine {
|
|
|
3404
4006
|
eachBindings = /* @__PURE__ */ new WeakMap();
|
|
3405
4007
|
lifecycleBindings = /* @__PURE__ */ new WeakMap();
|
|
3406
4008
|
behaviorRegistry = [];
|
|
4009
|
+
behaviorRegistryHashes = /* @__PURE__ */ new Set();
|
|
3407
4010
|
behaviorBindings = /* @__PURE__ */ new WeakMap();
|
|
3408
4011
|
behaviorListeners = /* @__PURE__ */ new WeakMap();
|
|
3409
4012
|
behaviorId = 0;
|
|
3410
4013
|
codeCache = /* @__PURE__ */ new Map();
|
|
3411
4014
|
behaviorCache = /* @__PURE__ */ new Map();
|
|
3412
4015
|
observer;
|
|
3413
|
-
observerRoot;
|
|
3414
4016
|
attributeHandlers = [];
|
|
3415
4017
|
globals = {};
|
|
3416
4018
|
importantFlags = /* @__PURE__ */ new WeakMap();
|
|
3417
4019
|
inlineDeclarations = /* @__PURE__ */ new WeakMap();
|
|
3418
4020
|
flagHandlers = /* @__PURE__ */ new Map();
|
|
4021
|
+
behaviorModifiers = /* @__PURE__ */ new Map();
|
|
3419
4022
|
pendingAdded = /* @__PURE__ */ new Set();
|
|
3420
4023
|
pendingRemoved = /* @__PURE__ */ new Set();
|
|
3421
4024
|
pendingUpdated = /* @__PURE__ */ new Set();
|
|
@@ -3424,10 +4027,119 @@ var Engine = class _Engine {
|
|
|
3424
4027
|
diagnostics;
|
|
3425
4028
|
logger;
|
|
3426
4029
|
pendingUses = [];
|
|
4030
|
+
pendingAutoBindToScope = [];
|
|
4031
|
+
scopeWatchers = /* @__PURE__ */ new WeakMap();
|
|
4032
|
+
executionStack = [];
|
|
4033
|
+
groupProxyCache = /* @__PURE__ */ new WeakMap();
|
|
3427
4034
|
constructor(options = {}) {
|
|
3428
4035
|
this.diagnostics = options.diagnostics ?? false;
|
|
3429
4036
|
this.logger = options.logger ?? console;
|
|
3430
4037
|
this.registerGlobal("console", console);
|
|
4038
|
+
this.registerFlag("important");
|
|
4039
|
+
this.registerFlag("trusted");
|
|
4040
|
+
this.registerFlag("debounce", {
|
|
4041
|
+
onEventBind: ({ args }) => ({
|
|
4042
|
+
debounceMs: typeof args === "number" ? args : 200
|
|
4043
|
+
})
|
|
4044
|
+
});
|
|
4045
|
+
this.registerFlag("prevent", {
|
|
4046
|
+
onEventBefore: ({ event }) => {
|
|
4047
|
+
event?.preventDefault();
|
|
4048
|
+
}
|
|
4049
|
+
});
|
|
4050
|
+
this.registerFlag("stop", {
|
|
4051
|
+
onEventBefore: ({ event }) => {
|
|
4052
|
+
event?.stopPropagation();
|
|
4053
|
+
}
|
|
4054
|
+
});
|
|
4055
|
+
this.registerFlag("self", {
|
|
4056
|
+
onEventBefore: ({ event, element }) => {
|
|
4057
|
+
const target = event?.target;
|
|
4058
|
+
if (!(target instanceof Node)) {
|
|
4059
|
+
return false;
|
|
4060
|
+
}
|
|
4061
|
+
return target === element;
|
|
4062
|
+
}
|
|
4063
|
+
});
|
|
4064
|
+
this.registerFlag("outside", {
|
|
4065
|
+
onEventBind: ({ element }) => ({ listenerTarget: element.ownerDocument }),
|
|
4066
|
+
onEventBefore: ({ event, element }) => {
|
|
4067
|
+
const target = event?.target;
|
|
4068
|
+
if (!(target instanceof Node)) {
|
|
4069
|
+
return false;
|
|
4070
|
+
}
|
|
4071
|
+
return !element.contains(target);
|
|
4072
|
+
}
|
|
4073
|
+
});
|
|
4074
|
+
this.registerFlag("once", {
|
|
4075
|
+
onEventBind: () => ({ options: { once: true } })
|
|
4076
|
+
});
|
|
4077
|
+
this.registerFlag("passive", {
|
|
4078
|
+
onEventBind: () => ({ options: { passive: true } })
|
|
4079
|
+
});
|
|
4080
|
+
this.registerFlag("capture", {
|
|
4081
|
+
onEventBind: () => ({ options: { capture: true } })
|
|
4082
|
+
});
|
|
4083
|
+
this.registerFlag("shift", {
|
|
4084
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "shift")
|
|
4085
|
+
});
|
|
4086
|
+
this.registerFlag("ctrl", {
|
|
4087
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "ctrl")
|
|
4088
|
+
});
|
|
4089
|
+
this.registerFlag("control", {
|
|
4090
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "ctrl")
|
|
4091
|
+
});
|
|
4092
|
+
this.registerFlag("alt", {
|
|
4093
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "alt")
|
|
4094
|
+
});
|
|
4095
|
+
this.registerFlag("meta", {
|
|
4096
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "meta")
|
|
4097
|
+
});
|
|
4098
|
+
this.registerFlag("enter", {
|
|
4099
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "enter")
|
|
4100
|
+
});
|
|
4101
|
+
this.registerFlag("escape", {
|
|
4102
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "escape")
|
|
4103
|
+
});
|
|
4104
|
+
this.registerFlag("esc", {
|
|
4105
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "escape")
|
|
4106
|
+
});
|
|
4107
|
+
this.registerFlag("tab", {
|
|
4108
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "tab")
|
|
4109
|
+
});
|
|
4110
|
+
this.registerFlag("space", {
|
|
4111
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "space")
|
|
4112
|
+
});
|
|
4113
|
+
this.registerFlag("up", {
|
|
4114
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowup")
|
|
4115
|
+
});
|
|
4116
|
+
this.registerFlag("down", {
|
|
4117
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowdown")
|
|
4118
|
+
});
|
|
4119
|
+
this.registerFlag("left", {
|
|
4120
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowleft")
|
|
4121
|
+
});
|
|
4122
|
+
this.registerFlag("right", {
|
|
4123
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowright")
|
|
4124
|
+
});
|
|
4125
|
+
this.registerFlag("arrowup", {
|
|
4126
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowup")
|
|
4127
|
+
});
|
|
4128
|
+
this.registerFlag("arrowdown", {
|
|
4129
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowdown")
|
|
4130
|
+
});
|
|
4131
|
+
this.registerFlag("arrowleft", {
|
|
4132
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowleft")
|
|
4133
|
+
});
|
|
4134
|
+
this.registerFlag("arrowright", {
|
|
4135
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "arrowright")
|
|
4136
|
+
});
|
|
4137
|
+
this.registerFlag("delete", {
|
|
4138
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "delete")
|
|
4139
|
+
});
|
|
4140
|
+
this.registerFlag("backspace", {
|
|
4141
|
+
onEventBefore: ({ event }) => this.matchesKeyFlag(event, "backspace")
|
|
4142
|
+
});
|
|
3431
4143
|
this.registerGlobal("list", {
|
|
3432
4144
|
async map(items, fn) {
|
|
3433
4145
|
if (!Array.isArray(items) || typeof fn !== "function") {
|
|
@@ -3465,6 +4177,96 @@ var Engine = class _Engine {
|
|
|
3465
4177
|
}
|
|
3466
4178
|
});
|
|
3467
4179
|
this.registerDefaultAttributeHandlers();
|
|
4180
|
+
this.registerFlag("int", {
|
|
4181
|
+
transformValue: (_context, value) => this.coerceInt(value)
|
|
4182
|
+
});
|
|
4183
|
+
this.registerFlag("float", {
|
|
4184
|
+
transformValue: (_context, value) => this.coerceFloat(value)
|
|
4185
|
+
});
|
|
4186
|
+
this.registerBehaviorModifier("group", {
|
|
4187
|
+
onConstruct: ({ args, scope, rootScope, behavior, element }) => {
|
|
4188
|
+
const key = typeof args === "string" ? args : void 0;
|
|
4189
|
+
if (!key) {
|
|
4190
|
+
return;
|
|
4191
|
+
}
|
|
4192
|
+
const targetScope = this.getGroupTargetScope(element, behavior, scope, rootScope);
|
|
4193
|
+
const existing = targetScope.getPath?.(key);
|
|
4194
|
+
const list = Array.isArray(existing) ? existing : [];
|
|
4195
|
+
const proxy = this.getGroupProxy(scope);
|
|
4196
|
+
if (!list.includes(proxy)) {
|
|
4197
|
+
list.push(proxy);
|
|
4198
|
+
targetScope.setPath?.(key, list);
|
|
4199
|
+
} else if (!Array.isArray(existing)) {
|
|
4200
|
+
targetScope.setPath?.(key, list);
|
|
4201
|
+
}
|
|
4202
|
+
},
|
|
4203
|
+
onUnbind: ({ args, scope, rootScope, behavior, element }) => {
|
|
4204
|
+
const key = typeof args === "string" ? args : void 0;
|
|
4205
|
+
if (!key) {
|
|
4206
|
+
return;
|
|
4207
|
+
}
|
|
4208
|
+
const targetScope = this.getGroupTargetScope(element, behavior, scope, rootScope);
|
|
4209
|
+
const existing = targetScope.getPath?.(key);
|
|
4210
|
+
if (!Array.isArray(existing)) {
|
|
4211
|
+
return;
|
|
4212
|
+
}
|
|
4213
|
+
const proxy = this.getGroupProxy(scope);
|
|
4214
|
+
const next = existing.filter((entry) => entry !== proxy);
|
|
4215
|
+
if (next.length !== existing.length) {
|
|
4216
|
+
targetScope.setPath?.(key, next);
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
});
|
|
4220
|
+
}
|
|
4221
|
+
getGroupTargetScope(element, behavior, scope, rootScope) {
|
|
4222
|
+
let targetScope = rootScope ?? scope;
|
|
4223
|
+
if (behavior.parentSelector) {
|
|
4224
|
+
const parentElement = element.closest(behavior.parentSelector);
|
|
4225
|
+
if (parentElement) {
|
|
4226
|
+
targetScope = this.getScope(parentElement);
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
return targetScope;
|
|
4230
|
+
}
|
|
4231
|
+
getGroupProxy(scope) {
|
|
4232
|
+
const cached = this.groupProxyCache.get(scope);
|
|
4233
|
+
if (cached) {
|
|
4234
|
+
return cached;
|
|
4235
|
+
}
|
|
4236
|
+
const proxy = new Proxy(
|
|
4237
|
+
{},
|
|
4238
|
+
{
|
|
4239
|
+
get: (_target, prop) => {
|
|
4240
|
+
if (typeof prop === "symbol") {
|
|
4241
|
+
return void 0;
|
|
4242
|
+
}
|
|
4243
|
+
if (prop === "__scope") {
|
|
4244
|
+
return scope;
|
|
4245
|
+
}
|
|
4246
|
+
return scope.getPath(String(prop));
|
|
4247
|
+
},
|
|
4248
|
+
set: (_target, prop, value) => {
|
|
4249
|
+
if (typeof prop === "symbol") {
|
|
4250
|
+
return false;
|
|
4251
|
+
}
|
|
4252
|
+
scope.setPath(String(prop), value);
|
|
4253
|
+
return true;
|
|
4254
|
+
},
|
|
4255
|
+
has: (_target, prop) => {
|
|
4256
|
+
if (typeof prop === "symbol") {
|
|
4257
|
+
return false;
|
|
4258
|
+
}
|
|
4259
|
+
return scope.getPath(String(prop)) !== void 0;
|
|
4260
|
+
},
|
|
4261
|
+
getOwnPropertyDescriptor: () => ({
|
|
4262
|
+
enumerable: true,
|
|
4263
|
+
configurable: true
|
|
4264
|
+
}),
|
|
4265
|
+
ownKeys: () => []
|
|
4266
|
+
}
|
|
4267
|
+
);
|
|
4268
|
+
this.groupProxyCache.set(scope, proxy);
|
|
4269
|
+
return proxy;
|
|
3468
4270
|
}
|
|
3469
4271
|
async mount(root) {
|
|
3470
4272
|
const documentRoot = root.ownerDocument;
|
|
@@ -3491,7 +4293,10 @@ var Engine = class _Engine {
|
|
|
3491
4293
|
this.disconnectObserver();
|
|
3492
4294
|
}
|
|
3493
4295
|
registerBehaviors(source) {
|
|
3494
|
-
const program = new Parser(source, {
|
|
4296
|
+
const program = new Parser(source, {
|
|
4297
|
+
customFlags: new Set(this.flagHandlers.keys()),
|
|
4298
|
+
behaviorFlags: new Set(this.behaviorModifiers.keys())
|
|
4299
|
+
}).parseProgram();
|
|
3495
4300
|
for (const use of program.uses) {
|
|
3496
4301
|
if (use.flags?.wait) {
|
|
3497
4302
|
this.pendingUses.push(this.waitForUseGlobal(use));
|
|
@@ -3515,11 +4320,14 @@ var Engine = class _Engine {
|
|
|
3515
4320
|
Object.assign(this.globals, values);
|
|
3516
4321
|
}
|
|
3517
4322
|
registerFlag(name, handler = {}) {
|
|
4323
|
+
this.flagHandlers.set(name, handler);
|
|
4324
|
+
}
|
|
4325
|
+
registerBehaviorModifier(name, handler = {}) {
|
|
3518
4326
|
const reserved = /* @__PURE__ */ new Set(["important", "trusted", "debounce"]);
|
|
3519
4327
|
if (reserved.has(name)) {
|
|
3520
|
-
throw new Error(`
|
|
4328
|
+
throw new Error(`Behavior modifier '${name}' is reserved`);
|
|
3521
4329
|
}
|
|
3522
|
-
this.
|
|
4330
|
+
this.behaviorModifiers.set(name, handler);
|
|
3523
4331
|
}
|
|
3524
4332
|
getRegistryStats() {
|
|
3525
4333
|
return {
|
|
@@ -3627,7 +4435,6 @@ var Engine = class _Engine {
|
|
|
3627
4435
|
if (this.observer) {
|
|
3628
4436
|
return;
|
|
3629
4437
|
}
|
|
3630
|
-
this.observerRoot = root;
|
|
3631
4438
|
this.observerFlush = debounce(() => this.flushObserverQueue(), 10);
|
|
3632
4439
|
this.observer = new MutationObserver((mutations) => {
|
|
3633
4440
|
for (const mutation of mutations) {
|
|
@@ -3657,7 +4464,6 @@ var Engine = class _Engine {
|
|
|
3657
4464
|
disconnectObserver() {
|
|
3658
4465
|
this.observer?.disconnect();
|
|
3659
4466
|
this.observer = void 0;
|
|
3660
|
-
this.observerRoot = void 0;
|
|
3661
4467
|
this.pendingAdded.clear();
|
|
3662
4468
|
this.pendingRemoved.clear();
|
|
3663
4469
|
this.pendingUpdated.clear();
|
|
@@ -3686,6 +4492,8 @@ var Engine = class _Engine {
|
|
|
3686
4492
|
if (this.behaviorBindings.has(node)) {
|
|
3687
4493
|
this.runBehaviorDestruct(node);
|
|
3688
4494
|
}
|
|
4495
|
+
this.cleanupScopeWatchers(node);
|
|
4496
|
+
this.cleanupBehaviorListeners(node);
|
|
3689
4497
|
for (const child of Array.from(node.querySelectorAll("*"))) {
|
|
3690
4498
|
if (this.lifecycleBindings.has(child)) {
|
|
3691
4499
|
this.runDestruct(child);
|
|
@@ -3693,6 +4501,8 @@ var Engine = class _Engine {
|
|
|
3693
4501
|
if (this.behaviorBindings.has(child)) {
|
|
3694
4502
|
this.runBehaviorDestruct(child);
|
|
3695
4503
|
}
|
|
4504
|
+
this.cleanupScopeWatchers(child);
|
|
4505
|
+
this.cleanupBehaviorListeners(child);
|
|
3696
4506
|
}
|
|
3697
4507
|
}
|
|
3698
4508
|
handleAddedNode(node) {
|
|
@@ -3716,13 +4526,13 @@ var Engine = class _Engine {
|
|
|
3716
4526
|
}
|
|
3717
4527
|
async applyBehaviors(root) {
|
|
3718
4528
|
await this.waitForUses();
|
|
3719
|
-
if (this.behaviorRegistry.length
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
await this.reapplyBehaviorsForElement(element);
|
|
4529
|
+
if (this.behaviorRegistry.length > 0) {
|
|
4530
|
+
const elements = [root, ...Array.from(root.querySelectorAll("*"))];
|
|
4531
|
+
for (const element of elements) {
|
|
4532
|
+
await this.reapplyBehaviorsForElement(element);
|
|
4533
|
+
}
|
|
3725
4534
|
}
|
|
4535
|
+
this.flushAutoBindQueue();
|
|
3726
4536
|
}
|
|
3727
4537
|
async reapplyBehaviorsForElement(element) {
|
|
3728
4538
|
if (this.behaviorRegistry.length === 0) {
|
|
@@ -3754,15 +4564,18 @@ var Engine = class _Engine {
|
|
|
3754
4564
|
const rootScope = this.getBehaviorRootScope(element, behavior);
|
|
3755
4565
|
this.applyBehaviorFunctions(element, scope, behavior.functions, rootScope);
|
|
3756
4566
|
await this.applyBehaviorDeclarations(element, scope, behavior.declarations, rootScope);
|
|
4567
|
+
await this.applyBehaviorModifierHook("onBind", behavior, element, scope, rootScope);
|
|
3757
4568
|
if (behavior.construct) {
|
|
3758
4569
|
await this.safeExecuteBlock(behavior.construct, scope, element, rootScope);
|
|
3759
4570
|
}
|
|
4571
|
+
await this.applyBehaviorModifierHook("onConstruct", behavior, element, scope, rootScope);
|
|
3760
4572
|
for (const onBlock of behavior.onBlocks) {
|
|
3761
4573
|
this.attachBehaviorOnHandler(
|
|
3762
4574
|
element,
|
|
3763
4575
|
onBlock.event,
|
|
3764
4576
|
onBlock.body,
|
|
3765
|
-
onBlock.
|
|
4577
|
+
onBlock.flags,
|
|
4578
|
+
onBlock.flagArgs,
|
|
3766
4579
|
onBlock.args,
|
|
3767
4580
|
behavior.id,
|
|
3768
4581
|
rootScope
|
|
@@ -3772,10 +4585,11 @@ var Engine = class _Engine {
|
|
|
3772
4585
|
}
|
|
3773
4586
|
unbindBehaviorForElement(behavior, element, scope, bound) {
|
|
3774
4587
|
bound.delete(behavior.id);
|
|
4588
|
+
const rootScope = this.getBehaviorRootScope(element, behavior);
|
|
3775
4589
|
if (behavior.destruct) {
|
|
3776
|
-
const rootScope = this.getBehaviorRootScope(element, behavior);
|
|
3777
4590
|
void this.safeExecuteBlock(behavior.destruct, scope, element, rootScope);
|
|
3778
4591
|
}
|
|
4592
|
+
void this.applyBehaviorModifierHook("onDestruct", behavior, element, scope, rootScope);
|
|
3779
4593
|
const listenerMap = this.behaviorListeners.get(element);
|
|
3780
4594
|
const listeners = listenerMap?.get(behavior.id);
|
|
3781
4595
|
if (listeners) {
|
|
@@ -3784,6 +4598,7 @@ var Engine = class _Engine {
|
|
|
3784
4598
|
}
|
|
3785
4599
|
listenerMap?.delete(behavior.id);
|
|
3786
4600
|
}
|
|
4601
|
+
void this.applyBehaviorModifierHook("onUnbind", behavior, element, scope, rootScope);
|
|
3787
4602
|
this.logDiagnostic("unbind", element, behavior);
|
|
3788
4603
|
}
|
|
3789
4604
|
runBehaviorDestruct(element) {
|
|
@@ -3793,11 +4608,15 @@ var Engine = class _Engine {
|
|
|
3793
4608
|
}
|
|
3794
4609
|
const scope = this.getScope(element);
|
|
3795
4610
|
for (const behavior of this.behaviorRegistry) {
|
|
3796
|
-
if (!bound.has(behavior.id) || !behavior.destruct) {
|
|
4611
|
+
if (!bound.has(behavior.id) || !behavior.destruct && !this.behaviorHasModifierHooks(behavior)) {
|
|
3797
4612
|
continue;
|
|
3798
4613
|
}
|
|
3799
4614
|
const rootScope = this.getBehaviorRootScope(element, behavior);
|
|
3800
|
-
|
|
4615
|
+
if (behavior.destruct) {
|
|
4616
|
+
void this.safeExecuteBlock(behavior.destruct, scope, element, rootScope);
|
|
4617
|
+
}
|
|
4618
|
+
void this.applyBehaviorModifierHook("onDestruct", behavior, element, scope, rootScope);
|
|
4619
|
+
void this.applyBehaviorModifierHook("onUnbind", behavior, element, scope, rootScope);
|
|
3801
4620
|
}
|
|
3802
4621
|
}
|
|
3803
4622
|
attachAttributes(element) {
|
|
@@ -3881,6 +4700,7 @@ var Engine = class _Engine {
|
|
|
3881
4700
|
const fragment = element.content.cloneNode(true);
|
|
3882
4701
|
const roots = Array.from(fragment.children);
|
|
3883
4702
|
const itemScope = new Scope(scope);
|
|
4703
|
+
itemScope.isEachItem = true;
|
|
3884
4704
|
itemScope.setPath(`self.${binding.itemName}`, item);
|
|
3885
4705
|
if (binding.indexName) {
|
|
3886
4706
|
itemScope.setPath(`self.${binding.indexName}`, index);
|
|
@@ -3919,7 +4739,93 @@ var Engine = class _Engine {
|
|
|
3919
4739
|
if (name.includes(":to")) {
|
|
3920
4740
|
return "to";
|
|
3921
4741
|
}
|
|
3922
|
-
return "
|
|
4742
|
+
return "auto";
|
|
4743
|
+
}
|
|
4744
|
+
resolveBindConfig(element, expr, scope, direction) {
|
|
4745
|
+
if (direction !== "auto") {
|
|
4746
|
+
return {
|
|
4747
|
+
direction,
|
|
4748
|
+
seedFromScope: false,
|
|
4749
|
+
syncToScope: direction === "to" || direction === "both",
|
|
4750
|
+
deferToScope: false
|
|
4751
|
+
};
|
|
4752
|
+
}
|
|
4753
|
+
if (this.isInEachScope(scope)) {
|
|
4754
|
+
return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: false };
|
|
4755
|
+
}
|
|
4756
|
+
if (this.isFormControl(element)) {
|
|
4757
|
+
if (this.hasScopeValue(scope, expr)) {
|
|
4758
|
+
return { direction: "both", seedFromScope: true, syncToScope: false, deferToScope: false };
|
|
4759
|
+
}
|
|
4760
|
+
return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: true };
|
|
4761
|
+
}
|
|
4762
|
+
if (this.hasScopeValue(scope, expr)) {
|
|
4763
|
+
return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: false };
|
|
4764
|
+
}
|
|
4765
|
+
if (this.hasElementValue(element)) {
|
|
4766
|
+
return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: true };
|
|
4767
|
+
}
|
|
4768
|
+
return { direction: "both", seedFromScope: false, syncToScope: false, deferToScope: false };
|
|
4769
|
+
}
|
|
4770
|
+
isFormControl(element) {
|
|
4771
|
+
return element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement;
|
|
4772
|
+
}
|
|
4773
|
+
hasScopeValue(scope, expr) {
|
|
4774
|
+
const key = expr.trim();
|
|
4775
|
+
if (!key) {
|
|
4776
|
+
return false;
|
|
4777
|
+
}
|
|
4778
|
+
const value = scope.get(key);
|
|
4779
|
+
return value !== void 0 && value !== null;
|
|
4780
|
+
}
|
|
4781
|
+
hasElementValue(element) {
|
|
4782
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
4783
|
+
return element.value.length > 0;
|
|
4784
|
+
}
|
|
4785
|
+
return (element.textContent ?? "").trim().length > 0;
|
|
4786
|
+
}
|
|
4787
|
+
coerceInt(value) {
|
|
4788
|
+
if (value == null || value === "") {
|
|
4789
|
+
return value;
|
|
4790
|
+
}
|
|
4791
|
+
const num = typeof value === "number" ? value : Number.parseInt(String(value), 10);
|
|
4792
|
+
return Number.isNaN(num) ? value : num;
|
|
4793
|
+
}
|
|
4794
|
+
coerceFloat(value) {
|
|
4795
|
+
if (value == null || value === "") {
|
|
4796
|
+
return value;
|
|
4797
|
+
}
|
|
4798
|
+
const num = typeof value === "number" ? value : Number.parseFloat(String(value));
|
|
4799
|
+
return Number.isNaN(num) ? value : num;
|
|
4800
|
+
}
|
|
4801
|
+
isInEachScope(scope) {
|
|
4802
|
+
let cursor = scope;
|
|
4803
|
+
while (cursor) {
|
|
4804
|
+
if (cursor.isEachItem) {
|
|
4805
|
+
return true;
|
|
4806
|
+
}
|
|
4807
|
+
cursor = cursor.parent;
|
|
4808
|
+
}
|
|
4809
|
+
return false;
|
|
4810
|
+
}
|
|
4811
|
+
flushAutoBindQueue() {
|
|
4812
|
+
if (this.pendingAutoBindToScope.length === 0) {
|
|
4813
|
+
return;
|
|
4814
|
+
}
|
|
4815
|
+
const pending = this.pendingAutoBindToScope;
|
|
4816
|
+
this.pendingAutoBindToScope = [];
|
|
4817
|
+
for (const entry of pending) {
|
|
4818
|
+
if (!entry.element.isConnected) {
|
|
4819
|
+
continue;
|
|
4820
|
+
}
|
|
4821
|
+
if (this.hasScopeValue(entry.scope, entry.expr)) {
|
|
4822
|
+
continue;
|
|
4823
|
+
}
|
|
4824
|
+
if (!this.hasElementValue(entry.element)) {
|
|
4825
|
+
continue;
|
|
4826
|
+
}
|
|
4827
|
+
applyBindToScope(entry.element, entry.expr, entry.scope);
|
|
4828
|
+
}
|
|
3923
4829
|
}
|
|
3924
4830
|
hasVsnAttributes(element) {
|
|
3925
4831
|
return element.getAttributeNames().some((name) => name.startsWith("vsn-"));
|
|
@@ -3944,7 +4850,7 @@ var Engine = class _Engine {
|
|
|
3944
4850
|
}
|
|
3945
4851
|
return void 0;
|
|
3946
4852
|
}
|
|
3947
|
-
watch(scope, expr, handler) {
|
|
4853
|
+
watch(scope, expr, handler, element) {
|
|
3948
4854
|
const key = expr.trim();
|
|
3949
4855
|
if (!key) {
|
|
3950
4856
|
return;
|
|
@@ -3959,29 +4865,70 @@ var Engine = class _Engine {
|
|
|
3959
4865
|
}
|
|
3960
4866
|
if (target) {
|
|
3961
4867
|
target.on(key, handler);
|
|
4868
|
+
if (element) {
|
|
4869
|
+
this.trackScopeWatcher(element, target, "path", handler, key);
|
|
4870
|
+
}
|
|
3962
4871
|
return;
|
|
3963
4872
|
}
|
|
3964
4873
|
let cursor = scope;
|
|
3965
4874
|
while (cursor) {
|
|
3966
4875
|
cursor.on(key, handler);
|
|
4876
|
+
if (element) {
|
|
4877
|
+
this.trackScopeWatcher(element, cursor, "path", handler, key);
|
|
4878
|
+
}
|
|
3967
4879
|
cursor = cursor.parent;
|
|
3968
4880
|
}
|
|
3969
4881
|
}
|
|
3970
|
-
watchWithDebounce(scope, expr, handler, debounceMs) {
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
} else {
|
|
3974
|
-
this.watch(scope, expr, handler);
|
|
3975
|
-
}
|
|
4882
|
+
watchWithDebounce(scope, expr, handler, debounceMs, element) {
|
|
4883
|
+
const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
|
|
4884
|
+
this.watch(scope, expr, effectiveHandler, element);
|
|
3976
4885
|
}
|
|
3977
|
-
watchAllScopes(scope, handler, debounceMs) {
|
|
4886
|
+
watchAllScopes(scope, handler, debounceMs, element) {
|
|
3978
4887
|
const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
|
|
3979
4888
|
let cursor = scope;
|
|
3980
4889
|
while (cursor) {
|
|
3981
4890
|
cursor.onAny(effectiveHandler);
|
|
4891
|
+
if (element) {
|
|
4892
|
+
this.trackScopeWatcher(element, cursor, "any", effectiveHandler);
|
|
4893
|
+
}
|
|
3982
4894
|
cursor = cursor.parent;
|
|
3983
4895
|
}
|
|
3984
4896
|
}
|
|
4897
|
+
trackScopeWatcher(element, scope, kind, handler, key) {
|
|
4898
|
+
const watchers = this.scopeWatchers.get(element) ?? [];
|
|
4899
|
+
watchers.push({ scope, kind, handler, ...key ? { key } : {} });
|
|
4900
|
+
this.scopeWatchers.set(element, watchers);
|
|
4901
|
+
}
|
|
4902
|
+
cleanupScopeWatchers(element) {
|
|
4903
|
+
const watchers = this.scopeWatchers.get(element);
|
|
4904
|
+
if (!watchers) {
|
|
4905
|
+
return;
|
|
4906
|
+
}
|
|
4907
|
+
for (const watcher of watchers) {
|
|
4908
|
+
if (watcher.kind === "any") {
|
|
4909
|
+
watcher.scope.offAny(watcher.handler);
|
|
4910
|
+
continue;
|
|
4911
|
+
}
|
|
4912
|
+
if (watcher.key) {
|
|
4913
|
+
watcher.scope.off(watcher.key, watcher.handler);
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
this.scopeWatchers.delete(element);
|
|
4917
|
+
}
|
|
4918
|
+
cleanupBehaviorListeners(element) {
|
|
4919
|
+
const listenerMap = this.behaviorListeners.get(element);
|
|
4920
|
+
if (!listenerMap) {
|
|
4921
|
+
return;
|
|
4922
|
+
}
|
|
4923
|
+
for (const listeners of listenerMap.values()) {
|
|
4924
|
+
for (const listener of listeners) {
|
|
4925
|
+
listener.target.removeEventListener(listener.event, listener.handler, listener.options);
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
listenerMap.clear();
|
|
4929
|
+
this.behaviorListeners.delete(element);
|
|
4930
|
+
this.behaviorBindings.delete(element);
|
|
4931
|
+
}
|
|
3985
4932
|
parseOnAttribute(name, value) {
|
|
3986
4933
|
if (!name.startsWith("vsn-on:")) {
|
|
3987
4934
|
return null;
|
|
@@ -3991,111 +4938,56 @@ var Engine = class _Engine {
|
|
|
3991
4938
|
if (!event) {
|
|
3992
4939
|
return null;
|
|
3993
4940
|
}
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
return null;
|
|
3997
|
-
}
|
|
3998
|
-
let debounceMs;
|
|
3999
|
-
const modifiers = [];
|
|
4000
|
-
for (const flag of flags) {
|
|
4001
|
-
if (flag.startsWith("debounce")) {
|
|
4002
|
-
const match = flag.match(/debounce\((\d+)\)/);
|
|
4003
|
-
debounceMs = match ? Number(match[1]) : 200;
|
|
4004
|
-
continue;
|
|
4005
|
-
}
|
|
4006
|
-
modifiers.push(flag);
|
|
4941
|
+
if (event.includes(".")) {
|
|
4942
|
+
throw new Error("vsn:on does not support dot modifiers; use !flags instead");
|
|
4007
4943
|
}
|
|
4008
|
-
const
|
|
4944
|
+
const { flagMap, flagArgs } = this.parseInlineFlags(flags);
|
|
4009
4945
|
const config = {
|
|
4010
|
-
event
|
|
4946
|
+
event,
|
|
4011
4947
|
code: value,
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
...descriptor.keyModifiers.length > 0 ? { keyModifiers: descriptor.keyModifiers } : {}
|
|
4948
|
+
flags: flagMap,
|
|
4949
|
+
flagArgs
|
|
4015
4950
|
};
|
|
4016
4951
|
return config;
|
|
4017
4952
|
}
|
|
4018
|
-
|
|
4019
|
-
const
|
|
4020
|
-
const
|
|
4021
|
-
const
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
if (modifierSet.has(part)) {
|
|
4026
|
-
modifiers.push(part);
|
|
4027
|
-
} else {
|
|
4028
|
-
keyModifiers.push(part);
|
|
4953
|
+
parseInlineFlags(parts) {
|
|
4954
|
+
const flagMap = {};
|
|
4955
|
+
const flagArgs = {};
|
|
4956
|
+
for (const raw of parts) {
|
|
4957
|
+
const trimmed = raw.trim();
|
|
4958
|
+
if (!trimmed) {
|
|
4959
|
+
continue;
|
|
4029
4960
|
}
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
}
|
|
4033
|
-
matchesKeyModifiers(event, keyModifiers) {
|
|
4034
|
-
if (!keyModifiers || keyModifiers.length === 0) {
|
|
4035
|
-
return true;
|
|
4036
|
-
}
|
|
4037
|
-
if (!(event instanceof KeyboardEvent)) {
|
|
4038
|
-
return false;
|
|
4039
|
-
}
|
|
4040
|
-
const modifierChecks = {
|
|
4041
|
-
shift: event.shiftKey,
|
|
4042
|
-
ctrl: event.ctrlKey,
|
|
4043
|
-
control: event.ctrlKey,
|
|
4044
|
-
alt: event.altKey,
|
|
4045
|
-
meta: event.metaKey
|
|
4046
|
-
};
|
|
4047
|
-
const keyAliases = {
|
|
4048
|
-
esc: "escape",
|
|
4049
|
-
escape: "escape",
|
|
4050
|
-
enter: "enter",
|
|
4051
|
-
tab: "tab",
|
|
4052
|
-
space: "space",
|
|
4053
|
-
spacebar: "space",
|
|
4054
|
-
up: "arrowup",
|
|
4055
|
-
down: "arrowdown",
|
|
4056
|
-
left: "arrowleft",
|
|
4057
|
-
right: "arrowright",
|
|
4058
|
-
arrowup: "arrowup",
|
|
4059
|
-
arrowdown: "arrowdown",
|
|
4060
|
-
arrowleft: "arrowleft",
|
|
4061
|
-
arrowright: "arrowright",
|
|
4062
|
-
delete: "delete",
|
|
4063
|
-
backspace: "backspace"
|
|
4064
|
-
};
|
|
4065
|
-
let key = event.key?.toLowerCase() ?? "";
|
|
4066
|
-
if (key === " ") {
|
|
4067
|
-
key = "space";
|
|
4068
|
-
}
|
|
4069
|
-
for (const rawModifier of keyModifiers) {
|
|
4070
|
-
const modifier = rawModifier.toLowerCase();
|
|
4071
|
-
if (modifier in modifierChecks) {
|
|
4072
|
-
if (!modifierChecks[modifier]) {
|
|
4073
|
-
return false;
|
|
4074
|
-
}
|
|
4961
|
+
const match = trimmed.match(/^([a-zA-Z][\w-]*)(?:\((.+)\))?$/);
|
|
4962
|
+
if (!match) {
|
|
4075
4963
|
continue;
|
|
4076
4964
|
}
|
|
4077
|
-
const
|
|
4078
|
-
if (
|
|
4079
|
-
|
|
4965
|
+
const name = match[1] ?? "";
|
|
4966
|
+
if (!name) {
|
|
4967
|
+
continue;
|
|
4968
|
+
}
|
|
4969
|
+
if (!this.flagHandlers.has(name)) {
|
|
4970
|
+
throw new Error(`Unknown flag ${name}`);
|
|
4971
|
+
}
|
|
4972
|
+
flagMap[name] = true;
|
|
4973
|
+
if (match[2] !== void 0) {
|
|
4974
|
+
flagArgs[name] = this.parseInlineFlagArg(match[2]);
|
|
4080
4975
|
}
|
|
4081
4976
|
}
|
|
4082
|
-
return
|
|
4977
|
+
return { flagMap, flagArgs };
|
|
4083
4978
|
}
|
|
4084
|
-
|
|
4085
|
-
|
|
4979
|
+
parseInlineFlagArg(raw) {
|
|
4980
|
+
const trimmed = raw.trim();
|
|
4981
|
+
if (trimmed === "true") {
|
|
4086
4982
|
return true;
|
|
4087
4983
|
}
|
|
4088
|
-
|
|
4089
|
-
if (!target || !(target instanceof Node)) {
|
|
4090
|
-
return !modifiers.includes("self") && !modifiers.includes("outside");
|
|
4091
|
-
}
|
|
4092
|
-
if (modifiers.includes("self") && target !== element) {
|
|
4984
|
+
if (trimmed === "false") {
|
|
4093
4985
|
return false;
|
|
4094
4986
|
}
|
|
4095
|
-
if (
|
|
4096
|
-
return
|
|
4987
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
4988
|
+
return Number(trimmed);
|
|
4097
4989
|
}
|
|
4098
|
-
return
|
|
4990
|
+
return trimmed;
|
|
4099
4991
|
}
|
|
4100
4992
|
describeElement(element) {
|
|
4101
4993
|
const tag = element.tagName.toLowerCase();
|
|
@@ -4137,51 +5029,50 @@ var Engine = class _Engine {
|
|
|
4137
5029
|
}
|
|
4138
5030
|
}
|
|
4139
5031
|
attachOnHandler(element, config) {
|
|
4140
|
-
const options = this.
|
|
4141
|
-
|
|
5032
|
+
const { listenerTarget, options, debounceMs } = this.getEventBindingConfig(
|
|
5033
|
+
element,
|
|
5034
|
+
config.flags,
|
|
5035
|
+
config.flagArgs
|
|
5036
|
+
);
|
|
4142
5037
|
let effectiveHandler;
|
|
4143
5038
|
const handler = async (event) => {
|
|
4144
5039
|
if (!element.isConnected) {
|
|
4145
5040
|
listenerTarget.removeEventListener(config.event, effectiveHandler, options);
|
|
4146
5041
|
return;
|
|
4147
5042
|
}
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
}
|
|
4151
|
-
if (!this.matchesTargetModifiers(element, event, config.modifiers)) {
|
|
5043
|
+
const scope = this.getScope(element);
|
|
5044
|
+
if (!this.applyEventFlagBefore(element, scope, config.flags, config.flagArgs, event)) {
|
|
4152
5045
|
return;
|
|
4153
5046
|
}
|
|
4154
|
-
this.applyEventModifiers(event, config.modifiers);
|
|
4155
|
-
const scope = this.getScope(element);
|
|
4156
5047
|
try {
|
|
4157
5048
|
await this.execute(config.code, scope, element);
|
|
4158
5049
|
this.evaluate(element);
|
|
4159
5050
|
} catch (error) {
|
|
4160
5051
|
this.emitError(element, error);
|
|
5052
|
+
} finally {
|
|
5053
|
+
this.applyEventFlagAfter(element, scope, config.flags, config.flagArgs, event);
|
|
4161
5054
|
}
|
|
4162
5055
|
};
|
|
4163
|
-
effectiveHandler =
|
|
5056
|
+
effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
|
|
4164
5057
|
listenerTarget.addEventListener(config.event, effectiveHandler, options);
|
|
4165
5058
|
}
|
|
4166
|
-
attachBehaviorOnHandler(element, event, body,
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
5059
|
+
attachBehaviorOnHandler(element, event, body, flags, flagArgs, args, behaviorId, rootScope) {
|
|
5060
|
+
if (event.includes(".")) {
|
|
5061
|
+
throw new Error("vsn:on does not support dot modifiers; use !flags instead");
|
|
5062
|
+
}
|
|
5063
|
+
const { listenerTarget, options, debounceMs } = this.getEventBindingConfig(element, flags, flagArgs);
|
|
4170
5064
|
const handler = async (evt) => {
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
}
|
|
4174
|
-
if (!this.matchesTargetModifiers(element, evt, combinedModifiers)) {
|
|
5065
|
+
const scope = this.getScope(element);
|
|
5066
|
+
if (!this.applyEventFlagBefore(element, scope, flags, flagArgs, evt)) {
|
|
4175
5067
|
return;
|
|
4176
5068
|
}
|
|
4177
|
-
this.applyEventModifiers(evt, combinedModifiers);
|
|
4178
|
-
const scope = this.getScope(element);
|
|
4179
5069
|
const previousValues = /* @__PURE__ */ new Map();
|
|
4180
5070
|
if (args && args.length > 0) {
|
|
4181
5071
|
const argName = args[0];
|
|
4182
5072
|
if (argName) {
|
|
4183
5073
|
previousValues.set(argName, scope.getPath(argName));
|
|
4184
|
-
|
|
5074
|
+
const [nextArg] = this.applyEventFlagArgTransforms(element, scope, flags, flagArgs, evt);
|
|
5075
|
+
scope.setPath(argName, nextArg);
|
|
4185
5076
|
}
|
|
4186
5077
|
}
|
|
4187
5078
|
let failed = false;
|
|
@@ -4194,16 +5085,17 @@ var Engine = class _Engine {
|
|
|
4194
5085
|
for (const [name, value] of previousValues.entries()) {
|
|
4195
5086
|
scope.setPath(name, value);
|
|
4196
5087
|
}
|
|
5088
|
+
this.applyEventFlagAfter(element, scope, flags, flagArgs, evt);
|
|
4197
5089
|
}
|
|
4198
5090
|
if (!failed) {
|
|
4199
5091
|
this.evaluate(element);
|
|
4200
5092
|
}
|
|
4201
5093
|
};
|
|
4202
|
-
const
|
|
4203
|
-
listenerTarget.addEventListener(
|
|
5094
|
+
const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
|
|
5095
|
+
listenerTarget.addEventListener(event, effectiveHandler, options);
|
|
4204
5096
|
const listenerMap = this.behaviorListeners.get(element) ?? /* @__PURE__ */ new Map();
|
|
4205
5097
|
const listeners = listenerMap.get(behaviorId) ?? [];
|
|
4206
|
-
listeners.push({ target: listenerTarget, event
|
|
5098
|
+
listeners.push({ target: listenerTarget, event, handler: effectiveHandler, options });
|
|
4207
5099
|
listenerMap.set(behaviorId, listeners);
|
|
4208
5100
|
this.behaviorListeners.set(element, listenerMap);
|
|
4209
5101
|
}
|
|
@@ -4234,33 +5126,158 @@ var Engine = class _Engine {
|
|
|
4234
5126
|
Promise.resolve().then(handler);
|
|
4235
5127
|
}
|
|
4236
5128
|
}
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
5129
|
+
getEventBindingConfig(element, flags, flagArgs) {
|
|
5130
|
+
let listenerTarget = element;
|
|
5131
|
+
let options = {};
|
|
5132
|
+
let debounceMs;
|
|
5133
|
+
for (const name of Object.keys(flags)) {
|
|
5134
|
+
const handler = this.flagHandlers.get(name);
|
|
5135
|
+
if (!handler?.onEventBind) {
|
|
5136
|
+
continue;
|
|
5137
|
+
}
|
|
5138
|
+
const patch = handler.onEventBind({
|
|
5139
|
+
name,
|
|
5140
|
+
args: flagArgs[name],
|
|
5141
|
+
element,
|
|
5142
|
+
scope: this.getScope(element),
|
|
5143
|
+
rootScope: void 0,
|
|
5144
|
+
event: void 0,
|
|
5145
|
+
engine: this
|
|
5146
|
+
});
|
|
5147
|
+
if (!patch) {
|
|
5148
|
+
continue;
|
|
5149
|
+
}
|
|
5150
|
+
if (patch.listenerTarget) {
|
|
5151
|
+
listenerTarget = patch.listenerTarget;
|
|
5152
|
+
}
|
|
5153
|
+
if (patch.options) {
|
|
5154
|
+
options = { ...options, ...patch.options };
|
|
5155
|
+
}
|
|
5156
|
+
if (patch.debounceMs !== void 0) {
|
|
5157
|
+
debounceMs = patch.debounceMs;
|
|
5158
|
+
}
|
|
5159
|
+
}
|
|
5160
|
+
return {
|
|
5161
|
+
listenerTarget,
|
|
5162
|
+
...Object.keys(options).length > 0 ? { options } : {},
|
|
5163
|
+
...debounceMs !== void 0 ? { debounceMs } : {}
|
|
5164
|
+
};
|
|
5165
|
+
}
|
|
5166
|
+
applyEventFlagBefore(element, scope, flags, flagArgs, event) {
|
|
5167
|
+
for (const name of Object.keys(flags)) {
|
|
5168
|
+
const handler = this.flagHandlers.get(name);
|
|
5169
|
+
if (!handler?.onEventBefore) {
|
|
5170
|
+
continue;
|
|
5171
|
+
}
|
|
5172
|
+
const result = handler.onEventBefore({
|
|
5173
|
+
name,
|
|
5174
|
+
args: flagArgs[name],
|
|
5175
|
+
element,
|
|
5176
|
+
scope,
|
|
5177
|
+
rootScope: void 0,
|
|
5178
|
+
event,
|
|
5179
|
+
engine: this
|
|
5180
|
+
});
|
|
5181
|
+
if (result === false) {
|
|
5182
|
+
return false;
|
|
5183
|
+
}
|
|
4240
5184
|
}
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
5185
|
+
return true;
|
|
5186
|
+
}
|
|
5187
|
+
applyEventFlagAfter(element, scope, flags, flagArgs, event) {
|
|
5188
|
+
for (const name of Object.keys(flags)) {
|
|
5189
|
+
const handler = this.flagHandlers.get(name);
|
|
5190
|
+
if (!handler?.onEventAfter) {
|
|
5191
|
+
continue;
|
|
4246
5192
|
}
|
|
5193
|
+
handler.onEventAfter({
|
|
5194
|
+
name,
|
|
5195
|
+
args: flagArgs[name],
|
|
5196
|
+
element,
|
|
5197
|
+
scope,
|
|
5198
|
+
rootScope: void 0,
|
|
5199
|
+
event,
|
|
5200
|
+
engine: this
|
|
5201
|
+
});
|
|
4247
5202
|
}
|
|
4248
5203
|
}
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
5204
|
+
applyEventFlagArgTransforms(element, scope, flags, flagArgs, event) {
|
|
5205
|
+
let args = [event];
|
|
5206
|
+
for (const name of Object.keys(flags)) {
|
|
5207
|
+
const handler = this.flagHandlers.get(name);
|
|
5208
|
+
if (!handler?.transformEventArgs) {
|
|
5209
|
+
continue;
|
|
5210
|
+
}
|
|
5211
|
+
const nextArgs = handler.transformEventArgs(
|
|
5212
|
+
{
|
|
5213
|
+
name,
|
|
5214
|
+
args: flagArgs[name],
|
|
5215
|
+
element,
|
|
5216
|
+
scope,
|
|
5217
|
+
rootScope: void 0,
|
|
5218
|
+
event,
|
|
5219
|
+
engine: this
|
|
5220
|
+
},
|
|
5221
|
+
args
|
|
5222
|
+
);
|
|
5223
|
+
if (Array.isArray(nextArgs)) {
|
|
5224
|
+
args = nextArgs;
|
|
5225
|
+
}
|
|
5226
|
+
}
|
|
5227
|
+
return args;
|
|
5228
|
+
}
|
|
5229
|
+
matchesKeyFlag(event, flag) {
|
|
5230
|
+
if (!(event instanceof KeyboardEvent)) {
|
|
5231
|
+
return false;
|
|
5232
|
+
}
|
|
5233
|
+
const modifierChecks = {
|
|
5234
|
+
shift: event.shiftKey,
|
|
5235
|
+
ctrl: event.ctrlKey,
|
|
5236
|
+
alt: event.altKey,
|
|
5237
|
+
meta: event.metaKey
|
|
5238
|
+
};
|
|
5239
|
+
if (flag in modifierChecks) {
|
|
5240
|
+
return modifierChecks[flag] ?? false;
|
|
4252
5241
|
}
|
|
4253
|
-
const
|
|
4254
|
-
|
|
4255
|
-
|
|
5242
|
+
const keyAliases = {
|
|
5243
|
+
escape: "escape",
|
|
5244
|
+
esc: "escape",
|
|
5245
|
+
enter: "enter",
|
|
5246
|
+
tab: "tab",
|
|
5247
|
+
space: "space",
|
|
5248
|
+
spacebar: "space",
|
|
5249
|
+
up: "arrowup",
|
|
5250
|
+
down: "arrowdown",
|
|
5251
|
+
left: "arrowleft",
|
|
5252
|
+
right: "arrowright",
|
|
5253
|
+
arrowup: "arrowup",
|
|
5254
|
+
arrowdown: "arrowdown",
|
|
5255
|
+
arrowleft: "arrowleft",
|
|
5256
|
+
arrowright: "arrowright",
|
|
5257
|
+
delete: "delete",
|
|
5258
|
+
backspace: "backspace"
|
|
5259
|
+
};
|
|
5260
|
+
let key = event.key?.toLowerCase() ?? "";
|
|
5261
|
+
if (key === " ") {
|
|
5262
|
+
key = "space";
|
|
4256
5263
|
}
|
|
4257
|
-
|
|
4258
|
-
|
|
5264
|
+
const expectedKey = keyAliases[flag] ?? flag;
|
|
5265
|
+
return key === expectedKey;
|
|
5266
|
+
}
|
|
5267
|
+
async withExecutionElement(element, fn) {
|
|
5268
|
+
if (!element) {
|
|
5269
|
+
await fn();
|
|
5270
|
+
return;
|
|
4259
5271
|
}
|
|
4260
|
-
|
|
4261
|
-
|
|
5272
|
+
this.executionStack.push(element);
|
|
5273
|
+
try {
|
|
5274
|
+
await fn();
|
|
5275
|
+
} finally {
|
|
5276
|
+
this.executionStack.pop();
|
|
4262
5277
|
}
|
|
4263
|
-
|
|
5278
|
+
}
|
|
5279
|
+
getCurrentElement() {
|
|
5280
|
+
return this.executionStack[this.executionStack.length - 1];
|
|
4264
5281
|
}
|
|
4265
5282
|
async execute(code, scope, element, rootScope) {
|
|
4266
5283
|
let block = this.codeCache.get(code);
|
|
@@ -4268,22 +5285,26 @@ var Engine = class _Engine {
|
|
|
4268
5285
|
block = Parser.parseInline(code);
|
|
4269
5286
|
this.codeCache.set(code, block);
|
|
4270
5287
|
}
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
5288
|
+
await this.withExecutionElement(element, async () => {
|
|
5289
|
+
const context = {
|
|
5290
|
+
scope,
|
|
5291
|
+
rootScope,
|
|
5292
|
+
globals: this.globals,
|
|
5293
|
+
...element ? { element } : {}
|
|
5294
|
+
};
|
|
5295
|
+
await block.evaluate(context);
|
|
5296
|
+
});
|
|
4278
5297
|
}
|
|
4279
5298
|
async executeBlock(block, scope, element, rootScope) {
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
5299
|
+
await this.withExecutionElement(element, async () => {
|
|
5300
|
+
const context = {
|
|
5301
|
+
scope,
|
|
5302
|
+
rootScope,
|
|
5303
|
+
globals: this.globals,
|
|
5304
|
+
...element ? { element } : {}
|
|
5305
|
+
};
|
|
5306
|
+
await block.evaluate(context);
|
|
5307
|
+
});
|
|
4287
5308
|
}
|
|
4288
5309
|
async safeExecute(code, scope, element, rootScope) {
|
|
4289
5310
|
try {
|
|
@@ -4306,15 +5327,26 @@ var Engine = class _Engine {
|
|
|
4306
5327
|
collectBehavior(behavior, parentSelector, rootSelectorOverride) {
|
|
4307
5328
|
const selector = parentSelector ? `${parentSelector} ${behavior.selector.selectorText}` : behavior.selector.selectorText;
|
|
4308
5329
|
const rootSelector = rootSelectorOverride ?? (parentSelector ?? behavior.selector.selectorText);
|
|
5330
|
+
const behaviorHash = this.hashBehavior(behavior);
|
|
5331
|
+
const hash = `${selector}::${rootSelector}::${behaviorHash}`;
|
|
5332
|
+
if (this.behaviorRegistryHashes.has(hash)) {
|
|
5333
|
+
return;
|
|
5334
|
+
}
|
|
4309
5335
|
const cached = this.getCachedBehavior(behavior);
|
|
4310
|
-
|
|
5336
|
+
const entry = {
|
|
4311
5337
|
id: this.behaviorId += 1,
|
|
5338
|
+
hash,
|
|
4312
5339
|
selector,
|
|
4313
5340
|
rootSelector,
|
|
4314
5341
|
specificity: this.computeSpecificity(selector),
|
|
4315
5342
|
order: this.behaviorRegistry.length,
|
|
4316
|
-
|
|
4317
|
-
|
|
5343
|
+
flags: behavior.flags ?? {},
|
|
5344
|
+
flagArgs: behavior.flagArgs ?? {},
|
|
5345
|
+
...cached,
|
|
5346
|
+
...parentSelector ? { parentSelector } : {}
|
|
5347
|
+
};
|
|
5348
|
+
this.behaviorRegistry.push(entry);
|
|
5349
|
+
this.behaviorRegistryHashes.add(hash);
|
|
4318
5350
|
this.collectNestedBehaviors(behavior.body, selector, rootSelector);
|
|
4319
5351
|
}
|
|
4320
5352
|
collectNestedBehaviors(block, parentSelector, rootSelector) {
|
|
@@ -4387,7 +5419,8 @@ var Engine = class _Engine {
|
|
|
4387
5419
|
blocks.push({
|
|
4388
5420
|
event: statement.eventName,
|
|
4389
5421
|
body: statement.body,
|
|
4390
|
-
|
|
5422
|
+
flags: statement.flags,
|
|
5423
|
+
flagArgs: statement.flagArgs,
|
|
4391
5424
|
args: statement.args
|
|
4392
5425
|
});
|
|
4393
5426
|
}
|
|
@@ -4452,6 +5485,8 @@ var Engine = class _Engine {
|
|
|
4452
5485
|
return {
|
|
4453
5486
|
type,
|
|
4454
5487
|
selector: node.selector?.selectorText ?? "",
|
|
5488
|
+
flags: node.flags ?? {},
|
|
5489
|
+
flagArgs: node.flagArgs ?? {},
|
|
4455
5490
|
body: this.normalizeNode(node.body)
|
|
4456
5491
|
};
|
|
4457
5492
|
}
|
|
@@ -4469,6 +5504,8 @@ var Engine = class _Engine {
|
|
|
4469
5504
|
type,
|
|
4470
5505
|
eventName: node.eventName ?? "",
|
|
4471
5506
|
args: Array.isArray(node.args) ? node.args : [],
|
|
5507
|
+
flags: node.flags ?? {},
|
|
5508
|
+
flagArgs: node.flagArgs ?? {},
|
|
4472
5509
|
body: this.normalizeNode(node.body)
|
|
4473
5510
|
};
|
|
4474
5511
|
}
|
|
@@ -4486,21 +5523,9 @@ var Engine = class _Engine {
|
|
|
4486
5523
|
return {
|
|
4487
5524
|
type,
|
|
4488
5525
|
target: this.normalizeNode(node.target),
|
|
4489
|
-
value: this.normalizeNode(node.value)
|
|
4490
|
-
};
|
|
4491
|
-
}
|
|
4492
|
-
if (type === "StateBlock") {
|
|
4493
|
-
return {
|
|
4494
|
-
type,
|
|
4495
|
-
entries: Array.isArray(node.entries) ? node.entries.map((entry) => this.normalizeNode(entry)) : []
|
|
4496
|
-
};
|
|
4497
|
-
}
|
|
4498
|
-
if (type === "StateEntry") {
|
|
4499
|
-
return {
|
|
4500
|
-
type,
|
|
4501
|
-
name: node.name ?? "",
|
|
4502
5526
|
value: this.normalizeNode(node.value),
|
|
4503
|
-
|
|
5527
|
+
operator: node.operator ?? "",
|
|
5528
|
+
prefix: Boolean(node.prefix)
|
|
4504
5529
|
};
|
|
4505
5530
|
}
|
|
4506
5531
|
if (type === "FunctionDeclaration") {
|
|
@@ -4534,6 +5559,15 @@ var Engine = class _Engine {
|
|
|
4534
5559
|
value: this.normalizeNode(node.value ?? null)
|
|
4535
5560
|
};
|
|
4536
5561
|
}
|
|
5562
|
+
if (type === "Assert") {
|
|
5563
|
+
return {
|
|
5564
|
+
type,
|
|
5565
|
+
test: this.normalizeNode(node.test)
|
|
5566
|
+
};
|
|
5567
|
+
}
|
|
5568
|
+
if (type === "Break" || type === "Continue") {
|
|
5569
|
+
return { type };
|
|
5570
|
+
}
|
|
4537
5571
|
if (type === "If") {
|
|
4538
5572
|
return {
|
|
4539
5573
|
type,
|
|
@@ -4558,6 +5592,15 @@ var Engine = class _Engine {
|
|
|
4558
5592
|
body: this.normalizeNode(node.body)
|
|
4559
5593
|
};
|
|
4560
5594
|
}
|
|
5595
|
+
if (type === "ForEach") {
|
|
5596
|
+
return {
|
|
5597
|
+
type,
|
|
5598
|
+
kind: node.kind ?? "of",
|
|
5599
|
+
target: this.normalizeNode(node.target),
|
|
5600
|
+
iterable: this.normalizeNode(node.iterable),
|
|
5601
|
+
body: this.normalizeNode(node.body)
|
|
5602
|
+
};
|
|
5603
|
+
}
|
|
4561
5604
|
if (type === "Try") {
|
|
4562
5605
|
return {
|
|
4563
5606
|
type,
|
|
@@ -4680,7 +5723,9 @@ var Engine = class _Engine {
|
|
|
4680
5723
|
globals: this.globals,
|
|
4681
5724
|
element,
|
|
4682
5725
|
returnValue: void 0,
|
|
4683
|
-
returning: false
|
|
5726
|
+
returning: false,
|
|
5727
|
+
breaking: false,
|
|
5728
|
+
continuing: false
|
|
4684
5729
|
};
|
|
4685
5730
|
const previousValues = /* @__PURE__ */ new Map();
|
|
4686
5731
|
await this.applyFunctionParams(callScope, declaration.params, previousValues, context, args);
|
|
@@ -4731,6 +5776,7 @@ var Engine = class _Engine {
|
|
|
4731
5776
|
const context = { scope, rootScope, element };
|
|
4732
5777
|
const operator = declaration.operator;
|
|
4733
5778
|
const debounceMs = declaration.flags.debounce ? declaration.flagArgs.debounce ?? 200 : void 0;
|
|
5779
|
+
const transform = (value) => this.applyCustomFlagTransforms(value, element, scope, declaration);
|
|
4734
5780
|
const importantKey = this.getImportantKey(declaration);
|
|
4735
5781
|
if (!declaration.flags.important && importantKey && this.isImportant(element, importantKey)) {
|
|
4736
5782
|
return;
|
|
@@ -4741,7 +5787,8 @@ var Engine = class _Engine {
|
|
|
4741
5787
|
this.applyCustomFlags(element, scope, declaration);
|
|
4742
5788
|
if (declaration.target instanceof IdentifierExpression) {
|
|
4743
5789
|
const value = await declaration.value.evaluate(context);
|
|
4744
|
-
|
|
5790
|
+
const transformed = this.applyCustomFlagTransforms(value, element, scope, declaration);
|
|
5791
|
+
scope.setPath(declaration.target.name, transformed);
|
|
4745
5792
|
if (declaration.flags.important && importantKey) {
|
|
4746
5793
|
this.markImportant(element, importantKey);
|
|
4747
5794
|
}
|
|
@@ -4754,7 +5801,7 @@ var Engine = class _Engine {
|
|
|
4754
5801
|
const exprIdentifier = declaration.value instanceof IdentifierExpression ? declaration.value.name : void 0;
|
|
4755
5802
|
if (operator === ":>") {
|
|
4756
5803
|
if (exprIdentifier) {
|
|
4757
|
-
this.applyDirectiveToScope(element, target, exprIdentifier, scope, debounceMs, rootScope);
|
|
5804
|
+
this.applyDirectiveToScope(element, target, exprIdentifier, scope, debounceMs, rootScope, transform);
|
|
4758
5805
|
}
|
|
4759
5806
|
if (declaration.flags.important && importantKey) {
|
|
4760
5807
|
this.markImportant(element, importantKey);
|
|
@@ -4762,11 +5809,12 @@ var Engine = class _Engine {
|
|
|
4762
5809
|
return;
|
|
4763
5810
|
}
|
|
4764
5811
|
if (operator === ":=" && exprIdentifier) {
|
|
4765
|
-
this.applyDirectiveToScope(element, target, exprIdentifier, scope, debounceMs, rootScope);
|
|
5812
|
+
this.applyDirectiveToScope(element, target, exprIdentifier, scope, debounceMs, rootScope, transform);
|
|
4766
5813
|
}
|
|
4767
5814
|
if (!exprIdentifier) {
|
|
4768
5815
|
const value = await declaration.value.evaluate(context);
|
|
4769
|
-
this.
|
|
5816
|
+
const transformed = this.applyCustomFlagTransforms(value, element, scope, declaration);
|
|
5817
|
+
this.setDirectiveValue(element, target, transformed, declaration.flags.trusted);
|
|
4770
5818
|
const shouldWatch2 = operator === ":<" || operator === ":=";
|
|
4771
5819
|
if (shouldWatch2) {
|
|
4772
5820
|
this.applyDirectiveFromExpression(
|
|
@@ -4816,6 +5864,63 @@ var Engine = class _Engine {
|
|
|
4816
5864
|
});
|
|
4817
5865
|
}
|
|
4818
5866
|
}
|
|
5867
|
+
applyCustomFlagTransforms(value, element, scope, declaration) {
|
|
5868
|
+
if (this.flagHandlers.size === 0) {
|
|
5869
|
+
return value;
|
|
5870
|
+
}
|
|
5871
|
+
let nextValue = value;
|
|
5872
|
+
for (const [name, handler] of this.flagHandlers) {
|
|
5873
|
+
if (!declaration.flags[name] || !handler.transformValue) {
|
|
5874
|
+
continue;
|
|
5875
|
+
}
|
|
5876
|
+
nextValue = handler.transformValue(
|
|
5877
|
+
{
|
|
5878
|
+
name,
|
|
5879
|
+
args: declaration.flagArgs[name],
|
|
5880
|
+
element,
|
|
5881
|
+
scope,
|
|
5882
|
+
declaration
|
|
5883
|
+
},
|
|
5884
|
+
nextValue
|
|
5885
|
+
);
|
|
5886
|
+
}
|
|
5887
|
+
return nextValue;
|
|
5888
|
+
}
|
|
5889
|
+
async applyBehaviorModifierHook(hook, behavior, element, scope, rootScope) {
|
|
5890
|
+
if (this.behaviorModifiers.size === 0) {
|
|
5891
|
+
return;
|
|
5892
|
+
}
|
|
5893
|
+
for (const [name, handler] of this.behaviorModifiers) {
|
|
5894
|
+
if (!behavior.flags?.[name]) {
|
|
5895
|
+
continue;
|
|
5896
|
+
}
|
|
5897
|
+
const callback = handler[hook];
|
|
5898
|
+
if (!callback) {
|
|
5899
|
+
continue;
|
|
5900
|
+
}
|
|
5901
|
+
await callback({
|
|
5902
|
+
name,
|
|
5903
|
+
args: behavior.flagArgs?.[name],
|
|
5904
|
+
element,
|
|
5905
|
+
scope,
|
|
5906
|
+
rootScope,
|
|
5907
|
+
behavior,
|
|
5908
|
+
engine: this
|
|
5909
|
+
});
|
|
5910
|
+
}
|
|
5911
|
+
}
|
|
5912
|
+
behaviorHasModifierHooks(behavior) {
|
|
5913
|
+
if (this.behaviorModifiers.size === 0) {
|
|
5914
|
+
return false;
|
|
5915
|
+
}
|
|
5916
|
+
const flags = behavior.flags ?? {};
|
|
5917
|
+
for (const name of Object.keys(flags)) {
|
|
5918
|
+
if (flags[name] && this.behaviorModifiers.has(name)) {
|
|
5919
|
+
return true;
|
|
5920
|
+
}
|
|
5921
|
+
}
|
|
5922
|
+
return false;
|
|
5923
|
+
}
|
|
4819
5924
|
applyDirectiveFromScope(element, target, expr, scope, trusted, debounceMs, watch = true, rootScope) {
|
|
4820
5925
|
if (target.kind === "attr" && target.name === "html" && element instanceof HTMLElement) {
|
|
4821
5926
|
const handler2 = () => {
|
|
@@ -4832,7 +5937,7 @@ var Engine = class _Engine {
|
|
|
4832
5937
|
const useRoot = expr.startsWith("root.") && rootScope;
|
|
4833
5938
|
const sourceScope = useRoot ? rootScope : scope;
|
|
4834
5939
|
const watchExpr = useRoot ? expr.slice("root.".length) : expr;
|
|
4835
|
-
this.watchWithDebounce(sourceScope, watchExpr, handler2, debounceMs);
|
|
5940
|
+
this.watchWithDebounce(sourceScope, watchExpr, handler2, debounceMs, element);
|
|
4836
5941
|
}
|
|
4837
5942
|
return;
|
|
4838
5943
|
}
|
|
@@ -4851,7 +5956,7 @@ var Engine = class _Engine {
|
|
|
4851
5956
|
const useRoot = expr.startsWith("root.") && rootScope;
|
|
4852
5957
|
const sourceScope = useRoot ? rootScope : scope;
|
|
4853
5958
|
const watchExpr = useRoot ? expr.slice("root.".length) : expr;
|
|
4854
|
-
this.watchWithDebounce(sourceScope, watchExpr, handler, debounceMs);
|
|
5959
|
+
this.watchWithDebounce(sourceScope, watchExpr, handler, debounceMs, element);
|
|
4855
5960
|
}
|
|
4856
5961
|
}
|
|
4857
5962
|
applyDirectiveFromExpression(element, target, expr, scope, trusted, debounceMs, rootScope) {
|
|
@@ -4863,45 +5968,49 @@ var Engine = class _Engine {
|
|
|
4863
5968
|
void handler();
|
|
4864
5969
|
this.watchAllScopes(scope, () => {
|
|
4865
5970
|
void handler();
|
|
4866
|
-
}, debounceMs);
|
|
5971
|
+
}, debounceMs, element);
|
|
4867
5972
|
}
|
|
4868
|
-
applyDirectiveToScope(element, target, expr, scope, debounceMs, rootScope) {
|
|
5973
|
+
applyDirectiveToScope(element, target, expr, scope, debounceMs, rootScope, transform) {
|
|
4869
5974
|
const useRoot = expr.startsWith("root.") && rootScope;
|
|
4870
5975
|
const targetScope = useRoot ? rootScope : scope;
|
|
4871
5976
|
const targetExpr = useRoot ? `self.${expr.slice("root.".length)}` : expr;
|
|
4872
5977
|
if (target.kind === "attr" && target.name === "value") {
|
|
4873
|
-
this.applyValueBindingToScope(element, targetExpr, debounceMs, targetScope);
|
|
5978
|
+
this.applyValueBindingToScope(element, targetExpr, debounceMs, targetScope, transform);
|
|
4874
5979
|
return;
|
|
4875
5980
|
}
|
|
4876
5981
|
if (target.kind === "attr" && target.name === "checked") {
|
|
4877
|
-
this.applyCheckedBindingToScope(element, targetExpr, debounceMs, targetScope);
|
|
5982
|
+
this.applyCheckedBindingToScope(element, targetExpr, debounceMs, targetScope, transform);
|
|
4878
5983
|
return;
|
|
4879
5984
|
}
|
|
4880
5985
|
const value = this.getDirectiveValue(element, target);
|
|
4881
5986
|
if (value != null) {
|
|
4882
|
-
|
|
5987
|
+
const nextValue = transform ? transform(value) : value;
|
|
5988
|
+
targetScope.set(targetExpr, nextValue);
|
|
4883
5989
|
}
|
|
4884
5990
|
}
|
|
4885
|
-
applyCheckedBindingToScope(element, expr, debounceMs, scope) {
|
|
5991
|
+
applyCheckedBindingToScope(element, expr, debounceMs, scope, transform) {
|
|
4886
5992
|
if (!(element instanceof HTMLInputElement)) {
|
|
4887
5993
|
return;
|
|
4888
5994
|
}
|
|
4889
5995
|
const handler = () => {
|
|
4890
5996
|
const targetScope = scope ?? this.getScope(element);
|
|
4891
|
-
|
|
5997
|
+
const value = transform ? transform(element.checked) : element.checked;
|
|
5998
|
+
targetScope.set(expr, value);
|
|
4892
5999
|
};
|
|
4893
6000
|
const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
|
|
4894
6001
|
effectiveHandler();
|
|
4895
6002
|
element.addEventListener("change", effectiveHandler);
|
|
4896
6003
|
element.addEventListener("input", effectiveHandler);
|
|
4897
6004
|
}
|
|
4898
|
-
applyValueBindingToScope(element, expr, debounceMs, scope) {
|
|
6005
|
+
applyValueBindingToScope(element, expr, debounceMs, scope, transform) {
|
|
4899
6006
|
if (!(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)) {
|
|
4900
6007
|
return;
|
|
4901
6008
|
}
|
|
4902
6009
|
const handler = () => {
|
|
4903
6010
|
const targetScope = scope ?? this.getScope(element);
|
|
4904
|
-
|
|
6011
|
+
const value = element.value;
|
|
6012
|
+
const nextValue = transform ? transform(value) : value;
|
|
6013
|
+
targetScope.set(expr, nextValue);
|
|
4905
6014
|
};
|
|
4906
6015
|
const effectiveHandler = debounceMs ? debounce(handler, debounceMs) : handler;
|
|
4907
6016
|
effectiveHandler();
|
|
@@ -4918,6 +6027,14 @@ var Engine = class _Engine {
|
|
|
4918
6027
|
return;
|
|
4919
6028
|
}
|
|
4920
6029
|
if (target.kind === "attr") {
|
|
6030
|
+
if (target.name === "text" && element instanceof HTMLElement) {
|
|
6031
|
+
element.innerText = value == null ? "" : String(value);
|
|
6032
|
+
return;
|
|
6033
|
+
}
|
|
6034
|
+
if (target.name === "content" && element instanceof HTMLElement) {
|
|
6035
|
+
element.textContent = value == null ? "" : String(value);
|
|
6036
|
+
return;
|
|
6037
|
+
}
|
|
4921
6038
|
if (target.name === "value") {
|
|
4922
6039
|
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
|
|
4923
6040
|
element.value = value == null ? "" : String(value);
|
|
@@ -4948,6 +6065,12 @@ var Engine = class _Engine {
|
|
|
4948
6065
|
}
|
|
4949
6066
|
getDirectiveValue(element, target) {
|
|
4950
6067
|
if (target.kind === "attr") {
|
|
6068
|
+
if (target.name === "text" && element instanceof HTMLElement) {
|
|
6069
|
+
return element.innerText;
|
|
6070
|
+
}
|
|
6071
|
+
if (target.name === "content" && element instanceof HTMLElement) {
|
|
6072
|
+
return element.textContent ?? "";
|
|
6073
|
+
}
|
|
4951
6074
|
if (target.name === "value") {
|
|
4952
6075
|
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) {
|
|
4953
6076
|
return element.value;
|
|
@@ -4983,17 +6106,27 @@ var Engine = class _Engine {
|
|
|
4983
6106
|
id: "vsn-bind",
|
|
4984
6107
|
match: (name) => name.startsWith("vsn-bind"),
|
|
4985
6108
|
handle: (element, name, value, scope) => {
|
|
4986
|
-
const
|
|
4987
|
-
this.
|
|
4988
|
-
|
|
6109
|
+
const parsedDirection = this.parseBindDirection(name);
|
|
6110
|
+
const config = this.resolveBindConfig(element, value, scope, parsedDirection);
|
|
6111
|
+
const direction = config.direction;
|
|
6112
|
+
const auto = parsedDirection === "auto";
|
|
6113
|
+
this.bindBindings.set(element, { expr: value, direction, auto });
|
|
6114
|
+
if (!auto && (direction === "to" || direction === "both")) {
|
|
4989
6115
|
this.markInlineDeclaration(element, `state:${value}`);
|
|
4990
6116
|
}
|
|
4991
|
-
if (
|
|
6117
|
+
if (config.seedFromScope) {
|
|
6118
|
+
applyBindToElement(element, value, scope);
|
|
6119
|
+
}
|
|
6120
|
+
if (config.deferToScope) {
|
|
6121
|
+
this.pendingAutoBindToScope.push({ element, expr: value, scope });
|
|
6122
|
+
} else if (config.syncToScope) {
|
|
4992
6123
|
applyBindToScope(element, value, scope);
|
|
6124
|
+
}
|
|
6125
|
+
if (direction === "to" || direction === "both") {
|
|
4993
6126
|
this.attachBindInputHandler(element, value);
|
|
4994
6127
|
}
|
|
4995
6128
|
if (direction === "from" || direction === "both") {
|
|
4996
|
-
this.watch(scope, value, () => applyBindToElement(element, value, scope));
|
|
6129
|
+
this.watch(scope, value, () => applyBindToElement(element, value, scope), element);
|
|
4997
6130
|
}
|
|
4998
6131
|
}
|
|
4999
6132
|
});
|
|
@@ -5005,7 +6138,7 @@ var Engine = class _Engine {
|
|
|
5005
6138
|
if (element instanceof HTMLElement) {
|
|
5006
6139
|
applyIf(element, value, scope);
|
|
5007
6140
|
}
|
|
5008
|
-
this.watch(scope, value, () => this.evaluate(element));
|
|
6141
|
+
this.watch(scope, value, () => this.evaluate(element), element);
|
|
5009
6142
|
}
|
|
5010
6143
|
});
|
|
5011
6144
|
this.registerAttributeHandler({
|
|
@@ -5016,7 +6149,7 @@ var Engine = class _Engine {
|
|
|
5016
6149
|
if (element instanceof HTMLElement) {
|
|
5017
6150
|
applyShow(element, value, scope);
|
|
5018
6151
|
}
|
|
5019
|
-
this.watch(scope, value, () => this.evaluate(element));
|
|
6152
|
+
this.watch(scope, value, () => this.evaluate(element), element);
|
|
5020
6153
|
}
|
|
5021
6154
|
});
|
|
5022
6155
|
this.registerAttributeHandler({
|
|
@@ -5032,7 +6165,7 @@ var Engine = class _Engine {
|
|
|
5032
6165
|
this.handleTrustedHtml(element);
|
|
5033
6166
|
}
|
|
5034
6167
|
}
|
|
5035
|
-
this.watch(scope, value, () => this.evaluate(element));
|
|
6168
|
+
this.watch(scope, value, () => this.evaluate(element), element);
|
|
5036
6169
|
}
|
|
5037
6170
|
});
|
|
5038
6171
|
this.registerAttributeHandler({
|
|
@@ -5045,7 +6178,7 @@ var Engine = class _Engine {
|
|
|
5045
6178
|
}
|
|
5046
6179
|
this.eachBindings.set(element, { ...config, rendered: [] });
|
|
5047
6180
|
this.renderEach(element);
|
|
5048
|
-
this.watch(scope, config.listExpr, () => this.renderEach(element));
|
|
6181
|
+
this.watch(scope, config.listExpr, () => this.renderEach(element), element);
|
|
5049
6182
|
}
|
|
5050
6183
|
});
|
|
5051
6184
|
this.registerAttributeHandler({
|
|
@@ -5100,15 +6233,27 @@ function parseCFS(source) {
|
|
|
5100
6233
|
const parser = new Parser(source);
|
|
5101
6234
|
return parser.parseProgram();
|
|
5102
6235
|
}
|
|
6236
|
+
if (typeof window !== "undefined") {
|
|
6237
|
+
window["parseCFS"] = parseCFS;
|
|
6238
|
+
}
|
|
5103
6239
|
function autoMount(root = document) {
|
|
5104
6240
|
if (typeof document === "undefined") {
|
|
5105
6241
|
return null;
|
|
5106
6242
|
}
|
|
5107
6243
|
const engine = new Engine();
|
|
6244
|
+
globalThis.VSNEngine = engine;
|
|
5108
6245
|
const startTime = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
5109
6246
|
const mount = () => {
|
|
5110
6247
|
const target = root instanceof Document ? root.body : root;
|
|
5111
6248
|
if (target) {
|
|
6249
|
+
const plugins = globalThis.VSNPlugins;
|
|
6250
|
+
if (plugins && typeof plugins === "object") {
|
|
6251
|
+
for (const plugin of Object.values(plugins)) {
|
|
6252
|
+
if (typeof plugin === "function") {
|
|
6253
|
+
plugin(engine);
|
|
6254
|
+
}
|
|
6255
|
+
}
|
|
6256
|
+
}
|
|
5112
6257
|
const sources = Array.from(document.querySelectorAll('script[type="text/vsn"]')).map((script) => script.textContent ?? "").join("\n");
|
|
5113
6258
|
if (sources.trim()) {
|
|
5114
6259
|
engine.registerBehaviors(sources);
|
|
@@ -5120,9 +6265,9 @@ function autoMount(root = document) {
|
|
|
5120
6265
|
}
|
|
5121
6266
|
};
|
|
5122
6267
|
if (document.readyState === "loading") {
|
|
5123
|
-
document.addEventListener("DOMContentLoaded", mount, { once: true });
|
|
6268
|
+
document.addEventListener("DOMContentLoaded", () => setTimeout(mount, 0), { once: true });
|
|
5124
6269
|
} else {
|
|
5125
|
-
mount
|
|
6270
|
+
setTimeout(mount, 0);
|
|
5126
6271
|
}
|
|
5127
6272
|
return engine;
|
|
5128
6273
|
}
|
|
@@ -5136,16 +6281,21 @@ if (typeof document !== "undefined") {
|
|
|
5136
6281
|
0 && (module.exports = {
|
|
5137
6282
|
ArrayExpression,
|
|
5138
6283
|
ArrayPattern,
|
|
6284
|
+
AssertError,
|
|
6285
|
+
AssertNode,
|
|
5139
6286
|
AssignmentNode,
|
|
5140
6287
|
AwaitExpression,
|
|
5141
6288
|
BaseNode,
|
|
5142
6289
|
BehaviorNode,
|
|
5143
6290
|
BinaryExpression,
|
|
5144
6291
|
BlockNode,
|
|
6292
|
+
BreakNode,
|
|
5145
6293
|
CallExpression,
|
|
6294
|
+
ContinueNode,
|
|
5146
6295
|
DeclarationNode,
|
|
5147
6296
|
DirectiveExpression,
|
|
5148
6297
|
Engine,
|
|
6298
|
+
ForEachNode,
|
|
5149
6299
|
ForNode,
|
|
5150
6300
|
FunctionDeclarationNode,
|
|
5151
6301
|
FunctionExpression,
|
|
@@ -5165,8 +6315,6 @@ if (typeof document !== "undefined") {
|
|
|
5165
6315
|
ReturnNode,
|
|
5166
6316
|
SelectorNode,
|
|
5167
6317
|
SpreadElement,
|
|
5168
|
-
StateBlockNode,
|
|
5169
|
-
StateEntryNode,
|
|
5170
6318
|
TemplateExpression,
|
|
5171
6319
|
TernaryExpression,
|
|
5172
6320
|
TokenType,
|