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