@herb-tools/formatter 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/herb-format.js +398 -123
- package/dist/herb-format.js.map +1 -1
- package/dist/index.cjs +357 -117
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +357 -117
- package/dist/index.esm.js.map +1 -1
- package/dist/types/format-printer.d.ts +6 -0
- package/package.json +3 -3
- package/src/format-printer.ts +76 -46
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.6.
|
|
199
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.6.1/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.6.
|
|
649
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.6.1/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
|
}
|
|
@@ -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,9 @@ class HTMLElementNode extends Node {
|
|
|
863
951
|
body;
|
|
864
952
|
close_tag;
|
|
865
953
|
is_void;
|
|
954
|
+
static get type() {
|
|
955
|
+
return "AST_HTML_ELEMENT_NODE";
|
|
956
|
+
}
|
|
866
957
|
static from(data) {
|
|
867
958
|
return new HTMLElementNode({
|
|
868
959
|
type: data.type,
|
|
@@ -932,6 +1023,9 @@ class HTMLAttributeValueNode extends Node {
|
|
|
932
1023
|
children;
|
|
933
1024
|
close_quote;
|
|
934
1025
|
quoted;
|
|
1026
|
+
static get type() {
|
|
1027
|
+
return "AST_HTML_ATTRIBUTE_VALUE_NODE";
|
|
1028
|
+
}
|
|
935
1029
|
static from(data) {
|
|
936
1030
|
return new HTMLAttributeValueNode({
|
|
937
1031
|
type: data.type,
|
|
@@ -990,6 +1084,9 @@ class HTMLAttributeValueNode extends Node {
|
|
|
990
1084
|
}
|
|
991
1085
|
class HTMLAttributeNameNode extends Node {
|
|
992
1086
|
children;
|
|
1087
|
+
static get type() {
|
|
1088
|
+
return "AST_HTML_ATTRIBUTE_NAME_NODE";
|
|
1089
|
+
}
|
|
993
1090
|
static from(data) {
|
|
994
1091
|
return new HTMLAttributeNameNode({
|
|
995
1092
|
type: data.type,
|
|
@@ -1038,6 +1135,9 @@ class HTMLAttributeNode extends Node {
|
|
|
1038
1135
|
name;
|
|
1039
1136
|
equals;
|
|
1040
1137
|
value;
|
|
1138
|
+
static get type() {
|
|
1139
|
+
return "AST_HTML_ATTRIBUTE_NODE";
|
|
1140
|
+
}
|
|
1041
1141
|
static from(data) {
|
|
1042
1142
|
return new HTMLAttributeNode({
|
|
1043
1143
|
type: data.type,
|
|
@@ -1094,6 +1194,9 @@ class HTMLAttributeNode extends Node {
|
|
|
1094
1194
|
}
|
|
1095
1195
|
class HTMLTextNode extends Node {
|
|
1096
1196
|
content;
|
|
1197
|
+
static get type() {
|
|
1198
|
+
return "AST_HTML_TEXT_NODE";
|
|
1199
|
+
}
|
|
1097
1200
|
static from(data) {
|
|
1098
1201
|
return new HTMLTextNode({
|
|
1099
1202
|
type: data.type,
|
|
@@ -1139,6 +1242,9 @@ class HTMLCommentNode extends Node {
|
|
|
1139
1242
|
comment_start;
|
|
1140
1243
|
children;
|
|
1141
1244
|
comment_end;
|
|
1245
|
+
static get type() {
|
|
1246
|
+
return "AST_HTML_COMMENT_NODE";
|
|
1247
|
+
}
|
|
1142
1248
|
static from(data) {
|
|
1143
1249
|
return new HTMLCommentNode({
|
|
1144
1250
|
type: data.type,
|
|
@@ -1195,6 +1301,9 @@ class HTMLDoctypeNode extends Node {
|
|
|
1195
1301
|
tag_opening;
|
|
1196
1302
|
children;
|
|
1197
1303
|
tag_closing;
|
|
1304
|
+
static get type() {
|
|
1305
|
+
return "AST_HTML_DOCTYPE_NODE";
|
|
1306
|
+
}
|
|
1198
1307
|
static from(data) {
|
|
1199
1308
|
return new HTMLDoctypeNode({
|
|
1200
1309
|
type: data.type,
|
|
@@ -1251,6 +1360,9 @@ class XMLDeclarationNode extends Node {
|
|
|
1251
1360
|
tag_opening;
|
|
1252
1361
|
children;
|
|
1253
1362
|
tag_closing;
|
|
1363
|
+
static get type() {
|
|
1364
|
+
return "AST_XML_DECLARATION_NODE";
|
|
1365
|
+
}
|
|
1254
1366
|
static from(data) {
|
|
1255
1367
|
return new XMLDeclarationNode({
|
|
1256
1368
|
type: data.type,
|
|
@@ -1307,6 +1419,9 @@ class CDATANode extends Node {
|
|
|
1307
1419
|
tag_opening;
|
|
1308
1420
|
children;
|
|
1309
1421
|
tag_closing;
|
|
1422
|
+
static get type() {
|
|
1423
|
+
return "AST_CDATA_NODE";
|
|
1424
|
+
}
|
|
1310
1425
|
static from(data) {
|
|
1311
1426
|
return new CDATANode({
|
|
1312
1427
|
type: data.type,
|
|
@@ -1361,6 +1476,9 @@ class CDATANode extends Node {
|
|
|
1361
1476
|
}
|
|
1362
1477
|
class WhitespaceNode extends Node {
|
|
1363
1478
|
value;
|
|
1479
|
+
static get type() {
|
|
1480
|
+
return "AST_WHITESPACE_NODE";
|
|
1481
|
+
}
|
|
1364
1482
|
static from(data) {
|
|
1365
1483
|
return new WhitespaceNode({
|
|
1366
1484
|
type: data.type,
|
|
@@ -1409,6 +1527,9 @@ class ERBContentNode extends Node {
|
|
|
1409
1527
|
// no-op for analyzed_ruby
|
|
1410
1528
|
parsed;
|
|
1411
1529
|
valid;
|
|
1530
|
+
static get type() {
|
|
1531
|
+
return "AST_ERB_CONTENT_NODE";
|
|
1532
|
+
}
|
|
1412
1533
|
static from(data) {
|
|
1413
1534
|
return new ERBContentNode({
|
|
1414
1535
|
type: data.type,
|
|
@@ -1474,6 +1595,9 @@ class ERBEndNode extends Node {
|
|
|
1474
1595
|
tag_opening;
|
|
1475
1596
|
content;
|
|
1476
1597
|
tag_closing;
|
|
1598
|
+
static get type() {
|
|
1599
|
+
return "AST_ERB_END_NODE";
|
|
1600
|
+
}
|
|
1477
1601
|
static from(data) {
|
|
1478
1602
|
return new ERBEndNode({
|
|
1479
1603
|
type: data.type,
|
|
@@ -1528,6 +1652,9 @@ class ERBElseNode extends Node {
|
|
|
1528
1652
|
content;
|
|
1529
1653
|
tag_closing;
|
|
1530
1654
|
statements;
|
|
1655
|
+
static get type() {
|
|
1656
|
+
return "AST_ERB_ELSE_NODE";
|
|
1657
|
+
}
|
|
1531
1658
|
static from(data) {
|
|
1532
1659
|
return new ERBElseNode({
|
|
1533
1660
|
type: data.type,
|
|
@@ -1591,6 +1718,9 @@ class ERBIfNode extends Node {
|
|
|
1591
1718
|
statements;
|
|
1592
1719
|
subsequent;
|
|
1593
1720
|
end_node;
|
|
1721
|
+
static get type() {
|
|
1722
|
+
return "AST_ERB_IF_NODE";
|
|
1723
|
+
}
|
|
1594
1724
|
static from(data) {
|
|
1595
1725
|
return new ERBIfNode({
|
|
1596
1726
|
type: data.type,
|
|
@@ -1665,6 +1795,9 @@ class ERBBlockNode extends Node {
|
|
|
1665
1795
|
tag_closing;
|
|
1666
1796
|
body;
|
|
1667
1797
|
end_node;
|
|
1798
|
+
static get type() {
|
|
1799
|
+
return "AST_ERB_BLOCK_NODE";
|
|
1800
|
+
}
|
|
1668
1801
|
static from(data) {
|
|
1669
1802
|
return new ERBBlockNode({
|
|
1670
1803
|
type: data.type,
|
|
@@ -1732,6 +1865,9 @@ class ERBWhenNode extends Node {
|
|
|
1732
1865
|
content;
|
|
1733
1866
|
tag_closing;
|
|
1734
1867
|
statements;
|
|
1868
|
+
static get type() {
|
|
1869
|
+
return "AST_ERB_WHEN_NODE";
|
|
1870
|
+
}
|
|
1735
1871
|
static from(data) {
|
|
1736
1872
|
return new ERBWhenNode({
|
|
1737
1873
|
type: data.type,
|
|
@@ -1796,6 +1932,9 @@ class ERBCaseNode extends Node {
|
|
|
1796
1932
|
conditions;
|
|
1797
1933
|
else_clause;
|
|
1798
1934
|
end_node;
|
|
1935
|
+
static get type() {
|
|
1936
|
+
return "AST_ERB_CASE_NODE";
|
|
1937
|
+
}
|
|
1799
1938
|
static from(data) {
|
|
1800
1939
|
return new ERBCaseNode({
|
|
1801
1940
|
type: data.type,
|
|
@@ -1878,6 +2017,9 @@ class ERBCaseMatchNode extends Node {
|
|
|
1878
2017
|
conditions;
|
|
1879
2018
|
else_clause;
|
|
1880
2019
|
end_node;
|
|
2020
|
+
static get type() {
|
|
2021
|
+
return "AST_ERB_CASE_MATCH_NODE";
|
|
2022
|
+
}
|
|
1881
2023
|
static from(data) {
|
|
1882
2024
|
return new ERBCaseMatchNode({
|
|
1883
2025
|
type: data.type,
|
|
@@ -1958,6 +2100,9 @@ class ERBWhileNode extends Node {
|
|
|
1958
2100
|
tag_closing;
|
|
1959
2101
|
statements;
|
|
1960
2102
|
end_node;
|
|
2103
|
+
static get type() {
|
|
2104
|
+
return "AST_ERB_WHILE_NODE";
|
|
2105
|
+
}
|
|
1961
2106
|
static from(data) {
|
|
1962
2107
|
return new ERBWhileNode({
|
|
1963
2108
|
type: data.type,
|
|
@@ -2026,6 +2171,9 @@ class ERBUntilNode extends Node {
|
|
|
2026
2171
|
tag_closing;
|
|
2027
2172
|
statements;
|
|
2028
2173
|
end_node;
|
|
2174
|
+
static get type() {
|
|
2175
|
+
return "AST_ERB_UNTIL_NODE";
|
|
2176
|
+
}
|
|
2029
2177
|
static from(data) {
|
|
2030
2178
|
return new ERBUntilNode({
|
|
2031
2179
|
type: data.type,
|
|
@@ -2094,6 +2242,9 @@ class ERBForNode extends Node {
|
|
|
2094
2242
|
tag_closing;
|
|
2095
2243
|
statements;
|
|
2096
2244
|
end_node;
|
|
2245
|
+
static get type() {
|
|
2246
|
+
return "AST_ERB_FOR_NODE";
|
|
2247
|
+
}
|
|
2097
2248
|
static from(data) {
|
|
2098
2249
|
return new ERBForNode({
|
|
2099
2250
|
type: data.type,
|
|
@@ -2162,6 +2313,9 @@ class ERBRescueNode extends Node {
|
|
|
2162
2313
|
tag_closing;
|
|
2163
2314
|
statements;
|
|
2164
2315
|
subsequent;
|
|
2316
|
+
static get type() {
|
|
2317
|
+
return "AST_ERB_RESCUE_NODE";
|
|
2318
|
+
}
|
|
2165
2319
|
static from(data) {
|
|
2166
2320
|
return new ERBRescueNode({
|
|
2167
2321
|
type: data.type,
|
|
@@ -2229,6 +2383,9 @@ class ERBEnsureNode extends Node {
|
|
|
2229
2383
|
content;
|
|
2230
2384
|
tag_closing;
|
|
2231
2385
|
statements;
|
|
2386
|
+
static get type() {
|
|
2387
|
+
return "AST_ERB_ENSURE_NODE";
|
|
2388
|
+
}
|
|
2232
2389
|
static from(data) {
|
|
2233
2390
|
return new ERBEnsureNode({
|
|
2234
2391
|
type: data.type,
|
|
@@ -2294,6 +2451,9 @@ class ERBBeginNode extends Node {
|
|
|
2294
2451
|
else_clause;
|
|
2295
2452
|
ensure_clause;
|
|
2296
2453
|
end_node;
|
|
2454
|
+
static get type() {
|
|
2455
|
+
return "AST_ERB_BEGIN_NODE";
|
|
2456
|
+
}
|
|
2297
2457
|
static from(data) {
|
|
2298
2458
|
return new ERBBeginNode({
|
|
2299
2459
|
type: data.type,
|
|
@@ -2381,6 +2541,9 @@ class ERBUnlessNode extends Node {
|
|
|
2381
2541
|
statements;
|
|
2382
2542
|
else_clause;
|
|
2383
2543
|
end_node;
|
|
2544
|
+
static get type() {
|
|
2545
|
+
return "AST_ERB_UNLESS_NODE";
|
|
2546
|
+
}
|
|
2384
2547
|
static from(data) {
|
|
2385
2548
|
return new ERBUnlessNode({
|
|
2386
2549
|
type: data.type,
|
|
@@ -2453,6 +2616,9 @@ class ERBYieldNode extends Node {
|
|
|
2453
2616
|
tag_opening;
|
|
2454
2617
|
content;
|
|
2455
2618
|
tag_closing;
|
|
2619
|
+
static get type() {
|
|
2620
|
+
return "AST_ERB_YIELD_NODE";
|
|
2621
|
+
}
|
|
2456
2622
|
static from(data) {
|
|
2457
2623
|
return new ERBYieldNode({
|
|
2458
2624
|
type: data.type,
|
|
@@ -2507,6 +2673,9 @@ class ERBInNode extends Node {
|
|
|
2507
2673
|
content;
|
|
2508
2674
|
tag_closing;
|
|
2509
2675
|
statements;
|
|
2676
|
+
static get type() {
|
|
2677
|
+
return "AST_ERB_IN_NODE";
|
|
2678
|
+
}
|
|
2510
2679
|
static from(data) {
|
|
2511
2680
|
return new ERBInNode({
|
|
2512
2681
|
type: data.type,
|
|
@@ -2706,7 +2875,7 @@ class ParseResult extends Result {
|
|
|
2706
2875
|
}
|
|
2707
2876
|
|
|
2708
2877
|
// 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.6.
|
|
2878
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.6.1/templates/javascript/packages/core/src/node-type-guards.ts.erb
|
|
2710
2879
|
/**
|
|
2711
2880
|
* Type guard functions for AST nodes.
|
|
2712
2881
|
* These functions provide type checking by combining both instanceof
|
|
@@ -2717,187 +2886,187 @@ class ParseResult extends Result {
|
|
|
2717
2886
|
* Checks if a node is a DocumentNode
|
|
2718
2887
|
*/
|
|
2719
2888
|
function isDocumentNode(node) {
|
|
2720
|
-
return node instanceof DocumentNode || node.type === "AST_DOCUMENT_NODE";
|
|
2889
|
+
return node instanceof DocumentNode || node.type === "AST_DOCUMENT_NODE" || node.constructor.type === "AST_DOCUMENT_NODE";
|
|
2721
2890
|
}
|
|
2722
2891
|
/**
|
|
2723
2892
|
* Checks if a node is a LiteralNode
|
|
2724
2893
|
*/
|
|
2725
2894
|
function isLiteralNode(node) {
|
|
2726
|
-
return node instanceof LiteralNode || node.type === "AST_LITERAL_NODE";
|
|
2895
|
+
return node instanceof LiteralNode || node.type === "AST_LITERAL_NODE" || node.constructor.type === "AST_LITERAL_NODE";
|
|
2727
2896
|
}
|
|
2728
2897
|
/**
|
|
2729
2898
|
* Checks if a node is a HTMLOpenTagNode
|
|
2730
2899
|
*/
|
|
2731
2900
|
function isHTMLOpenTagNode(node) {
|
|
2732
|
-
return node instanceof HTMLOpenTagNode || node.type === "AST_HTML_OPEN_TAG_NODE";
|
|
2901
|
+
return node instanceof HTMLOpenTagNode || node.type === "AST_HTML_OPEN_TAG_NODE" || node.constructor.type === "AST_HTML_OPEN_TAG_NODE";
|
|
2733
2902
|
}
|
|
2734
2903
|
/**
|
|
2735
2904
|
* Checks if a node is a HTMLCloseTagNode
|
|
2736
2905
|
*/
|
|
2737
2906
|
function isHTMLCloseTagNode(node) {
|
|
2738
|
-
return node instanceof HTMLCloseTagNode || node.type === "AST_HTML_CLOSE_TAG_NODE";
|
|
2907
|
+
return node instanceof HTMLCloseTagNode || node.type === "AST_HTML_CLOSE_TAG_NODE" || node.constructor.type === "AST_HTML_CLOSE_TAG_NODE";
|
|
2739
2908
|
}
|
|
2740
2909
|
/**
|
|
2741
2910
|
* Checks if a node is a HTMLElementNode
|
|
2742
2911
|
*/
|
|
2743
2912
|
function isHTMLElementNode(node) {
|
|
2744
|
-
return node instanceof HTMLElementNode || node.type === "AST_HTML_ELEMENT_NODE";
|
|
2913
|
+
return node instanceof HTMLElementNode || node.type === "AST_HTML_ELEMENT_NODE" || node.constructor.type === "AST_HTML_ELEMENT_NODE";
|
|
2745
2914
|
}
|
|
2746
2915
|
/**
|
|
2747
2916
|
* Checks if a node is a HTMLAttributeValueNode
|
|
2748
2917
|
*/
|
|
2749
2918
|
function isHTMLAttributeValueNode(node) {
|
|
2750
|
-
return node instanceof HTMLAttributeValueNode || node.type === "AST_HTML_ATTRIBUTE_VALUE_NODE";
|
|
2919
|
+
return node instanceof HTMLAttributeValueNode || node.type === "AST_HTML_ATTRIBUTE_VALUE_NODE" || node.constructor.type === "AST_HTML_ATTRIBUTE_VALUE_NODE";
|
|
2751
2920
|
}
|
|
2752
2921
|
/**
|
|
2753
2922
|
* Checks if a node is a HTMLAttributeNameNode
|
|
2754
2923
|
*/
|
|
2755
2924
|
function isHTMLAttributeNameNode(node) {
|
|
2756
|
-
return node instanceof HTMLAttributeNameNode || node.type === "AST_HTML_ATTRIBUTE_NAME_NODE";
|
|
2925
|
+
return node instanceof HTMLAttributeNameNode || node.type === "AST_HTML_ATTRIBUTE_NAME_NODE" || node.constructor.type === "AST_HTML_ATTRIBUTE_NAME_NODE";
|
|
2757
2926
|
}
|
|
2758
2927
|
/**
|
|
2759
2928
|
* Checks if a node is a HTMLAttributeNode
|
|
2760
2929
|
*/
|
|
2761
2930
|
function isHTMLAttributeNode(node) {
|
|
2762
|
-
return node instanceof HTMLAttributeNode || node.type === "AST_HTML_ATTRIBUTE_NODE";
|
|
2931
|
+
return node instanceof HTMLAttributeNode || node.type === "AST_HTML_ATTRIBUTE_NODE" || node.constructor.type === "AST_HTML_ATTRIBUTE_NODE";
|
|
2763
2932
|
}
|
|
2764
2933
|
/**
|
|
2765
2934
|
* Checks if a node is a HTMLTextNode
|
|
2766
2935
|
*/
|
|
2767
2936
|
function isHTMLTextNode(node) {
|
|
2768
|
-
return node instanceof HTMLTextNode || node.type === "AST_HTML_TEXT_NODE";
|
|
2937
|
+
return node instanceof HTMLTextNode || node.type === "AST_HTML_TEXT_NODE" || node.constructor.type === "AST_HTML_TEXT_NODE";
|
|
2769
2938
|
}
|
|
2770
2939
|
/**
|
|
2771
2940
|
* Checks if a node is a HTMLCommentNode
|
|
2772
2941
|
*/
|
|
2773
2942
|
function isHTMLCommentNode(node) {
|
|
2774
|
-
return node instanceof HTMLCommentNode || node.type === "AST_HTML_COMMENT_NODE";
|
|
2943
|
+
return node instanceof HTMLCommentNode || node.type === "AST_HTML_COMMENT_NODE" || node.constructor.type === "AST_HTML_COMMENT_NODE";
|
|
2775
2944
|
}
|
|
2776
2945
|
/**
|
|
2777
2946
|
* Checks if a node is a HTMLDoctypeNode
|
|
2778
2947
|
*/
|
|
2779
2948
|
function isHTMLDoctypeNode(node) {
|
|
2780
|
-
return node instanceof HTMLDoctypeNode || node.type === "AST_HTML_DOCTYPE_NODE";
|
|
2949
|
+
return node instanceof HTMLDoctypeNode || node.type === "AST_HTML_DOCTYPE_NODE" || node.constructor.type === "AST_HTML_DOCTYPE_NODE";
|
|
2781
2950
|
}
|
|
2782
2951
|
/**
|
|
2783
2952
|
* Checks if a node is a XMLDeclarationNode
|
|
2784
2953
|
*/
|
|
2785
2954
|
function isXMLDeclarationNode(node) {
|
|
2786
|
-
return node instanceof XMLDeclarationNode || node.type === "AST_XML_DECLARATION_NODE";
|
|
2955
|
+
return node instanceof XMLDeclarationNode || node.type === "AST_XML_DECLARATION_NODE" || node.constructor.type === "AST_XML_DECLARATION_NODE";
|
|
2787
2956
|
}
|
|
2788
2957
|
/**
|
|
2789
2958
|
* Checks if a node is a CDATANode
|
|
2790
2959
|
*/
|
|
2791
2960
|
function isCDATANode(node) {
|
|
2792
|
-
return node instanceof CDATANode || node.type === "AST_CDATA_NODE";
|
|
2961
|
+
return node instanceof CDATANode || node.type === "AST_CDATA_NODE" || node.constructor.type === "AST_CDATA_NODE";
|
|
2793
2962
|
}
|
|
2794
2963
|
/**
|
|
2795
2964
|
* Checks if a node is a WhitespaceNode
|
|
2796
2965
|
*/
|
|
2797
2966
|
function isWhitespaceNode(node) {
|
|
2798
|
-
return node instanceof WhitespaceNode || node.type === "AST_WHITESPACE_NODE";
|
|
2967
|
+
return node instanceof WhitespaceNode || node.type === "AST_WHITESPACE_NODE" || node.constructor.type === "AST_WHITESPACE_NODE";
|
|
2799
2968
|
}
|
|
2800
2969
|
/**
|
|
2801
2970
|
* Checks if a node is a ERBContentNode
|
|
2802
2971
|
*/
|
|
2803
2972
|
function isERBContentNode(node) {
|
|
2804
|
-
return node instanceof ERBContentNode || node.type === "AST_ERB_CONTENT_NODE";
|
|
2973
|
+
return node instanceof ERBContentNode || node.type === "AST_ERB_CONTENT_NODE" || node.constructor.type === "AST_ERB_CONTENT_NODE";
|
|
2805
2974
|
}
|
|
2806
2975
|
/**
|
|
2807
2976
|
* Checks if a node is a ERBEndNode
|
|
2808
2977
|
*/
|
|
2809
2978
|
function isERBEndNode(node) {
|
|
2810
|
-
return node instanceof ERBEndNode || node.type === "AST_ERB_END_NODE";
|
|
2979
|
+
return node instanceof ERBEndNode || node.type === "AST_ERB_END_NODE" || node.constructor.type === "AST_ERB_END_NODE";
|
|
2811
2980
|
}
|
|
2812
2981
|
/**
|
|
2813
2982
|
* Checks if a node is a ERBElseNode
|
|
2814
2983
|
*/
|
|
2815
2984
|
function isERBElseNode(node) {
|
|
2816
|
-
return node instanceof ERBElseNode || node.type === "AST_ERB_ELSE_NODE";
|
|
2985
|
+
return node instanceof ERBElseNode || node.type === "AST_ERB_ELSE_NODE" || node.constructor.type === "AST_ERB_ELSE_NODE";
|
|
2817
2986
|
}
|
|
2818
2987
|
/**
|
|
2819
2988
|
* Checks if a node is a ERBIfNode
|
|
2820
2989
|
*/
|
|
2821
2990
|
function isERBIfNode(node) {
|
|
2822
|
-
return node instanceof ERBIfNode || node.type === "AST_ERB_IF_NODE";
|
|
2991
|
+
return node instanceof ERBIfNode || node.type === "AST_ERB_IF_NODE" || node.constructor.type === "AST_ERB_IF_NODE";
|
|
2823
2992
|
}
|
|
2824
2993
|
/**
|
|
2825
2994
|
* Checks if a node is a ERBBlockNode
|
|
2826
2995
|
*/
|
|
2827
2996
|
function isERBBlockNode(node) {
|
|
2828
|
-
return node instanceof ERBBlockNode || node.type === "AST_ERB_BLOCK_NODE";
|
|
2997
|
+
return node instanceof ERBBlockNode || node.type === "AST_ERB_BLOCK_NODE" || node.constructor.type === "AST_ERB_BLOCK_NODE";
|
|
2829
2998
|
}
|
|
2830
2999
|
/**
|
|
2831
3000
|
* Checks if a node is a ERBWhenNode
|
|
2832
3001
|
*/
|
|
2833
3002
|
function isERBWhenNode(node) {
|
|
2834
|
-
return node instanceof ERBWhenNode || node.type === "AST_ERB_WHEN_NODE";
|
|
3003
|
+
return node instanceof ERBWhenNode || node.type === "AST_ERB_WHEN_NODE" || node.constructor.type === "AST_ERB_WHEN_NODE";
|
|
2835
3004
|
}
|
|
2836
3005
|
/**
|
|
2837
3006
|
* Checks if a node is a ERBCaseNode
|
|
2838
3007
|
*/
|
|
2839
3008
|
function isERBCaseNode(node) {
|
|
2840
|
-
return node instanceof ERBCaseNode || node.type === "AST_ERB_CASE_NODE";
|
|
3009
|
+
return node instanceof ERBCaseNode || node.type === "AST_ERB_CASE_NODE" || node.constructor.type === "AST_ERB_CASE_NODE";
|
|
2841
3010
|
}
|
|
2842
3011
|
/**
|
|
2843
3012
|
* Checks if a node is a ERBCaseMatchNode
|
|
2844
3013
|
*/
|
|
2845
3014
|
function isERBCaseMatchNode(node) {
|
|
2846
|
-
return node instanceof ERBCaseMatchNode || node.type === "AST_ERB_CASE_MATCH_NODE";
|
|
3015
|
+
return node instanceof ERBCaseMatchNode || node.type === "AST_ERB_CASE_MATCH_NODE" || node.constructor.type === "AST_ERB_CASE_MATCH_NODE";
|
|
2847
3016
|
}
|
|
2848
3017
|
/**
|
|
2849
3018
|
* Checks if a node is a ERBWhileNode
|
|
2850
3019
|
*/
|
|
2851
3020
|
function isERBWhileNode(node) {
|
|
2852
|
-
return node instanceof ERBWhileNode || node.type === "AST_ERB_WHILE_NODE";
|
|
3021
|
+
return node instanceof ERBWhileNode || node.type === "AST_ERB_WHILE_NODE" || node.constructor.type === "AST_ERB_WHILE_NODE";
|
|
2853
3022
|
}
|
|
2854
3023
|
/**
|
|
2855
3024
|
* Checks if a node is a ERBUntilNode
|
|
2856
3025
|
*/
|
|
2857
3026
|
function isERBUntilNode(node) {
|
|
2858
|
-
return node instanceof ERBUntilNode || node.type === "AST_ERB_UNTIL_NODE";
|
|
3027
|
+
return node instanceof ERBUntilNode || node.type === "AST_ERB_UNTIL_NODE" || node.constructor.type === "AST_ERB_UNTIL_NODE";
|
|
2859
3028
|
}
|
|
2860
3029
|
/**
|
|
2861
3030
|
* Checks if a node is a ERBForNode
|
|
2862
3031
|
*/
|
|
2863
3032
|
function isERBForNode(node) {
|
|
2864
|
-
return node instanceof ERBForNode || node.type === "AST_ERB_FOR_NODE";
|
|
3033
|
+
return node instanceof ERBForNode || node.type === "AST_ERB_FOR_NODE" || node.constructor.type === "AST_ERB_FOR_NODE";
|
|
2865
3034
|
}
|
|
2866
3035
|
/**
|
|
2867
3036
|
* Checks if a node is a ERBRescueNode
|
|
2868
3037
|
*/
|
|
2869
3038
|
function isERBRescueNode(node) {
|
|
2870
|
-
return node instanceof ERBRescueNode || node.type === "AST_ERB_RESCUE_NODE";
|
|
3039
|
+
return node instanceof ERBRescueNode || node.type === "AST_ERB_RESCUE_NODE" || node.constructor.type === "AST_ERB_RESCUE_NODE";
|
|
2871
3040
|
}
|
|
2872
3041
|
/**
|
|
2873
3042
|
* Checks if a node is a ERBEnsureNode
|
|
2874
3043
|
*/
|
|
2875
3044
|
function isERBEnsureNode(node) {
|
|
2876
|
-
return node instanceof ERBEnsureNode || node.type === "AST_ERB_ENSURE_NODE";
|
|
3045
|
+
return node instanceof ERBEnsureNode || node.type === "AST_ERB_ENSURE_NODE" || node.constructor.type === "AST_ERB_ENSURE_NODE";
|
|
2877
3046
|
}
|
|
2878
3047
|
/**
|
|
2879
3048
|
* Checks if a node is a ERBBeginNode
|
|
2880
3049
|
*/
|
|
2881
3050
|
function isERBBeginNode(node) {
|
|
2882
|
-
return node instanceof ERBBeginNode || node.type === "AST_ERB_BEGIN_NODE";
|
|
3051
|
+
return node instanceof ERBBeginNode || node.type === "AST_ERB_BEGIN_NODE" || node.constructor.type === "AST_ERB_BEGIN_NODE";
|
|
2883
3052
|
}
|
|
2884
3053
|
/**
|
|
2885
3054
|
* Checks if a node is a ERBUnlessNode
|
|
2886
3055
|
*/
|
|
2887
3056
|
function isERBUnlessNode(node) {
|
|
2888
|
-
return node instanceof ERBUnlessNode || node.type === "AST_ERB_UNLESS_NODE";
|
|
3057
|
+
return node instanceof ERBUnlessNode || node.type === "AST_ERB_UNLESS_NODE" || node.constructor.type === "AST_ERB_UNLESS_NODE";
|
|
2889
3058
|
}
|
|
2890
3059
|
/**
|
|
2891
3060
|
* Checks if a node is a ERBYieldNode
|
|
2892
3061
|
*/
|
|
2893
3062
|
function isERBYieldNode(node) {
|
|
2894
|
-
return node instanceof ERBYieldNode || node.type === "AST_ERB_YIELD_NODE";
|
|
3063
|
+
return node instanceof ERBYieldNode || node.type === "AST_ERB_YIELD_NODE" || node.constructor.type === "AST_ERB_YIELD_NODE";
|
|
2895
3064
|
}
|
|
2896
3065
|
/**
|
|
2897
3066
|
* Checks if a node is a ERBInNode
|
|
2898
3067
|
*/
|
|
2899
3068
|
function isERBInNode(node) {
|
|
2900
|
-
return node instanceof ERBInNode || node.type === "AST_ERB_IN_NODE";
|
|
3069
|
+
return node instanceof ERBInNode || node.type === "AST_ERB_IN_NODE" || node.constructor.type === "AST_ERB_IN_NODE";
|
|
2901
3070
|
}
|
|
2902
3071
|
/**
|
|
2903
3072
|
* Checks if a node is any ERB node type
|
|
@@ -3066,6 +3235,8 @@ function filterNodes(nodes, ...types) {
|
|
|
3066
3235
|
return nodes.filter(node => isAnyOf(node, ...types));
|
|
3067
3236
|
}
|
|
3068
3237
|
function isNode(node, type) {
|
|
3238
|
+
if (!node)
|
|
3239
|
+
return false;
|
|
3069
3240
|
if (typeof type === 'string') {
|
|
3070
3241
|
const guard = AST_TYPE_GUARDS.get(type);
|
|
3071
3242
|
return guard ? guard(node) : false;
|
|
@@ -3147,9 +3318,47 @@ function getTagName(node) {
|
|
|
3147
3318
|
function isCommentNode(node) {
|
|
3148
3319
|
return isNode(node, HTMLCommentNode) || (isERBNode(node) && !isERBControlFlowNode(node));
|
|
3149
3320
|
}
|
|
3321
|
+
/**
|
|
3322
|
+
* Compares two positions to determine if the first comes before the second
|
|
3323
|
+
* Returns true if pos1 comes before pos2 in source order
|
|
3324
|
+
* @param inclusive - If true, returns true when positions are equal
|
|
3325
|
+
*/
|
|
3326
|
+
function isPositionBefore(position1, position2, inclusive = false) {
|
|
3327
|
+
if (position1.line < position2.line)
|
|
3328
|
+
return true;
|
|
3329
|
+
if (position1.line > position2.line)
|
|
3330
|
+
return false;
|
|
3331
|
+
return inclusive ? position1.column <= position2.column : position1.column < position2.column;
|
|
3332
|
+
}
|
|
3333
|
+
/**
|
|
3334
|
+
* Compares two positions to determine if the first comes after the second
|
|
3335
|
+
* Returns true if pos1 comes after pos2 in source order
|
|
3336
|
+
* @param inclusive - If true, returns true when positions are equal
|
|
3337
|
+
*/
|
|
3338
|
+
function isPositionAfter(position1, position2, inclusive = false) {
|
|
3339
|
+
if (position1.line > position2.line)
|
|
3340
|
+
return true;
|
|
3341
|
+
if (position1.line < position2.line)
|
|
3342
|
+
return false;
|
|
3343
|
+
return inclusive ? position1.column >= position2.column : position1.column > position2.column;
|
|
3344
|
+
}
|
|
3345
|
+
/**
|
|
3346
|
+
* Gets nodes that end before the specified position
|
|
3347
|
+
* @param inclusive - If true, includes nodes that end exactly at the position (default: false, matching half-open interval semantics)
|
|
3348
|
+
*/
|
|
3349
|
+
function getNodesBeforePosition(nodes, position, inclusive = false) {
|
|
3350
|
+
return nodes.filter(node => node.location && isPositionBefore(node.location.end, position, inclusive));
|
|
3351
|
+
}
|
|
3352
|
+
/**
|
|
3353
|
+
* Gets nodes that start after the specified position
|
|
3354
|
+
* @param inclusive - If true, includes nodes that start exactly at the position (default: true, matching typical boundary behavior)
|
|
3355
|
+
*/
|
|
3356
|
+
function getNodesAfterPosition(nodes, position, inclusive = true) {
|
|
3357
|
+
return nodes.filter(node => node.location && isPositionAfter(node.location.start, position, inclusive));
|
|
3358
|
+
}
|
|
3150
3359
|
|
|
3151
3360
|
// 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.6.
|
|
3361
|
+
// be modified manually. See /Users/marcoroth/Development/herb-release-0.6.1/templates/javascript/packages/core/src/visitor.ts.erb
|
|
3153
3362
|
class Visitor {
|
|
3154
3363
|
visit(node) {
|
|
3155
3364
|
if (!node)
|
|
@@ -3381,6 +3590,11 @@ class Printer extends Visitor {
|
|
|
3381
3590
|
if (isToken(input)) {
|
|
3382
3591
|
return input.value;
|
|
3383
3592
|
}
|
|
3593
|
+
if (Array.isArray(input)) {
|
|
3594
|
+
this.context.reset();
|
|
3595
|
+
input.forEach(node => this.visit(node));
|
|
3596
|
+
return this.context.getOutput();
|
|
3597
|
+
}
|
|
3384
3598
|
const node = isParseResult(input) ? input.value : input;
|
|
3385
3599
|
if (options.ignoreErrors === false && node.recursiveErrors().length > 0) {
|
|
3386
3600
|
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 +3603,23 @@ class Printer extends Visitor {
|
|
|
3389
3603
|
this.visit(node);
|
|
3390
3604
|
return this.context.getOutput();
|
|
3391
3605
|
}
|
|
3392
|
-
|
|
3393
|
-
this.
|
|
3606
|
+
write(content) {
|
|
3607
|
+
this.context.write(content);
|
|
3394
3608
|
}
|
|
3609
|
+
}
|
|
3610
|
+
|
|
3611
|
+
/**
|
|
3612
|
+
* IdentityPrinter - Provides lossless reconstruction of the original source
|
|
3613
|
+
*
|
|
3614
|
+
* This printer aims to reconstruct the original input as faithfully as possible,
|
|
3615
|
+
* preserving all whitespace, formatting, and structure. It's useful for:
|
|
3616
|
+
* - Testing parser accuracy (input should equal output)
|
|
3617
|
+
* - Baseline printing before applying transformations
|
|
3618
|
+
* - Verifying AST round-trip fidelity
|
|
3619
|
+
*/
|
|
3620
|
+
class IdentityPrinter extends Printer {
|
|
3395
3621
|
visitLiteralNode(node) {
|
|
3396
|
-
this.
|
|
3622
|
+
this.write(node.content);
|
|
3397
3623
|
}
|
|
3398
3624
|
visitHTMLTextNode(node) {
|
|
3399
3625
|
this.write(node.content);
|
|
@@ -3405,25 +3631,32 @@ class Printer extends Visitor {
|
|
|
3405
3631
|
}
|
|
3406
3632
|
visitHTMLOpenTagNode(node) {
|
|
3407
3633
|
if (node.tag_opening) {
|
|
3408
|
-
this.
|
|
3634
|
+
this.write(node.tag_opening.value);
|
|
3409
3635
|
}
|
|
3410
3636
|
if (node.tag_name) {
|
|
3411
|
-
this.
|
|
3637
|
+
this.write(node.tag_name.value);
|
|
3412
3638
|
}
|
|
3413
3639
|
this.visitChildNodes(node);
|
|
3414
3640
|
if (node.tag_closing) {
|
|
3415
|
-
this.
|
|
3641
|
+
this.write(node.tag_closing.value);
|
|
3416
3642
|
}
|
|
3417
3643
|
}
|
|
3418
3644
|
visitHTMLCloseTagNode(node) {
|
|
3419
3645
|
if (node.tag_opening) {
|
|
3420
|
-
this.
|
|
3646
|
+
this.write(node.tag_opening.value);
|
|
3421
3647
|
}
|
|
3422
3648
|
if (node.tag_name) {
|
|
3423
|
-
|
|
3649
|
+
const before = getNodesBeforePosition(node.children, node.tag_name.location.start, true);
|
|
3650
|
+
const after = getNodesAfterPosition(node.children, node.tag_name.location.end);
|
|
3651
|
+
this.visitAll(before);
|
|
3652
|
+
this.write(node.tag_name.value);
|
|
3653
|
+
this.visitAll(after);
|
|
3654
|
+
}
|
|
3655
|
+
else {
|
|
3656
|
+
this.visitAll(node.children);
|
|
3424
3657
|
}
|
|
3425
3658
|
if (node.tag_closing) {
|
|
3426
|
-
this.
|
|
3659
|
+
this.write(node.tag_closing.value);
|
|
3427
3660
|
}
|
|
3428
3661
|
}
|
|
3429
3662
|
visitHTMLElementNode(node) {
|
|
@@ -3449,7 +3682,7 @@ class Printer extends Visitor {
|
|
|
3449
3682
|
this.visit(node.name);
|
|
3450
3683
|
}
|
|
3451
3684
|
if (node.equals) {
|
|
3452
|
-
this.
|
|
3685
|
+
this.write(node.equals.value);
|
|
3453
3686
|
}
|
|
3454
3687
|
if (node.equals && node.value) {
|
|
3455
3688
|
this.visit(node.value);
|
|
@@ -3460,47 +3693,47 @@ class Printer extends Visitor {
|
|
|
3460
3693
|
}
|
|
3461
3694
|
visitHTMLAttributeValueNode(node) {
|
|
3462
3695
|
if (node.quoted && node.open_quote) {
|
|
3463
|
-
this.
|
|
3696
|
+
this.write(node.open_quote.value);
|
|
3464
3697
|
}
|
|
3465
3698
|
this.visitChildNodes(node);
|
|
3466
3699
|
if (node.quoted && node.close_quote) {
|
|
3467
|
-
this.
|
|
3700
|
+
this.write(node.close_quote.value);
|
|
3468
3701
|
}
|
|
3469
3702
|
}
|
|
3470
3703
|
visitHTMLCommentNode(node) {
|
|
3471
3704
|
if (node.comment_start) {
|
|
3472
|
-
this.
|
|
3705
|
+
this.write(node.comment_start.value);
|
|
3473
3706
|
}
|
|
3474
3707
|
this.visitChildNodes(node);
|
|
3475
3708
|
if (node.comment_end) {
|
|
3476
|
-
this.
|
|
3709
|
+
this.write(node.comment_end.value);
|
|
3477
3710
|
}
|
|
3478
3711
|
}
|
|
3479
3712
|
visitHTMLDoctypeNode(node) {
|
|
3480
3713
|
if (node.tag_opening) {
|
|
3481
|
-
this.
|
|
3714
|
+
this.write(node.tag_opening.value);
|
|
3482
3715
|
}
|
|
3483
3716
|
this.visitChildNodes(node);
|
|
3484
3717
|
if (node.tag_closing) {
|
|
3485
|
-
this.
|
|
3718
|
+
this.write(node.tag_closing.value);
|
|
3486
3719
|
}
|
|
3487
3720
|
}
|
|
3488
3721
|
visitXMLDeclarationNode(node) {
|
|
3489
3722
|
if (node.tag_opening) {
|
|
3490
|
-
this.
|
|
3723
|
+
this.write(node.tag_opening.value);
|
|
3491
3724
|
}
|
|
3492
3725
|
this.visitChildNodes(node);
|
|
3493
3726
|
if (node.tag_closing) {
|
|
3494
|
-
this.
|
|
3727
|
+
this.write(node.tag_closing.value);
|
|
3495
3728
|
}
|
|
3496
3729
|
}
|
|
3497
3730
|
visitCDATANode(node) {
|
|
3498
3731
|
if (node.tag_opening) {
|
|
3499
|
-
this.
|
|
3732
|
+
this.write(node.tag_opening.value);
|
|
3500
3733
|
}
|
|
3501
3734
|
this.visitChildNodes(node);
|
|
3502
3735
|
if (node.tag_closing) {
|
|
3503
|
-
this.
|
|
3736
|
+
this.write(node.tag_closing.value);
|
|
3504
3737
|
}
|
|
3505
3738
|
}
|
|
3506
3739
|
visitERBContentNode(node) {
|
|
@@ -3658,31 +3891,19 @@ class Printer extends Visitor {
|
|
|
3658
3891
|
*/
|
|
3659
3892
|
printERBNode(node) {
|
|
3660
3893
|
if (node.tag_opening) {
|
|
3661
|
-
this.
|
|
3894
|
+
this.write(node.tag_opening.value);
|
|
3662
3895
|
}
|
|
3663
3896
|
if (node.content) {
|
|
3664
|
-
this.
|
|
3897
|
+
this.write(node.content.value);
|
|
3665
3898
|
}
|
|
3666
3899
|
if (node.tag_closing) {
|
|
3667
|
-
this.
|
|
3900
|
+
this.write(node.tag_closing.value);
|
|
3668
3901
|
}
|
|
3669
3902
|
}
|
|
3670
|
-
write(content) {
|
|
3671
|
-
this.context.write(content);
|
|
3672
|
-
}
|
|
3673
3903
|
}
|
|
3674
3904
|
|
|
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
|
-
}
|
|
3905
|
+
({
|
|
3906
|
+
...DEFAULT_PRINT_OPTIONS});
|
|
3686
3907
|
|
|
3687
3908
|
// TODO: we can probably expand this list with more tags/attributes
|
|
3688
3909
|
const FORMATTABLE_ATTRIBUTES = {
|
|
@@ -3719,12 +3940,16 @@ class FormatPrinter extends Printer {
|
|
|
3719
3940
|
'samp', 'small', 'span', 'strong', 'sub', 'sup',
|
|
3720
3941
|
'tt', 'var', 'del', 'ins', 'mark', 's', 'u', 'time', 'wbr'
|
|
3721
3942
|
]);
|
|
3943
|
+
static CONTENT_PRESERVING_ELEMENTS = new Set([
|
|
3944
|
+
'script', 'style', 'pre', 'textarea'
|
|
3945
|
+
]);
|
|
3722
3946
|
static SPACEABLE_CONTAINERS = new Set([
|
|
3723
3947
|
'div', 'section', 'article', 'main', 'header', 'footer', 'aside',
|
|
3724
3948
|
'figure', 'details', 'summary', 'dialog', 'fieldset'
|
|
3725
3949
|
]);
|
|
3726
3950
|
static TIGHT_GROUP_PARENTS = new Set([
|
|
3727
|
-
'ul', 'ol', 'nav', 'select', 'datalist', 'optgroup', 'tr', 'thead',
|
|
3951
|
+
'ul', 'ol', 'nav', 'select', 'datalist', 'optgroup', 'tr', 'thead',
|
|
3952
|
+
'tbody', 'tfoot'
|
|
3728
3953
|
]);
|
|
3729
3954
|
static TIGHT_GROUP_CHILDREN = new Set([
|
|
3730
3955
|
'li', 'option', 'td', 'th', 'dt', 'dd'
|
|
@@ -3822,6 +4047,13 @@ class FormatPrinter extends Printer {
|
|
|
3822
4047
|
push(line) {
|
|
3823
4048
|
this.lines.push(line);
|
|
3824
4049
|
}
|
|
4050
|
+
/**
|
|
4051
|
+
* @deprecated refactor to use @herb-tools/printer infrastructre (or rework printer use push and this.lines)
|
|
4052
|
+
*/
|
|
4053
|
+
pushWithIndent(line) {
|
|
4054
|
+
const indent = line.trim() === "" ? "" : this.indent;
|
|
4055
|
+
this.push(indent + line);
|
|
4056
|
+
}
|
|
3825
4057
|
withIndent(callback) {
|
|
3826
4058
|
this.indentLevel++;
|
|
3827
4059
|
const result = callback();
|
|
@@ -4022,20 +4254,14 @@ class FormatPrinter extends Printer {
|
|
|
4022
4254
|
return false;
|
|
4023
4255
|
}
|
|
4024
4256
|
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;
|
|
4257
|
+
if (isNode(attribute.value, HTMLAttributeValueNode)) {
|
|
4258
|
+
return attribute.value.children.map(child => isNode(child, HTMLTextNode) ? child.content : IdentityPrinter.print(child)).join('');
|
|
4033
4259
|
}
|
|
4034
4260
|
return '';
|
|
4035
4261
|
}
|
|
4036
4262
|
hasMultilineAttributes(attributes) {
|
|
4037
4263
|
return attributes.some(attribute => {
|
|
4038
|
-
if (
|
|
4264
|
+
if (isNode(attribute.value, HTMLAttributeValueNode)) {
|
|
4039
4265
|
const content = getCombinedStringFromNodes(attribute.value.children);
|
|
4040
4266
|
if (/\r?\n/.test(content)) {
|
|
4041
4267
|
const name = attribute.name ? getCombinedAttributeName(attribute.name) : "";
|
|
@@ -4123,11 +4349,11 @@ class FormatPrinter extends Printer {
|
|
|
4123
4349
|
* Render multiline attributes for a tag
|
|
4124
4350
|
*/
|
|
4125
4351
|
renderMultilineAttributes(tagName, allChildren = [], isSelfClosing = false) {
|
|
4126
|
-
this.
|
|
4352
|
+
this.pushWithIndent(`<${tagName}`);
|
|
4127
4353
|
this.withIndent(() => {
|
|
4128
4354
|
allChildren.forEach(child => {
|
|
4129
4355
|
if (isNode(child, HTMLAttributeNode)) {
|
|
4130
|
-
this.
|
|
4356
|
+
this.pushWithIndent(this.renderAttribute(child));
|
|
4131
4357
|
}
|
|
4132
4358
|
else if (!isNode(child, WhitespaceNode)) {
|
|
4133
4359
|
this.visit(child);
|
|
@@ -4135,10 +4361,10 @@ class FormatPrinter extends Printer {
|
|
|
4135
4361
|
});
|
|
4136
4362
|
});
|
|
4137
4363
|
if (isSelfClosing) {
|
|
4138
|
-
this.
|
|
4364
|
+
this.pushWithIndent("/>");
|
|
4139
4365
|
}
|
|
4140
4366
|
else {
|
|
4141
|
-
this.
|
|
4367
|
+
this.pushWithIndent(">");
|
|
4142
4368
|
}
|
|
4143
4369
|
}
|
|
4144
4370
|
/**
|
|
@@ -4202,6 +4428,10 @@ class FormatPrinter extends Printer {
|
|
|
4202
4428
|
this.elementStack.pop();
|
|
4203
4429
|
}
|
|
4204
4430
|
visitHTMLElementBody(body, element) {
|
|
4431
|
+
if (this.isContentPreserving(element)) {
|
|
4432
|
+
element.body.map(child => this.pushToLastLine(IdentityPrinter.print(child)));
|
|
4433
|
+
return;
|
|
4434
|
+
}
|
|
4205
4435
|
const analysis = this.elementFormattingAnalysis.get(element);
|
|
4206
4436
|
const hasTextFlow = this.isInTextFlowContext(null, body);
|
|
4207
4437
|
const children = this.filterSignificantChildren(body, hasTextFlow);
|
|
@@ -4327,7 +4557,7 @@ class FormatPrinter extends Printer {
|
|
|
4327
4557
|
this.pushToLastLine(closingTag);
|
|
4328
4558
|
}
|
|
4329
4559
|
else {
|
|
4330
|
-
this.
|
|
4560
|
+
this.pushWithIndent(closingTag);
|
|
4331
4561
|
}
|
|
4332
4562
|
}
|
|
4333
4563
|
visitHTMLTextNode(node) {
|
|
@@ -4359,13 +4589,13 @@ class FormatPrinter extends Printer {
|
|
|
4359
4589
|
lines.forEach(line => this.push(line));
|
|
4360
4590
|
}
|
|
4361
4591
|
visitHTMLAttributeNode(node) {
|
|
4362
|
-
this.
|
|
4592
|
+
this.pushWithIndent(this.renderAttribute(node));
|
|
4363
4593
|
}
|
|
4364
4594
|
visitHTMLAttributeNameNode(node) {
|
|
4365
|
-
this.
|
|
4595
|
+
this.pushWithIndent(getCombinedAttributeName(node));
|
|
4366
4596
|
}
|
|
4367
4597
|
visitHTMLAttributeValueNode(node) {
|
|
4368
|
-
this.
|
|
4598
|
+
this.pushWithIndent(IdentityPrinter.print(node));
|
|
4369
4599
|
}
|
|
4370
4600
|
// TODO: rework
|
|
4371
4601
|
visitHTMLCommentNode(node) {
|
|
@@ -4420,37 +4650,40 @@ class FormatPrinter extends Printer {
|
|
|
4420
4650
|
else {
|
|
4421
4651
|
inner = "";
|
|
4422
4652
|
}
|
|
4423
|
-
this.
|
|
4653
|
+
this.pushWithIndent(open + inner + close);
|
|
4424
4654
|
}
|
|
4425
4655
|
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
|
-
}
|
|
4656
|
+
const open = node.tag_opening?.value || "<%#";
|
|
4657
|
+
const content = node?.content?.value || "";
|
|
4658
|
+
const close = node.tag_closing?.value || "%>";
|
|
4659
|
+
const contentLines = content.split("\n");
|
|
4660
|
+
const contentTrimmedLines = content.trim().split("\n");
|
|
4661
|
+
if (contentLines.length === 1 && contentTrimmedLines.length === 1) {
|
|
4662
|
+
const startsWithSpace = content[0] === " ";
|
|
4663
|
+
const before = startsWithSpace ? "" : " ";
|
|
4664
|
+
this.pushWithIndent(open + before + content.trimEnd() + ' ' + close);
|
|
4665
|
+
return;
|
|
4440
4666
|
}
|
|
4441
|
-
|
|
4442
|
-
|
|
4667
|
+
if (contentTrimmedLines.length === 1) {
|
|
4668
|
+
this.pushWithIndent(open + ' ' + content.trim() + ' ' + close);
|
|
4669
|
+
return;
|
|
4443
4670
|
}
|
|
4444
|
-
|
|
4671
|
+
const firstLineEmpty = contentLines[0].trim() === "";
|
|
4672
|
+
const dedentedContent = dedent(firstLineEmpty ? content : content.trimStart());
|
|
4673
|
+
this.pushWithIndent(open);
|
|
4674
|
+
this.withIndent(() => {
|
|
4675
|
+
dedentedContent.split("\n").forEach(line => this.pushWithIndent(line));
|
|
4676
|
+
});
|
|
4677
|
+
this.pushWithIndent(close);
|
|
4445
4678
|
}
|
|
4446
4679
|
visitHTMLDoctypeNode(node) {
|
|
4447
|
-
this.
|
|
4680
|
+
this.pushWithIndent(IdentityPrinter.print(node));
|
|
4448
4681
|
}
|
|
4449
4682
|
visitXMLDeclarationNode(node) {
|
|
4450
|
-
this.
|
|
4683
|
+
this.pushWithIndent(IdentityPrinter.print(node));
|
|
4451
4684
|
}
|
|
4452
4685
|
visitCDATANode(node) {
|
|
4453
|
-
this.
|
|
4686
|
+
this.pushWithIndent(IdentityPrinter.print(node));
|
|
4454
4687
|
}
|
|
4455
4688
|
visitERBContentNode(node) {
|
|
4456
4689
|
// TODO: this feels hacky
|
|
@@ -4666,8 +4899,11 @@ class FormatPrinter extends Printer {
|
|
|
4666
4899
|
* Determines if the close tag should be rendered inline (usually follows content decision)
|
|
4667
4900
|
*/
|
|
4668
4901
|
shouldRenderCloseTagInline(node, elementContentInline) {
|
|
4669
|
-
|
|
4670
|
-
|
|
4902
|
+
if (node.is_void)
|
|
4903
|
+
return true;
|
|
4904
|
+
if (node.open_tag?.tag_closing?.value === "/>")
|
|
4905
|
+
return true;
|
|
4906
|
+
if (this.isContentPreserving(node))
|
|
4671
4907
|
return true;
|
|
4672
4908
|
const children = this.filterSignificantChildren(node.body, this.isInTextFlowContext(null, node.body));
|
|
4673
4909
|
if (children.length === 0)
|
|
@@ -4726,7 +4962,7 @@ class FormatPrinter extends Printer {
|
|
|
4726
4962
|
}
|
|
4727
4963
|
else {
|
|
4728
4964
|
if (currentLineContent.trim()) {
|
|
4729
|
-
this.
|
|
4965
|
+
this.pushWithIndent(currentLineContent.trim());
|
|
4730
4966
|
currentLineContent = "";
|
|
4731
4967
|
}
|
|
4732
4968
|
this.visit(child);
|
|
@@ -4734,7 +4970,7 @@ class FormatPrinter extends Printer {
|
|
|
4734
4970
|
}
|
|
4735
4971
|
else {
|
|
4736
4972
|
if (currentLineContent.trim()) {
|
|
4737
|
-
this.
|
|
4973
|
+
this.pushWithIndent(currentLineContent.trim());
|
|
4738
4974
|
currentLineContent = "";
|
|
4739
4975
|
}
|
|
4740
4976
|
this.visit(child);
|
|
@@ -4764,7 +5000,7 @@ class FormatPrinter extends Printer {
|
|
|
4764
5000
|
}
|
|
4765
5001
|
else {
|
|
4766
5002
|
if (currentLineContent.trim()) {
|
|
4767
|
-
this.
|
|
5003
|
+
this.pushWithIndent(currentLineContent.trim());
|
|
4768
5004
|
currentLineContent = "";
|
|
4769
5005
|
}
|
|
4770
5006
|
this.visit(child);
|
|
@@ -4845,7 +5081,7 @@ class FormatPrinter extends Printer {
|
|
|
4845
5081
|
const equals = attribute.equals?.value ?? "";
|
|
4846
5082
|
this.currentAttributeName = name;
|
|
4847
5083
|
let value = "";
|
|
4848
|
-
if (
|
|
5084
|
+
if (isNode(attribute.value, HTMLAttributeValueNode)) {
|
|
4849
5085
|
const attributeValue = attribute.value;
|
|
4850
5086
|
let open_quote = attributeValue.open_quote?.value ?? "";
|
|
4851
5087
|
let close_quote = attributeValue.close_quote?.value ?? "";
|
|
@@ -5094,6 +5330,10 @@ class FormatPrinter extends Printer {
|
|
|
5094
5330
|
}
|
|
5095
5331
|
return content.replace(/\s+/g, ' ').trim();
|
|
5096
5332
|
}
|
|
5333
|
+
isContentPreserving(element) {
|
|
5334
|
+
const tagName = getTagName(element);
|
|
5335
|
+
return FormatPrinter.CONTENT_PRESERVING_ELEMENTS.has(tagName);
|
|
5336
|
+
}
|
|
5097
5337
|
}
|
|
5098
5338
|
|
|
5099
5339
|
/**
|