@herb-tools/formatter 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/herb-format.js +447 -132
- package/dist/herb-format.js.map +1 -1
- package/dist/index.cjs +372 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +372 -121
- package/dist/index.esm.js.map +1 -1
- package/dist/types/format-printer.d.ts +6 -0
- package/package.json +3 -3
- package/src/cli.ts +4 -2
- package/src/format-printer.ts +80 -50
package/dist/index.esm.js
CHANGED
|
@@ -1,3 +1,70 @@
|
|
|
1
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
2
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
3
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
4
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
5
|
+
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
6
|
+
const dedent = createDedent({});
|
|
7
|
+
function createDedent(options) {
|
|
8
|
+
dedent.withOptions = newOptions => createDedent(_objectSpread(_objectSpread({}, options), newOptions));
|
|
9
|
+
return dedent;
|
|
10
|
+
function dedent(strings, ...values) {
|
|
11
|
+
const raw = typeof strings === "string" ? [strings] : strings.raw;
|
|
12
|
+
const {
|
|
13
|
+
escapeSpecialCharacters = Array.isArray(strings),
|
|
14
|
+
trimWhitespace = true
|
|
15
|
+
} = options;
|
|
16
|
+
|
|
17
|
+
// first, perform interpolation
|
|
18
|
+
let result = "";
|
|
19
|
+
for (let i = 0; i < raw.length; i++) {
|
|
20
|
+
let next = raw[i];
|
|
21
|
+
if (escapeSpecialCharacters) {
|
|
22
|
+
// handle escaped newlines, backticks, and interpolation characters
|
|
23
|
+
next = next.replace(/\\\n[ \t]*/g, "").replace(/\\`/g, "`").replace(/\\\$/g, "$").replace(/\\\{/g, "{");
|
|
24
|
+
}
|
|
25
|
+
result += next;
|
|
26
|
+
if (i < values.length) {
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
|
28
|
+
result += values[i];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// now strip indentation
|
|
33
|
+
const lines = result.split("\n");
|
|
34
|
+
let mindent = null;
|
|
35
|
+
for (const l of lines) {
|
|
36
|
+
const m = l.match(/^(\s+)\S+/);
|
|
37
|
+
if (m) {
|
|
38
|
+
const indent = m[1].length;
|
|
39
|
+
if (!mindent) {
|
|
40
|
+
// this is the first indented line
|
|
41
|
+
mindent = indent;
|
|
42
|
+
} else {
|
|
43
|
+
mindent = Math.min(mindent, indent);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (mindent !== null) {
|
|
48
|
+
const m = mindent; // appease TypeScript
|
|
49
|
+
result = lines
|
|
50
|
+
// https://github.com/typescript-eslint/typescript-eslint/issues/7140
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
|
|
52
|
+
.map(l => l[0] === " " || l[0] === "\t" ? l.slice(m) : l).join("\n");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// dedent eats leading and trailing whitespace too
|
|
56
|
+
if (trimWhitespace) {
|
|
57
|
+
result = result.trim();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// handle escaped newlines at the end to ensure they don't get stripped too
|
|
61
|
+
if (escapeSpecialCharacters) {
|
|
62
|
+
result = result.replace(/\\n/g, "\n");
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
1
68
|
class Position {
|
|
2
69
|
line;
|
|
3
70
|
column;
|
|
@@ -129,7 +196,7 @@ class Token {
|
|
|
129
196
|
}
|
|
130
197
|
|
|
131
198
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
|
132
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-0.
|
|
199
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.7.0/templates/javascript/packages/core/src/errors.ts.erb
|
|
133
200
|
class HerbError {
|
|
134
201
|
type;
|
|
135
202
|
message;
|
|
@@ -579,7 +646,7 @@ function convertToUTF8(string) {
|
|
|
579
646
|
}
|
|
580
647
|
|
|
581
648
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
|
582
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-0.
|
|
649
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.7.0/templates/javascript/packages/core/src/nodes.ts.erb
|
|
583
650
|
class Node {
|
|
584
651
|
type;
|
|
585
652
|
location;
|
|
@@ -587,6 +654,9 @@ class Node {
|
|
|
587
654
|
static from(node) {
|
|
588
655
|
return fromSerializedNode(node);
|
|
589
656
|
}
|
|
657
|
+
static get type() {
|
|
658
|
+
throw new Error("AST_NODE");
|
|
659
|
+
}
|
|
590
660
|
constructor(type, location, errors) {
|
|
591
661
|
this.type = type;
|
|
592
662
|
this.location = location;
|
|
@@ -602,6 +672,12 @@ class Node {
|
|
|
602
672
|
inspect() {
|
|
603
673
|
return this.treeInspect(0);
|
|
604
674
|
}
|
|
675
|
+
is(nodeClass) {
|
|
676
|
+
return this.type === nodeClass.type;
|
|
677
|
+
}
|
|
678
|
+
isOfType(type) {
|
|
679
|
+
return this.type === type;
|
|
680
|
+
}
|
|
605
681
|
get isSingleLine() {
|
|
606
682
|
return this.location.start.line === this.location.end.line;
|
|
607
683
|
}
|
|
@@ -610,7 +686,7 @@ class Node {
|
|
|
610
686
|
return "∅\n";
|
|
611
687
|
if (array.length === 0)
|
|
612
688
|
return "[]\n";
|
|
613
|
-
let output = `(${array.length} item${array.length
|
|
689
|
+
let output = `(${array.length} item${array.length === 1 ? "" : "s"})\n`;
|
|
614
690
|
array.forEach((item, index) => {
|
|
615
691
|
const isLast = index === array.length - 1;
|
|
616
692
|
if (item instanceof Node || item instanceof HerbError) {
|
|
@@ -634,7 +710,7 @@ class Node {
|
|
|
634
710
|
.treeInspect()
|
|
635
711
|
.trimStart()
|
|
636
712
|
.split("\n")
|
|
637
|
-
.map((line, index) => index
|
|
713
|
+
.map((line, index) => index === 0 ? line.trimStart() : `${prefix}${prefix2}${line}`)
|
|
638
714
|
.join("\n")
|
|
639
715
|
.trimStart();
|
|
640
716
|
output += `\n`;
|
|
@@ -643,6 +719,9 @@ class Node {
|
|
|
643
719
|
}
|
|
644
720
|
class DocumentNode extends Node {
|
|
645
721
|
children;
|
|
722
|
+
static get type() {
|
|
723
|
+
return "AST_DOCUMENT_NODE";
|
|
724
|
+
}
|
|
646
725
|
static from(data) {
|
|
647
726
|
return new DocumentNode({
|
|
648
727
|
type: data.type,
|
|
@@ -689,6 +768,9 @@ class DocumentNode extends Node {
|
|
|
689
768
|
}
|
|
690
769
|
class LiteralNode extends Node {
|
|
691
770
|
content;
|
|
771
|
+
static get type() {
|
|
772
|
+
return "AST_LITERAL_NODE";
|
|
773
|
+
}
|
|
692
774
|
static from(data) {
|
|
693
775
|
return new LiteralNode({
|
|
694
776
|
type: data.type,
|
|
@@ -736,6 +818,9 @@ class HTMLOpenTagNode extends Node {
|
|
|
736
818
|
tag_closing;
|
|
737
819
|
children;
|
|
738
820
|
is_void;
|
|
821
|
+
static get type() {
|
|
822
|
+
return "AST_HTML_OPEN_TAG_NODE";
|
|
823
|
+
}
|
|
739
824
|
static from(data) {
|
|
740
825
|
return new HTMLOpenTagNode({
|
|
741
826
|
type: data.type,
|
|
@@ -801,6 +886,9 @@ class HTMLCloseTagNode extends Node {
|
|
|
801
886
|
tag_name;
|
|
802
887
|
children;
|
|
803
888
|
tag_closing;
|
|
889
|
+
static get type() {
|
|
890
|
+
return "AST_HTML_CLOSE_TAG_NODE";
|
|
891
|
+
}
|
|
804
892
|
static from(data) {
|
|
805
893
|
return new HTMLCloseTagNode({
|
|
806
894
|
type: data.type,
|
|
@@ -863,6 +951,10 @@ class HTMLElementNode extends Node {
|
|
|
863
951
|
body;
|
|
864
952
|
close_tag;
|
|
865
953
|
is_void;
|
|
954
|
+
source;
|
|
955
|
+
static get type() {
|
|
956
|
+
return "AST_HTML_ELEMENT_NODE";
|
|
957
|
+
}
|
|
866
958
|
static from(data) {
|
|
867
959
|
return new HTMLElementNode({
|
|
868
960
|
type: data.type,
|
|
@@ -873,6 +965,7 @@ class HTMLElementNode extends Node {
|
|
|
873
965
|
body: (data.body || []).map(node => fromSerializedNode(node)),
|
|
874
966
|
close_tag: data.close_tag ? fromSerializedNode((data.close_tag)) : null,
|
|
875
967
|
is_void: data.is_void,
|
|
968
|
+
source: data.source,
|
|
876
969
|
});
|
|
877
970
|
}
|
|
878
971
|
constructor(props) {
|
|
@@ -882,6 +975,7 @@ class HTMLElementNode extends Node {
|
|
|
882
975
|
this.body = props.body;
|
|
883
976
|
this.close_tag = props.close_tag;
|
|
884
977
|
this.is_void = props.is_void;
|
|
978
|
+
this.source = props.source;
|
|
885
979
|
}
|
|
886
980
|
accept(visitor) {
|
|
887
981
|
visitor.visitHTMLElementNode(this);
|
|
@@ -913,6 +1007,7 @@ class HTMLElementNode extends Node {
|
|
|
913
1007
|
body: this.body.map(node => node.toJSON()),
|
|
914
1008
|
close_tag: this.close_tag ? this.close_tag.toJSON() : null,
|
|
915
1009
|
is_void: this.is_void,
|
|
1010
|
+
source: this.source,
|
|
916
1011
|
};
|
|
917
1012
|
}
|
|
918
1013
|
treeInspect() {
|
|
@@ -923,7 +1018,8 @@ class HTMLElementNode extends Node {
|
|
|
923
1018
|
output += `├── tag_name: ${this.tag_name ? this.tag_name.treeInspect() : "∅"}\n`;
|
|
924
1019
|
output += `├── body: ${this.inspectArray(this.body, "│ ")}`;
|
|
925
1020
|
output += `├── close_tag: ${this.inspectNode(this.close_tag, "│ ")}`;
|
|
926
|
-
output +=
|
|
1021
|
+
output += `├── is_void: ${typeof this.is_void === 'boolean' ? String(this.is_void) : "∅"}\n`;
|
|
1022
|
+
output += `└── source: ${this.source ? JSON.stringify(this.source) : "∅"}\n`;
|
|
927
1023
|
return output;
|
|
928
1024
|
}
|
|
929
1025
|
}
|
|
@@ -932,6 +1028,9 @@ class HTMLAttributeValueNode extends Node {
|
|
|
932
1028
|
children;
|
|
933
1029
|
close_quote;
|
|
934
1030
|
quoted;
|
|
1031
|
+
static get type() {
|
|
1032
|
+
return "AST_HTML_ATTRIBUTE_VALUE_NODE";
|
|
1033
|
+
}
|
|
935
1034
|
static from(data) {
|
|
936
1035
|
return new HTMLAttributeValueNode({
|
|
937
1036
|
type: data.type,
|
|
@@ -990,6 +1089,9 @@ class HTMLAttributeValueNode extends Node {
|
|
|
990
1089
|
}
|
|
991
1090
|
class HTMLAttributeNameNode extends Node {
|
|
992
1091
|
children;
|
|
1092
|
+
static get type() {
|
|
1093
|
+
return "AST_HTML_ATTRIBUTE_NAME_NODE";
|
|
1094
|
+
}
|
|
993
1095
|
static from(data) {
|
|
994
1096
|
return new HTMLAttributeNameNode({
|
|
995
1097
|
type: data.type,
|
|
@@ -1038,6 +1140,9 @@ class HTMLAttributeNode extends Node {
|
|
|
1038
1140
|
name;
|
|
1039
1141
|
equals;
|
|
1040
1142
|
value;
|
|
1143
|
+
static get type() {
|
|
1144
|
+
return "AST_HTML_ATTRIBUTE_NODE";
|
|
1145
|
+
}
|
|
1041
1146
|
static from(data) {
|
|
1042
1147
|
return new HTMLAttributeNode({
|
|
1043
1148
|
type: data.type,
|
|
@@ -1094,6 +1199,9 @@ class HTMLAttributeNode extends Node {
|
|
|
1094
1199
|
}
|
|
1095
1200
|
class HTMLTextNode extends Node {
|
|
1096
1201
|
content;
|
|
1202
|
+
static get type() {
|
|
1203
|
+
return "AST_HTML_TEXT_NODE";
|
|
1204
|
+
}
|
|
1097
1205
|
static from(data) {
|
|
1098
1206
|
return new HTMLTextNode({
|
|
1099
1207
|
type: data.type,
|
|
@@ -1139,6 +1247,9 @@ class HTMLCommentNode extends Node {
|
|
|
1139
1247
|
comment_start;
|
|
1140
1248
|
children;
|
|
1141
1249
|
comment_end;
|
|
1250
|
+
static get type() {
|
|
1251
|
+
return "AST_HTML_COMMENT_NODE";
|
|
1252
|
+
}
|
|
1142
1253
|
static from(data) {
|
|
1143
1254
|
return new HTMLCommentNode({
|
|
1144
1255
|
type: data.type,
|
|
@@ -1195,6 +1306,9 @@ class HTMLDoctypeNode extends Node {
|
|
|
1195
1306
|
tag_opening;
|
|
1196
1307
|
children;
|
|
1197
1308
|
tag_closing;
|
|
1309
|
+
static get type() {
|
|
1310
|
+
return "AST_HTML_DOCTYPE_NODE";
|
|
1311
|
+
}
|
|
1198
1312
|
static from(data) {
|
|
1199
1313
|
return new HTMLDoctypeNode({
|
|
1200
1314
|
type: data.type,
|
|
@@ -1251,6 +1365,9 @@ class XMLDeclarationNode extends Node {
|
|
|
1251
1365
|
tag_opening;
|
|
1252
1366
|
children;
|
|
1253
1367
|
tag_closing;
|
|
1368
|
+
static get type() {
|
|
1369
|
+
return "AST_XML_DECLARATION_NODE";
|
|
1370
|
+
}
|
|
1254
1371
|
static from(data) {
|
|
1255
1372
|
return new XMLDeclarationNode({
|
|
1256
1373
|
type: data.type,
|
|
@@ -1307,6 +1424,9 @@ class CDATANode extends Node {
|
|
|
1307
1424
|
tag_opening;
|
|
1308
1425
|
children;
|
|
1309
1426
|
tag_closing;
|
|
1427
|
+
static get type() {
|
|
1428
|
+
return "AST_CDATA_NODE";
|
|
1429
|
+
}
|
|
1310
1430
|
static from(data) {
|
|
1311
1431
|
return new CDATANode({
|
|
1312
1432
|
type: data.type,
|
|
@@ -1361,6 +1481,9 @@ class CDATANode extends Node {
|
|
|
1361
1481
|
}
|
|
1362
1482
|
class WhitespaceNode extends Node {
|
|
1363
1483
|
value;
|
|
1484
|
+
static get type() {
|
|
1485
|
+
return "AST_WHITESPACE_NODE";
|
|
1486
|
+
}
|
|
1364
1487
|
static from(data) {
|
|
1365
1488
|
return new WhitespaceNode({
|
|
1366
1489
|
type: data.type,
|
|
@@ -1409,6 +1532,9 @@ class ERBContentNode extends Node {
|
|
|
1409
1532
|
// no-op for analyzed_ruby
|
|
1410
1533
|
parsed;
|
|
1411
1534
|
valid;
|
|
1535
|
+
static get type() {
|
|
1536
|
+
return "AST_ERB_CONTENT_NODE";
|
|
1537
|
+
}
|
|
1412
1538
|
static from(data) {
|
|
1413
1539
|
return new ERBContentNode({
|
|
1414
1540
|
type: data.type,
|
|
@@ -1474,6 +1600,9 @@ class ERBEndNode extends Node {
|
|
|
1474
1600
|
tag_opening;
|
|
1475
1601
|
content;
|
|
1476
1602
|
tag_closing;
|
|
1603
|
+
static get type() {
|
|
1604
|
+
return "AST_ERB_END_NODE";
|
|
1605
|
+
}
|
|
1477
1606
|
static from(data) {
|
|
1478
1607
|
return new ERBEndNode({
|
|
1479
1608
|
type: data.type,
|
|
@@ -1528,6 +1657,9 @@ class ERBElseNode extends Node {
|
|
|
1528
1657
|
content;
|
|
1529
1658
|
tag_closing;
|
|
1530
1659
|
statements;
|
|
1660
|
+
static get type() {
|
|
1661
|
+
return "AST_ERB_ELSE_NODE";
|
|
1662
|
+
}
|
|
1531
1663
|
static from(data) {
|
|
1532
1664
|
return new ERBElseNode({
|
|
1533
1665
|
type: data.type,
|
|
@@ -1591,6 +1723,9 @@ class ERBIfNode extends Node {
|
|
|
1591
1723
|
statements;
|
|
1592
1724
|
subsequent;
|
|
1593
1725
|
end_node;
|
|
1726
|
+
static get type() {
|
|
1727
|
+
return "AST_ERB_IF_NODE";
|
|
1728
|
+
}
|
|
1594
1729
|
static from(data) {
|
|
1595
1730
|
return new ERBIfNode({
|
|
1596
1731
|
type: data.type,
|
|
@@ -1665,6 +1800,9 @@ class ERBBlockNode extends Node {
|
|
|
1665
1800
|
tag_closing;
|
|
1666
1801
|
body;
|
|
1667
1802
|
end_node;
|
|
1803
|
+
static get type() {
|
|
1804
|
+
return "AST_ERB_BLOCK_NODE";
|
|
1805
|
+
}
|
|
1668
1806
|
static from(data) {
|
|
1669
1807
|
return new ERBBlockNode({
|
|
1670
1808
|
type: data.type,
|
|
@@ -1732,6 +1870,9 @@ class ERBWhenNode extends Node {
|
|
|
1732
1870
|
content;
|
|
1733
1871
|
tag_closing;
|
|
1734
1872
|
statements;
|
|
1873
|
+
static get type() {
|
|
1874
|
+
return "AST_ERB_WHEN_NODE";
|
|
1875
|
+
}
|
|
1735
1876
|
static from(data) {
|
|
1736
1877
|
return new ERBWhenNode({
|
|
1737
1878
|
type: data.type,
|
|
@@ -1796,6 +1937,9 @@ class ERBCaseNode extends Node {
|
|
|
1796
1937
|
conditions;
|
|
1797
1938
|
else_clause;
|
|
1798
1939
|
end_node;
|
|
1940
|
+
static get type() {
|
|
1941
|
+
return "AST_ERB_CASE_NODE";
|
|
1942
|
+
}
|
|
1799
1943
|
static from(data) {
|
|
1800
1944
|
return new ERBCaseNode({
|
|
1801
1945
|
type: data.type,
|
|
@@ -1878,6 +2022,9 @@ class ERBCaseMatchNode extends Node {
|
|
|
1878
2022
|
conditions;
|
|
1879
2023
|
else_clause;
|
|
1880
2024
|
end_node;
|
|
2025
|
+
static get type() {
|
|
2026
|
+
return "AST_ERB_CASE_MATCH_NODE";
|
|
2027
|
+
}
|
|
1881
2028
|
static from(data) {
|
|
1882
2029
|
return new ERBCaseMatchNode({
|
|
1883
2030
|
type: data.type,
|
|
@@ -1958,6 +2105,9 @@ class ERBWhileNode extends Node {
|
|
|
1958
2105
|
tag_closing;
|
|
1959
2106
|
statements;
|
|
1960
2107
|
end_node;
|
|
2108
|
+
static get type() {
|
|
2109
|
+
return "AST_ERB_WHILE_NODE";
|
|
2110
|
+
}
|
|
1961
2111
|
static from(data) {
|
|
1962
2112
|
return new ERBWhileNode({
|
|
1963
2113
|
type: data.type,
|
|
@@ -2026,6 +2176,9 @@ class ERBUntilNode extends Node {
|
|
|
2026
2176
|
tag_closing;
|
|
2027
2177
|
statements;
|
|
2028
2178
|
end_node;
|
|
2179
|
+
static get type() {
|
|
2180
|
+
return "AST_ERB_UNTIL_NODE";
|
|
2181
|
+
}
|
|
2029
2182
|
static from(data) {
|
|
2030
2183
|
return new ERBUntilNode({
|
|
2031
2184
|
type: data.type,
|
|
@@ -2094,6 +2247,9 @@ class ERBForNode extends Node {
|
|
|
2094
2247
|
tag_closing;
|
|
2095
2248
|
statements;
|
|
2096
2249
|
end_node;
|
|
2250
|
+
static get type() {
|
|
2251
|
+
return "AST_ERB_FOR_NODE";
|
|
2252
|
+
}
|
|
2097
2253
|
static from(data) {
|
|
2098
2254
|
return new ERBForNode({
|
|
2099
2255
|
type: data.type,
|
|
@@ -2162,6 +2318,9 @@ class ERBRescueNode extends Node {
|
|
|
2162
2318
|
tag_closing;
|
|
2163
2319
|
statements;
|
|
2164
2320
|
subsequent;
|
|
2321
|
+
static get type() {
|
|
2322
|
+
return "AST_ERB_RESCUE_NODE";
|
|
2323
|
+
}
|
|
2165
2324
|
static from(data) {
|
|
2166
2325
|
return new ERBRescueNode({
|
|
2167
2326
|
type: data.type,
|
|
@@ -2229,6 +2388,9 @@ class ERBEnsureNode extends Node {
|
|
|
2229
2388
|
content;
|
|
2230
2389
|
tag_closing;
|
|
2231
2390
|
statements;
|
|
2391
|
+
static get type() {
|
|
2392
|
+
return "AST_ERB_ENSURE_NODE";
|
|
2393
|
+
}
|
|
2232
2394
|
static from(data) {
|
|
2233
2395
|
return new ERBEnsureNode({
|
|
2234
2396
|
type: data.type,
|
|
@@ -2294,6 +2456,9 @@ class ERBBeginNode extends Node {
|
|
|
2294
2456
|
else_clause;
|
|
2295
2457
|
ensure_clause;
|
|
2296
2458
|
end_node;
|
|
2459
|
+
static get type() {
|
|
2460
|
+
return "AST_ERB_BEGIN_NODE";
|
|
2461
|
+
}
|
|
2297
2462
|
static from(data) {
|
|
2298
2463
|
return new ERBBeginNode({
|
|
2299
2464
|
type: data.type,
|
|
@@ -2381,6 +2546,9 @@ class ERBUnlessNode extends Node {
|
|
|
2381
2546
|
statements;
|
|
2382
2547
|
else_clause;
|
|
2383
2548
|
end_node;
|
|
2549
|
+
static get type() {
|
|
2550
|
+
return "AST_ERB_UNLESS_NODE";
|
|
2551
|
+
}
|
|
2384
2552
|
static from(data) {
|
|
2385
2553
|
return new ERBUnlessNode({
|
|
2386
2554
|
type: data.type,
|
|
@@ -2453,6 +2621,9 @@ class ERBYieldNode extends Node {
|
|
|
2453
2621
|
tag_opening;
|
|
2454
2622
|
content;
|
|
2455
2623
|
tag_closing;
|
|
2624
|
+
static get type() {
|
|
2625
|
+
return "AST_ERB_YIELD_NODE";
|
|
2626
|
+
}
|
|
2456
2627
|
static from(data) {
|
|
2457
2628
|
return new ERBYieldNode({
|
|
2458
2629
|
type: data.type,
|
|
@@ -2507,6 +2678,9 @@ class ERBInNode extends Node {
|
|
|
2507
2678
|
content;
|
|
2508
2679
|
tag_closing;
|
|
2509
2680
|
statements;
|
|
2681
|
+
static get type() {
|
|
2682
|
+
return "AST_ERB_IN_NODE";
|
|
2683
|
+
}
|
|
2510
2684
|
static from(data) {
|
|
2511
2685
|
return new ERBInNode({
|
|
2512
2686
|
type: data.type,
|
|
@@ -2706,7 +2880,7 @@ class ParseResult extends Result {
|
|
|
2706
2880
|
}
|
|
2707
2881
|
|
|
2708
2882
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
|
2709
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-0.
|
|
2883
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.7.0/templates/javascript/packages/core/src/node-type-guards.ts.erb
|
|
2710
2884
|
/**
|
|
2711
2885
|
* Type guard functions for AST nodes.
|
|
2712
2886
|
* These functions provide type checking by combining both instanceof
|
|
@@ -2717,187 +2891,187 @@ class ParseResult extends Result {
|
|
|
2717
2891
|
* Checks if a node is a DocumentNode
|
|
2718
2892
|
*/
|
|
2719
2893
|
function isDocumentNode(node) {
|
|
2720
|
-
return node instanceof DocumentNode || node.type === "AST_DOCUMENT_NODE";
|
|
2894
|
+
return node instanceof DocumentNode || node.type === "AST_DOCUMENT_NODE" || node.constructor.type === "AST_DOCUMENT_NODE";
|
|
2721
2895
|
}
|
|
2722
2896
|
/**
|
|
2723
2897
|
* Checks if a node is a LiteralNode
|
|
2724
2898
|
*/
|
|
2725
2899
|
function isLiteralNode(node) {
|
|
2726
|
-
return node instanceof LiteralNode || node.type === "AST_LITERAL_NODE";
|
|
2900
|
+
return node instanceof LiteralNode || node.type === "AST_LITERAL_NODE" || node.constructor.type === "AST_LITERAL_NODE";
|
|
2727
2901
|
}
|
|
2728
2902
|
/**
|
|
2729
2903
|
* Checks if a node is a HTMLOpenTagNode
|
|
2730
2904
|
*/
|
|
2731
2905
|
function isHTMLOpenTagNode(node) {
|
|
2732
|
-
return node instanceof HTMLOpenTagNode || node.type === "AST_HTML_OPEN_TAG_NODE";
|
|
2906
|
+
return node instanceof HTMLOpenTagNode || node.type === "AST_HTML_OPEN_TAG_NODE" || node.constructor.type === "AST_HTML_OPEN_TAG_NODE";
|
|
2733
2907
|
}
|
|
2734
2908
|
/**
|
|
2735
2909
|
* Checks if a node is a HTMLCloseTagNode
|
|
2736
2910
|
*/
|
|
2737
2911
|
function isHTMLCloseTagNode(node) {
|
|
2738
|
-
return node instanceof HTMLCloseTagNode || node.type === "AST_HTML_CLOSE_TAG_NODE";
|
|
2912
|
+
return node instanceof HTMLCloseTagNode || node.type === "AST_HTML_CLOSE_TAG_NODE" || node.constructor.type === "AST_HTML_CLOSE_TAG_NODE";
|
|
2739
2913
|
}
|
|
2740
2914
|
/**
|
|
2741
2915
|
* Checks if a node is a HTMLElementNode
|
|
2742
2916
|
*/
|
|
2743
2917
|
function isHTMLElementNode(node) {
|
|
2744
|
-
return node instanceof HTMLElementNode || node.type === "AST_HTML_ELEMENT_NODE";
|
|
2918
|
+
return node instanceof HTMLElementNode || node.type === "AST_HTML_ELEMENT_NODE" || node.constructor.type === "AST_HTML_ELEMENT_NODE";
|
|
2745
2919
|
}
|
|
2746
2920
|
/**
|
|
2747
2921
|
* Checks if a node is a HTMLAttributeValueNode
|
|
2748
2922
|
*/
|
|
2749
2923
|
function isHTMLAttributeValueNode(node) {
|
|
2750
|
-
return node instanceof HTMLAttributeValueNode || node.type === "AST_HTML_ATTRIBUTE_VALUE_NODE";
|
|
2924
|
+
return node instanceof HTMLAttributeValueNode || node.type === "AST_HTML_ATTRIBUTE_VALUE_NODE" || node.constructor.type === "AST_HTML_ATTRIBUTE_VALUE_NODE";
|
|
2751
2925
|
}
|
|
2752
2926
|
/**
|
|
2753
2927
|
* Checks if a node is a HTMLAttributeNameNode
|
|
2754
2928
|
*/
|
|
2755
2929
|
function isHTMLAttributeNameNode(node) {
|
|
2756
|
-
return node instanceof HTMLAttributeNameNode || node.type === "AST_HTML_ATTRIBUTE_NAME_NODE";
|
|
2930
|
+
return node instanceof HTMLAttributeNameNode || node.type === "AST_HTML_ATTRIBUTE_NAME_NODE" || node.constructor.type === "AST_HTML_ATTRIBUTE_NAME_NODE";
|
|
2757
2931
|
}
|
|
2758
2932
|
/**
|
|
2759
2933
|
* Checks if a node is a HTMLAttributeNode
|
|
2760
2934
|
*/
|
|
2761
2935
|
function isHTMLAttributeNode(node) {
|
|
2762
|
-
return node instanceof HTMLAttributeNode || node.type === "AST_HTML_ATTRIBUTE_NODE";
|
|
2936
|
+
return node instanceof HTMLAttributeNode || node.type === "AST_HTML_ATTRIBUTE_NODE" || node.constructor.type === "AST_HTML_ATTRIBUTE_NODE";
|
|
2763
2937
|
}
|
|
2764
2938
|
/**
|
|
2765
2939
|
* Checks if a node is a HTMLTextNode
|
|
2766
2940
|
*/
|
|
2767
2941
|
function isHTMLTextNode(node) {
|
|
2768
|
-
return node instanceof HTMLTextNode || node.type === "AST_HTML_TEXT_NODE";
|
|
2942
|
+
return node instanceof HTMLTextNode || node.type === "AST_HTML_TEXT_NODE" || node.constructor.type === "AST_HTML_TEXT_NODE";
|
|
2769
2943
|
}
|
|
2770
2944
|
/**
|
|
2771
2945
|
* Checks if a node is a HTMLCommentNode
|
|
2772
2946
|
*/
|
|
2773
2947
|
function isHTMLCommentNode(node) {
|
|
2774
|
-
return node instanceof HTMLCommentNode || node.type === "AST_HTML_COMMENT_NODE";
|
|
2948
|
+
return node instanceof HTMLCommentNode || node.type === "AST_HTML_COMMENT_NODE" || node.constructor.type === "AST_HTML_COMMENT_NODE";
|
|
2775
2949
|
}
|
|
2776
2950
|
/**
|
|
2777
2951
|
* Checks if a node is a HTMLDoctypeNode
|
|
2778
2952
|
*/
|
|
2779
2953
|
function isHTMLDoctypeNode(node) {
|
|
2780
|
-
return node instanceof HTMLDoctypeNode || node.type === "AST_HTML_DOCTYPE_NODE";
|
|
2954
|
+
return node instanceof HTMLDoctypeNode || node.type === "AST_HTML_DOCTYPE_NODE" || node.constructor.type === "AST_HTML_DOCTYPE_NODE";
|
|
2781
2955
|
}
|
|
2782
2956
|
/**
|
|
2783
2957
|
* Checks if a node is a XMLDeclarationNode
|
|
2784
2958
|
*/
|
|
2785
2959
|
function isXMLDeclarationNode(node) {
|
|
2786
|
-
return node instanceof XMLDeclarationNode || node.type === "AST_XML_DECLARATION_NODE";
|
|
2960
|
+
return node instanceof XMLDeclarationNode || node.type === "AST_XML_DECLARATION_NODE" || node.constructor.type === "AST_XML_DECLARATION_NODE";
|
|
2787
2961
|
}
|
|
2788
2962
|
/**
|
|
2789
2963
|
* Checks if a node is a CDATANode
|
|
2790
2964
|
*/
|
|
2791
2965
|
function isCDATANode(node) {
|
|
2792
|
-
return node instanceof CDATANode || node.type === "AST_CDATA_NODE";
|
|
2966
|
+
return node instanceof CDATANode || node.type === "AST_CDATA_NODE" || node.constructor.type === "AST_CDATA_NODE";
|
|
2793
2967
|
}
|
|
2794
2968
|
/**
|
|
2795
2969
|
* Checks if a node is a WhitespaceNode
|
|
2796
2970
|
*/
|
|
2797
2971
|
function isWhitespaceNode(node) {
|
|
2798
|
-
return node instanceof WhitespaceNode || node.type === "AST_WHITESPACE_NODE";
|
|
2972
|
+
return node instanceof WhitespaceNode || node.type === "AST_WHITESPACE_NODE" || node.constructor.type === "AST_WHITESPACE_NODE";
|
|
2799
2973
|
}
|
|
2800
2974
|
/**
|
|
2801
2975
|
* Checks if a node is a ERBContentNode
|
|
2802
2976
|
*/
|
|
2803
2977
|
function isERBContentNode(node) {
|
|
2804
|
-
return node instanceof ERBContentNode || node.type === "AST_ERB_CONTENT_NODE";
|
|
2978
|
+
return node instanceof ERBContentNode || node.type === "AST_ERB_CONTENT_NODE" || node.constructor.type === "AST_ERB_CONTENT_NODE";
|
|
2805
2979
|
}
|
|
2806
2980
|
/**
|
|
2807
2981
|
* Checks if a node is a ERBEndNode
|
|
2808
2982
|
*/
|
|
2809
2983
|
function isERBEndNode(node) {
|
|
2810
|
-
return node instanceof ERBEndNode || node.type === "AST_ERB_END_NODE";
|
|
2984
|
+
return node instanceof ERBEndNode || node.type === "AST_ERB_END_NODE" || node.constructor.type === "AST_ERB_END_NODE";
|
|
2811
2985
|
}
|
|
2812
2986
|
/**
|
|
2813
2987
|
* Checks if a node is a ERBElseNode
|
|
2814
2988
|
*/
|
|
2815
2989
|
function isERBElseNode(node) {
|
|
2816
|
-
return node instanceof ERBElseNode || node.type === "AST_ERB_ELSE_NODE";
|
|
2990
|
+
return node instanceof ERBElseNode || node.type === "AST_ERB_ELSE_NODE" || node.constructor.type === "AST_ERB_ELSE_NODE";
|
|
2817
2991
|
}
|
|
2818
2992
|
/**
|
|
2819
2993
|
* Checks if a node is a ERBIfNode
|
|
2820
2994
|
*/
|
|
2821
2995
|
function isERBIfNode(node) {
|
|
2822
|
-
return node instanceof ERBIfNode || node.type === "AST_ERB_IF_NODE";
|
|
2996
|
+
return node instanceof ERBIfNode || node.type === "AST_ERB_IF_NODE" || node.constructor.type === "AST_ERB_IF_NODE";
|
|
2823
2997
|
}
|
|
2824
2998
|
/**
|
|
2825
2999
|
* Checks if a node is a ERBBlockNode
|
|
2826
3000
|
*/
|
|
2827
3001
|
function isERBBlockNode(node) {
|
|
2828
|
-
return node instanceof ERBBlockNode || node.type === "AST_ERB_BLOCK_NODE";
|
|
3002
|
+
return node instanceof ERBBlockNode || node.type === "AST_ERB_BLOCK_NODE" || node.constructor.type === "AST_ERB_BLOCK_NODE";
|
|
2829
3003
|
}
|
|
2830
3004
|
/**
|
|
2831
3005
|
* Checks if a node is a ERBWhenNode
|
|
2832
3006
|
*/
|
|
2833
3007
|
function isERBWhenNode(node) {
|
|
2834
|
-
return node instanceof ERBWhenNode || node.type === "AST_ERB_WHEN_NODE";
|
|
3008
|
+
return node instanceof ERBWhenNode || node.type === "AST_ERB_WHEN_NODE" || node.constructor.type === "AST_ERB_WHEN_NODE";
|
|
2835
3009
|
}
|
|
2836
3010
|
/**
|
|
2837
3011
|
* Checks if a node is a ERBCaseNode
|
|
2838
3012
|
*/
|
|
2839
3013
|
function isERBCaseNode(node) {
|
|
2840
|
-
return node instanceof ERBCaseNode || node.type === "AST_ERB_CASE_NODE";
|
|
3014
|
+
return node instanceof ERBCaseNode || node.type === "AST_ERB_CASE_NODE" || node.constructor.type === "AST_ERB_CASE_NODE";
|
|
2841
3015
|
}
|
|
2842
3016
|
/**
|
|
2843
3017
|
* Checks if a node is a ERBCaseMatchNode
|
|
2844
3018
|
*/
|
|
2845
3019
|
function isERBCaseMatchNode(node) {
|
|
2846
|
-
return node instanceof ERBCaseMatchNode || node.type === "AST_ERB_CASE_MATCH_NODE";
|
|
3020
|
+
return node instanceof ERBCaseMatchNode || node.type === "AST_ERB_CASE_MATCH_NODE" || node.constructor.type === "AST_ERB_CASE_MATCH_NODE";
|
|
2847
3021
|
}
|
|
2848
3022
|
/**
|
|
2849
3023
|
* Checks if a node is a ERBWhileNode
|
|
2850
3024
|
*/
|
|
2851
3025
|
function isERBWhileNode(node) {
|
|
2852
|
-
return node instanceof ERBWhileNode || node.type === "AST_ERB_WHILE_NODE";
|
|
3026
|
+
return node instanceof ERBWhileNode || node.type === "AST_ERB_WHILE_NODE" || node.constructor.type === "AST_ERB_WHILE_NODE";
|
|
2853
3027
|
}
|
|
2854
3028
|
/**
|
|
2855
3029
|
* Checks if a node is a ERBUntilNode
|
|
2856
3030
|
*/
|
|
2857
3031
|
function isERBUntilNode(node) {
|
|
2858
|
-
return node instanceof ERBUntilNode || node.type === "AST_ERB_UNTIL_NODE";
|
|
3032
|
+
return node instanceof ERBUntilNode || node.type === "AST_ERB_UNTIL_NODE" || node.constructor.type === "AST_ERB_UNTIL_NODE";
|
|
2859
3033
|
}
|
|
2860
3034
|
/**
|
|
2861
3035
|
* Checks if a node is a ERBForNode
|
|
2862
3036
|
*/
|
|
2863
3037
|
function isERBForNode(node) {
|
|
2864
|
-
return node instanceof ERBForNode || node.type === "AST_ERB_FOR_NODE";
|
|
3038
|
+
return node instanceof ERBForNode || node.type === "AST_ERB_FOR_NODE" || node.constructor.type === "AST_ERB_FOR_NODE";
|
|
2865
3039
|
}
|
|
2866
3040
|
/**
|
|
2867
3041
|
* Checks if a node is a ERBRescueNode
|
|
2868
3042
|
*/
|
|
2869
3043
|
function isERBRescueNode(node) {
|
|
2870
|
-
return node instanceof ERBRescueNode || node.type === "AST_ERB_RESCUE_NODE";
|
|
3044
|
+
return node instanceof ERBRescueNode || node.type === "AST_ERB_RESCUE_NODE" || node.constructor.type === "AST_ERB_RESCUE_NODE";
|
|
2871
3045
|
}
|
|
2872
3046
|
/**
|
|
2873
3047
|
* Checks if a node is a ERBEnsureNode
|
|
2874
3048
|
*/
|
|
2875
3049
|
function isERBEnsureNode(node) {
|
|
2876
|
-
return node instanceof ERBEnsureNode || node.type === "AST_ERB_ENSURE_NODE";
|
|
3050
|
+
return node instanceof ERBEnsureNode || node.type === "AST_ERB_ENSURE_NODE" || node.constructor.type === "AST_ERB_ENSURE_NODE";
|
|
2877
3051
|
}
|
|
2878
3052
|
/**
|
|
2879
3053
|
* Checks if a node is a ERBBeginNode
|
|
2880
3054
|
*/
|
|
2881
3055
|
function isERBBeginNode(node) {
|
|
2882
|
-
return node instanceof ERBBeginNode || node.type === "AST_ERB_BEGIN_NODE";
|
|
3056
|
+
return node instanceof ERBBeginNode || node.type === "AST_ERB_BEGIN_NODE" || node.constructor.type === "AST_ERB_BEGIN_NODE";
|
|
2883
3057
|
}
|
|
2884
3058
|
/**
|
|
2885
3059
|
* Checks if a node is a ERBUnlessNode
|
|
2886
3060
|
*/
|
|
2887
3061
|
function isERBUnlessNode(node) {
|
|
2888
|
-
return node instanceof ERBUnlessNode || node.type === "AST_ERB_UNLESS_NODE";
|
|
3062
|
+
return node instanceof ERBUnlessNode || node.type === "AST_ERB_UNLESS_NODE" || node.constructor.type === "AST_ERB_UNLESS_NODE";
|
|
2889
3063
|
}
|
|
2890
3064
|
/**
|
|
2891
3065
|
* Checks if a node is a ERBYieldNode
|
|
2892
3066
|
*/
|
|
2893
3067
|
function isERBYieldNode(node) {
|
|
2894
|
-
return node instanceof ERBYieldNode || node.type === "AST_ERB_YIELD_NODE";
|
|
3068
|
+
return node instanceof ERBYieldNode || node.type === "AST_ERB_YIELD_NODE" || node.constructor.type === "AST_ERB_YIELD_NODE";
|
|
2895
3069
|
}
|
|
2896
3070
|
/**
|
|
2897
3071
|
* Checks if a node is a ERBInNode
|
|
2898
3072
|
*/
|
|
2899
3073
|
function isERBInNode(node) {
|
|
2900
|
-
return node instanceof ERBInNode || node.type === "AST_ERB_IN_NODE";
|
|
3074
|
+
return node instanceof ERBInNode || node.type === "AST_ERB_IN_NODE" || node.constructor.type === "AST_ERB_IN_NODE";
|
|
2901
3075
|
}
|
|
2902
3076
|
/**
|
|
2903
3077
|
* Checks if a node is any ERB node type
|
|
@@ -3066,6 +3240,8 @@ function filterNodes(nodes, ...types) {
|
|
|
3066
3240
|
return nodes.filter(node => isAnyOf(node, ...types));
|
|
3067
3241
|
}
|
|
3068
3242
|
function isNode(node, type) {
|
|
3243
|
+
if (!node)
|
|
3244
|
+
return false;
|
|
3069
3245
|
if (typeof type === 'string') {
|
|
3070
3246
|
const guard = AST_TYPE_GUARDS.get(type);
|
|
3071
3247
|
return guard ? guard(node) : false;
|
|
@@ -3089,7 +3265,11 @@ function isParseResult(object) {
|
|
|
3089
3265
|
* Checks if a node is an ERB output node (generates content: <%= %> or <%== %>)
|
|
3090
3266
|
*/
|
|
3091
3267
|
function isERBOutputNode(node) {
|
|
3092
|
-
|
|
3268
|
+
if (!isNode(node, ERBContentNode))
|
|
3269
|
+
return false;
|
|
3270
|
+
if (!node.tag_opening?.value)
|
|
3271
|
+
return false;
|
|
3272
|
+
return ["<%=", "<%=="].includes(node.tag_opening?.value);
|
|
3093
3273
|
}
|
|
3094
3274
|
/**
|
|
3095
3275
|
* Checks if a node is a non-output ERB node (control flow: <% %>)
|
|
@@ -3147,9 +3327,47 @@ function getTagName(node) {
|
|
|
3147
3327
|
function isCommentNode(node) {
|
|
3148
3328
|
return isNode(node, HTMLCommentNode) || (isERBNode(node) && !isERBControlFlowNode(node));
|
|
3149
3329
|
}
|
|
3330
|
+
/**
|
|
3331
|
+
* Compares two positions to determine if the first comes before the second
|
|
3332
|
+
* Returns true if pos1 comes before pos2 in source order
|
|
3333
|
+
* @param inclusive - If true, returns true when positions are equal
|
|
3334
|
+
*/
|
|
3335
|
+
function isPositionBefore(position1, position2, inclusive = false) {
|
|
3336
|
+
if (position1.line < position2.line)
|
|
3337
|
+
return true;
|
|
3338
|
+
if (position1.line > position2.line)
|
|
3339
|
+
return false;
|
|
3340
|
+
return inclusive ? position1.column <= position2.column : position1.column < position2.column;
|
|
3341
|
+
}
|
|
3342
|
+
/**
|
|
3343
|
+
* Compares two positions to determine if the first comes after the second
|
|
3344
|
+
* Returns true if pos1 comes after pos2 in source order
|
|
3345
|
+
* @param inclusive - If true, returns true when positions are equal
|
|
3346
|
+
*/
|
|
3347
|
+
function isPositionAfter(position1, position2, inclusive = false) {
|
|
3348
|
+
if (position1.line > position2.line)
|
|
3349
|
+
return true;
|
|
3350
|
+
if (position1.line < position2.line)
|
|
3351
|
+
return false;
|
|
3352
|
+
return inclusive ? position1.column >= position2.column : position1.column > position2.column;
|
|
3353
|
+
}
|
|
3354
|
+
/**
|
|
3355
|
+
* Gets nodes that end before the specified position
|
|
3356
|
+
* @param inclusive - If true, includes nodes that end exactly at the position (default: false, matching half-open interval semantics)
|
|
3357
|
+
*/
|
|
3358
|
+
function getNodesBeforePosition(nodes, position, inclusive = false) {
|
|
3359
|
+
return nodes.filter(node => node.location && isPositionBefore(node.location.end, position, inclusive));
|
|
3360
|
+
}
|
|
3361
|
+
/**
|
|
3362
|
+
* Gets nodes that start after the specified position
|
|
3363
|
+
* @param inclusive - If true, includes nodes that start exactly at the position (default: true, matching typical boundary behavior)
|
|
3364
|
+
*/
|
|
3365
|
+
function getNodesAfterPosition(nodes, position, inclusive = true) {
|
|
3366
|
+
return nodes.filter(node => node.location && isPositionAfter(node.location.start, position, inclusive));
|
|
3367
|
+
}
|
|
3150
3368
|
|
|
3151
3369
|
// NOTE: This file is generated by the templates/template.rb script and should not
|
|
3152
|
-
// be modified manually. See /Users/marcoroth/Development/herb-release-0.
|
|
3370
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.7.0/templates/javascript/packages/core/src/visitor.ts.erb
|
|
3153
3371
|
class Visitor {
|
|
3154
3372
|
visit(node) {
|
|
3155
3373
|
if (!node)
|
|
@@ -3378,9 +3596,16 @@ class Printer extends Visitor {
|
|
|
3378
3596
|
* @throws {Error} When node has parse errors and ignoreErrors is false
|
|
3379
3597
|
*/
|
|
3380
3598
|
print(input, options = DEFAULT_PRINT_OPTIONS) {
|
|
3599
|
+
if (!input)
|
|
3600
|
+
return "";
|
|
3381
3601
|
if (isToken(input)) {
|
|
3382
3602
|
return input.value;
|
|
3383
3603
|
}
|
|
3604
|
+
if (Array.isArray(input)) {
|
|
3605
|
+
this.context.reset();
|
|
3606
|
+
input.forEach(node => this.visit(node));
|
|
3607
|
+
return this.context.getOutput();
|
|
3608
|
+
}
|
|
3384
3609
|
const node = isParseResult(input) ? input.value : input;
|
|
3385
3610
|
if (options.ignoreErrors === false && node.recursiveErrors().length > 0) {
|
|
3386
3611
|
throw new Error(`Cannot print the node (${node.type}) since it or any of its children has parse errors. Either pass in a valid Node or call \`print()\` using \`print(node, { ignoreErrors: true })\``);
|
|
@@ -3389,11 +3614,23 @@ class Printer extends Visitor {
|
|
|
3389
3614
|
this.visit(node);
|
|
3390
3615
|
return this.context.getOutput();
|
|
3391
3616
|
}
|
|
3392
|
-
|
|
3393
|
-
this.
|
|
3617
|
+
write(content) {
|
|
3618
|
+
this.context.write(content);
|
|
3394
3619
|
}
|
|
3620
|
+
}
|
|
3621
|
+
|
|
3622
|
+
/**
|
|
3623
|
+
* IdentityPrinter - Provides lossless reconstruction of the original source
|
|
3624
|
+
*
|
|
3625
|
+
* This printer aims to reconstruct the original input as faithfully as possible,
|
|
3626
|
+
* preserving all whitespace, formatting, and structure. It's useful for:
|
|
3627
|
+
* - Testing parser accuracy (input should equal output)
|
|
3628
|
+
* - Baseline printing before applying transformations
|
|
3629
|
+
* - Verifying AST round-trip fidelity
|
|
3630
|
+
*/
|
|
3631
|
+
class IdentityPrinter extends Printer {
|
|
3395
3632
|
visitLiteralNode(node) {
|
|
3396
|
-
this.
|
|
3633
|
+
this.write(node.content);
|
|
3397
3634
|
}
|
|
3398
3635
|
visitHTMLTextNode(node) {
|
|
3399
3636
|
this.write(node.content);
|
|
@@ -3405,25 +3642,32 @@ class Printer extends Visitor {
|
|
|
3405
3642
|
}
|
|
3406
3643
|
visitHTMLOpenTagNode(node) {
|
|
3407
3644
|
if (node.tag_opening) {
|
|
3408
|
-
this.
|
|
3645
|
+
this.write(node.tag_opening.value);
|
|
3409
3646
|
}
|
|
3410
3647
|
if (node.tag_name) {
|
|
3411
|
-
this.
|
|
3648
|
+
this.write(node.tag_name.value);
|
|
3412
3649
|
}
|
|
3413
3650
|
this.visitChildNodes(node);
|
|
3414
3651
|
if (node.tag_closing) {
|
|
3415
|
-
this.
|
|
3652
|
+
this.write(node.tag_closing.value);
|
|
3416
3653
|
}
|
|
3417
3654
|
}
|
|
3418
3655
|
visitHTMLCloseTagNode(node) {
|
|
3419
3656
|
if (node.tag_opening) {
|
|
3420
|
-
this.
|
|
3657
|
+
this.write(node.tag_opening.value);
|
|
3421
3658
|
}
|
|
3422
3659
|
if (node.tag_name) {
|
|
3423
|
-
|
|
3660
|
+
const before = getNodesBeforePosition(node.children, node.tag_name.location.start, true);
|
|
3661
|
+
const after = getNodesAfterPosition(node.children, node.tag_name.location.end);
|
|
3662
|
+
this.visitAll(before);
|
|
3663
|
+
this.write(node.tag_name.value);
|
|
3664
|
+
this.visitAll(after);
|
|
3665
|
+
}
|
|
3666
|
+
else {
|
|
3667
|
+
this.visitAll(node.children);
|
|
3424
3668
|
}
|
|
3425
3669
|
if (node.tag_closing) {
|
|
3426
|
-
this.
|
|
3670
|
+
this.write(node.tag_closing.value);
|
|
3427
3671
|
}
|
|
3428
3672
|
}
|
|
3429
3673
|
visitHTMLElementNode(node) {
|
|
@@ -3449,7 +3693,7 @@ class Printer extends Visitor {
|
|
|
3449
3693
|
this.visit(node.name);
|
|
3450
3694
|
}
|
|
3451
3695
|
if (node.equals) {
|
|
3452
|
-
this.
|
|
3696
|
+
this.write(node.equals.value);
|
|
3453
3697
|
}
|
|
3454
3698
|
if (node.equals && node.value) {
|
|
3455
3699
|
this.visit(node.value);
|
|
@@ -3460,47 +3704,47 @@ class Printer extends Visitor {
|
|
|
3460
3704
|
}
|
|
3461
3705
|
visitHTMLAttributeValueNode(node) {
|
|
3462
3706
|
if (node.quoted && node.open_quote) {
|
|
3463
|
-
this.
|
|
3707
|
+
this.write(node.open_quote.value);
|
|
3464
3708
|
}
|
|
3465
3709
|
this.visitChildNodes(node);
|
|
3466
3710
|
if (node.quoted && node.close_quote) {
|
|
3467
|
-
this.
|
|
3711
|
+
this.write(node.close_quote.value);
|
|
3468
3712
|
}
|
|
3469
3713
|
}
|
|
3470
3714
|
visitHTMLCommentNode(node) {
|
|
3471
3715
|
if (node.comment_start) {
|
|
3472
|
-
this.
|
|
3716
|
+
this.write(node.comment_start.value);
|
|
3473
3717
|
}
|
|
3474
3718
|
this.visitChildNodes(node);
|
|
3475
3719
|
if (node.comment_end) {
|
|
3476
|
-
this.
|
|
3720
|
+
this.write(node.comment_end.value);
|
|
3477
3721
|
}
|
|
3478
3722
|
}
|
|
3479
3723
|
visitHTMLDoctypeNode(node) {
|
|
3480
3724
|
if (node.tag_opening) {
|
|
3481
|
-
this.
|
|
3725
|
+
this.write(node.tag_opening.value);
|
|
3482
3726
|
}
|
|
3483
3727
|
this.visitChildNodes(node);
|
|
3484
3728
|
if (node.tag_closing) {
|
|
3485
|
-
this.
|
|
3729
|
+
this.write(node.tag_closing.value);
|
|
3486
3730
|
}
|
|
3487
3731
|
}
|
|
3488
3732
|
visitXMLDeclarationNode(node) {
|
|
3489
3733
|
if (node.tag_opening) {
|
|
3490
|
-
this.
|
|
3734
|
+
this.write(node.tag_opening.value);
|
|
3491
3735
|
}
|
|
3492
3736
|
this.visitChildNodes(node);
|
|
3493
3737
|
if (node.tag_closing) {
|
|
3494
|
-
this.
|
|
3738
|
+
this.write(node.tag_closing.value);
|
|
3495
3739
|
}
|
|
3496
3740
|
}
|
|
3497
3741
|
visitCDATANode(node) {
|
|
3498
3742
|
if (node.tag_opening) {
|
|
3499
|
-
this.
|
|
3743
|
+
this.write(node.tag_opening.value);
|
|
3500
3744
|
}
|
|
3501
3745
|
this.visitChildNodes(node);
|
|
3502
3746
|
if (node.tag_closing) {
|
|
3503
|
-
this.
|
|
3747
|
+
this.write(node.tag_closing.value);
|
|
3504
3748
|
}
|
|
3505
3749
|
}
|
|
3506
3750
|
visitERBContentNode(node) {
|
|
@@ -3658,31 +3902,19 @@ class Printer extends Visitor {
|
|
|
3658
3902
|
*/
|
|
3659
3903
|
printERBNode(node) {
|
|
3660
3904
|
if (node.tag_opening) {
|
|
3661
|
-
this.
|
|
3905
|
+
this.write(node.tag_opening.value);
|
|
3662
3906
|
}
|
|
3663
3907
|
if (node.content) {
|
|
3664
|
-
this.
|
|
3908
|
+
this.write(node.content.value);
|
|
3665
3909
|
}
|
|
3666
3910
|
if (node.tag_closing) {
|
|
3667
|
-
this.
|
|
3911
|
+
this.write(node.tag_closing.value);
|
|
3668
3912
|
}
|
|
3669
3913
|
}
|
|
3670
|
-
write(content) {
|
|
3671
|
-
this.context.write(content);
|
|
3672
|
-
}
|
|
3673
3914
|
}
|
|
3674
3915
|
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
*
|
|
3678
|
-
* This printer aims to reconstruct the original input as faithfully as possible,
|
|
3679
|
-
* preserving all whitespace, formatting, and structure. It's useful for:
|
|
3680
|
-
* - Testing parser accuracy (input should equal output)
|
|
3681
|
-
* - Baseline printing before applying transformations
|
|
3682
|
-
* - Verifying AST round-trip fidelity
|
|
3683
|
-
*/
|
|
3684
|
-
class IdentityPrinter extends Printer {
|
|
3685
|
-
}
|
|
3916
|
+
({
|
|
3917
|
+
...DEFAULT_PRINT_OPTIONS});
|
|
3686
3918
|
|
|
3687
3919
|
// TODO: we can probably expand this list with more tags/attributes
|
|
3688
3920
|
const FORMATTABLE_ATTRIBUTES = {
|
|
@@ -3719,12 +3951,16 @@ class FormatPrinter extends Printer {
|
|
|
3719
3951
|
'samp', 'small', 'span', 'strong', 'sub', 'sup',
|
|
3720
3952
|
'tt', 'var', 'del', 'ins', 'mark', 's', 'u', 'time', 'wbr'
|
|
3721
3953
|
]);
|
|
3954
|
+
static CONTENT_PRESERVING_ELEMENTS = new Set([
|
|
3955
|
+
'script', 'style', 'pre', 'textarea'
|
|
3956
|
+
]);
|
|
3722
3957
|
static SPACEABLE_CONTAINERS = new Set([
|
|
3723
3958
|
'div', 'section', 'article', 'main', 'header', 'footer', 'aside',
|
|
3724
3959
|
'figure', 'details', 'summary', 'dialog', 'fieldset'
|
|
3725
3960
|
]);
|
|
3726
3961
|
static TIGHT_GROUP_PARENTS = new Set([
|
|
3727
|
-
'ul', 'ol', 'nav', 'select', 'datalist', 'optgroup', 'tr', 'thead',
|
|
3962
|
+
'ul', 'ol', 'nav', 'select', 'datalist', 'optgroup', 'tr', 'thead',
|
|
3963
|
+
'tbody', 'tfoot'
|
|
3728
3964
|
]);
|
|
3729
3965
|
static TIGHT_GROUP_CHILDREN = new Set([
|
|
3730
3966
|
'li', 'option', 'td', 'th', 'dt', 'dd'
|
|
@@ -3822,6 +4058,13 @@ class FormatPrinter extends Printer {
|
|
|
3822
4058
|
push(line) {
|
|
3823
4059
|
this.lines.push(line);
|
|
3824
4060
|
}
|
|
4061
|
+
/**
|
|
4062
|
+
* @deprecated refactor to use @herb-tools/printer infrastructre (or rework printer use push and this.lines)
|
|
4063
|
+
*/
|
|
4064
|
+
pushWithIndent(line) {
|
|
4065
|
+
const indent = line.trim() === "" ? "" : this.indent;
|
|
4066
|
+
this.push(indent + line);
|
|
4067
|
+
}
|
|
3825
4068
|
withIndent(callback) {
|
|
3826
4069
|
this.indentLevel++;
|
|
3827
4070
|
const result = callback();
|
|
@@ -4022,20 +4265,14 @@ class FormatPrinter extends Printer {
|
|
|
4022
4265
|
return false;
|
|
4023
4266
|
}
|
|
4024
4267
|
getAttributeValue(attribute) {
|
|
4025
|
-
if (
|
|
4026
|
-
|
|
4027
|
-
if (isNode(child, HTMLTextNode)) {
|
|
4028
|
-
return child.content;
|
|
4029
|
-
}
|
|
4030
|
-
return IdentityPrinter.print(child);
|
|
4031
|
-
}).join('');
|
|
4032
|
-
return content;
|
|
4268
|
+
if (isNode(attribute.value, HTMLAttributeValueNode)) {
|
|
4269
|
+
return attribute.value.children.map(child => isNode(child, HTMLTextNode) ? child.content : IdentityPrinter.print(child)).join('');
|
|
4033
4270
|
}
|
|
4034
4271
|
return '';
|
|
4035
4272
|
}
|
|
4036
4273
|
hasMultilineAttributes(attributes) {
|
|
4037
4274
|
return attributes.some(attribute => {
|
|
4038
|
-
if (
|
|
4275
|
+
if (isNode(attribute.value, HTMLAttributeValueNode)) {
|
|
4039
4276
|
const content = getCombinedStringFromNodes(attribute.value.children);
|
|
4040
4277
|
if (/\r?\n/.test(content)) {
|
|
4041
4278
|
const name = attribute.name ? getCombinedAttributeName(attribute.name) : "";
|
|
@@ -4123,11 +4360,11 @@ class FormatPrinter extends Printer {
|
|
|
4123
4360
|
* Render multiline attributes for a tag
|
|
4124
4361
|
*/
|
|
4125
4362
|
renderMultilineAttributes(tagName, allChildren = [], isSelfClosing = false) {
|
|
4126
|
-
this.
|
|
4363
|
+
this.pushWithIndent(`<${tagName}`);
|
|
4127
4364
|
this.withIndent(() => {
|
|
4128
4365
|
allChildren.forEach(child => {
|
|
4129
4366
|
if (isNode(child, HTMLAttributeNode)) {
|
|
4130
|
-
this.
|
|
4367
|
+
this.pushWithIndent(this.renderAttribute(child));
|
|
4131
4368
|
}
|
|
4132
4369
|
else if (!isNode(child, WhitespaceNode)) {
|
|
4133
4370
|
this.visit(child);
|
|
@@ -4135,10 +4372,10 @@ class FormatPrinter extends Printer {
|
|
|
4135
4372
|
});
|
|
4136
4373
|
});
|
|
4137
4374
|
if (isSelfClosing) {
|
|
4138
|
-
this.
|
|
4375
|
+
this.pushWithIndent("/>");
|
|
4139
4376
|
}
|
|
4140
4377
|
else {
|
|
4141
|
-
this.
|
|
4378
|
+
this.pushWithIndent(">");
|
|
4142
4379
|
}
|
|
4143
4380
|
}
|
|
4144
4381
|
/**
|
|
@@ -4202,6 +4439,10 @@ class FormatPrinter extends Printer {
|
|
|
4202
4439
|
this.elementStack.pop();
|
|
4203
4440
|
}
|
|
4204
4441
|
visitHTMLElementBody(body, element) {
|
|
4442
|
+
if (this.isContentPreserving(element)) {
|
|
4443
|
+
element.body.map(child => this.pushToLastLine(IdentityPrinter.print(child)));
|
|
4444
|
+
return;
|
|
4445
|
+
}
|
|
4205
4446
|
const analysis = this.elementFormattingAnalysis.get(element);
|
|
4206
4447
|
const hasTextFlow = this.isInTextFlowContext(null, body);
|
|
4207
4448
|
const children = this.filterSignificantChildren(body, hasTextFlow);
|
|
@@ -4327,7 +4568,7 @@ class FormatPrinter extends Printer {
|
|
|
4327
4568
|
this.pushToLastLine(closingTag);
|
|
4328
4569
|
}
|
|
4329
4570
|
else {
|
|
4330
|
-
this.
|
|
4571
|
+
this.pushWithIndent(closingTag);
|
|
4331
4572
|
}
|
|
4332
4573
|
}
|
|
4333
4574
|
visitHTMLTextNode(node) {
|
|
@@ -4359,13 +4600,13 @@ class FormatPrinter extends Printer {
|
|
|
4359
4600
|
lines.forEach(line => this.push(line));
|
|
4360
4601
|
}
|
|
4361
4602
|
visitHTMLAttributeNode(node) {
|
|
4362
|
-
this.
|
|
4603
|
+
this.pushWithIndent(this.renderAttribute(node));
|
|
4363
4604
|
}
|
|
4364
4605
|
visitHTMLAttributeNameNode(node) {
|
|
4365
|
-
this.
|
|
4606
|
+
this.pushWithIndent(getCombinedAttributeName(node));
|
|
4366
4607
|
}
|
|
4367
4608
|
visitHTMLAttributeValueNode(node) {
|
|
4368
|
-
this.
|
|
4609
|
+
this.pushWithIndent(IdentityPrinter.print(node));
|
|
4369
4610
|
}
|
|
4370
4611
|
// TODO: rework
|
|
4371
4612
|
visitHTMLCommentNode(node) {
|
|
@@ -4420,37 +4661,40 @@ class FormatPrinter extends Printer {
|
|
|
4420
4661
|
else {
|
|
4421
4662
|
inner = "";
|
|
4422
4663
|
}
|
|
4423
|
-
this.
|
|
4664
|
+
this.pushWithIndent(open + inner + close);
|
|
4424
4665
|
}
|
|
4425
4666
|
visitERBCommentNode(node) {
|
|
4426
|
-
const open = node.tag_opening?.value
|
|
4427
|
-
const
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
}
|
|
4437
|
-
else {
|
|
4438
|
-
inner = ` ${rawInner.trim()} `;
|
|
4439
|
-
}
|
|
4667
|
+
const open = node.tag_opening?.value || "<%#";
|
|
4668
|
+
const content = node?.content?.value || "";
|
|
4669
|
+
const close = node.tag_closing?.value || "%>";
|
|
4670
|
+
const contentLines = content.split("\n");
|
|
4671
|
+
const contentTrimmedLines = content.trim().split("\n");
|
|
4672
|
+
if (contentLines.length === 1 && contentTrimmedLines.length === 1) {
|
|
4673
|
+
const startsWithSpace = content[0] === " ";
|
|
4674
|
+
const before = startsWithSpace ? "" : " ";
|
|
4675
|
+
this.pushWithIndent(open + before + content.trimEnd() + ' ' + close);
|
|
4676
|
+
return;
|
|
4440
4677
|
}
|
|
4441
|
-
|
|
4442
|
-
|
|
4678
|
+
if (contentTrimmedLines.length === 1) {
|
|
4679
|
+
this.pushWithIndent(open + ' ' + content.trim() + ' ' + close);
|
|
4680
|
+
return;
|
|
4443
4681
|
}
|
|
4444
|
-
|
|
4682
|
+
const firstLineEmpty = contentLines[0].trim() === "";
|
|
4683
|
+
const dedentedContent = dedent(firstLineEmpty ? content : content.trimStart());
|
|
4684
|
+
this.pushWithIndent(open);
|
|
4685
|
+
this.withIndent(() => {
|
|
4686
|
+
dedentedContent.split("\n").forEach(line => this.pushWithIndent(line));
|
|
4687
|
+
});
|
|
4688
|
+
this.pushWithIndent(close);
|
|
4445
4689
|
}
|
|
4446
4690
|
visitHTMLDoctypeNode(node) {
|
|
4447
|
-
this.
|
|
4691
|
+
this.pushWithIndent(IdentityPrinter.print(node));
|
|
4448
4692
|
}
|
|
4449
4693
|
visitXMLDeclarationNode(node) {
|
|
4450
|
-
this.
|
|
4694
|
+
this.pushWithIndent(IdentityPrinter.print(node));
|
|
4451
4695
|
}
|
|
4452
4696
|
visitCDATANode(node) {
|
|
4453
|
-
this.
|
|
4697
|
+
this.pushWithIndent(IdentityPrinter.print(node));
|
|
4454
4698
|
}
|
|
4455
4699
|
visitERBContentNode(node) {
|
|
4456
4700
|
// TODO: this feels hacky
|
|
@@ -4666,8 +4910,11 @@ class FormatPrinter extends Printer {
|
|
|
4666
4910
|
* Determines if the close tag should be rendered inline (usually follows content decision)
|
|
4667
4911
|
*/
|
|
4668
4912
|
shouldRenderCloseTagInline(node, elementContentInline) {
|
|
4669
|
-
|
|
4670
|
-
|
|
4913
|
+
if (node.is_void)
|
|
4914
|
+
return true;
|
|
4915
|
+
if (node.open_tag?.tag_closing?.value === "/>")
|
|
4916
|
+
return true;
|
|
4917
|
+
if (this.isContentPreserving(node))
|
|
4671
4918
|
return true;
|
|
4672
4919
|
const children = this.filterSignificantChildren(node.body, this.isInTextFlowContext(null, node.body));
|
|
4673
4920
|
if (children.length === 0)
|
|
@@ -4726,7 +4973,7 @@ class FormatPrinter extends Printer {
|
|
|
4726
4973
|
}
|
|
4727
4974
|
else {
|
|
4728
4975
|
if (currentLineContent.trim()) {
|
|
4729
|
-
this.
|
|
4976
|
+
this.pushWithIndent(currentLineContent.trim());
|
|
4730
4977
|
currentLineContent = "";
|
|
4731
4978
|
}
|
|
4732
4979
|
this.visit(child);
|
|
@@ -4734,7 +4981,7 @@ class FormatPrinter extends Printer {
|
|
|
4734
4981
|
}
|
|
4735
4982
|
else {
|
|
4736
4983
|
if (currentLineContent.trim()) {
|
|
4737
|
-
this.
|
|
4984
|
+
this.pushWithIndent(currentLineContent.trim());
|
|
4738
4985
|
currentLineContent = "";
|
|
4739
4986
|
}
|
|
4740
4987
|
this.visit(child);
|
|
@@ -4764,7 +5011,7 @@ class FormatPrinter extends Printer {
|
|
|
4764
5011
|
}
|
|
4765
5012
|
else {
|
|
4766
5013
|
if (currentLineContent.trim()) {
|
|
4767
|
-
this.
|
|
5014
|
+
this.pushWithIndent(currentLineContent.trim());
|
|
4768
5015
|
currentLineContent = "";
|
|
4769
5016
|
}
|
|
4770
5017
|
this.visit(child);
|
|
@@ -4845,7 +5092,7 @@ class FormatPrinter extends Printer {
|
|
|
4845
5092
|
const equals = attribute.equals?.value ?? "";
|
|
4846
5093
|
this.currentAttributeName = name;
|
|
4847
5094
|
let value = "";
|
|
4848
|
-
if (
|
|
5095
|
+
if (isNode(attribute.value, HTMLAttributeValueNode)) {
|
|
4849
5096
|
const attributeValue = attribute.value;
|
|
4850
5097
|
let open_quote = attributeValue.open_quote?.value ?? "";
|
|
4851
5098
|
let close_quote = attributeValue.close_quote?.value ?? "";
|
|
@@ -5094,6 +5341,10 @@ class FormatPrinter extends Printer {
|
|
|
5094
5341
|
}
|
|
5095
5342
|
return content.replace(/\s+/g, ' ').trim();
|
|
5096
5343
|
}
|
|
5344
|
+
isContentPreserving(element) {
|
|
5345
|
+
const tagName = getTagName(element);
|
|
5346
|
+
return FormatPrinter.CONTENT_PRESERVING_ELEMENTS.has(tagName);
|
|
5347
|
+
}
|
|
5097
5348
|
}
|
|
5098
5349
|
|
|
5099
5350
|
/**
|