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