@herb-tools/core 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,53 +1,3 @@
1
- const expectedFunctions = [
2
- "parse",
3
- "lex",
4
- "parseFile",
5
- "lexFile",
6
- "extractRuby",
7
- "extractHTML",
8
- "version",
9
- ];
10
- // NOTE: This function should never be called and is only for type checking
11
- // so we can make sure `expectedFunctions` matches the functions defined
12
- // in `LibHerbBackendFunctions` and the other way around.
13
- //
14
- function _TYPECHECK() {
15
- const checkFunctionsExist = true;
16
- const checkInterfaceComplete = true;
17
- return { checkFunctionsExist, checkInterfaceComplete };
18
- }
19
- function isLibHerbBackend(object, libherbpath = "unknown") {
20
- for (const expectedFunction of expectedFunctions) {
21
- if (object[expectedFunction] === undefined) {
22
- throw new Error(`Libherb at "${libherbpath}" doesn't expose function "${expectedFunction}".`);
23
- }
24
- if (typeof object[expectedFunction] !== "function") {
25
- throw new Error(`Libherb at "${libherbpath}" has "${expectedFunction}" but it's not a function.`);
26
- }
27
- }
28
- return true;
29
- }
30
- function ensureLibHerbBackend(object, libherbpath = "unknown") {
31
- isLibHerbBackend(object, libherbpath);
32
- return object;
33
- }
34
-
35
- /**
36
- * Converts a Diagnostic to Monaco/VSCode-compatible MonacoDiagnostic format
37
- */
38
- function toMonacoDiagnostic(diagnostic) {
39
- const { message, location } = diagnostic;
40
- const severity = diagnostic.severity === "hint" ? "info" : diagnostic.severity;
41
- return {
42
- line: location.start.line,
43
- column: location.start.column,
44
- endLine: location.end.line,
45
- endColumn: location.end.column,
46
- message,
47
- severity
48
- };
49
- }
50
-
51
1
  class Position {
52
2
  line;
53
3
  column;
@@ -179,7 +129,7 @@ class Token {
179
129
  }
180
130
 
181
131
  // NOTE: This file is generated by the templates/template.rb script and should not
182
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/javascript/packages/core/src/errors.ts.erb
132
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.6.1/templates/javascript/packages/core/src/errors.ts.erb
183
133
  class HerbError {
184
134
  type;
185
135
  message;
@@ -620,12 +570,6 @@ function fromSerializedError(error) {
620
570
  }
621
571
  }
622
572
 
623
- var name = "@herb-tools/core";
624
- var version = "0.5.0";
625
- var packageJSON = {
626
- name: name,
627
- version: version};
628
-
629
573
  function ensureString(object) {
630
574
  if (typeof object === "string") {
631
575
  return object;
@@ -641,139 +585,8 @@ function convertToUTF8(string) {
641
585
  return decoder.decode(new Uint8Array(bytes));
642
586
  }
643
587
 
644
- class Result {
645
- source;
646
- warnings;
647
- errors;
648
- constructor(source, warnings = [], errors = []) {
649
- this.source = source;
650
- this.warnings = warnings || [];
651
- this.errors = errors || [];
652
- }
653
- /**
654
- * Determines if the parsing was successful.
655
- * @returns `true` if there are no errors, otherwise `false`.
656
- */
657
- get successful() {
658
- return this.errors.length === 0;
659
- }
660
- /**
661
- * Determines if the parsing failed.
662
- * @returns `true` if there are errors, otherwise `false`.
663
- */
664
- get failed() {
665
- return this.errors.length > 0;
666
- }
667
- }
668
-
669
- class TokenList {
670
- list;
671
- static from(list) {
672
- return new TokenList(list.map((token) => Token.from(token)));
673
- }
674
- constructor(list) {
675
- this.list = list;
676
- }
677
- get length() {
678
- return this.list.length;
679
- }
680
- get tokens() {
681
- return this.list;
682
- }
683
- [Symbol.iterator]() {
684
- return this.list[Symbol.iterator]();
685
- }
686
- at(index) {
687
- return this.list.at(index);
688
- }
689
- forEach(callback) {
690
- this.list.forEach(callback);
691
- }
692
- map(callback) {
693
- return this.list.map(callback);
694
- }
695
- filter(predicate) {
696
- return this.list.filter(predicate);
697
- }
698
- __getobj__() {
699
- return this.list;
700
- }
701
- inspect() {
702
- return this.list.map((token) => token.inspect()).join("\n") + "\n";
703
- }
704
- toString() {
705
- return this.inspect();
706
- }
707
- }
708
-
709
- class HerbWarning {
710
- message;
711
- location;
712
- static from(warning) {
713
- return new HerbWarning(warning.message, Location.from(warning.location));
714
- }
715
- constructor(message, location) {
716
- this.message = message;
717
- this.location = location;
718
- }
719
- }
720
-
721
- /**
722
- * Represents the result of a lexical analysis, extending the base `Result` class.
723
- * It contains the token list, source code, warnings, and errors.
724
- */
725
- class LexResult extends Result {
726
- /** The list of tokens generated from the source code. */
727
- value;
728
- /**
729
- * Creates a `LexResult` instance from a serialized result.
730
- * @param result - The serialized lexical result containing tokens, source, warnings, and errors.
731
- * @returns A new `LexResult` instance.
732
- */
733
- static from(result) {
734
- return new LexResult(TokenList.from(result.tokens || []), result.source, result.warnings.map((warning) => HerbWarning.from(warning)), result.errors.map((error) => HerbError.from(error)));
735
- }
736
- /**
737
- * Constructs a new `LexResult`.
738
- * @param value - The list of tokens.
739
- * @param source - The source code that was lexed.
740
- * @param warnings - An array of warnings encountered during lexing.
741
- * @param errors - An array of errors encountered during lexing.
742
- */
743
- constructor(value, source, warnings = [], errors = []) {
744
- super(source, warnings, errors);
745
- this.value = value;
746
- }
747
- /**
748
- * Determines if the lexing was successful.
749
- * @returns `true` if there are no errors, otherwise `false`.
750
- */
751
- get successful() {
752
- return this.errors.length === 0;
753
- }
754
- /**
755
- * Determines if the lexing failed.
756
- * @returns `true` if there are errors, otherwise `false`.
757
- */
758
- get failed() {
759
- return this.errors.length > 0;
760
- }
761
- /**
762
- * Converts the `LexResult` to a JSON representation.
763
- * @returns An object containing the token list, source, warnings, and errors.
764
- */
765
- toJSON() {
766
- return {
767
- value: this.value,
768
- source: this.source,
769
- warnings: this.warnings,
770
- errors: this.errors,
771
- };
772
- }
773
- }
774
-
775
588
  // NOTE: This file is generated by the templates/template.rb script and should not
776
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/javascript/packages/core/src/nodes.ts.erb
589
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.6.1/templates/javascript/packages/core/src/nodes.ts.erb
777
590
  class Node {
778
591
  type;
779
592
  location;
@@ -781,6 +594,9 @@ class Node {
781
594
  static from(node) {
782
595
  return fromSerializedNode(node);
783
596
  }
597
+ static get type() {
598
+ throw new Error("AST_NODE");
599
+ }
784
600
  constructor(type, location, errors) {
785
601
  this.type = type;
786
602
  this.location = location;
@@ -796,6 +612,12 @@ class Node {
796
612
  inspect() {
797
613
  return this.treeInspect(0);
798
614
  }
615
+ is(nodeClass) {
616
+ return this.type === nodeClass.type;
617
+ }
618
+ isOfType(type) {
619
+ return this.type === type;
620
+ }
799
621
  get isSingleLine() {
800
622
  return this.location.start.line === this.location.end.line;
801
623
  }
@@ -837,6 +659,9 @@ class Node {
837
659
  }
838
660
  class DocumentNode extends Node {
839
661
  children;
662
+ static get type() {
663
+ return "AST_DOCUMENT_NODE";
664
+ }
840
665
  static from(data) {
841
666
  return new DocumentNode({
842
667
  type: data.type,
@@ -878,12 +703,14 @@ class DocumentNode extends Node {
878
703
  output += `@ DocumentNode ${this.location.treeInspectWithLabel()}\n`;
879
704
  output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
880
705
  output += `└── children: ${this.inspectArray(this.children, " ")}`;
881
- // output += "\n";
882
706
  return output;
883
707
  }
884
708
  }
885
709
  class LiteralNode extends Node {
886
710
  content;
711
+ static get type() {
712
+ return "AST_LITERAL_NODE";
713
+ }
887
714
  static from(data) {
888
715
  return new LiteralNode({
889
716
  type: data.type,
@@ -922,7 +749,6 @@ class LiteralNode extends Node {
922
749
  output += `@ LiteralNode ${this.location.treeInspectWithLabel()}\n`;
923
750
  output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
924
751
  output += `└── content: ${this.content ? JSON.stringify(this.content) : "∅"}\n`;
925
- // output += "\n";
926
752
  return output;
927
753
  }
928
754
  }
@@ -932,6 +758,9 @@ class HTMLOpenTagNode extends Node {
932
758
  tag_closing;
933
759
  children;
934
760
  is_void;
761
+ static get type() {
762
+ return "AST_HTML_OPEN_TAG_NODE";
763
+ }
935
764
  static from(data) {
936
765
  return new HTMLOpenTagNode({
937
766
  type: data.type,
@@ -989,14 +818,17 @@ class HTMLOpenTagNode extends Node {
989
818
  output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
990
819
  output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
991
820
  output += `└── is_void: ${typeof this.is_void === 'boolean' ? String(this.is_void) : "∅"}\n`;
992
- // output += "\n";
993
821
  return output;
994
822
  }
995
823
  }
996
824
  class HTMLCloseTagNode extends Node {
997
825
  tag_opening;
998
826
  tag_name;
827
+ children;
999
828
  tag_closing;
829
+ static get type() {
830
+ return "AST_HTML_CLOSE_TAG_NODE";
831
+ }
1000
832
  static from(data) {
1001
833
  return new HTMLCloseTagNode({
1002
834
  type: data.type,
@@ -1004,6 +836,7 @@ class HTMLCloseTagNode extends Node {
1004
836
  errors: (data.errors || []).map(error => HerbError.from(error)),
1005
837
  tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1006
838
  tag_name: data.tag_name ? Token.from(data.tag_name) : null,
839
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1007
840
  tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1008
841
  });
1009
842
  }
@@ -1011,13 +844,16 @@ class HTMLCloseTagNode extends Node {
1011
844
  super(props.type, props.location, props.errors);
1012
845
  this.tag_opening = props.tag_opening;
1013
846
  this.tag_name = props.tag_name;
847
+ this.children = props.children;
1014
848
  this.tag_closing = props.tag_closing;
1015
849
  }
1016
850
  accept(visitor) {
1017
851
  visitor.visitHTMLCloseTagNode(this);
1018
852
  }
1019
853
  childNodes() {
1020
- return [];
854
+ return [
855
+ ...this.children,
856
+ ];
1021
857
  }
1022
858
  compactChildNodes() {
1023
859
  return this.childNodes().filter(node => node !== null && node !== undefined);
@@ -1025,6 +861,7 @@ class HTMLCloseTagNode extends Node {
1025
861
  recursiveErrors() {
1026
862
  return [
1027
863
  ...this.errors,
864
+ ...this.children.map(node => node.recursiveErrors()),
1028
865
  ].flat();
1029
866
  }
1030
867
  toJSON() {
@@ -1033,6 +870,7 @@ class HTMLCloseTagNode extends Node {
1033
870
  type: "AST_HTML_CLOSE_TAG_NODE",
1034
871
  tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1035
872
  tag_name: this.tag_name ? this.tag_name.toJSON() : null,
873
+ children: this.children.map(node => node.toJSON()),
1036
874
  tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1037
875
  };
1038
876
  }
@@ -1042,43 +880,48 @@ class HTMLCloseTagNode extends Node {
1042
880
  output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1043
881
  output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1044
882
  output += `├── tag_name: ${this.tag_name ? this.tag_name.treeInspect() : "∅"}\n`;
883
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1045
884
  output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1046
- // output += "\n";
1047
885
  return output;
1048
886
  }
1049
887
  }
1050
- class HTMLSelfCloseTagNode extends Node {
1051
- tag_opening;
888
+ class HTMLElementNode extends Node {
889
+ open_tag;
1052
890
  tag_name;
1053
- attributes;
1054
- tag_closing;
891
+ body;
892
+ close_tag;
1055
893
  is_void;
894
+ static get type() {
895
+ return "AST_HTML_ELEMENT_NODE";
896
+ }
1056
897
  static from(data) {
1057
- return new HTMLSelfCloseTagNode({
898
+ return new HTMLElementNode({
1058
899
  type: data.type,
1059
900
  location: Location.from(data.location),
1060
901
  errors: (data.errors || []).map(error => HerbError.from(error)),
1061
- tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
902
+ open_tag: data.open_tag ? fromSerializedNode((data.open_tag)) : null,
1062
903
  tag_name: data.tag_name ? Token.from(data.tag_name) : null,
1063
- attributes: (data.attributes || []).map(node => fromSerializedNode(node)),
1064
- tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
904
+ body: (data.body || []).map(node => fromSerializedNode(node)),
905
+ close_tag: data.close_tag ? fromSerializedNode((data.close_tag)) : null,
1065
906
  is_void: data.is_void,
1066
907
  });
1067
908
  }
1068
909
  constructor(props) {
1069
910
  super(props.type, props.location, props.errors);
1070
- this.tag_opening = props.tag_opening;
911
+ this.open_tag = props.open_tag;
1071
912
  this.tag_name = props.tag_name;
1072
- this.attributes = props.attributes;
1073
- this.tag_closing = props.tag_closing;
913
+ this.body = props.body;
914
+ this.close_tag = props.close_tag;
1074
915
  this.is_void = props.is_void;
1075
916
  }
1076
917
  accept(visitor) {
1077
- visitor.visitHTMLSelfCloseTagNode(this);
918
+ visitor.visitHTMLElementNode(this);
1078
919
  }
1079
920
  childNodes() {
1080
921
  return [
1081
- ...this.attributes,
922
+ this.open_tag,
923
+ ...this.body,
924
+ this.close_tag,
1082
925
  ];
1083
926
  }
1084
927
  compactChildNodes() {
@@ -1087,111 +930,44 @@ class HTMLSelfCloseTagNode extends Node {
1087
930
  recursiveErrors() {
1088
931
  return [
1089
932
  ...this.errors,
1090
- ...this.attributes.map(node => node.recursiveErrors()),
933
+ this.open_tag ? this.open_tag.recursiveErrors() : [],
934
+ ...this.body.map(node => node.recursiveErrors()),
935
+ this.close_tag ? this.close_tag.recursiveErrors() : [],
1091
936
  ].flat();
1092
937
  }
1093
938
  toJSON() {
1094
939
  return {
1095
940
  ...super.toJSON(),
1096
- type: "AST_HTML_SELF_CLOSE_TAG_NODE",
1097
- tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
941
+ type: "AST_HTML_ELEMENT_NODE",
942
+ open_tag: this.open_tag ? this.open_tag.toJSON() : null,
1098
943
  tag_name: this.tag_name ? this.tag_name.toJSON() : null,
1099
- attributes: this.attributes.map(node => node.toJSON()),
1100
- tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
944
+ body: this.body.map(node => node.toJSON()),
945
+ close_tag: this.close_tag ? this.close_tag.toJSON() : null,
1101
946
  is_void: this.is_void,
1102
947
  };
1103
948
  }
1104
949
  treeInspect() {
1105
950
  let output = "";
1106
- output += `@ HTMLSelfCloseTagNode ${this.location.treeInspectWithLabel()}\n`;
951
+ output += `@ HTMLElementNode ${this.location.treeInspectWithLabel()}\n`;
1107
952
  output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1108
- output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : ""}\n`;
953
+ output += `├── open_tag: ${this.inspectNode(this.open_tag, "")}`;
1109
954
  output += `├── tag_name: ${this.tag_name ? this.tag_name.treeInspect() : "∅"}\n`;
1110
- output += `├── attributes: ${this.inspectArray(this.attributes, "│ ")}`;
1111
- output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : ""}\n`;
955
+ output += `├── body: ${this.inspectArray(this.body, "│ ")}`;
956
+ output += `├── close_tag: ${this.inspectNode(this.close_tag, "")}`;
1112
957
  output += `└── is_void: ${typeof this.is_void === 'boolean' ? String(this.is_void) : "∅"}\n`;
1113
- // output += "\n";
1114
958
  return output;
1115
959
  }
1116
960
  }
1117
- class HTMLElementNode extends Node {
1118
- open_tag;
1119
- tag_name;
1120
- body;
1121
- close_tag;
1122
- is_void;
961
+ class HTMLAttributeValueNode extends Node {
962
+ open_quote;
963
+ children;
964
+ close_quote;
965
+ quoted;
966
+ static get type() {
967
+ return "AST_HTML_ATTRIBUTE_VALUE_NODE";
968
+ }
1123
969
  static from(data) {
1124
- return new HTMLElementNode({
1125
- type: data.type,
1126
- location: Location.from(data.location),
1127
- errors: (data.errors || []).map(error => HerbError.from(error)),
1128
- open_tag: data.open_tag ? fromSerializedNode(data.open_tag) : null,
1129
- tag_name: data.tag_name ? Token.from(data.tag_name) : null,
1130
- body: (data.body || []).map(node => fromSerializedNode(node)),
1131
- close_tag: data.close_tag ? fromSerializedNode(data.close_tag) : null,
1132
- is_void: data.is_void,
1133
- });
1134
- }
1135
- constructor(props) {
1136
- super(props.type, props.location, props.errors);
1137
- this.open_tag = props.open_tag;
1138
- this.tag_name = props.tag_name;
1139
- this.body = props.body;
1140
- this.close_tag = props.close_tag;
1141
- this.is_void = props.is_void;
1142
- }
1143
- accept(visitor) {
1144
- visitor.visitHTMLElementNode(this);
1145
- }
1146
- childNodes() {
1147
- return [
1148
- this.open_tag,
1149
- ...this.body,
1150
- this.close_tag,
1151
- ];
1152
- }
1153
- compactChildNodes() {
1154
- return this.childNodes().filter(node => node !== null && node !== undefined);
1155
- }
1156
- recursiveErrors() {
1157
- return [
1158
- ...this.errors,
1159
- this.open_tag ? this.open_tag.recursiveErrors() : [],
1160
- ...this.body.map(node => node.recursiveErrors()),
1161
- this.close_tag ? this.close_tag.recursiveErrors() : [],
1162
- ].flat();
1163
- }
1164
- toJSON() {
1165
- return {
1166
- ...super.toJSON(),
1167
- type: "AST_HTML_ELEMENT_NODE",
1168
- open_tag: this.open_tag ? this.open_tag.toJSON() : null,
1169
- tag_name: this.tag_name ? this.tag_name.toJSON() : null,
1170
- body: this.body.map(node => node.toJSON()),
1171
- close_tag: this.close_tag ? this.close_tag.toJSON() : null,
1172
- is_void: this.is_void,
1173
- };
1174
- }
1175
- treeInspect() {
1176
- let output = "";
1177
- output += `@ HTMLElementNode ${this.location.treeInspectWithLabel()}\n`;
1178
- output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1179
- output += `├── open_tag: ${this.inspectNode(this.open_tag, "│ ")}`;
1180
- output += `├── tag_name: ${this.tag_name ? this.tag_name.treeInspect() : "∅"}\n`;
1181
- output += `├── body: ${this.inspectArray(this.body, "│ ")}`;
1182
- output += `├── close_tag: ${this.inspectNode(this.close_tag, "│ ")}`;
1183
- output += `└── is_void: ${typeof this.is_void === 'boolean' ? String(this.is_void) : "∅"}\n`;
1184
- // output += "\n";
1185
- return output;
1186
- }
1187
- }
1188
- class HTMLAttributeValueNode extends Node {
1189
- open_quote;
1190
- children;
1191
- close_quote;
1192
- quoted;
1193
- static from(data) {
1194
- return new HTMLAttributeValueNode({
970
+ return new HTMLAttributeValueNode({
1195
971
  type: data.type,
1196
972
  location: Location.from(data.location),
1197
973
  errors: (data.errors || []).map(error => HerbError.from(error)),
@@ -1243,29 +1019,33 @@ class HTMLAttributeValueNode extends Node {
1243
1019
  output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1244
1020
  output += `├── close_quote: ${this.close_quote ? this.close_quote.treeInspect() : "∅"}\n`;
1245
1021
  output += `└── quoted: ${typeof this.quoted === 'boolean' ? String(this.quoted) : "∅"}\n`;
1246
- // output += "\n";
1247
1022
  return output;
1248
1023
  }
1249
1024
  }
1250
1025
  class HTMLAttributeNameNode extends Node {
1251
- name;
1026
+ children;
1027
+ static get type() {
1028
+ return "AST_HTML_ATTRIBUTE_NAME_NODE";
1029
+ }
1252
1030
  static from(data) {
1253
1031
  return new HTMLAttributeNameNode({
1254
1032
  type: data.type,
1255
1033
  location: Location.from(data.location),
1256
1034
  errors: (data.errors || []).map(error => HerbError.from(error)),
1257
- name: data.name ? Token.from(data.name) : null,
1035
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1258
1036
  });
1259
1037
  }
1260
1038
  constructor(props) {
1261
1039
  super(props.type, props.location, props.errors);
1262
- this.name = props.name;
1040
+ this.children = props.children;
1263
1041
  }
1264
1042
  accept(visitor) {
1265
1043
  visitor.visitHTMLAttributeNameNode(this);
1266
1044
  }
1267
1045
  childNodes() {
1268
- return [];
1046
+ return [
1047
+ ...this.children,
1048
+ ];
1269
1049
  }
1270
1050
  compactChildNodes() {
1271
1051
  return this.childNodes().filter(node => node !== null && node !== undefined);
@@ -1273,21 +1053,21 @@ class HTMLAttributeNameNode extends Node {
1273
1053
  recursiveErrors() {
1274
1054
  return [
1275
1055
  ...this.errors,
1056
+ ...this.children.map(node => node.recursiveErrors()),
1276
1057
  ].flat();
1277
1058
  }
1278
1059
  toJSON() {
1279
1060
  return {
1280
1061
  ...super.toJSON(),
1281
1062
  type: "AST_HTML_ATTRIBUTE_NAME_NODE",
1282
- name: this.name ? this.name.toJSON() : null,
1063
+ children: this.children.map(node => node.toJSON()),
1283
1064
  };
1284
1065
  }
1285
1066
  treeInspect() {
1286
1067
  let output = "";
1287
1068
  output += `@ HTMLAttributeNameNode ${this.location.treeInspectWithLabel()}\n`;
1288
1069
  output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1289
- output += `└── name: ${this.name ? this.name.treeInspect() : ""}\n`;
1290
- // output += "\n";
1070
+ output += `└── children: ${this.inspectArray(this.children, " ")}`;
1291
1071
  return output;
1292
1072
  }
1293
1073
  }
@@ -1295,14 +1075,17 @@ class HTMLAttributeNode extends Node {
1295
1075
  name;
1296
1076
  equals;
1297
1077
  value;
1078
+ static get type() {
1079
+ return "AST_HTML_ATTRIBUTE_NODE";
1080
+ }
1298
1081
  static from(data) {
1299
1082
  return new HTMLAttributeNode({
1300
1083
  type: data.type,
1301
1084
  location: Location.from(data.location),
1302
1085
  errors: (data.errors || []).map(error => HerbError.from(error)),
1303
- name: data.name ? fromSerializedNode(data.name) : null,
1086
+ name: data.name ? fromSerializedNode((data.name)) : null,
1304
1087
  equals: data.equals ? Token.from(data.equals) : null,
1305
- value: data.value ? fromSerializedNode(data.value) : null,
1088
+ value: data.value ? fromSerializedNode((data.value)) : null,
1306
1089
  });
1307
1090
  }
1308
1091
  constructor(props) {
@@ -1346,12 +1129,14 @@ class HTMLAttributeNode extends Node {
1346
1129
  output += `├── name: ${this.inspectNode(this.name, "│ ")}`;
1347
1130
  output += `├── equals: ${this.equals ? this.equals.treeInspect() : "∅"}\n`;
1348
1131
  output += `└── value: ${this.inspectNode(this.value, " ")}`;
1349
- // output += "\n";
1350
1132
  return output;
1351
1133
  }
1352
1134
  }
1353
1135
  class HTMLTextNode extends Node {
1354
1136
  content;
1137
+ static get type() {
1138
+ return "AST_HTML_TEXT_NODE";
1139
+ }
1355
1140
  static from(data) {
1356
1141
  return new HTMLTextNode({
1357
1142
  type: data.type,
@@ -1390,7 +1175,6 @@ class HTMLTextNode extends Node {
1390
1175
  output += `@ HTMLTextNode ${this.location.treeInspectWithLabel()}\n`;
1391
1176
  output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1392
1177
  output += `└── content: ${this.content ? JSON.stringify(this.content) : "∅"}\n`;
1393
- // output += "\n";
1394
1178
  return output;
1395
1179
  }
1396
1180
  }
@@ -1398,6 +1182,9 @@ class HTMLCommentNode extends Node {
1398
1182
  comment_start;
1399
1183
  children;
1400
1184
  comment_end;
1185
+ static get type() {
1186
+ return "AST_HTML_COMMENT_NODE";
1187
+ }
1401
1188
  static from(data) {
1402
1189
  return new HTMLCommentNode({
1403
1190
  type: data.type,
@@ -1447,7 +1234,6 @@ class HTMLCommentNode extends Node {
1447
1234
  output += `├── comment_start: ${this.comment_start ? this.comment_start.treeInspect() : "∅"}\n`;
1448
1235
  output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1449
1236
  output += `└── comment_end: ${this.comment_end ? this.comment_end.treeInspect() : "∅"}\n`;
1450
- // output += "\n";
1451
1237
  return output;
1452
1238
  }
1453
1239
  }
@@ -1455,6 +1241,9 @@ class HTMLDoctypeNode extends Node {
1455
1241
  tag_opening;
1456
1242
  children;
1457
1243
  tag_closing;
1244
+ static get type() {
1245
+ return "AST_HTML_DOCTYPE_NODE";
1246
+ }
1458
1247
  static from(data) {
1459
1248
  return new HTMLDoctypeNode({
1460
1249
  type: data.type,
@@ -1504,12 +1293,132 @@ class HTMLDoctypeNode extends Node {
1504
1293
  output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1505
1294
  output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1506
1295
  output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1507
- // output += "\n";
1296
+ return output;
1297
+ }
1298
+ }
1299
+ class XMLDeclarationNode extends Node {
1300
+ tag_opening;
1301
+ children;
1302
+ tag_closing;
1303
+ static get type() {
1304
+ return "AST_XML_DECLARATION_NODE";
1305
+ }
1306
+ static from(data) {
1307
+ return new XMLDeclarationNode({
1308
+ type: data.type,
1309
+ location: Location.from(data.location),
1310
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1311
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1312
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1313
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1314
+ });
1315
+ }
1316
+ constructor(props) {
1317
+ super(props.type, props.location, props.errors);
1318
+ this.tag_opening = props.tag_opening;
1319
+ this.children = props.children;
1320
+ this.tag_closing = props.tag_closing;
1321
+ }
1322
+ accept(visitor) {
1323
+ visitor.visitXMLDeclarationNode(this);
1324
+ }
1325
+ childNodes() {
1326
+ return [
1327
+ ...this.children,
1328
+ ];
1329
+ }
1330
+ compactChildNodes() {
1331
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1332
+ }
1333
+ recursiveErrors() {
1334
+ return [
1335
+ ...this.errors,
1336
+ ...this.children.map(node => node.recursiveErrors()),
1337
+ ].flat();
1338
+ }
1339
+ toJSON() {
1340
+ return {
1341
+ ...super.toJSON(),
1342
+ type: "AST_XML_DECLARATION_NODE",
1343
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1344
+ children: this.children.map(node => node.toJSON()),
1345
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1346
+ };
1347
+ }
1348
+ treeInspect() {
1349
+ let output = "";
1350
+ output += `@ XMLDeclarationNode ${this.location.treeInspectWithLabel()}\n`;
1351
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1352
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1353
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1354
+ output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1355
+ return output;
1356
+ }
1357
+ }
1358
+ class CDATANode extends Node {
1359
+ tag_opening;
1360
+ children;
1361
+ tag_closing;
1362
+ static get type() {
1363
+ return "AST_CDATA_NODE";
1364
+ }
1365
+ static from(data) {
1366
+ return new CDATANode({
1367
+ type: data.type,
1368
+ location: Location.from(data.location),
1369
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1370
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1371
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1372
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1373
+ });
1374
+ }
1375
+ constructor(props) {
1376
+ super(props.type, props.location, props.errors);
1377
+ this.tag_opening = props.tag_opening;
1378
+ this.children = props.children;
1379
+ this.tag_closing = props.tag_closing;
1380
+ }
1381
+ accept(visitor) {
1382
+ visitor.visitCDATANode(this);
1383
+ }
1384
+ childNodes() {
1385
+ return [
1386
+ ...this.children,
1387
+ ];
1388
+ }
1389
+ compactChildNodes() {
1390
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1391
+ }
1392
+ recursiveErrors() {
1393
+ return [
1394
+ ...this.errors,
1395
+ ...this.children.map(node => node.recursiveErrors()),
1396
+ ].flat();
1397
+ }
1398
+ toJSON() {
1399
+ return {
1400
+ ...super.toJSON(),
1401
+ type: "AST_CDATA_NODE",
1402
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1403
+ children: this.children.map(node => node.toJSON()),
1404
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1405
+ };
1406
+ }
1407
+ treeInspect() {
1408
+ let output = "";
1409
+ output += `@ CDATANode ${this.location.treeInspectWithLabel()}\n`;
1410
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1411
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1412
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1413
+ output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1508
1414
  return output;
1509
1415
  }
1510
1416
  }
1511
1417
  class WhitespaceNode extends Node {
1512
1418
  value;
1419
+ static get type() {
1420
+ return "AST_WHITESPACE_NODE";
1421
+ }
1513
1422
  static from(data) {
1514
1423
  return new WhitespaceNode({
1515
1424
  type: data.type,
@@ -1548,7 +1457,6 @@ class WhitespaceNode extends Node {
1548
1457
  output += `@ WhitespaceNode ${this.location.treeInspectWithLabel()}\n`;
1549
1458
  output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1550
1459
  output += `└── value: ${this.value ? this.value.treeInspect() : "∅"}\n`;
1551
- // output += "\n";
1552
1460
  return output;
1553
1461
  }
1554
1462
  }
@@ -1559,6 +1467,9 @@ class ERBContentNode extends Node {
1559
1467
  // no-op for analyzed_ruby
1560
1468
  parsed;
1561
1469
  valid;
1470
+ static get type() {
1471
+ return "AST_ERB_CONTENT_NODE";
1472
+ }
1562
1473
  static from(data) {
1563
1474
  return new ERBContentNode({
1564
1475
  type: data.type,
@@ -1617,7 +1528,6 @@ class ERBContentNode extends Node {
1617
1528
  // no-op for analyzed_ruby
1618
1529
  output += `├── parsed: ${typeof this.parsed === 'boolean' ? String(this.parsed) : "∅"}\n`;
1619
1530
  output += `└── valid: ${typeof this.valid === 'boolean' ? String(this.valid) : "∅"}\n`;
1620
- // output += "\n";
1621
1531
  return output;
1622
1532
  }
1623
1533
  }
@@ -1625,6 +1535,9 @@ class ERBEndNode extends Node {
1625
1535
  tag_opening;
1626
1536
  content;
1627
1537
  tag_closing;
1538
+ static get type() {
1539
+ return "AST_ERB_END_NODE";
1540
+ }
1628
1541
  static from(data) {
1629
1542
  return new ERBEndNode({
1630
1543
  type: data.type,
@@ -1671,7 +1584,6 @@ class ERBEndNode extends Node {
1671
1584
  output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1672
1585
  output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1673
1586
  output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1674
- // output += "\n";
1675
1587
  return output;
1676
1588
  }
1677
1589
  }
@@ -1680,6 +1592,9 @@ class ERBElseNode extends Node {
1680
1592
  content;
1681
1593
  tag_closing;
1682
1594
  statements;
1595
+ static get type() {
1596
+ return "AST_ERB_ELSE_NODE";
1597
+ }
1683
1598
  static from(data) {
1684
1599
  return new ERBElseNode({
1685
1600
  type: data.type,
@@ -1733,7 +1648,6 @@ class ERBElseNode extends Node {
1733
1648
  output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1734
1649
  output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1735
1650
  output += `└── statements: ${this.inspectArray(this.statements, " ")}`;
1736
- // output += "\n";
1737
1651
  return output;
1738
1652
  }
1739
1653
  }
@@ -1744,6 +1658,9 @@ class ERBIfNode extends Node {
1744
1658
  statements;
1745
1659
  subsequent;
1746
1660
  end_node;
1661
+ static get type() {
1662
+ return "AST_ERB_IF_NODE";
1663
+ }
1747
1664
  static from(data) {
1748
1665
  return new ERBIfNode({
1749
1666
  type: data.type,
@@ -1753,8 +1670,8 @@ class ERBIfNode extends Node {
1753
1670
  content: data.content ? Token.from(data.content) : null,
1754
1671
  tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1755
1672
  statements: (data.statements || []).map(node => fromSerializedNode(node)),
1756
- subsequent: data.subsequent ? fromSerializedNode(data.subsequent) : null,
1757
- end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
1673
+ subsequent: data.subsequent ? fromSerializedNode((data.subsequent)) : null,
1674
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
1758
1675
  });
1759
1676
  }
1760
1677
  constructor(props) {
@@ -1809,7 +1726,6 @@ class ERBIfNode extends Node {
1809
1726
  output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
1810
1727
  output += `├── subsequent: ${this.inspectNode(this.subsequent, "│ ")}`;
1811
1728
  output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
1812
- // output += "\n";
1813
1729
  return output;
1814
1730
  }
1815
1731
  }
@@ -1819,6 +1735,9 @@ class ERBBlockNode extends Node {
1819
1735
  tag_closing;
1820
1736
  body;
1821
1737
  end_node;
1738
+ static get type() {
1739
+ return "AST_ERB_BLOCK_NODE";
1740
+ }
1822
1741
  static from(data) {
1823
1742
  return new ERBBlockNode({
1824
1743
  type: data.type,
@@ -1828,7 +1747,7 @@ class ERBBlockNode extends Node {
1828
1747
  content: data.content ? Token.from(data.content) : null,
1829
1748
  tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1830
1749
  body: (data.body || []).map(node => fromSerializedNode(node)),
1831
- end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
1750
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
1832
1751
  });
1833
1752
  }
1834
1753
  constructor(props) {
@@ -1878,7 +1797,6 @@ class ERBBlockNode extends Node {
1878
1797
  output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1879
1798
  output += `├── body: ${this.inspectArray(this.body, "│ ")}`;
1880
1799
  output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
1881
- // output += "\n";
1882
1800
  return output;
1883
1801
  }
1884
1802
  }
@@ -1887,6 +1805,9 @@ class ERBWhenNode extends Node {
1887
1805
  content;
1888
1806
  tag_closing;
1889
1807
  statements;
1808
+ static get type() {
1809
+ return "AST_ERB_WHEN_NODE";
1810
+ }
1890
1811
  static from(data) {
1891
1812
  return new ERBWhenNode({
1892
1813
  type: data.type,
@@ -1940,7 +1861,6 @@ class ERBWhenNode extends Node {
1940
1861
  output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1941
1862
  output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1942
1863
  output += `└── statements: ${this.inspectArray(this.statements, " ")}`;
1943
- // output += "\n";
1944
1864
  return output;
1945
1865
  }
1946
1866
  }
@@ -1952,6 +1872,9 @@ class ERBCaseNode extends Node {
1952
1872
  conditions;
1953
1873
  else_clause;
1954
1874
  end_node;
1875
+ static get type() {
1876
+ return "AST_ERB_CASE_NODE";
1877
+ }
1955
1878
  static from(data) {
1956
1879
  return new ERBCaseNode({
1957
1880
  type: data.type,
@@ -1962,8 +1885,8 @@ class ERBCaseNode extends Node {
1962
1885
  tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1963
1886
  children: (data.children || []).map(node => fromSerializedNode(node)),
1964
1887
  conditions: (data.conditions || []).map(node => fromSerializedNode(node)),
1965
- else_clause: data.else_clause ? fromSerializedNode(data.else_clause) : null,
1966
- end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
1888
+ else_clause: data.else_clause ? fromSerializedNode((data.else_clause)) : null,
1889
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
1967
1890
  });
1968
1891
  }
1969
1892
  constructor(props) {
@@ -2023,7 +1946,6 @@ class ERBCaseNode extends Node {
2023
1946
  output += `├── conditions: ${this.inspectArray(this.conditions, "│ ")}`;
2024
1947
  output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
2025
1948
  output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2026
- // output += "\n";
2027
1949
  return output;
2028
1950
  }
2029
1951
  }
@@ -2035,6 +1957,9 @@ class ERBCaseMatchNode extends Node {
2035
1957
  conditions;
2036
1958
  else_clause;
2037
1959
  end_node;
1960
+ static get type() {
1961
+ return "AST_ERB_CASE_MATCH_NODE";
1962
+ }
2038
1963
  static from(data) {
2039
1964
  return new ERBCaseMatchNode({
2040
1965
  type: data.type,
@@ -2045,8 +1970,8 @@ class ERBCaseMatchNode extends Node {
2045
1970
  tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2046
1971
  children: (data.children || []).map(node => fromSerializedNode(node)),
2047
1972
  conditions: (data.conditions || []).map(node => fromSerializedNode(node)),
2048
- else_clause: data.else_clause ? fromSerializedNode(data.else_clause) : null,
2049
- end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
1973
+ else_clause: data.else_clause ? fromSerializedNode((data.else_clause)) : null,
1974
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
2050
1975
  });
2051
1976
  }
2052
1977
  constructor(props) {
@@ -2106,7 +2031,6 @@ class ERBCaseMatchNode extends Node {
2106
2031
  output += `├── conditions: ${this.inspectArray(this.conditions, "│ ")}`;
2107
2032
  output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
2108
2033
  output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2109
- // output += "\n";
2110
2034
  return output;
2111
2035
  }
2112
2036
  }
@@ -2116,6 +2040,9 @@ class ERBWhileNode extends Node {
2116
2040
  tag_closing;
2117
2041
  statements;
2118
2042
  end_node;
2043
+ static get type() {
2044
+ return "AST_ERB_WHILE_NODE";
2045
+ }
2119
2046
  static from(data) {
2120
2047
  return new ERBWhileNode({
2121
2048
  type: data.type,
@@ -2125,7 +2052,7 @@ class ERBWhileNode extends Node {
2125
2052
  content: data.content ? Token.from(data.content) : null,
2126
2053
  tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2127
2054
  statements: (data.statements || []).map(node => fromSerializedNode(node)),
2128
- end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
2055
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
2129
2056
  });
2130
2057
  }
2131
2058
  constructor(props) {
@@ -2175,7 +2102,6 @@ class ERBWhileNode extends Node {
2175
2102
  output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2176
2103
  output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2177
2104
  output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2178
- // output += "\n";
2179
2105
  return output;
2180
2106
  }
2181
2107
  }
@@ -2185,6 +2111,9 @@ class ERBUntilNode extends Node {
2185
2111
  tag_closing;
2186
2112
  statements;
2187
2113
  end_node;
2114
+ static get type() {
2115
+ return "AST_ERB_UNTIL_NODE";
2116
+ }
2188
2117
  static from(data) {
2189
2118
  return new ERBUntilNode({
2190
2119
  type: data.type,
@@ -2194,7 +2123,7 @@ class ERBUntilNode extends Node {
2194
2123
  content: data.content ? Token.from(data.content) : null,
2195
2124
  tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2196
2125
  statements: (data.statements || []).map(node => fromSerializedNode(node)),
2197
- end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
2126
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
2198
2127
  });
2199
2128
  }
2200
2129
  constructor(props) {
@@ -2244,7 +2173,6 @@ class ERBUntilNode extends Node {
2244
2173
  output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2245
2174
  output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2246
2175
  output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2247
- // output += "\n";
2248
2176
  return output;
2249
2177
  }
2250
2178
  }
@@ -2254,6 +2182,9 @@ class ERBForNode extends Node {
2254
2182
  tag_closing;
2255
2183
  statements;
2256
2184
  end_node;
2185
+ static get type() {
2186
+ return "AST_ERB_FOR_NODE";
2187
+ }
2257
2188
  static from(data) {
2258
2189
  return new ERBForNode({
2259
2190
  type: data.type,
@@ -2263,7 +2194,7 @@ class ERBForNode extends Node {
2263
2194
  content: data.content ? Token.from(data.content) : null,
2264
2195
  tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2265
2196
  statements: (data.statements || []).map(node => fromSerializedNode(node)),
2266
- end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
2197
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
2267
2198
  });
2268
2199
  }
2269
2200
  constructor(props) {
@@ -2313,7 +2244,6 @@ class ERBForNode extends Node {
2313
2244
  output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2314
2245
  output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2315
2246
  output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2316
- // output += "\n";
2317
2247
  return output;
2318
2248
  }
2319
2249
  }
@@ -2323,6 +2253,9 @@ class ERBRescueNode extends Node {
2323
2253
  tag_closing;
2324
2254
  statements;
2325
2255
  subsequent;
2256
+ static get type() {
2257
+ return "AST_ERB_RESCUE_NODE";
2258
+ }
2326
2259
  static from(data) {
2327
2260
  return new ERBRescueNode({
2328
2261
  type: data.type,
@@ -2332,7 +2265,7 @@ class ERBRescueNode extends Node {
2332
2265
  content: data.content ? Token.from(data.content) : null,
2333
2266
  tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2334
2267
  statements: (data.statements || []).map(node => fromSerializedNode(node)),
2335
- subsequent: data.subsequent ? fromSerializedNode(data.subsequent) : null,
2268
+ subsequent: data.subsequent ? fromSerializedNode((data.subsequent)) : null,
2336
2269
  });
2337
2270
  }
2338
2271
  constructor(props) {
@@ -2382,7 +2315,6 @@ class ERBRescueNode extends Node {
2382
2315
  output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2383
2316
  output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2384
2317
  output += `└── subsequent: ${this.inspectNode(this.subsequent, " ")}`;
2385
- // output += "\n";
2386
2318
  return output;
2387
2319
  }
2388
2320
  }
@@ -2391,6 +2323,9 @@ class ERBEnsureNode extends Node {
2391
2323
  content;
2392
2324
  tag_closing;
2393
2325
  statements;
2326
+ static get type() {
2327
+ return "AST_ERB_ENSURE_NODE";
2328
+ }
2394
2329
  static from(data) {
2395
2330
  return new ERBEnsureNode({
2396
2331
  type: data.type,
@@ -2444,7 +2379,6 @@ class ERBEnsureNode extends Node {
2444
2379
  output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2445
2380
  output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2446
2381
  output += `└── statements: ${this.inspectArray(this.statements, " ")}`;
2447
- // output += "\n";
2448
2382
  return output;
2449
2383
  }
2450
2384
  }
@@ -2457,6 +2391,9 @@ class ERBBeginNode extends Node {
2457
2391
  else_clause;
2458
2392
  ensure_clause;
2459
2393
  end_node;
2394
+ static get type() {
2395
+ return "AST_ERB_BEGIN_NODE";
2396
+ }
2460
2397
  static from(data) {
2461
2398
  return new ERBBeginNode({
2462
2399
  type: data.type,
@@ -2466,10 +2403,10 @@ class ERBBeginNode extends Node {
2466
2403
  content: data.content ? Token.from(data.content) : null,
2467
2404
  tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2468
2405
  statements: (data.statements || []).map(node => fromSerializedNode(node)),
2469
- rescue_clause: data.rescue_clause ? fromSerializedNode(data.rescue_clause) : null,
2470
- else_clause: data.else_clause ? fromSerializedNode(data.else_clause) : null,
2471
- ensure_clause: data.ensure_clause ? fromSerializedNode(data.ensure_clause) : null,
2472
- end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
2406
+ rescue_clause: data.rescue_clause ? fromSerializedNode((data.rescue_clause)) : null,
2407
+ else_clause: data.else_clause ? fromSerializedNode((data.else_clause)) : null,
2408
+ ensure_clause: data.ensure_clause ? fromSerializedNode((data.ensure_clause)) : null,
2409
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
2473
2410
  });
2474
2411
  }
2475
2412
  constructor(props) {
@@ -2534,7 +2471,6 @@ class ERBBeginNode extends Node {
2534
2471
  output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
2535
2472
  output += `├── ensure_clause: ${this.inspectNode(this.ensure_clause, "│ ")}`;
2536
2473
  output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2537
- // output += "\n";
2538
2474
  return output;
2539
2475
  }
2540
2476
  }
@@ -2545,6 +2481,9 @@ class ERBUnlessNode extends Node {
2545
2481
  statements;
2546
2482
  else_clause;
2547
2483
  end_node;
2484
+ static get type() {
2485
+ return "AST_ERB_UNLESS_NODE";
2486
+ }
2548
2487
  static from(data) {
2549
2488
  return new ERBUnlessNode({
2550
2489
  type: data.type,
@@ -2554,8 +2493,8 @@ class ERBUnlessNode extends Node {
2554
2493
  content: data.content ? Token.from(data.content) : null,
2555
2494
  tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2556
2495
  statements: (data.statements || []).map(node => fromSerializedNode(node)),
2557
- else_clause: data.else_clause ? fromSerializedNode(data.else_clause) : null,
2558
- end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
2496
+ else_clause: data.else_clause ? fromSerializedNode((data.else_clause)) : null,
2497
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
2559
2498
  });
2560
2499
  }
2561
2500
  constructor(props) {
@@ -2610,7 +2549,6 @@ class ERBUnlessNode extends Node {
2610
2549
  output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2611
2550
  output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
2612
2551
  output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2613
- // output += "\n";
2614
2552
  return output;
2615
2553
  }
2616
2554
  }
@@ -2618,6 +2556,9 @@ class ERBYieldNode extends Node {
2618
2556
  tag_opening;
2619
2557
  content;
2620
2558
  tag_closing;
2559
+ static get type() {
2560
+ return "AST_ERB_YIELD_NODE";
2561
+ }
2621
2562
  static from(data) {
2622
2563
  return new ERBYieldNode({
2623
2564
  type: data.type,
@@ -2664,7 +2605,6 @@ class ERBYieldNode extends Node {
2664
2605
  output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2665
2606
  output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2666
2607
  output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2667
- // output += "\n";
2668
2608
  return output;
2669
2609
  }
2670
2610
  }
@@ -2673,6 +2613,9 @@ class ERBInNode extends Node {
2673
2613
  content;
2674
2614
  tag_closing;
2675
2615
  statements;
2616
+ static get type() {
2617
+ return "AST_ERB_IN_NODE";
2618
+ }
2676
2619
  static from(data) {
2677
2620
  return new ERBInNode({
2678
2621
  type: data.type,
@@ -2726,7 +2669,6 @@ class ERBInNode extends Node {
2726
2669
  output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2727
2670
  output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2728
2671
  output += `└── statements: ${this.inspectArray(this.statements, " ")}`;
2729
- // output += "\n";
2730
2672
  return output;
2731
2673
  }
2732
2674
  }
@@ -2736,7 +2678,6 @@ function fromSerializedNode(node) {
2736
2678
  case "AST_LITERAL_NODE": return LiteralNode.from(node);
2737
2679
  case "AST_HTML_OPEN_TAG_NODE": return HTMLOpenTagNode.from(node);
2738
2680
  case "AST_HTML_CLOSE_TAG_NODE": return HTMLCloseTagNode.from(node);
2739
- case "AST_HTML_SELF_CLOSE_TAG_NODE": return HTMLSelfCloseTagNode.from(node);
2740
2681
  case "AST_HTML_ELEMENT_NODE": return HTMLElementNode.from(node);
2741
2682
  case "AST_HTML_ATTRIBUTE_VALUE_NODE": return HTMLAttributeValueNode.from(node);
2742
2683
  case "AST_HTML_ATTRIBUTE_NAME_NODE": return HTMLAttributeNameNode.from(node);
@@ -2744,6 +2685,8 @@ function fromSerializedNode(node) {
2744
2685
  case "AST_HTML_TEXT_NODE": return HTMLTextNode.from(node);
2745
2686
  case "AST_HTML_COMMENT_NODE": return HTMLCommentNode.from(node);
2746
2687
  case "AST_HTML_DOCTYPE_NODE": return HTMLDoctypeNode.from(node);
2688
+ case "AST_XML_DECLARATION_NODE": return XMLDeclarationNode.from(node);
2689
+ case "AST_CDATA_NODE": return CDATANode.from(node);
2747
2690
  case "AST_WHITESPACE_NODE": return WhitespaceNode.from(node);
2748
2691
  case "AST_ERB_CONTENT_NODE": return ERBContentNode.from(node);
2749
2692
  case "AST_ERB_END_NODE": return ERBEndNode.from(node);
@@ -2785,8 +2728,42 @@ const ERBNodeClasses = [
2785
2728
  ERBYieldNode,
2786
2729
  ERBInNode,
2787
2730
  ];
2788
- function isERBNode(node) {
2789
- return node.constructor.name.startsWith("ERB");
2731
+
2732
+ class Result {
2733
+ source;
2734
+ warnings;
2735
+ errors;
2736
+ constructor(source, warnings = [], errors = []) {
2737
+ this.source = source;
2738
+ this.warnings = warnings || [];
2739
+ this.errors = errors || [];
2740
+ }
2741
+ /**
2742
+ * Determines if the parsing was successful.
2743
+ * @returns `true` if there are no errors, otherwise `false`.
2744
+ */
2745
+ get successful() {
2746
+ return this.errors.length === 0;
2747
+ }
2748
+ /**
2749
+ * Determines if the parsing failed.
2750
+ * @returns `true` if there are errors, otherwise `false`.
2751
+ */
2752
+ get failed() {
2753
+ return this.errors.length > 0;
2754
+ }
2755
+ }
2756
+
2757
+ class HerbWarning {
2758
+ message;
2759
+ location;
2760
+ static from(warning) {
2761
+ return new HerbWarning(warning.message, Location.from(warning.location));
2762
+ }
2763
+ constructor(message, location) {
2764
+ this.message = message;
2765
+ this.location = location;
2766
+ }
2790
2767
  }
2791
2768
 
2792
2769
  /**
@@ -2856,23 +2833,1017 @@ class ParseResult extends Result {
2856
2833
  }
2857
2834
  }
2858
2835
 
2836
+ // NOTE: This file is generated by the templates/template.rb script and should not
2837
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.6.1/templates/javascript/packages/core/src/node-type-guards.ts.erb
2859
2838
  /**
2860
- * The main Herb parser interface, providing methods to lex and parse input.
2839
+ * Type guard functions for AST nodes.
2840
+ * These functions provide type checking by combining both instanceof
2841
+ * checks and type string comparisons for maximum reliability across different
2842
+ * runtime scenarios (e.g., serialized/deserialized nodes).
2861
2843
  */
2862
- class HerbBackend {
2863
- /** The backend instance handling lexing and parsing. */
2864
- backend = undefined;
2865
- backendPromise;
2866
- /**
2867
- * Creates a new Herb instance.
2868
- * @param backendPromise - A promise resolving to a `LibHerbBackend` implementation for lexing and parsing.
2869
- * @throws Error if no valid backend is provided.
2870
- */
2871
- constructor(backendPromise) {
2872
- if (!backendPromise) {
2873
- throw new Error("No LibHerb backend provided");
2874
- }
2875
- this.backendPromise = backendPromise;
2844
+ /**
2845
+ * Checks if a node is a DocumentNode
2846
+ */
2847
+ function isDocumentNode(node) {
2848
+ return node instanceof DocumentNode || node.type === "AST_DOCUMENT_NODE" || node.constructor.type === "AST_DOCUMENT_NODE";
2849
+ }
2850
+ /**
2851
+ * Checks if a node is a LiteralNode
2852
+ */
2853
+ function isLiteralNode(node) {
2854
+ return node instanceof LiteralNode || node.type === "AST_LITERAL_NODE" || node.constructor.type === "AST_LITERAL_NODE";
2855
+ }
2856
+ /**
2857
+ * Checks if a node is a HTMLOpenTagNode
2858
+ */
2859
+ function isHTMLOpenTagNode(node) {
2860
+ return node instanceof HTMLOpenTagNode || node.type === "AST_HTML_OPEN_TAG_NODE" || node.constructor.type === "AST_HTML_OPEN_TAG_NODE";
2861
+ }
2862
+ /**
2863
+ * Checks if a node is a HTMLCloseTagNode
2864
+ */
2865
+ function isHTMLCloseTagNode(node) {
2866
+ return node instanceof HTMLCloseTagNode || node.type === "AST_HTML_CLOSE_TAG_NODE" || node.constructor.type === "AST_HTML_CLOSE_TAG_NODE";
2867
+ }
2868
+ /**
2869
+ * Checks if a node is a HTMLElementNode
2870
+ */
2871
+ function isHTMLElementNode(node) {
2872
+ return node instanceof HTMLElementNode || node.type === "AST_HTML_ELEMENT_NODE" || node.constructor.type === "AST_HTML_ELEMENT_NODE";
2873
+ }
2874
+ /**
2875
+ * Checks if a node is a HTMLAttributeValueNode
2876
+ */
2877
+ function isHTMLAttributeValueNode(node) {
2878
+ return node instanceof HTMLAttributeValueNode || node.type === "AST_HTML_ATTRIBUTE_VALUE_NODE" || node.constructor.type === "AST_HTML_ATTRIBUTE_VALUE_NODE";
2879
+ }
2880
+ /**
2881
+ * Checks if a node is a HTMLAttributeNameNode
2882
+ */
2883
+ function isHTMLAttributeNameNode(node) {
2884
+ return node instanceof HTMLAttributeNameNode || node.type === "AST_HTML_ATTRIBUTE_NAME_NODE" || node.constructor.type === "AST_HTML_ATTRIBUTE_NAME_NODE";
2885
+ }
2886
+ /**
2887
+ * Checks if a node is a HTMLAttributeNode
2888
+ */
2889
+ function isHTMLAttributeNode(node) {
2890
+ return node instanceof HTMLAttributeNode || node.type === "AST_HTML_ATTRIBUTE_NODE" || node.constructor.type === "AST_HTML_ATTRIBUTE_NODE";
2891
+ }
2892
+ /**
2893
+ * Checks if a node is a HTMLTextNode
2894
+ */
2895
+ function isHTMLTextNode(node) {
2896
+ return node instanceof HTMLTextNode || node.type === "AST_HTML_TEXT_NODE" || node.constructor.type === "AST_HTML_TEXT_NODE";
2897
+ }
2898
+ /**
2899
+ * Checks if a node is a HTMLCommentNode
2900
+ */
2901
+ function isHTMLCommentNode(node) {
2902
+ return node instanceof HTMLCommentNode || node.type === "AST_HTML_COMMENT_NODE" || node.constructor.type === "AST_HTML_COMMENT_NODE";
2903
+ }
2904
+ /**
2905
+ * Checks if a node is a HTMLDoctypeNode
2906
+ */
2907
+ function isHTMLDoctypeNode(node) {
2908
+ return node instanceof HTMLDoctypeNode || node.type === "AST_HTML_DOCTYPE_NODE" || node.constructor.type === "AST_HTML_DOCTYPE_NODE";
2909
+ }
2910
+ /**
2911
+ * Checks if a node is a XMLDeclarationNode
2912
+ */
2913
+ function isXMLDeclarationNode(node) {
2914
+ return node instanceof XMLDeclarationNode || node.type === "AST_XML_DECLARATION_NODE" || node.constructor.type === "AST_XML_DECLARATION_NODE";
2915
+ }
2916
+ /**
2917
+ * Checks if a node is a CDATANode
2918
+ */
2919
+ function isCDATANode(node) {
2920
+ return node instanceof CDATANode || node.type === "AST_CDATA_NODE" || node.constructor.type === "AST_CDATA_NODE";
2921
+ }
2922
+ /**
2923
+ * Checks if a node is a WhitespaceNode
2924
+ */
2925
+ function isWhitespaceNode(node) {
2926
+ return node instanceof WhitespaceNode || node.type === "AST_WHITESPACE_NODE" || node.constructor.type === "AST_WHITESPACE_NODE";
2927
+ }
2928
+ /**
2929
+ * Checks if a node is a ERBContentNode
2930
+ */
2931
+ function isERBContentNode(node) {
2932
+ return node instanceof ERBContentNode || node.type === "AST_ERB_CONTENT_NODE" || node.constructor.type === "AST_ERB_CONTENT_NODE";
2933
+ }
2934
+ /**
2935
+ * Checks if a node is a ERBEndNode
2936
+ */
2937
+ function isERBEndNode(node) {
2938
+ return node instanceof ERBEndNode || node.type === "AST_ERB_END_NODE" || node.constructor.type === "AST_ERB_END_NODE";
2939
+ }
2940
+ /**
2941
+ * Checks if a node is a ERBElseNode
2942
+ */
2943
+ function isERBElseNode(node) {
2944
+ return node instanceof ERBElseNode || node.type === "AST_ERB_ELSE_NODE" || node.constructor.type === "AST_ERB_ELSE_NODE";
2945
+ }
2946
+ /**
2947
+ * Checks if a node is a ERBIfNode
2948
+ */
2949
+ function isERBIfNode(node) {
2950
+ return node instanceof ERBIfNode || node.type === "AST_ERB_IF_NODE" || node.constructor.type === "AST_ERB_IF_NODE";
2951
+ }
2952
+ /**
2953
+ * Checks if a node is a ERBBlockNode
2954
+ */
2955
+ function isERBBlockNode(node) {
2956
+ return node instanceof ERBBlockNode || node.type === "AST_ERB_BLOCK_NODE" || node.constructor.type === "AST_ERB_BLOCK_NODE";
2957
+ }
2958
+ /**
2959
+ * Checks if a node is a ERBWhenNode
2960
+ */
2961
+ function isERBWhenNode(node) {
2962
+ return node instanceof ERBWhenNode || node.type === "AST_ERB_WHEN_NODE" || node.constructor.type === "AST_ERB_WHEN_NODE";
2963
+ }
2964
+ /**
2965
+ * Checks if a node is a ERBCaseNode
2966
+ */
2967
+ function isERBCaseNode(node) {
2968
+ return node instanceof ERBCaseNode || node.type === "AST_ERB_CASE_NODE" || node.constructor.type === "AST_ERB_CASE_NODE";
2969
+ }
2970
+ /**
2971
+ * Checks if a node is a ERBCaseMatchNode
2972
+ */
2973
+ function isERBCaseMatchNode(node) {
2974
+ return node instanceof ERBCaseMatchNode || node.type === "AST_ERB_CASE_MATCH_NODE" || node.constructor.type === "AST_ERB_CASE_MATCH_NODE";
2975
+ }
2976
+ /**
2977
+ * Checks if a node is a ERBWhileNode
2978
+ */
2979
+ function isERBWhileNode(node) {
2980
+ return node instanceof ERBWhileNode || node.type === "AST_ERB_WHILE_NODE" || node.constructor.type === "AST_ERB_WHILE_NODE";
2981
+ }
2982
+ /**
2983
+ * Checks if a node is a ERBUntilNode
2984
+ */
2985
+ function isERBUntilNode(node) {
2986
+ return node instanceof ERBUntilNode || node.type === "AST_ERB_UNTIL_NODE" || node.constructor.type === "AST_ERB_UNTIL_NODE";
2987
+ }
2988
+ /**
2989
+ * Checks if a node is a ERBForNode
2990
+ */
2991
+ function isERBForNode(node) {
2992
+ return node instanceof ERBForNode || node.type === "AST_ERB_FOR_NODE" || node.constructor.type === "AST_ERB_FOR_NODE";
2993
+ }
2994
+ /**
2995
+ * Checks if a node is a ERBRescueNode
2996
+ */
2997
+ function isERBRescueNode(node) {
2998
+ return node instanceof ERBRescueNode || node.type === "AST_ERB_RESCUE_NODE" || node.constructor.type === "AST_ERB_RESCUE_NODE";
2999
+ }
3000
+ /**
3001
+ * Checks if a node is a ERBEnsureNode
3002
+ */
3003
+ function isERBEnsureNode(node) {
3004
+ return node instanceof ERBEnsureNode || node.type === "AST_ERB_ENSURE_NODE" || node.constructor.type === "AST_ERB_ENSURE_NODE";
3005
+ }
3006
+ /**
3007
+ * Checks if a node is a ERBBeginNode
3008
+ */
3009
+ function isERBBeginNode(node) {
3010
+ return node instanceof ERBBeginNode || node.type === "AST_ERB_BEGIN_NODE" || node.constructor.type === "AST_ERB_BEGIN_NODE";
3011
+ }
3012
+ /**
3013
+ * Checks if a node is a ERBUnlessNode
3014
+ */
3015
+ function isERBUnlessNode(node) {
3016
+ return node instanceof ERBUnlessNode || node.type === "AST_ERB_UNLESS_NODE" || node.constructor.type === "AST_ERB_UNLESS_NODE";
3017
+ }
3018
+ /**
3019
+ * Checks if a node is a ERBYieldNode
3020
+ */
3021
+ function isERBYieldNode(node) {
3022
+ return node instanceof ERBYieldNode || node.type === "AST_ERB_YIELD_NODE" || node.constructor.type === "AST_ERB_YIELD_NODE";
3023
+ }
3024
+ /**
3025
+ * Checks if a node is a ERBInNode
3026
+ */
3027
+ function isERBInNode(node) {
3028
+ return node instanceof ERBInNode || node.type === "AST_ERB_IN_NODE" || node.constructor.type === "AST_ERB_IN_NODE";
3029
+ }
3030
+ /**
3031
+ * Convenience type guards for common node categories
3032
+ */
3033
+ /**
3034
+ * Checks if a node is any HTML node type
3035
+ */
3036
+ function isHTMLNode(node) {
3037
+ return isHTMLOpenTagNode(node) ||
3038
+ isHTMLCloseTagNode(node) ||
3039
+ isHTMLElementNode(node) ||
3040
+ isHTMLAttributeValueNode(node) ||
3041
+ isHTMLAttributeNameNode(node) ||
3042
+ isHTMLAttributeNode(node) ||
3043
+ isHTMLTextNode(node) ||
3044
+ isHTMLCommentNode(node) ||
3045
+ isHTMLDoctypeNode(node);
3046
+ }
3047
+ /**
3048
+ * Checks if a node is any ERB node type
3049
+ */
3050
+ function isERBNode(node) {
3051
+ return isERBContentNode(node) ||
3052
+ isERBEndNode(node) ||
3053
+ isERBElseNode(node) ||
3054
+ isERBIfNode(node) ||
3055
+ isERBBlockNode(node) ||
3056
+ isERBWhenNode(node) ||
3057
+ isERBCaseNode(node) ||
3058
+ isERBCaseMatchNode(node) ||
3059
+ isERBWhileNode(node) ||
3060
+ isERBUntilNode(node) ||
3061
+ isERBForNode(node) ||
3062
+ isERBRescueNode(node) ||
3063
+ isERBEnsureNode(node) ||
3064
+ isERBBeginNode(node) ||
3065
+ isERBUnlessNode(node) ||
3066
+ isERBYieldNode(node) ||
3067
+ isERBInNode(node);
3068
+ }
3069
+ /**
3070
+ * Map of node classes to their corresponding type guard functions
3071
+ *
3072
+ * @example
3073
+ * const guard = NODE_TYPE_GUARDS[HTMLTextNode]
3074
+ *
3075
+ * if (guard(node)) {
3076
+ * // node is HTMLTextNode
3077
+ * }
3078
+ */
3079
+ const NODE_TYPE_GUARDS = new Map([
3080
+ [DocumentNode, isDocumentNode],
3081
+ [LiteralNode, isLiteralNode],
3082
+ [HTMLOpenTagNode, isHTMLOpenTagNode],
3083
+ [HTMLCloseTagNode, isHTMLCloseTagNode],
3084
+ [HTMLElementNode, isHTMLElementNode],
3085
+ [HTMLAttributeValueNode, isHTMLAttributeValueNode],
3086
+ [HTMLAttributeNameNode, isHTMLAttributeNameNode],
3087
+ [HTMLAttributeNode, isHTMLAttributeNode],
3088
+ [HTMLTextNode, isHTMLTextNode],
3089
+ [HTMLCommentNode, isHTMLCommentNode],
3090
+ [HTMLDoctypeNode, isHTMLDoctypeNode],
3091
+ [XMLDeclarationNode, isXMLDeclarationNode],
3092
+ [CDATANode, isCDATANode],
3093
+ [WhitespaceNode, isWhitespaceNode],
3094
+ [ERBContentNode, isERBContentNode],
3095
+ [ERBEndNode, isERBEndNode],
3096
+ [ERBElseNode, isERBElseNode],
3097
+ [ERBIfNode, isERBIfNode],
3098
+ [ERBBlockNode, isERBBlockNode],
3099
+ [ERBWhenNode, isERBWhenNode],
3100
+ [ERBCaseNode, isERBCaseNode],
3101
+ [ERBCaseMatchNode, isERBCaseMatchNode],
3102
+ [ERBWhileNode, isERBWhileNode],
3103
+ [ERBUntilNode, isERBUntilNode],
3104
+ [ERBForNode, isERBForNode],
3105
+ [ERBRescueNode, isERBRescueNode],
3106
+ [ERBEnsureNode, isERBEnsureNode],
3107
+ [ERBBeginNode, isERBBeginNode],
3108
+ [ERBUnlessNode, isERBUnlessNode],
3109
+ [ERBYieldNode, isERBYieldNode],
3110
+ [ERBInNode, isERBInNode],
3111
+ ]);
3112
+ /**
3113
+ * Map of AST node type strings to their corresponding type guard functions
3114
+ *
3115
+ * @example
3116
+ * const guard = AST_TYPE_GUARDS["AST_HTML_TEXT_NODE"]
3117
+ *
3118
+ * if (guard(node)) {
3119
+ * // node is HTMLTextNode
3120
+ * }
3121
+ */
3122
+ const AST_TYPE_GUARDS = new Map([
3123
+ ["AST_DOCUMENT_NODE", isDocumentNode],
3124
+ ["AST_LITERAL_NODE", isLiteralNode],
3125
+ ["AST_HTML_OPEN_TAG_NODE", isHTMLOpenTagNode],
3126
+ ["AST_HTML_CLOSE_TAG_NODE", isHTMLCloseTagNode],
3127
+ ["AST_HTML_ELEMENT_NODE", isHTMLElementNode],
3128
+ ["AST_HTML_ATTRIBUTE_VALUE_NODE", isHTMLAttributeValueNode],
3129
+ ["AST_HTML_ATTRIBUTE_NAME_NODE", isHTMLAttributeNameNode],
3130
+ ["AST_HTML_ATTRIBUTE_NODE", isHTMLAttributeNode],
3131
+ ["AST_HTML_TEXT_NODE", isHTMLTextNode],
3132
+ ["AST_HTML_COMMENT_NODE", isHTMLCommentNode],
3133
+ ["AST_HTML_DOCTYPE_NODE", isHTMLDoctypeNode],
3134
+ ["AST_XML_DECLARATION_NODE", isXMLDeclarationNode],
3135
+ ["AST_CDATA_NODE", isCDATANode],
3136
+ ["AST_WHITESPACE_NODE", isWhitespaceNode],
3137
+ ["AST_ERB_CONTENT_NODE", isERBContentNode],
3138
+ ["AST_ERB_END_NODE", isERBEndNode],
3139
+ ["AST_ERB_ELSE_NODE", isERBElseNode],
3140
+ ["AST_ERB_IF_NODE", isERBIfNode],
3141
+ ["AST_ERB_BLOCK_NODE", isERBBlockNode],
3142
+ ["AST_ERB_WHEN_NODE", isERBWhenNode],
3143
+ ["AST_ERB_CASE_NODE", isERBCaseNode],
3144
+ ["AST_ERB_CASE_MATCH_NODE", isERBCaseMatchNode],
3145
+ ["AST_ERB_WHILE_NODE", isERBWhileNode],
3146
+ ["AST_ERB_UNTIL_NODE", isERBUntilNode],
3147
+ ["AST_ERB_FOR_NODE", isERBForNode],
3148
+ ["AST_ERB_RESCUE_NODE", isERBRescueNode],
3149
+ ["AST_ERB_ENSURE_NODE", isERBEnsureNode],
3150
+ ["AST_ERB_BEGIN_NODE", isERBBeginNode],
3151
+ ["AST_ERB_UNLESS_NODE", isERBUnlessNode],
3152
+ ["AST_ERB_YIELD_NODE", isERBYieldNode],
3153
+ ["AST_ERB_IN_NODE", isERBInNode],
3154
+ ]);
3155
+ /**
3156
+ * Checks if a node matches any of the provided type identifiers with proper type narrowing
3157
+ * Supports AST type strings, node classes, or type guard functions
3158
+ *
3159
+ * @example
3160
+ * if (isAnyOf(node, "AST_HTML_TEXT_NODE", "AST_LITERAL_NODE")) {
3161
+ * // node is narrowed to HTMLTextNode | LiteralNode
3162
+ * }
3163
+ *
3164
+ * @example
3165
+ * if (isAnyOf(node, HTMLTextNode, LiteralNode)) {
3166
+ * // node is narrowed to HTMLTextNode | LiteralNode
3167
+ * }
3168
+ */
3169
+ function isAnyOf(node, ...types) {
3170
+ return types.some(type => {
3171
+ if (typeof type === 'string') {
3172
+ return isNode(node, type);
3173
+ }
3174
+ else if (typeof type === 'function' && type.prototype && type.prototype.constructor === type && NODE_TYPE_GUARDS.has(type)) {
3175
+ return isNode(node, type);
3176
+ }
3177
+ else if (typeof type === 'function') {
3178
+ return type(node);
3179
+ }
3180
+ else {
3181
+ return false;
3182
+ }
3183
+ });
3184
+ }
3185
+ /**
3186
+ * Checks if a node does NOT match any of the provided type identifiers
3187
+ * Supports AST type strings, node classes, or type guard functions
3188
+ * This is the logical inverse of isAnyOf
3189
+ *
3190
+ * @example
3191
+ * if (isNoneOf(node, "AST_HTML_TEXT_NODE", "AST_LITERAL_NODE")) {
3192
+ * // node is neither HTMLTextNode nor LiteralNode
3193
+ * }
3194
+ *
3195
+ * @example
3196
+ * if (isNoneOf(node, HTMLTextNode, LiteralNode)) {
3197
+ * // node is neither HTMLTextNode nor LiteralNode
3198
+ * }
3199
+ *
3200
+ * @example
3201
+ * if (isNoneOf(node, isHTMLTextNode, isLiteralNode)) {
3202
+ * // node is neither HTMLTextNode nor LiteralNode
3203
+ * }
3204
+ */
3205
+ function isNoneOf(node, ...types) {
3206
+ return !isAnyOf(node, ...types);
3207
+ }
3208
+ function areAllOfType(nodes, ...types) {
3209
+ return nodes.every(node => isAnyOf(node, ...types));
3210
+ }
3211
+ function filterNodes(nodes, ...types) {
3212
+ if (!nodes)
3213
+ return [];
3214
+ return nodes.filter(node => isAnyOf(node, ...types));
3215
+ }
3216
+ function isNode(node, type) {
3217
+ if (!node)
3218
+ return false;
3219
+ if (typeof type === 'string') {
3220
+ const guard = AST_TYPE_GUARDS.get(type);
3221
+ return guard ? guard(node) : false;
3222
+ }
3223
+ else if (typeof type === 'function') {
3224
+ const guard = NODE_TYPE_GUARDS.get(type);
3225
+ return guard ? guard(node) : false;
3226
+ }
3227
+ else {
3228
+ return false;
3229
+ }
3230
+ }
3231
+ function isToken(object) {
3232
+ return (object instanceof Token) || (object?.constructor?.name === "Token" && "value" in object) || object.type?.startsWith('TOKEN_');
3233
+ }
3234
+ function isParseResult(object) {
3235
+ return (object instanceof ParseResult) || (object?.constructor?.name === "ParseResult" && "value" in object);
3236
+ }
3237
+ /**
3238
+ * Checks if a node has children (contains other nodes)
3239
+ */
3240
+ function hasChildren(node) {
3241
+ return isDocumentNode(node) ||
3242
+ isHTMLOpenTagNode(node) ||
3243
+ isHTMLCloseTagNode(node) ||
3244
+ isHTMLElementNode(node) ||
3245
+ isHTMLAttributeValueNode(node) ||
3246
+ isHTMLAttributeNameNode(node) ||
3247
+ isHTMLCommentNode(node) ||
3248
+ isHTMLDoctypeNode(node) ||
3249
+ isERBElseNode(node) ||
3250
+ isERBIfNode(node) ||
3251
+ isERBBlockNode(node) ||
3252
+ isERBWhenNode(node) ||
3253
+ isERBCaseNode(node) ||
3254
+ isERBCaseMatchNode(node) ||
3255
+ isERBWhileNode(node) ||
3256
+ isERBUntilNode(node) ||
3257
+ isERBForNode(node) ||
3258
+ isERBRescueNode(node) ||
3259
+ isERBEnsureNode(node) ||
3260
+ isERBBeginNode(node) ||
3261
+ isERBUnlessNode(node) ||
3262
+ isERBInNode(node);
3263
+ }
3264
+ /**
3265
+ * Filter functions for extracting specific node types from arrays
3266
+ */
3267
+ /**
3268
+ * Filters an array of nodes to only include DocumentNode nodes
3269
+ */
3270
+ function filterDocumentNodes(nodes) {
3271
+ return nodes.filter(isDocumentNode);
3272
+ }
3273
+ /**
3274
+ * Filters an array of nodes to only include LiteralNode nodes
3275
+ */
3276
+ function filterLiteralNodes(nodes) {
3277
+ return nodes.filter(isLiteralNode);
3278
+ }
3279
+ /**
3280
+ * Filters an array of nodes to only include HTMLOpenTagNode nodes
3281
+ */
3282
+ function filterHTMLOpenTagNodes(nodes) {
3283
+ return nodes.filter(isHTMLOpenTagNode);
3284
+ }
3285
+ /**
3286
+ * Filters an array of nodes to only include HTMLCloseTagNode nodes
3287
+ */
3288
+ function filterHTMLCloseTagNodes(nodes) {
3289
+ return nodes.filter(isHTMLCloseTagNode);
3290
+ }
3291
+ /**
3292
+ * Filters an array of nodes to only include HTMLElementNode nodes
3293
+ */
3294
+ function filterHTMLElementNodes(nodes) {
3295
+ return nodes.filter(isHTMLElementNode);
3296
+ }
3297
+ /**
3298
+ * Filters an array of nodes to only include HTMLAttributeValueNode nodes
3299
+ */
3300
+ function filterHTMLAttributeValueNodes(nodes) {
3301
+ return nodes.filter(isHTMLAttributeValueNode);
3302
+ }
3303
+ /**
3304
+ * Filters an array of nodes to only include HTMLAttributeNameNode nodes
3305
+ */
3306
+ function filterHTMLAttributeNameNodes(nodes) {
3307
+ return nodes.filter(isHTMLAttributeNameNode);
3308
+ }
3309
+ /**
3310
+ * Filters an array of nodes to only include HTMLAttributeNode nodes
3311
+ */
3312
+ function filterHTMLAttributeNodes(nodes) {
3313
+ return nodes.filter(isHTMLAttributeNode);
3314
+ }
3315
+ /**
3316
+ * Filters an array of nodes to only include HTMLTextNode nodes
3317
+ */
3318
+ function filterHTMLTextNodes(nodes) {
3319
+ return nodes.filter(isHTMLTextNode);
3320
+ }
3321
+ /**
3322
+ * Filters an array of nodes to only include HTMLCommentNode nodes
3323
+ */
3324
+ function filterHTMLCommentNodes(nodes) {
3325
+ return nodes.filter(isHTMLCommentNode);
3326
+ }
3327
+ /**
3328
+ * Filters an array of nodes to only include HTMLDoctypeNode nodes
3329
+ */
3330
+ function filterHTMLDoctypeNodes(nodes) {
3331
+ return nodes.filter(isHTMLDoctypeNode);
3332
+ }
3333
+ /**
3334
+ * Filters an array of nodes to only include XMLDeclarationNode nodes
3335
+ */
3336
+ function filterXMLDeclarationNodes(nodes) {
3337
+ return nodes.filter(isXMLDeclarationNode);
3338
+ }
3339
+ /**
3340
+ * Filters an array of nodes to only include CDATANode nodes
3341
+ */
3342
+ function filterCDATANodes(nodes) {
3343
+ return nodes.filter(isCDATANode);
3344
+ }
3345
+ /**
3346
+ * Filters an array of nodes to only include WhitespaceNode nodes
3347
+ */
3348
+ function filterWhitespaceNodes(nodes) {
3349
+ return nodes.filter(isWhitespaceNode);
3350
+ }
3351
+ /**
3352
+ * Filters an array of nodes to only include ERBContentNode nodes
3353
+ */
3354
+ function filterERBContentNodes(nodes) {
3355
+ return nodes.filter(isERBContentNode);
3356
+ }
3357
+ /**
3358
+ * Filters an array of nodes to only include ERBEndNode nodes
3359
+ */
3360
+ function filterERBEndNodes(nodes) {
3361
+ return nodes.filter(isERBEndNode);
3362
+ }
3363
+ /**
3364
+ * Filters an array of nodes to only include ERBElseNode nodes
3365
+ */
3366
+ function filterERBElseNodes(nodes) {
3367
+ return nodes.filter(isERBElseNode);
3368
+ }
3369
+ /**
3370
+ * Filters an array of nodes to only include ERBIfNode nodes
3371
+ */
3372
+ function filterERBIfNodes(nodes) {
3373
+ return nodes.filter(isERBIfNode);
3374
+ }
3375
+ /**
3376
+ * Filters an array of nodes to only include ERBBlockNode nodes
3377
+ */
3378
+ function filterERBBlockNodes(nodes) {
3379
+ return nodes.filter(isERBBlockNode);
3380
+ }
3381
+ /**
3382
+ * Filters an array of nodes to only include ERBWhenNode nodes
3383
+ */
3384
+ function filterERBWhenNodes(nodes) {
3385
+ return nodes.filter(isERBWhenNode);
3386
+ }
3387
+ /**
3388
+ * Filters an array of nodes to only include ERBCaseNode nodes
3389
+ */
3390
+ function filterERBCaseNodes(nodes) {
3391
+ return nodes.filter(isERBCaseNode);
3392
+ }
3393
+ /**
3394
+ * Filters an array of nodes to only include ERBCaseMatchNode nodes
3395
+ */
3396
+ function filterERBCaseMatchNodes(nodes) {
3397
+ return nodes.filter(isERBCaseMatchNode);
3398
+ }
3399
+ /**
3400
+ * Filters an array of nodes to only include ERBWhileNode nodes
3401
+ */
3402
+ function filterERBWhileNodes(nodes) {
3403
+ return nodes.filter(isERBWhileNode);
3404
+ }
3405
+ /**
3406
+ * Filters an array of nodes to only include ERBUntilNode nodes
3407
+ */
3408
+ function filterERBUntilNodes(nodes) {
3409
+ return nodes.filter(isERBUntilNode);
3410
+ }
3411
+ /**
3412
+ * Filters an array of nodes to only include ERBForNode nodes
3413
+ */
3414
+ function filterERBForNodes(nodes) {
3415
+ return nodes.filter(isERBForNode);
3416
+ }
3417
+ /**
3418
+ * Filters an array of nodes to only include ERBRescueNode nodes
3419
+ */
3420
+ function filterERBRescueNodes(nodes) {
3421
+ return nodes.filter(isERBRescueNode);
3422
+ }
3423
+ /**
3424
+ * Filters an array of nodes to only include ERBEnsureNode nodes
3425
+ */
3426
+ function filterERBEnsureNodes(nodes) {
3427
+ return nodes.filter(isERBEnsureNode);
3428
+ }
3429
+ /**
3430
+ * Filters an array of nodes to only include ERBBeginNode nodes
3431
+ */
3432
+ function filterERBBeginNodes(nodes) {
3433
+ return nodes.filter(isERBBeginNode);
3434
+ }
3435
+ /**
3436
+ * Filters an array of nodes to only include ERBUnlessNode nodes
3437
+ */
3438
+ function filterERBUnlessNodes(nodes) {
3439
+ return nodes.filter(isERBUnlessNode);
3440
+ }
3441
+ /**
3442
+ * Filters an array of nodes to only include ERBYieldNode nodes
3443
+ */
3444
+ function filterERBYieldNodes(nodes) {
3445
+ return nodes.filter(isERBYieldNode);
3446
+ }
3447
+ /**
3448
+ * Filters an array of nodes to only include ERBInNode nodes
3449
+ */
3450
+ function filterERBInNodes(nodes) {
3451
+ return nodes.filter(isERBInNode);
3452
+ }
3453
+
3454
+ /**
3455
+ * Checks if a node is an ERB output node (generates content: <%= %> or <%== %>)
3456
+ */
3457
+ function isERBOutputNode(node) {
3458
+ return isNode(node, ERBContentNode) && ["<%=", "<%=="].includes(node.tag_opening?.value);
3459
+ }
3460
+ /**
3461
+ * Checks if a node is a non-output ERB node (control flow: <% %>)
3462
+ */
3463
+ function isERBControlFlowNode(node) {
3464
+ return isAnyOf(node, ERBIfNode, ERBUnlessNode, ERBBlockNode, ERBCaseNode, ERBCaseMatchNode, ERBWhileNode, ERBForNode, ERBBeginNode);
3465
+ }
3466
+ /**
3467
+ * Checks if an array of nodes contains any ERB content nodes
3468
+ */
3469
+ function hasERBContent(nodes) {
3470
+ return nodes.some(isERBContentNode);
3471
+ }
3472
+ /**
3473
+ * Checks if an array of nodes contains any ERB output nodes (dynamic content)
3474
+ */
3475
+ function hasERBOutput(nodes) {
3476
+ return nodes.some(isERBOutputNode);
3477
+ }
3478
+ /**
3479
+ * Extracts a static string from an array of literal nodes
3480
+ * Returns null if any node is not a literal node
3481
+ */
3482
+ function getStaticStringFromNodes(nodes) {
3483
+ if (!areAllOfType(nodes, LiteralNode)) {
3484
+ return null;
3485
+ }
3486
+ return nodes.map(node => node.content).join("");
3487
+ }
3488
+ /**
3489
+ * Extracts static content from nodes, including mixed literal/ERB content
3490
+ * Returns the concatenated literal content, or null if no literal nodes exist
3491
+ */
3492
+ function getStaticContentFromNodes(nodes) {
3493
+ const literalNodes = filterLiteralNodes(nodes);
3494
+ if (literalNodes.length === 0) {
3495
+ return null;
3496
+ }
3497
+ return literalNodes.map(node => node.content).join("");
3498
+ }
3499
+ /**
3500
+ * Checks if nodes contain any literal content (for static validation)
3501
+ */
3502
+ function hasStaticContent(nodes) {
3503
+ return nodes.some(isLiteralNode);
3504
+ }
3505
+ /**
3506
+ * Checks if nodes are effectively static (only literals and non-output ERB)
3507
+ * Non-output ERB like <% if %> doesn't affect static validation
3508
+ */
3509
+ function isEffectivelyStatic(nodes) {
3510
+ return !hasERBOutput(nodes);
3511
+ }
3512
+ /**
3513
+ * Gets static-validatable content from nodes (ignores control ERB, includes literals)
3514
+ * Returns concatenated literal content for validation, or null if contains output ERB
3515
+ */
3516
+ function getValidatableStaticContent(nodes) {
3517
+ if (hasERBOutput(nodes)) {
3518
+ return null;
3519
+ }
3520
+ return filterLiteralNodes(nodes).map(node => node.content).join("");
3521
+ }
3522
+ /**
3523
+ * Extracts a combined string from nodes, including ERB content
3524
+ * For ERB nodes, includes the full tag syntax (e.g., "<%= foo %>")
3525
+ * This is useful for debugging or displaying the full attribute name
3526
+ */
3527
+ function getCombinedStringFromNodes(nodes) {
3528
+ return nodes.map(node => {
3529
+ if (isLiteralNode(node)) {
3530
+ return node.content;
3531
+ }
3532
+ else if (isERBContentNode(node)) {
3533
+ const opening = node.tag_opening?.value || "";
3534
+ const content = node.content?.value || "";
3535
+ const closing = node.tag_closing?.value || "";
3536
+ return `${opening}${content}${closing}`;
3537
+ }
3538
+ else {
3539
+ // For other node types, return a placeholder or empty string
3540
+ return `[${node.type}]`;
3541
+ }
3542
+ }).join("");
3543
+ }
3544
+ /**
3545
+ * Checks if an HTML attribute name node has a static (literal-only) name
3546
+ */
3547
+ function hasStaticAttributeName(attributeNameNode) {
3548
+ if (!attributeNameNode.children) {
3549
+ return false;
3550
+ }
3551
+ return areAllOfType(attributeNameNode.children, LiteralNode);
3552
+ }
3553
+ /**
3554
+ * Checks if an HTML attribute name node has dynamic content (contains ERB)
3555
+ */
3556
+ function hasDynamicAttributeName(attributeNameNode) {
3557
+ if (!attributeNameNode.children) {
3558
+ return false;
3559
+ }
3560
+ return hasERBContent(attributeNameNode.children);
3561
+ }
3562
+ /**
3563
+ * Gets the static string value of an HTML attribute name node
3564
+ * Returns null if the attribute name contains dynamic content (ERB)
3565
+ */
3566
+ function getStaticAttributeName(attributeNameNode) {
3567
+ if (!attributeNameNode.children) {
3568
+ return null;
3569
+ }
3570
+ return getStaticStringFromNodes(attributeNameNode.children);
3571
+ }
3572
+ /**
3573
+ * Gets the combined string representation of an HTML attribute name node
3574
+ * This includes both static and dynamic content, useful for debugging
3575
+ */
3576
+ function getCombinedAttributeName(attributeNameNode) {
3577
+ if (!attributeNameNode.children) {
3578
+ return "";
3579
+ }
3580
+ return getCombinedStringFromNodes(attributeNameNode.children);
3581
+ }
3582
+ /**
3583
+ * Gets the tag name of an HTML element node
3584
+ */
3585
+ function getTagName(node) {
3586
+ return node.tag_name?.value ?? "";
3587
+ }
3588
+ /**
3589
+ * Check if a node is a comment (HTML comment or ERB comment)
3590
+ */
3591
+ function isCommentNode(node) {
3592
+ return isNode(node, HTMLCommentNode) || (isERBNode(node) && !isERBControlFlowNode(node));
3593
+ }
3594
+ /**
3595
+ * Compares two positions to determine if the first comes before the second
3596
+ * Returns true if pos1 comes before pos2 in source order
3597
+ * @param inclusive - If true, returns true when positions are equal
3598
+ */
3599
+ function isPositionBefore(position1, position2, inclusive = false) {
3600
+ if (position1.line < position2.line)
3601
+ return true;
3602
+ if (position1.line > position2.line)
3603
+ return false;
3604
+ return inclusive ? position1.column <= position2.column : position1.column < position2.column;
3605
+ }
3606
+ /**
3607
+ * Compares two positions to determine if they are equal
3608
+ * Returns true if pos1 and pos2 are at the same location
3609
+ */
3610
+ function isPositionEqual(position1, position2) {
3611
+ return position1.line === position2.line && position1.column === position2.column;
3612
+ }
3613
+ /**
3614
+ * Compares two positions to determine if the first comes after the second
3615
+ * Returns true if pos1 comes after pos2 in source order
3616
+ * @param inclusive - If true, returns true when positions are equal
3617
+ */
3618
+ function isPositionAfter(position1, position2, inclusive = false) {
3619
+ if (position1.line > position2.line)
3620
+ return true;
3621
+ if (position1.line < position2.line)
3622
+ return false;
3623
+ return inclusive ? position1.column >= position2.column : position1.column > position2.column;
3624
+ }
3625
+ /**
3626
+ * Gets nodes that appear before the specified location in source order
3627
+ * Uses line and column positions to determine ordering
3628
+ */
3629
+ function getNodesBeforeLocation(nodes, location) {
3630
+ return nodes.filter(node => node.location && isPositionBefore(node.location.end, location.start));
3631
+ }
3632
+ /**
3633
+ * Gets nodes that appear after the specified location in source order
3634
+ * Uses line and column positions to determine ordering
3635
+ */
3636
+ function getNodesAfterLocation(nodes, location) {
3637
+ return nodes.filter(node => node.location && isPositionAfter(node.location.start, location.end));
3638
+ }
3639
+ /**
3640
+ * Splits nodes into before and after the specified location
3641
+ * Returns an object with `before` and `after` arrays
3642
+ */
3643
+ function splitNodesAroundLocation(nodes, location) {
3644
+ return {
3645
+ before: getNodesBeforeLocation(nodes, location),
3646
+ after: getNodesAfterLocation(nodes, location)
3647
+ };
3648
+ }
3649
+ /**
3650
+ * Splits nodes at a specific position
3651
+ * Returns nodes that end before the position and nodes that start after the position
3652
+ * More precise than splitNodesAroundLocation as it uses a single position point
3653
+ * Uses the same defaults as the individual functions: before=exclusive, after=inclusive
3654
+ */
3655
+ function splitNodesAroundPosition(nodes, position) {
3656
+ return {
3657
+ before: getNodesBeforePosition(nodes, position), // uses default: inclusive = false
3658
+ after: getNodesAfterPosition(nodes, position) // uses default: inclusive = true
3659
+ };
3660
+ }
3661
+ /**
3662
+ * Gets nodes that end before the specified position
3663
+ * @param inclusive - If true, includes nodes that end exactly at the position (default: false, matching half-open interval semantics)
3664
+ */
3665
+ function getNodesBeforePosition(nodes, position, inclusive = false) {
3666
+ return nodes.filter(node => node.location && isPositionBefore(node.location.end, position, inclusive));
3667
+ }
3668
+ /**
3669
+ * Gets nodes that start after the specified position
3670
+ * @param inclusive - If true, includes nodes that start exactly at the position (default: true, matching typical boundary behavior)
3671
+ */
3672
+ function getNodesAfterPosition(nodes, position, inclusive = true) {
3673
+ return nodes.filter(node => node.location && isPositionAfter(node.location.start, position, inclusive));
3674
+ }
3675
+
3676
+ const expectedFunctions = [
3677
+ "parse",
3678
+ "lex",
3679
+ "parseFile",
3680
+ "lexFile",
3681
+ "extractRuby",
3682
+ "extractHTML",
3683
+ "version",
3684
+ ];
3685
+ // NOTE: This function should never be called and is only for type checking
3686
+ // so we can make sure `expectedFunctions` matches the functions defined
3687
+ // in `LibHerbBackendFunctions` and the other way around.
3688
+ //
3689
+ function _TYPECHECK() {
3690
+ const checkFunctionsExist = true;
3691
+ const checkInterfaceComplete = true;
3692
+ return { checkFunctionsExist, checkInterfaceComplete };
3693
+ }
3694
+ function isLibHerbBackend(object, libherbpath = "unknown") {
3695
+ for (const expectedFunction of expectedFunctions) {
3696
+ if (object[expectedFunction] === undefined) {
3697
+ throw new Error(`Libherb at "${libherbpath}" doesn't expose function "${expectedFunction}".`);
3698
+ }
3699
+ if (typeof object[expectedFunction] !== "function") {
3700
+ throw new Error(`Libherb at "${libherbpath}" has "${expectedFunction}" but it's not a function.`);
3701
+ }
3702
+ }
3703
+ return true;
3704
+ }
3705
+ function ensureLibHerbBackend(object, libherbpath = "unknown") {
3706
+ isLibHerbBackend(object, libherbpath);
3707
+ return object;
3708
+ }
3709
+
3710
+ /**
3711
+ * Converts a Diagnostic to Monaco/VSCode-compatible MonacoDiagnostic format
3712
+ */
3713
+ function toMonacoDiagnostic(diagnostic) {
3714
+ const { message, location } = diagnostic;
3715
+ const severity = diagnostic.severity === "hint" ? "info" : diagnostic.severity;
3716
+ return {
3717
+ line: location.start.line,
3718
+ column: location.start.column,
3719
+ endLine: location.end.line,
3720
+ endColumn: location.end.column,
3721
+ message,
3722
+ severity
3723
+ };
3724
+ }
3725
+
3726
+ var name = "@herb-tools/core";
3727
+ var version = "0.6.1";
3728
+ var packageJSON = {
3729
+ name: name,
3730
+ version: version};
3731
+
3732
+ class TokenList {
3733
+ list;
3734
+ static from(list) {
3735
+ return new TokenList(list.map((token) => Token.from(token)));
3736
+ }
3737
+ constructor(list) {
3738
+ this.list = list;
3739
+ }
3740
+ get length() {
3741
+ return this.list.length;
3742
+ }
3743
+ get tokens() {
3744
+ return this.list;
3745
+ }
3746
+ [Symbol.iterator]() {
3747
+ return this.list[Symbol.iterator]();
3748
+ }
3749
+ at(index) {
3750
+ return this.list.at(index);
3751
+ }
3752
+ forEach(callback) {
3753
+ this.list.forEach(callback);
3754
+ }
3755
+ map(callback) {
3756
+ return this.list.map(callback);
3757
+ }
3758
+ filter(predicate) {
3759
+ return this.list.filter(predicate);
3760
+ }
3761
+ __getobj__() {
3762
+ return this.list;
3763
+ }
3764
+ inspect() {
3765
+ return this.list.map((token) => token.inspect()).join("\n") + "\n";
3766
+ }
3767
+ toString() {
3768
+ return this.inspect();
3769
+ }
3770
+ }
3771
+
3772
+ /**
3773
+ * Represents the result of a lexical analysis, extending the base `Result` class.
3774
+ * It contains the token list, source code, warnings, and errors.
3775
+ */
3776
+ class LexResult extends Result {
3777
+ /** The list of tokens generated from the source code. */
3778
+ value;
3779
+ /**
3780
+ * Creates a `LexResult` instance from a serialized result.
3781
+ * @param result - The serialized lexical result containing tokens, source, warnings, and errors.
3782
+ * @returns A new `LexResult` instance.
3783
+ */
3784
+ static from(result) {
3785
+ return new LexResult(TokenList.from(result.tokens || []), result.source, result.warnings.map((warning) => HerbWarning.from(warning)), result.errors.map((error) => HerbError.from(error)));
3786
+ }
3787
+ /**
3788
+ * Constructs a new `LexResult`.
3789
+ * @param value - The list of tokens.
3790
+ * @param source - The source code that was lexed.
3791
+ * @param warnings - An array of warnings encountered during lexing.
3792
+ * @param errors - An array of errors encountered during lexing.
3793
+ */
3794
+ constructor(value, source, warnings = [], errors = []) {
3795
+ super(source, warnings, errors);
3796
+ this.value = value;
3797
+ }
3798
+ /**
3799
+ * Determines if the lexing was successful.
3800
+ * @returns `true` if there are no errors, otherwise `false`.
3801
+ */
3802
+ get successful() {
3803
+ return this.errors.length === 0;
3804
+ }
3805
+ /**
3806
+ * Determines if the lexing failed.
3807
+ * @returns `true` if there are errors, otherwise `false`.
3808
+ */
3809
+ get failed() {
3810
+ return this.errors.length > 0;
3811
+ }
3812
+ /**
3813
+ * Converts the `LexResult` to a JSON representation.
3814
+ * @returns An object containing the token list, source, warnings, and errors.
3815
+ */
3816
+ toJSON() {
3817
+ return {
3818
+ value: this.value,
3819
+ source: this.source,
3820
+ warnings: this.warnings,
3821
+ errors: this.errors,
3822
+ };
3823
+ }
3824
+ }
3825
+
3826
+ const DEFAULT_PARSER_OPTIONS = {
3827
+ track_whitespace: false,
3828
+ };
3829
+
3830
+ /**
3831
+ * The main Herb parser interface, providing methods to lex and parse input.
3832
+ */
3833
+ class HerbBackend {
3834
+ /** The backend instance handling lexing and parsing. */
3835
+ backend = undefined;
3836
+ backendPromise;
3837
+ /**
3838
+ * Creates a new Herb instance.
3839
+ * @param backendPromise - A promise resolving to a `LibHerbBackend` implementation for lexing and parsing.
3840
+ * @throws Error if no valid backend is provided.
3841
+ */
3842
+ constructor(backendPromise) {
3843
+ if (!backendPromise) {
3844
+ throw new Error("No LibHerb backend provided");
3845
+ }
3846
+ this.backendPromise = backendPromise;
2876
3847
  }
2877
3848
  /**
2878
3849
  * Loads the backend by resolving the backend promise.
@@ -2906,12 +3877,14 @@ class HerbBackend {
2906
3877
  /**
2907
3878
  * Parses the given source string into a `ParseResult`.
2908
3879
  * @param source - The source code to parse.
3880
+ * @param options - Optional parsing options.
2909
3881
  * @returns A `ParseResult` instance.
2910
3882
  * @throws Error if the backend is not loaded.
2911
3883
  */
2912
- parse(source) {
3884
+ parse(source, options) {
2913
3885
  this.ensureBackend();
2914
- return ParseResult.from(this.backend.parse(ensureString(source)));
3886
+ const mergedOptions = { ...DEFAULT_PARSER_OPTIONS, ...options };
3887
+ return ParseResult.from(this.backend.parse(ensureString(source), mergedOptions));
2915
3888
  }
2916
3889
  /**
2917
3890
  * Parses a file.
@@ -2974,7 +3947,7 @@ class HerbBackend {
2974
3947
  }
2975
3948
 
2976
3949
  // NOTE: This file is generated by the templates/template.rb script and should not
2977
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.5.0/templates/javascript/packages/core/src/visitor.ts.erb
3950
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.6.1/templates/javascript/packages/core/src/visitor.ts.erb
2978
3951
  class Visitor {
2979
3952
  visit(node) {
2980
3953
  if (!node)
@@ -2999,9 +3972,6 @@ class Visitor {
2999
3972
  visitHTMLCloseTagNode(node) {
3000
3973
  this.visitChildNodes(node);
3001
3974
  }
3002
- visitHTMLSelfCloseTagNode(node) {
3003
- this.visitChildNodes(node);
3004
- }
3005
3975
  visitHTMLElementNode(node) {
3006
3976
  this.visitChildNodes(node);
3007
3977
  }
@@ -3023,6 +3993,12 @@ class Visitor {
3023
3993
  visitHTMLDoctypeNode(node) {
3024
3994
  this.visitChildNodes(node);
3025
3995
  }
3996
+ visitXMLDeclarationNode(node) {
3997
+ this.visitChildNodes(node);
3998
+ }
3999
+ visitCDATANode(node) {
4000
+ this.visitChildNodes(node);
4001
+ }
3026
4002
  visitWhitespaceNode(node) {
3027
4003
  this.visitChildNodes(node);
3028
4004
  }
@@ -3079,5 +4055,5 @@ class Visitor {
3079
4055
  }
3080
4056
  }
3081
4057
 
3082
- export { DocumentNode, ERBBeginNode, ERBBlockNode, ERBCaseMatchNode, ERBCaseNode, ERBContentNode, ERBElseNode, ERBEndNode, ERBEnsureNode, ERBForNode, ERBIfNode, ERBInNode, ERBNodeClasses, ERBRescueNode, ERBUnlessNode, ERBUntilNode, ERBWhenNode, ERBWhileNode, ERBYieldNode, HTMLAttributeNameNode, HTMLAttributeNode, HTMLAttributeValueNode, HTMLCloseTagNode, HTMLCommentNode, HTMLDoctypeNode, HTMLElementNode, HTMLOpenTagNode, HTMLSelfCloseTagNode, HTMLTextNode, HerbBackend, HerbError, HerbWarning, LexResult, LiteralNode, Location, MissingClosingTagError, MissingOpeningTagError, Node, ParseResult, Position, QuotesMismatchError, Range, Result, RubyParseError, TagNamesMismatchError, Token, TokenList, UnclosedElementError, UnexpectedError, UnexpectedTokenError, Visitor, VoidElementClosingTagError, WhitespaceNode, _TYPECHECK, convertToUTF8, ensureLibHerbBackend, ensureString, fromSerializedError, fromSerializedNode, isERBNode, isLibHerbBackend, toMonacoDiagnostic };
4058
+ export { AST_TYPE_GUARDS, CDATANode, DEFAULT_PARSER_OPTIONS, DocumentNode, ERBBeginNode, ERBBlockNode, ERBCaseMatchNode, ERBCaseNode, ERBContentNode, ERBElseNode, ERBEndNode, ERBEnsureNode, ERBForNode, ERBIfNode, ERBInNode, ERBNodeClasses, ERBRescueNode, ERBUnlessNode, ERBUntilNode, ERBWhenNode, ERBWhileNode, ERBYieldNode, HTMLAttributeNameNode, HTMLAttributeNode, HTMLAttributeValueNode, HTMLCloseTagNode, HTMLCommentNode, HTMLDoctypeNode, HTMLElementNode, HTMLOpenTagNode, HTMLTextNode, HerbBackend, HerbError, HerbWarning, LexResult, LiteralNode, Location, MissingClosingTagError, MissingOpeningTagError, NODE_TYPE_GUARDS, Node, ParseResult, Position, QuotesMismatchError, Range, Result, RubyParseError, TagNamesMismatchError, Token, TokenList, UnclosedElementError, UnexpectedError, UnexpectedTokenError, Visitor, VoidElementClosingTagError, WhitespaceNode, XMLDeclarationNode, _TYPECHECK, areAllOfType, convertToUTF8, ensureLibHerbBackend, ensureString, filterCDATANodes, filterDocumentNodes, filterERBBeginNodes, filterERBBlockNodes, filterERBCaseMatchNodes, filterERBCaseNodes, filterERBContentNodes, filterERBElseNodes, filterERBEndNodes, filterERBEnsureNodes, filterERBForNodes, filterERBIfNodes, filterERBInNodes, filterERBRescueNodes, filterERBUnlessNodes, filterERBUntilNodes, filterERBWhenNodes, filterERBWhileNodes, filterERBYieldNodes, filterHTMLAttributeNameNodes, filterHTMLAttributeNodes, filterHTMLAttributeValueNodes, filterHTMLCloseTagNodes, filterHTMLCommentNodes, filterHTMLDoctypeNodes, filterHTMLElementNodes, filterHTMLOpenTagNodes, filterHTMLTextNodes, filterLiteralNodes, filterNodes, filterWhitespaceNodes, filterXMLDeclarationNodes, fromSerializedError, fromSerializedNode, getCombinedAttributeName, getCombinedStringFromNodes, getNodesAfterLocation, getNodesAfterPosition, getNodesBeforeLocation, getNodesBeforePosition, getStaticAttributeName, getStaticContentFromNodes, getStaticStringFromNodes, getTagName, getValidatableStaticContent, hasChildren, hasDynamicAttributeName, hasERBContent, hasERBOutput, hasStaticAttributeName, hasStaticContent, isAnyOf, isCDATANode, isCommentNode, isDocumentNode, isERBBeginNode, isERBBlockNode, isERBCaseMatchNode, isERBCaseNode, isERBContentNode, isERBControlFlowNode, isERBElseNode, isERBEndNode, isERBEnsureNode, isERBForNode, isERBIfNode, isERBInNode, isERBNode, isERBOutputNode, isERBRescueNode, isERBUnlessNode, isERBUntilNode, isERBWhenNode, isERBWhileNode, isERBYieldNode, isEffectivelyStatic, isHTMLAttributeNameNode, isHTMLAttributeNode, isHTMLAttributeValueNode, isHTMLCloseTagNode, isHTMLCommentNode, isHTMLDoctypeNode, isHTMLElementNode, isHTMLNode, isHTMLOpenTagNode, isHTMLTextNode, isLibHerbBackend, isLiteralNode, isNode, isNoneOf, isParseResult, isPositionAfter, isPositionEqual, isToken, isWhitespaceNode, isXMLDeclarationNode, splitNodesAroundLocation, splitNodesAroundPosition, toMonacoDiagnostic };
3083
4059
  //# sourceMappingURL=herb-core.browser.js.map