@herb-tools/formatter 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,3215 @@
1
+ 'use strict';
2
+
3
+ class Position {
4
+ line;
5
+ column;
6
+ static from(position) {
7
+ return new Position(position.line, position.column);
8
+ }
9
+ constructor(line, column) {
10
+ this.line = line;
11
+ this.column = column;
12
+ }
13
+ toHash() {
14
+ return { line: this.line, column: this.column };
15
+ }
16
+ toJSON() {
17
+ return this.toHash();
18
+ }
19
+ treeInspect() {
20
+ return `(${this.line}:${this.column})`;
21
+ }
22
+ inspect() {
23
+ return `#<Herb::Position ${this.treeInspect()}>`;
24
+ }
25
+ toString() {
26
+ return this.inspect();
27
+ }
28
+ }
29
+
30
+ class Location {
31
+ start;
32
+ end;
33
+ static from(location) {
34
+ const start = Position.from(location.start);
35
+ const end = Position.from(location.end);
36
+ return new Location(start, end);
37
+ }
38
+ constructor(start, end) {
39
+ this.start = start;
40
+ this.end = end;
41
+ }
42
+ toHash() {
43
+ return {
44
+ start: this.start.toHash(),
45
+ end: this.end.toHash(),
46
+ };
47
+ }
48
+ toJSON() {
49
+ return this.toHash();
50
+ }
51
+ treeInspect() {
52
+ return `${this.start.treeInspect()}-${this.end.treeInspect()}`;
53
+ }
54
+ treeInspectWithLabel() {
55
+ return `(location: ${this.treeInspect()})`;
56
+ }
57
+ inspect() {
58
+ return `#<Herb::Location ${this.treeInspect()}>`;
59
+ }
60
+ toString() {
61
+ return this.inspect();
62
+ }
63
+ }
64
+
65
+ class Range {
66
+ start;
67
+ end;
68
+ static from(range) {
69
+ return new Range(range[0], range[1]);
70
+ }
71
+ constructor(start, end) {
72
+ this.start = start;
73
+ this.end = end;
74
+ }
75
+ toArray() {
76
+ return [this.start, this.end];
77
+ }
78
+ toJSON() {
79
+ return this.toArray();
80
+ }
81
+ treeInspect() {
82
+ return `[${this.start}, ${this.end}]`;
83
+ }
84
+ inspect() {
85
+ return `#<Herb::Range ${this.toArray()}>`;
86
+ }
87
+ toString() {
88
+ return this.inspect();
89
+ }
90
+ }
91
+
92
+ class Token {
93
+ value;
94
+ range;
95
+ location;
96
+ type;
97
+ static from(token) {
98
+ return new Token(token.value, Range.from(token.range), Location.from(token.location), token.type);
99
+ }
100
+ constructor(value, range, location, type) {
101
+ this.value = value;
102
+ this.range = range;
103
+ this.location = location;
104
+ this.type = type;
105
+ }
106
+ toHash() {
107
+ return {
108
+ value: this.value,
109
+ range: this.range?.toArray(),
110
+ location: this.location?.toHash(),
111
+ type: this.type,
112
+ };
113
+ }
114
+ toJSON() {
115
+ return this.toHash();
116
+ }
117
+ treeInspect() {
118
+ return `"${this.value}" ${this.location.treeInspectWithLabel()}`;
119
+ }
120
+ valueInspect() {
121
+ return this.type === "TOKEN_EOF"
122
+ ? JSON.stringify("<EOF>")
123
+ : JSON.stringify(this.value);
124
+ }
125
+ inspect() {
126
+ return `#<Herb::Token type="${this.type}" value=${this.valueInspect()} range=${this.range.treeInspect()} start=${this.location.start.treeInspect()} end=${this.location.end.treeInspect()}>`;
127
+ }
128
+ toString() {
129
+ return this.inspect();
130
+ }
131
+ }
132
+
133
+ // NOTE: This file is generated by the templates/template.rb script and should not
134
+ // be modified manually. See /Users/marcoroth/Development/herb-release/templates/javascript/packages/core/src/errors.ts.erb
135
+ class HerbError {
136
+ type;
137
+ message;
138
+ location;
139
+ severity = "error";
140
+ source = "parser";
141
+ get code() {
142
+ return this.type;
143
+ }
144
+ static from(error) {
145
+ return fromSerializedError(error);
146
+ }
147
+ constructor(type, message, location) {
148
+ this.type = type;
149
+ this.message = message;
150
+ this.location = location;
151
+ }
152
+ toJSON() {
153
+ return {
154
+ type: this.type,
155
+ message: this.message,
156
+ location: this.location.toJSON(),
157
+ };
158
+ }
159
+ inspect() {
160
+ return this.treeInspect(0);
161
+ }
162
+ }
163
+ class UnexpectedError extends HerbError {
164
+ description;
165
+ expected;
166
+ found;
167
+ static from(data) {
168
+ return new UnexpectedError({
169
+ type: data.type,
170
+ message: data.message,
171
+ location: Location.from(data.location),
172
+ description: data.description,
173
+ expected: data.expected,
174
+ found: data.found,
175
+ });
176
+ }
177
+ constructor(props) {
178
+ super(props.type, props.message, props.location);
179
+ this.description = props.description;
180
+ this.expected = props.expected;
181
+ this.found = props.found;
182
+ }
183
+ toJSON() {
184
+ return {
185
+ ...super.toJSON(),
186
+ type: "UNEXPECTED_ERROR",
187
+ description: this.description,
188
+ expected: this.expected,
189
+ found: this.found,
190
+ };
191
+ }
192
+ toMonacoDiagnostic() {
193
+ return {
194
+ line: this.location.start.line,
195
+ column: this.location.start.column,
196
+ endLine: this.location.end.line,
197
+ endColumn: this.location.end.column,
198
+ message: this.message,
199
+ severity: 'error'
200
+ };
201
+ }
202
+ treeInspect() {
203
+ let output = "";
204
+ output += `@ UnexpectedError ${this.location.treeInspectWithLabel()}\n`;
205
+ output += `├── message: "${this.message}"\n`;
206
+ output += `├── description: ${JSON.stringify(this.description)}\n`;
207
+ output += `├── expected: ${JSON.stringify(this.expected)}\n`;
208
+ output += `└── found: ${JSON.stringify(this.found)}\n`;
209
+ return output;
210
+ }
211
+ }
212
+ class UnexpectedTokenError extends HerbError {
213
+ expected_type;
214
+ found;
215
+ static from(data) {
216
+ return new UnexpectedTokenError({
217
+ type: data.type,
218
+ message: data.message,
219
+ location: Location.from(data.location),
220
+ expected_type: data.expected_type,
221
+ found: data.found ? Token.from(data.found) : null,
222
+ });
223
+ }
224
+ constructor(props) {
225
+ super(props.type, props.message, props.location);
226
+ this.expected_type = props.expected_type;
227
+ this.found = props.found;
228
+ }
229
+ toJSON() {
230
+ return {
231
+ ...super.toJSON(),
232
+ type: "UNEXPECTED_TOKEN_ERROR",
233
+ expected_type: this.expected_type,
234
+ found: this.found ? this.found.toJSON() : null,
235
+ };
236
+ }
237
+ toMonacoDiagnostic() {
238
+ return {
239
+ line: this.location.start.line,
240
+ column: this.location.start.column,
241
+ endLine: this.location.end.line,
242
+ endColumn: this.location.end.column,
243
+ message: this.message,
244
+ severity: 'error'
245
+ };
246
+ }
247
+ treeInspect() {
248
+ let output = "";
249
+ output += `@ UnexpectedTokenError ${this.location.treeInspectWithLabel()}\n`;
250
+ output += `├── message: "${this.message}"\n`;
251
+ output += `├── expected_type: ${JSON.stringify(this.expected_type)}\n`;
252
+ output += `└── found: ${this.found ? this.found.treeInspect() : "∅"}\n`;
253
+ return output;
254
+ }
255
+ }
256
+ class MissingOpeningTagError extends HerbError {
257
+ closing_tag;
258
+ static from(data) {
259
+ return new MissingOpeningTagError({
260
+ type: data.type,
261
+ message: data.message,
262
+ location: Location.from(data.location),
263
+ closing_tag: data.closing_tag ? Token.from(data.closing_tag) : null,
264
+ });
265
+ }
266
+ constructor(props) {
267
+ super(props.type, props.message, props.location);
268
+ this.closing_tag = props.closing_tag;
269
+ }
270
+ toJSON() {
271
+ return {
272
+ ...super.toJSON(),
273
+ type: "MISSING_OPENING_TAG_ERROR",
274
+ closing_tag: this.closing_tag ? this.closing_tag.toJSON() : null,
275
+ };
276
+ }
277
+ toMonacoDiagnostic() {
278
+ return {
279
+ line: this.location.start.line,
280
+ column: this.location.start.column,
281
+ endLine: this.location.end.line,
282
+ endColumn: this.location.end.column,
283
+ message: this.message,
284
+ severity: 'error'
285
+ };
286
+ }
287
+ treeInspect() {
288
+ let output = "";
289
+ output += `@ MissingOpeningTagError ${this.location.treeInspectWithLabel()}\n`;
290
+ output += `├── message: "${this.message}"\n`;
291
+ output += `└── closing_tag: ${this.closing_tag ? this.closing_tag.treeInspect() : "∅"}\n`;
292
+ return output;
293
+ }
294
+ }
295
+ class MissingClosingTagError extends HerbError {
296
+ opening_tag;
297
+ static from(data) {
298
+ return new MissingClosingTagError({
299
+ type: data.type,
300
+ message: data.message,
301
+ location: Location.from(data.location),
302
+ opening_tag: data.opening_tag ? Token.from(data.opening_tag) : null,
303
+ });
304
+ }
305
+ constructor(props) {
306
+ super(props.type, props.message, props.location);
307
+ this.opening_tag = props.opening_tag;
308
+ }
309
+ toJSON() {
310
+ return {
311
+ ...super.toJSON(),
312
+ type: "MISSING_CLOSING_TAG_ERROR",
313
+ opening_tag: this.opening_tag ? this.opening_tag.toJSON() : null,
314
+ };
315
+ }
316
+ toMonacoDiagnostic() {
317
+ return {
318
+ line: this.location.start.line,
319
+ column: this.location.start.column,
320
+ endLine: this.location.end.line,
321
+ endColumn: this.location.end.column,
322
+ message: this.message,
323
+ severity: 'error'
324
+ };
325
+ }
326
+ treeInspect() {
327
+ let output = "";
328
+ output += `@ MissingClosingTagError ${this.location.treeInspectWithLabel()}\n`;
329
+ output += `├── message: "${this.message}"\n`;
330
+ output += `└── opening_tag: ${this.opening_tag ? this.opening_tag.treeInspect() : "∅"}\n`;
331
+ return output;
332
+ }
333
+ }
334
+ class TagNamesMismatchError extends HerbError {
335
+ opening_tag;
336
+ closing_tag;
337
+ static from(data) {
338
+ return new TagNamesMismatchError({
339
+ type: data.type,
340
+ message: data.message,
341
+ location: Location.from(data.location),
342
+ opening_tag: data.opening_tag ? Token.from(data.opening_tag) : null,
343
+ closing_tag: data.closing_tag ? Token.from(data.closing_tag) : null,
344
+ });
345
+ }
346
+ constructor(props) {
347
+ super(props.type, props.message, props.location);
348
+ this.opening_tag = props.opening_tag;
349
+ this.closing_tag = props.closing_tag;
350
+ }
351
+ toJSON() {
352
+ return {
353
+ ...super.toJSON(),
354
+ type: "TAG_NAMES_MISMATCH_ERROR",
355
+ opening_tag: this.opening_tag ? this.opening_tag.toJSON() : null,
356
+ closing_tag: this.closing_tag ? this.closing_tag.toJSON() : null,
357
+ };
358
+ }
359
+ toMonacoDiagnostic() {
360
+ return {
361
+ line: this.location.start.line,
362
+ column: this.location.start.column,
363
+ endLine: this.location.end.line,
364
+ endColumn: this.location.end.column,
365
+ message: this.message,
366
+ severity: 'error'
367
+ };
368
+ }
369
+ treeInspect() {
370
+ let output = "";
371
+ output += `@ TagNamesMismatchError ${this.location.treeInspectWithLabel()}\n`;
372
+ output += `├── message: "${this.message}"\n`;
373
+ output += `├── opening_tag: ${this.opening_tag ? this.opening_tag.treeInspect() : "∅"}\n`;
374
+ output += `└── closing_tag: ${this.closing_tag ? this.closing_tag.treeInspect() : "∅"}\n`;
375
+ return output;
376
+ }
377
+ }
378
+ class QuotesMismatchError extends HerbError {
379
+ opening_quote;
380
+ closing_quote;
381
+ static from(data) {
382
+ return new QuotesMismatchError({
383
+ type: data.type,
384
+ message: data.message,
385
+ location: Location.from(data.location),
386
+ opening_quote: data.opening_quote ? Token.from(data.opening_quote) : null,
387
+ closing_quote: data.closing_quote ? Token.from(data.closing_quote) : null,
388
+ });
389
+ }
390
+ constructor(props) {
391
+ super(props.type, props.message, props.location);
392
+ this.opening_quote = props.opening_quote;
393
+ this.closing_quote = props.closing_quote;
394
+ }
395
+ toJSON() {
396
+ return {
397
+ ...super.toJSON(),
398
+ type: "QUOTES_MISMATCH_ERROR",
399
+ opening_quote: this.opening_quote ? this.opening_quote.toJSON() : null,
400
+ closing_quote: this.closing_quote ? this.closing_quote.toJSON() : null,
401
+ };
402
+ }
403
+ toMonacoDiagnostic() {
404
+ return {
405
+ line: this.location.start.line,
406
+ column: this.location.start.column,
407
+ endLine: this.location.end.line,
408
+ endColumn: this.location.end.column,
409
+ message: this.message,
410
+ severity: 'error'
411
+ };
412
+ }
413
+ treeInspect() {
414
+ let output = "";
415
+ output += `@ QuotesMismatchError ${this.location.treeInspectWithLabel()}\n`;
416
+ output += `├── message: "${this.message}"\n`;
417
+ output += `├── opening_quote: ${this.opening_quote ? this.opening_quote.treeInspect() : "∅"}\n`;
418
+ output += `└── closing_quote: ${this.closing_quote ? this.closing_quote.treeInspect() : "∅"}\n`;
419
+ return output;
420
+ }
421
+ }
422
+ class VoidElementClosingTagError extends HerbError {
423
+ tag_name;
424
+ expected;
425
+ found;
426
+ static from(data) {
427
+ return new VoidElementClosingTagError({
428
+ type: data.type,
429
+ message: data.message,
430
+ location: Location.from(data.location),
431
+ tag_name: data.tag_name ? Token.from(data.tag_name) : null,
432
+ expected: data.expected,
433
+ found: data.found,
434
+ });
435
+ }
436
+ constructor(props) {
437
+ super(props.type, props.message, props.location);
438
+ this.tag_name = props.tag_name;
439
+ this.expected = props.expected;
440
+ this.found = props.found;
441
+ }
442
+ toJSON() {
443
+ return {
444
+ ...super.toJSON(),
445
+ type: "VOID_ELEMENT_CLOSING_TAG_ERROR",
446
+ tag_name: this.tag_name ? this.tag_name.toJSON() : null,
447
+ expected: this.expected,
448
+ found: this.found,
449
+ };
450
+ }
451
+ toMonacoDiagnostic() {
452
+ return {
453
+ line: this.location.start.line,
454
+ column: this.location.start.column,
455
+ endLine: this.location.end.line,
456
+ endColumn: this.location.end.column,
457
+ message: this.message,
458
+ severity: 'error'
459
+ };
460
+ }
461
+ treeInspect() {
462
+ let output = "";
463
+ output += `@ VoidElementClosingTagError ${this.location.treeInspectWithLabel()}\n`;
464
+ output += `├── message: "${this.message}"\n`;
465
+ output += `├── tag_name: ${this.tag_name ? this.tag_name.treeInspect() : "∅"}\n`;
466
+ output += `├── expected: ${JSON.stringify(this.expected)}\n`;
467
+ output += `└── found: ${JSON.stringify(this.found)}\n`;
468
+ return output;
469
+ }
470
+ }
471
+ class UnclosedElementError extends HerbError {
472
+ opening_tag;
473
+ static from(data) {
474
+ return new UnclosedElementError({
475
+ type: data.type,
476
+ message: data.message,
477
+ location: Location.from(data.location),
478
+ opening_tag: data.opening_tag ? Token.from(data.opening_tag) : null,
479
+ });
480
+ }
481
+ constructor(props) {
482
+ super(props.type, props.message, props.location);
483
+ this.opening_tag = props.opening_tag;
484
+ }
485
+ toJSON() {
486
+ return {
487
+ ...super.toJSON(),
488
+ type: "UNCLOSED_ELEMENT_ERROR",
489
+ opening_tag: this.opening_tag ? this.opening_tag.toJSON() : null,
490
+ };
491
+ }
492
+ toMonacoDiagnostic() {
493
+ return {
494
+ line: this.location.start.line,
495
+ column: this.location.start.column,
496
+ endLine: this.location.end.line,
497
+ endColumn: this.location.end.column,
498
+ message: this.message,
499
+ severity: 'error'
500
+ };
501
+ }
502
+ treeInspect() {
503
+ let output = "";
504
+ output += `@ UnclosedElementError ${this.location.treeInspectWithLabel()}\n`;
505
+ output += `├── message: "${this.message}"\n`;
506
+ output += `└── opening_tag: ${this.opening_tag ? this.opening_tag.treeInspect() : "∅"}\n`;
507
+ return output;
508
+ }
509
+ }
510
+ class RubyParseError extends HerbError {
511
+ error_message;
512
+ diagnostic_id;
513
+ level;
514
+ static from(data) {
515
+ return new RubyParseError({
516
+ type: data.type,
517
+ message: data.message,
518
+ location: Location.from(data.location),
519
+ error_message: data.error_message,
520
+ diagnostic_id: data.diagnostic_id,
521
+ level: data.level,
522
+ });
523
+ }
524
+ constructor(props) {
525
+ super(props.type, props.message, props.location);
526
+ this.error_message = props.error_message;
527
+ this.diagnostic_id = props.diagnostic_id;
528
+ this.level = props.level;
529
+ }
530
+ toJSON() {
531
+ return {
532
+ ...super.toJSON(),
533
+ type: "RUBY_PARSE_ERROR",
534
+ error_message: this.error_message,
535
+ diagnostic_id: this.diagnostic_id,
536
+ level: this.level,
537
+ };
538
+ }
539
+ toMonacoDiagnostic() {
540
+ return {
541
+ line: this.location.start.line,
542
+ column: this.location.start.column,
543
+ endLine: this.location.end.line,
544
+ endColumn: this.location.end.column,
545
+ message: this.message,
546
+ severity: 'error'
547
+ };
548
+ }
549
+ treeInspect() {
550
+ let output = "";
551
+ output += `@ RubyParseError ${this.location.treeInspectWithLabel()}\n`;
552
+ output += `├── message: "${this.message}"\n`;
553
+ output += `├── error_message: ${JSON.stringify(this.error_message)}\n`;
554
+ output += `├── diagnostic_id: ${JSON.stringify(this.diagnostic_id)}\n`;
555
+ output += `└── level: ${JSON.stringify(this.level)}\n`;
556
+ return output;
557
+ }
558
+ }
559
+ function fromSerializedError(error) {
560
+ switch (error.type) {
561
+ case "UNEXPECTED_ERROR": return UnexpectedError.from(error);
562
+ case "UNEXPECTED_TOKEN_ERROR": return UnexpectedTokenError.from(error);
563
+ case "MISSING_OPENING_TAG_ERROR": return MissingOpeningTagError.from(error);
564
+ case "MISSING_CLOSING_TAG_ERROR": return MissingClosingTagError.from(error);
565
+ case "TAG_NAMES_MISMATCH_ERROR": return TagNamesMismatchError.from(error);
566
+ case "QUOTES_MISMATCH_ERROR": return QuotesMismatchError.from(error);
567
+ case "VOID_ELEMENT_CLOSING_TAG_ERROR": return VoidElementClosingTagError.from(error);
568
+ case "UNCLOSED_ELEMENT_ERROR": return UnclosedElementError.from(error);
569
+ case "RUBY_PARSE_ERROR": return RubyParseError.from(error);
570
+ default:
571
+ throw new Error(`Unknown node type: ${error.type}`);
572
+ }
573
+ }
574
+ function convertToUTF8(string) {
575
+ const bytes = [];
576
+ for (let i = 0; i < string.length; i++) {
577
+ bytes.push(string.charCodeAt(i));
578
+ }
579
+ const decoder = new TextDecoder("utf-8");
580
+ return decoder.decode(new Uint8Array(bytes));
581
+ }
582
+
583
+ // NOTE: This file is generated by the templates/template.rb script and should not
584
+ // be modified manually. See /Users/marcoroth/Development/herb-release/templates/javascript/packages/core/src/nodes.ts.erb
585
+ class Node {
586
+ type;
587
+ location;
588
+ errors;
589
+ static from(node) {
590
+ return fromSerializedNode(node);
591
+ }
592
+ constructor(type, location, errors) {
593
+ this.type = type;
594
+ this.location = location;
595
+ this.errors = errors;
596
+ }
597
+ toJSON() {
598
+ return {
599
+ type: this.type,
600
+ location: this.location.toJSON(),
601
+ errors: this.errors,
602
+ };
603
+ }
604
+ inspect() {
605
+ return this.treeInspect(0);
606
+ }
607
+ get isSingleLine() {
608
+ return this.location.start.line === this.location.end.line;
609
+ }
610
+ inspectArray(array, prefix) {
611
+ if (!array)
612
+ return "∅\n";
613
+ if (array.length === 0)
614
+ return "[]\n";
615
+ let output = `(${array.length} item${array.length == 1 ? "" : "s"})\n`;
616
+ array.forEach((item, index) => {
617
+ const isLast = index === array.length - 1;
618
+ if (item instanceof Node || item instanceof HerbError) {
619
+ output += this.inspectNode(item, prefix, isLast ? " " : "│ ", isLast, false);
620
+ }
621
+ else {
622
+ const symbol = isLast ? "└── " : "├── ";
623
+ output += `${prefix}${symbol} ${item}\n`;
624
+ }
625
+ });
626
+ output += `${prefix}\n`;
627
+ return output;
628
+ }
629
+ inspectNode(node, prefix, prefix2 = " ", last = true, trailingNewline = true) {
630
+ if (!node)
631
+ return "∅\n";
632
+ let output = trailingNewline ? "\n" : "";
633
+ output += `${prefix}`;
634
+ output += last ? "└── " : "├── ";
635
+ output += node
636
+ .treeInspect()
637
+ .trimStart()
638
+ .split("\n")
639
+ .map((line, index) => index == 0 ? line.trimStart() : `${prefix}${prefix2}${line}`)
640
+ .join("\n")
641
+ .trimStart();
642
+ output += `\n`;
643
+ return output;
644
+ }
645
+ }
646
+ class DocumentNode extends Node {
647
+ children;
648
+ static from(data) {
649
+ return new DocumentNode({
650
+ type: data.type,
651
+ location: Location.from(data.location),
652
+ errors: (data.errors || []).map(error => HerbError.from(error)),
653
+ children: (data.children || []).map(node => fromSerializedNode(node)),
654
+ });
655
+ }
656
+ constructor(props) {
657
+ super(props.type, props.location, props.errors);
658
+ this.children = props.children;
659
+ }
660
+ accept(visitor) {
661
+ visitor.visitDocumentNode(this);
662
+ }
663
+ childNodes() {
664
+ return [
665
+ ...this.children,
666
+ ];
667
+ }
668
+ compactChildNodes() {
669
+ return this.childNodes().filter(node => node !== null && node !== undefined);
670
+ }
671
+ recursiveErrors() {
672
+ return [
673
+ ...this.errors,
674
+ ...this.children.map(node => node.recursiveErrors()),
675
+ ].flat();
676
+ }
677
+ toJSON() {
678
+ return {
679
+ ...super.toJSON(),
680
+ type: "AST_DOCUMENT_NODE",
681
+ children: this.children.map(node => node.toJSON()),
682
+ };
683
+ }
684
+ treeInspect() {
685
+ let output = "";
686
+ output += `@ DocumentNode ${this.location.treeInspectWithLabel()}\n`;
687
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
688
+ output += `└── children: ${this.inspectArray(this.children, " ")}`;
689
+ // output += "\n";
690
+ return output;
691
+ }
692
+ }
693
+ class LiteralNode extends Node {
694
+ content;
695
+ static from(data) {
696
+ return new LiteralNode({
697
+ type: data.type,
698
+ location: Location.from(data.location),
699
+ errors: (data.errors || []).map(error => HerbError.from(error)),
700
+ content: data.content,
701
+ });
702
+ }
703
+ constructor(props) {
704
+ super(props.type, props.location, props.errors);
705
+ this.content = convertToUTF8(props.content);
706
+ }
707
+ accept(visitor) {
708
+ visitor.visitLiteralNode(this);
709
+ }
710
+ childNodes() {
711
+ return [];
712
+ }
713
+ compactChildNodes() {
714
+ return this.childNodes().filter(node => node !== null && node !== undefined);
715
+ }
716
+ recursiveErrors() {
717
+ return [
718
+ ...this.errors,
719
+ ].flat();
720
+ }
721
+ toJSON() {
722
+ return {
723
+ ...super.toJSON(),
724
+ type: "AST_LITERAL_NODE",
725
+ content: this.content,
726
+ };
727
+ }
728
+ treeInspect() {
729
+ let output = "";
730
+ output += `@ LiteralNode ${this.location.treeInspectWithLabel()}\n`;
731
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
732
+ output += `└── content: ${this.content ? JSON.stringify(this.content) : "∅"}\n`;
733
+ // output += "\n";
734
+ return output;
735
+ }
736
+ }
737
+ class HTMLOpenTagNode extends Node {
738
+ tag_opening;
739
+ tag_name;
740
+ tag_closing;
741
+ children;
742
+ is_void;
743
+ static from(data) {
744
+ return new HTMLOpenTagNode({
745
+ type: data.type,
746
+ location: Location.from(data.location),
747
+ errors: (data.errors || []).map(error => HerbError.from(error)),
748
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
749
+ tag_name: data.tag_name ? Token.from(data.tag_name) : null,
750
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
751
+ children: (data.children || []).map(node => fromSerializedNode(node)),
752
+ is_void: data.is_void,
753
+ });
754
+ }
755
+ constructor(props) {
756
+ super(props.type, props.location, props.errors);
757
+ this.tag_opening = props.tag_opening;
758
+ this.tag_name = props.tag_name;
759
+ this.tag_closing = props.tag_closing;
760
+ this.children = props.children;
761
+ this.is_void = props.is_void;
762
+ }
763
+ accept(visitor) {
764
+ visitor.visitHTMLOpenTagNode(this);
765
+ }
766
+ childNodes() {
767
+ return [
768
+ ...this.children,
769
+ ];
770
+ }
771
+ compactChildNodes() {
772
+ return this.childNodes().filter(node => node !== null && node !== undefined);
773
+ }
774
+ recursiveErrors() {
775
+ return [
776
+ ...this.errors,
777
+ ...this.children.map(node => node.recursiveErrors()),
778
+ ].flat();
779
+ }
780
+ toJSON() {
781
+ return {
782
+ ...super.toJSON(),
783
+ type: "AST_HTML_OPEN_TAG_NODE",
784
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
785
+ tag_name: this.tag_name ? this.tag_name.toJSON() : null,
786
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
787
+ children: this.children.map(node => node.toJSON()),
788
+ is_void: this.is_void,
789
+ };
790
+ }
791
+ treeInspect() {
792
+ let output = "";
793
+ output += `@ HTMLOpenTagNode ${this.location.treeInspectWithLabel()}\n`;
794
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
795
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
796
+ output += `├── tag_name: ${this.tag_name ? this.tag_name.treeInspect() : "∅"}\n`;
797
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
798
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
799
+ output += `└── is_void: ${typeof this.is_void === 'boolean' ? String(this.is_void) : "∅"}\n`;
800
+ // output += "\n";
801
+ return output;
802
+ }
803
+ }
804
+ class HTMLCloseTagNode extends Node {
805
+ tag_opening;
806
+ tag_name;
807
+ tag_closing;
808
+ static from(data) {
809
+ return new HTMLCloseTagNode({
810
+ type: data.type,
811
+ location: Location.from(data.location),
812
+ errors: (data.errors || []).map(error => HerbError.from(error)),
813
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
814
+ tag_name: data.tag_name ? Token.from(data.tag_name) : null,
815
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
816
+ });
817
+ }
818
+ constructor(props) {
819
+ super(props.type, props.location, props.errors);
820
+ this.tag_opening = props.tag_opening;
821
+ this.tag_name = props.tag_name;
822
+ this.tag_closing = props.tag_closing;
823
+ }
824
+ accept(visitor) {
825
+ visitor.visitHTMLCloseTagNode(this);
826
+ }
827
+ childNodes() {
828
+ return [];
829
+ }
830
+ compactChildNodes() {
831
+ return this.childNodes().filter(node => node !== null && node !== undefined);
832
+ }
833
+ recursiveErrors() {
834
+ return [
835
+ ...this.errors,
836
+ ].flat();
837
+ }
838
+ toJSON() {
839
+ return {
840
+ ...super.toJSON(),
841
+ type: "AST_HTML_CLOSE_TAG_NODE",
842
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
843
+ tag_name: this.tag_name ? this.tag_name.toJSON() : null,
844
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
845
+ };
846
+ }
847
+ treeInspect() {
848
+ let output = "";
849
+ output += `@ HTMLCloseTagNode ${this.location.treeInspectWithLabel()}\n`;
850
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
851
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
852
+ output += `├── tag_name: ${this.tag_name ? this.tag_name.treeInspect() : "∅"}\n`;
853
+ output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
854
+ // output += "\n";
855
+ return output;
856
+ }
857
+ }
858
+ class HTMLSelfCloseTagNode extends Node {
859
+ tag_opening;
860
+ tag_name;
861
+ attributes;
862
+ tag_closing;
863
+ is_void;
864
+ static from(data) {
865
+ return new HTMLSelfCloseTagNode({
866
+ type: data.type,
867
+ location: Location.from(data.location),
868
+ errors: (data.errors || []).map(error => HerbError.from(error)),
869
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
870
+ tag_name: data.tag_name ? Token.from(data.tag_name) : null,
871
+ attributes: (data.attributes || []).map(node => fromSerializedNode(node)),
872
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
873
+ is_void: data.is_void,
874
+ });
875
+ }
876
+ constructor(props) {
877
+ super(props.type, props.location, props.errors);
878
+ this.tag_opening = props.tag_opening;
879
+ this.tag_name = props.tag_name;
880
+ this.attributes = props.attributes;
881
+ this.tag_closing = props.tag_closing;
882
+ this.is_void = props.is_void;
883
+ }
884
+ accept(visitor) {
885
+ visitor.visitHTMLSelfCloseTagNode(this);
886
+ }
887
+ childNodes() {
888
+ return [
889
+ ...this.attributes,
890
+ ];
891
+ }
892
+ compactChildNodes() {
893
+ return this.childNodes().filter(node => node !== null && node !== undefined);
894
+ }
895
+ recursiveErrors() {
896
+ return [
897
+ ...this.errors,
898
+ ...this.attributes.map(node => node.recursiveErrors()),
899
+ ].flat();
900
+ }
901
+ toJSON() {
902
+ return {
903
+ ...super.toJSON(),
904
+ type: "AST_HTML_SELF_CLOSE_TAG_NODE",
905
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
906
+ tag_name: this.tag_name ? this.tag_name.toJSON() : null,
907
+ attributes: this.attributes.map(node => node.toJSON()),
908
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
909
+ is_void: this.is_void,
910
+ };
911
+ }
912
+ treeInspect() {
913
+ let output = "";
914
+ output += `@ HTMLSelfCloseTagNode ${this.location.treeInspectWithLabel()}\n`;
915
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
916
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
917
+ output += `├── tag_name: ${this.tag_name ? this.tag_name.treeInspect() : "∅"}\n`;
918
+ output += `├── attributes: ${this.inspectArray(this.attributes, "│ ")}`;
919
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
920
+ output += `└── is_void: ${typeof this.is_void === 'boolean' ? String(this.is_void) : "∅"}\n`;
921
+ // output += "\n";
922
+ return output;
923
+ }
924
+ }
925
+ class HTMLElementNode extends Node {
926
+ open_tag;
927
+ tag_name;
928
+ body;
929
+ close_tag;
930
+ is_void;
931
+ static from(data) {
932
+ return new HTMLElementNode({
933
+ type: data.type,
934
+ location: Location.from(data.location),
935
+ errors: (data.errors || []).map(error => HerbError.from(error)),
936
+ open_tag: data.open_tag ? fromSerializedNode(data.open_tag) : null,
937
+ tag_name: data.tag_name ? Token.from(data.tag_name) : null,
938
+ body: (data.body || []).map(node => fromSerializedNode(node)),
939
+ close_tag: data.close_tag ? fromSerializedNode(data.close_tag) : null,
940
+ is_void: data.is_void,
941
+ });
942
+ }
943
+ constructor(props) {
944
+ super(props.type, props.location, props.errors);
945
+ this.open_tag = props.open_tag;
946
+ this.tag_name = props.tag_name;
947
+ this.body = props.body;
948
+ this.close_tag = props.close_tag;
949
+ this.is_void = props.is_void;
950
+ }
951
+ accept(visitor) {
952
+ visitor.visitHTMLElementNode(this);
953
+ }
954
+ childNodes() {
955
+ return [
956
+ this.open_tag,
957
+ ...this.body,
958
+ this.close_tag,
959
+ ];
960
+ }
961
+ compactChildNodes() {
962
+ return this.childNodes().filter(node => node !== null && node !== undefined);
963
+ }
964
+ recursiveErrors() {
965
+ return [
966
+ ...this.errors,
967
+ this.open_tag ? this.open_tag.recursiveErrors() : [],
968
+ ...this.body.map(node => node.recursiveErrors()),
969
+ this.close_tag ? this.close_tag.recursiveErrors() : [],
970
+ ].flat();
971
+ }
972
+ toJSON() {
973
+ return {
974
+ ...super.toJSON(),
975
+ type: "AST_HTML_ELEMENT_NODE",
976
+ open_tag: this.open_tag ? this.open_tag.toJSON() : null,
977
+ tag_name: this.tag_name ? this.tag_name.toJSON() : null,
978
+ body: this.body.map(node => node.toJSON()),
979
+ close_tag: this.close_tag ? this.close_tag.toJSON() : null,
980
+ is_void: this.is_void,
981
+ };
982
+ }
983
+ treeInspect() {
984
+ let output = "";
985
+ output += `@ HTMLElementNode ${this.location.treeInspectWithLabel()}\n`;
986
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
987
+ output += `├── open_tag: ${this.inspectNode(this.open_tag, "│ ")}`;
988
+ output += `├── tag_name: ${this.tag_name ? this.tag_name.treeInspect() : "∅"}\n`;
989
+ output += `├── body: ${this.inspectArray(this.body, "│ ")}`;
990
+ output += `├── close_tag: ${this.inspectNode(this.close_tag, "│ ")}`;
991
+ output += `└── is_void: ${typeof this.is_void === 'boolean' ? String(this.is_void) : "∅"}\n`;
992
+ // output += "\n";
993
+ return output;
994
+ }
995
+ }
996
+ class HTMLAttributeValueNode extends Node {
997
+ open_quote;
998
+ children;
999
+ close_quote;
1000
+ quoted;
1001
+ static from(data) {
1002
+ return new HTMLAttributeValueNode({
1003
+ type: data.type,
1004
+ location: Location.from(data.location),
1005
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1006
+ open_quote: data.open_quote ? Token.from(data.open_quote) : null,
1007
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1008
+ close_quote: data.close_quote ? Token.from(data.close_quote) : null,
1009
+ quoted: data.quoted,
1010
+ });
1011
+ }
1012
+ constructor(props) {
1013
+ super(props.type, props.location, props.errors);
1014
+ this.open_quote = props.open_quote;
1015
+ this.children = props.children;
1016
+ this.close_quote = props.close_quote;
1017
+ this.quoted = props.quoted;
1018
+ }
1019
+ accept(visitor) {
1020
+ visitor.visitHTMLAttributeValueNode(this);
1021
+ }
1022
+ childNodes() {
1023
+ return [
1024
+ ...this.children,
1025
+ ];
1026
+ }
1027
+ compactChildNodes() {
1028
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1029
+ }
1030
+ recursiveErrors() {
1031
+ return [
1032
+ ...this.errors,
1033
+ ...this.children.map(node => node.recursiveErrors()),
1034
+ ].flat();
1035
+ }
1036
+ toJSON() {
1037
+ return {
1038
+ ...super.toJSON(),
1039
+ type: "AST_HTML_ATTRIBUTE_VALUE_NODE",
1040
+ open_quote: this.open_quote ? this.open_quote.toJSON() : null,
1041
+ children: this.children.map(node => node.toJSON()),
1042
+ close_quote: this.close_quote ? this.close_quote.toJSON() : null,
1043
+ quoted: this.quoted,
1044
+ };
1045
+ }
1046
+ treeInspect() {
1047
+ let output = "";
1048
+ output += `@ HTMLAttributeValueNode ${this.location.treeInspectWithLabel()}\n`;
1049
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1050
+ output += `├── open_quote: ${this.open_quote ? this.open_quote.treeInspect() : "∅"}\n`;
1051
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1052
+ output += `├── close_quote: ${this.close_quote ? this.close_quote.treeInspect() : "∅"}\n`;
1053
+ output += `└── quoted: ${typeof this.quoted === 'boolean' ? String(this.quoted) : "∅"}\n`;
1054
+ // output += "\n";
1055
+ return output;
1056
+ }
1057
+ }
1058
+ class HTMLAttributeNameNode extends Node {
1059
+ name;
1060
+ static from(data) {
1061
+ return new HTMLAttributeNameNode({
1062
+ type: data.type,
1063
+ location: Location.from(data.location),
1064
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1065
+ name: data.name ? Token.from(data.name) : null,
1066
+ });
1067
+ }
1068
+ constructor(props) {
1069
+ super(props.type, props.location, props.errors);
1070
+ this.name = props.name;
1071
+ }
1072
+ accept(visitor) {
1073
+ visitor.visitHTMLAttributeNameNode(this);
1074
+ }
1075
+ childNodes() {
1076
+ return [];
1077
+ }
1078
+ compactChildNodes() {
1079
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1080
+ }
1081
+ recursiveErrors() {
1082
+ return [
1083
+ ...this.errors,
1084
+ ].flat();
1085
+ }
1086
+ toJSON() {
1087
+ return {
1088
+ ...super.toJSON(),
1089
+ type: "AST_HTML_ATTRIBUTE_NAME_NODE",
1090
+ name: this.name ? this.name.toJSON() : null,
1091
+ };
1092
+ }
1093
+ treeInspect() {
1094
+ let output = "";
1095
+ output += `@ HTMLAttributeNameNode ${this.location.treeInspectWithLabel()}\n`;
1096
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1097
+ output += `└── name: ${this.name ? this.name.treeInspect() : "∅"}\n`;
1098
+ // output += "\n";
1099
+ return output;
1100
+ }
1101
+ }
1102
+ class HTMLAttributeNode extends Node {
1103
+ name;
1104
+ equals;
1105
+ value;
1106
+ static from(data) {
1107
+ return new HTMLAttributeNode({
1108
+ type: data.type,
1109
+ location: Location.from(data.location),
1110
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1111
+ name: data.name ? fromSerializedNode(data.name) : null,
1112
+ equals: data.equals ? Token.from(data.equals) : null,
1113
+ value: data.value ? fromSerializedNode(data.value) : null,
1114
+ });
1115
+ }
1116
+ constructor(props) {
1117
+ super(props.type, props.location, props.errors);
1118
+ this.name = props.name;
1119
+ this.equals = props.equals;
1120
+ this.value = props.value;
1121
+ }
1122
+ accept(visitor) {
1123
+ visitor.visitHTMLAttributeNode(this);
1124
+ }
1125
+ childNodes() {
1126
+ return [
1127
+ this.name,
1128
+ this.value,
1129
+ ];
1130
+ }
1131
+ compactChildNodes() {
1132
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1133
+ }
1134
+ recursiveErrors() {
1135
+ return [
1136
+ ...this.errors,
1137
+ this.name ? this.name.recursiveErrors() : [],
1138
+ this.value ? this.value.recursiveErrors() : [],
1139
+ ].flat();
1140
+ }
1141
+ toJSON() {
1142
+ return {
1143
+ ...super.toJSON(),
1144
+ type: "AST_HTML_ATTRIBUTE_NODE",
1145
+ name: this.name ? this.name.toJSON() : null,
1146
+ equals: this.equals ? this.equals.toJSON() : null,
1147
+ value: this.value ? this.value.toJSON() : null,
1148
+ };
1149
+ }
1150
+ treeInspect() {
1151
+ let output = "";
1152
+ output += `@ HTMLAttributeNode ${this.location.treeInspectWithLabel()}\n`;
1153
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1154
+ output += `├── name: ${this.inspectNode(this.name, "│ ")}`;
1155
+ output += `├── equals: ${this.equals ? this.equals.treeInspect() : "∅"}\n`;
1156
+ output += `└── value: ${this.inspectNode(this.value, " ")}`;
1157
+ // output += "\n";
1158
+ return output;
1159
+ }
1160
+ }
1161
+ class HTMLTextNode extends Node {
1162
+ content;
1163
+ static from(data) {
1164
+ return new HTMLTextNode({
1165
+ type: data.type,
1166
+ location: Location.from(data.location),
1167
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1168
+ content: data.content,
1169
+ });
1170
+ }
1171
+ constructor(props) {
1172
+ super(props.type, props.location, props.errors);
1173
+ this.content = convertToUTF8(props.content);
1174
+ }
1175
+ accept(visitor) {
1176
+ visitor.visitHTMLTextNode(this);
1177
+ }
1178
+ childNodes() {
1179
+ return [];
1180
+ }
1181
+ compactChildNodes() {
1182
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1183
+ }
1184
+ recursiveErrors() {
1185
+ return [
1186
+ ...this.errors,
1187
+ ].flat();
1188
+ }
1189
+ toJSON() {
1190
+ return {
1191
+ ...super.toJSON(),
1192
+ type: "AST_HTML_TEXT_NODE",
1193
+ content: this.content,
1194
+ };
1195
+ }
1196
+ treeInspect() {
1197
+ let output = "";
1198
+ output += `@ HTMLTextNode ${this.location.treeInspectWithLabel()}\n`;
1199
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1200
+ output += `└── content: ${this.content ? JSON.stringify(this.content) : "∅"}\n`;
1201
+ // output += "\n";
1202
+ return output;
1203
+ }
1204
+ }
1205
+ class HTMLCommentNode extends Node {
1206
+ comment_start;
1207
+ children;
1208
+ comment_end;
1209
+ static from(data) {
1210
+ return new HTMLCommentNode({
1211
+ type: data.type,
1212
+ location: Location.from(data.location),
1213
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1214
+ comment_start: data.comment_start ? Token.from(data.comment_start) : null,
1215
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1216
+ comment_end: data.comment_end ? Token.from(data.comment_end) : null,
1217
+ });
1218
+ }
1219
+ constructor(props) {
1220
+ super(props.type, props.location, props.errors);
1221
+ this.comment_start = props.comment_start;
1222
+ this.children = props.children;
1223
+ this.comment_end = props.comment_end;
1224
+ }
1225
+ accept(visitor) {
1226
+ visitor.visitHTMLCommentNode(this);
1227
+ }
1228
+ childNodes() {
1229
+ return [
1230
+ ...this.children,
1231
+ ];
1232
+ }
1233
+ compactChildNodes() {
1234
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1235
+ }
1236
+ recursiveErrors() {
1237
+ return [
1238
+ ...this.errors,
1239
+ ...this.children.map(node => node.recursiveErrors()),
1240
+ ].flat();
1241
+ }
1242
+ toJSON() {
1243
+ return {
1244
+ ...super.toJSON(),
1245
+ type: "AST_HTML_COMMENT_NODE",
1246
+ comment_start: this.comment_start ? this.comment_start.toJSON() : null,
1247
+ children: this.children.map(node => node.toJSON()),
1248
+ comment_end: this.comment_end ? this.comment_end.toJSON() : null,
1249
+ };
1250
+ }
1251
+ treeInspect() {
1252
+ let output = "";
1253
+ output += `@ HTMLCommentNode ${this.location.treeInspectWithLabel()}\n`;
1254
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1255
+ output += `├── comment_start: ${this.comment_start ? this.comment_start.treeInspect() : "∅"}\n`;
1256
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1257
+ output += `└── comment_end: ${this.comment_end ? this.comment_end.treeInspect() : "∅"}\n`;
1258
+ // output += "\n";
1259
+ return output;
1260
+ }
1261
+ }
1262
+ class HTMLDoctypeNode extends Node {
1263
+ tag_opening;
1264
+ children;
1265
+ tag_closing;
1266
+ static from(data) {
1267
+ return new HTMLDoctypeNode({
1268
+ type: data.type,
1269
+ location: Location.from(data.location),
1270
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1271
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1272
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1273
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1274
+ });
1275
+ }
1276
+ constructor(props) {
1277
+ super(props.type, props.location, props.errors);
1278
+ this.tag_opening = props.tag_opening;
1279
+ this.children = props.children;
1280
+ this.tag_closing = props.tag_closing;
1281
+ }
1282
+ accept(visitor) {
1283
+ visitor.visitHTMLDoctypeNode(this);
1284
+ }
1285
+ childNodes() {
1286
+ return [
1287
+ ...this.children,
1288
+ ];
1289
+ }
1290
+ compactChildNodes() {
1291
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1292
+ }
1293
+ recursiveErrors() {
1294
+ return [
1295
+ ...this.errors,
1296
+ ...this.children.map(node => node.recursiveErrors()),
1297
+ ].flat();
1298
+ }
1299
+ toJSON() {
1300
+ return {
1301
+ ...super.toJSON(),
1302
+ type: "AST_HTML_DOCTYPE_NODE",
1303
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1304
+ children: this.children.map(node => node.toJSON()),
1305
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1306
+ };
1307
+ }
1308
+ treeInspect() {
1309
+ let output = "";
1310
+ output += `@ HTMLDoctypeNode ${this.location.treeInspectWithLabel()}\n`;
1311
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1312
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1313
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1314
+ output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1315
+ // output += "\n";
1316
+ return output;
1317
+ }
1318
+ }
1319
+ class WhitespaceNode extends Node {
1320
+ value;
1321
+ static from(data) {
1322
+ return new WhitespaceNode({
1323
+ type: data.type,
1324
+ location: Location.from(data.location),
1325
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1326
+ value: data.value ? Token.from(data.value) : null,
1327
+ });
1328
+ }
1329
+ constructor(props) {
1330
+ super(props.type, props.location, props.errors);
1331
+ this.value = props.value;
1332
+ }
1333
+ accept(visitor) {
1334
+ visitor.visitWhitespaceNode(this);
1335
+ }
1336
+ childNodes() {
1337
+ return [];
1338
+ }
1339
+ compactChildNodes() {
1340
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1341
+ }
1342
+ recursiveErrors() {
1343
+ return [
1344
+ ...this.errors,
1345
+ ].flat();
1346
+ }
1347
+ toJSON() {
1348
+ return {
1349
+ ...super.toJSON(),
1350
+ type: "AST_WHITESPACE_NODE",
1351
+ value: this.value ? this.value.toJSON() : null,
1352
+ };
1353
+ }
1354
+ treeInspect() {
1355
+ let output = "";
1356
+ output += `@ WhitespaceNode ${this.location.treeInspectWithLabel()}\n`;
1357
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1358
+ output += `└── value: ${this.value ? this.value.treeInspect() : "∅"}\n`;
1359
+ // output += "\n";
1360
+ return output;
1361
+ }
1362
+ }
1363
+ class ERBContentNode extends Node {
1364
+ tag_opening;
1365
+ content;
1366
+ tag_closing;
1367
+ // no-op for analyzed_ruby
1368
+ parsed;
1369
+ valid;
1370
+ static from(data) {
1371
+ return new ERBContentNode({
1372
+ type: data.type,
1373
+ location: Location.from(data.location),
1374
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1375
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1376
+ content: data.content ? Token.from(data.content) : null,
1377
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1378
+ // no-op for analyzed_ruby
1379
+ parsed: data.parsed,
1380
+ valid: data.valid,
1381
+ });
1382
+ }
1383
+ constructor(props) {
1384
+ super(props.type, props.location, props.errors);
1385
+ this.tag_opening = props.tag_opening;
1386
+ this.content = props.content;
1387
+ this.tag_closing = props.tag_closing;
1388
+ // no-op for analyzed_ruby
1389
+ this.parsed = props.parsed;
1390
+ this.valid = props.valid;
1391
+ }
1392
+ accept(visitor) {
1393
+ visitor.visitERBContentNode(this);
1394
+ }
1395
+ childNodes() {
1396
+ return [];
1397
+ }
1398
+ compactChildNodes() {
1399
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1400
+ }
1401
+ recursiveErrors() {
1402
+ return [
1403
+ ...this.errors,
1404
+ ].flat();
1405
+ }
1406
+ toJSON() {
1407
+ return {
1408
+ ...super.toJSON(),
1409
+ type: "AST_ERB_CONTENT_NODE",
1410
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1411
+ content: this.content ? this.content.toJSON() : null,
1412
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1413
+ // no-op for analyzed_ruby
1414
+ parsed: this.parsed,
1415
+ valid: this.valid,
1416
+ };
1417
+ }
1418
+ treeInspect() {
1419
+ let output = "";
1420
+ output += `@ ERBContentNode ${this.location.treeInspectWithLabel()}\n`;
1421
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1422
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1423
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1424
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1425
+ // no-op for analyzed_ruby
1426
+ output += `├── parsed: ${typeof this.parsed === 'boolean' ? String(this.parsed) : "∅"}\n`;
1427
+ output += `└── valid: ${typeof this.valid === 'boolean' ? String(this.valid) : "∅"}\n`;
1428
+ // output += "\n";
1429
+ return output;
1430
+ }
1431
+ }
1432
+ class ERBEndNode extends Node {
1433
+ tag_opening;
1434
+ content;
1435
+ tag_closing;
1436
+ static from(data) {
1437
+ return new ERBEndNode({
1438
+ type: data.type,
1439
+ location: Location.from(data.location),
1440
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1441
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1442
+ content: data.content ? Token.from(data.content) : null,
1443
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1444
+ });
1445
+ }
1446
+ constructor(props) {
1447
+ super(props.type, props.location, props.errors);
1448
+ this.tag_opening = props.tag_opening;
1449
+ this.content = props.content;
1450
+ this.tag_closing = props.tag_closing;
1451
+ }
1452
+ accept(visitor) {
1453
+ visitor.visitERBEndNode(this);
1454
+ }
1455
+ childNodes() {
1456
+ return [];
1457
+ }
1458
+ compactChildNodes() {
1459
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1460
+ }
1461
+ recursiveErrors() {
1462
+ return [
1463
+ ...this.errors,
1464
+ ].flat();
1465
+ }
1466
+ toJSON() {
1467
+ return {
1468
+ ...super.toJSON(),
1469
+ type: "AST_ERB_END_NODE",
1470
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1471
+ content: this.content ? this.content.toJSON() : null,
1472
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1473
+ };
1474
+ }
1475
+ treeInspect() {
1476
+ let output = "";
1477
+ output += `@ ERBEndNode ${this.location.treeInspectWithLabel()}\n`;
1478
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1479
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1480
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1481
+ output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1482
+ // output += "\n";
1483
+ return output;
1484
+ }
1485
+ }
1486
+ class ERBElseNode extends Node {
1487
+ tag_opening;
1488
+ content;
1489
+ tag_closing;
1490
+ statements;
1491
+ static from(data) {
1492
+ return new ERBElseNode({
1493
+ type: data.type,
1494
+ location: Location.from(data.location),
1495
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1496
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1497
+ content: data.content ? Token.from(data.content) : null,
1498
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1499
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
1500
+ });
1501
+ }
1502
+ constructor(props) {
1503
+ super(props.type, props.location, props.errors);
1504
+ this.tag_opening = props.tag_opening;
1505
+ this.content = props.content;
1506
+ this.tag_closing = props.tag_closing;
1507
+ this.statements = props.statements;
1508
+ }
1509
+ accept(visitor) {
1510
+ visitor.visitERBElseNode(this);
1511
+ }
1512
+ childNodes() {
1513
+ return [
1514
+ ...this.statements,
1515
+ ];
1516
+ }
1517
+ compactChildNodes() {
1518
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1519
+ }
1520
+ recursiveErrors() {
1521
+ return [
1522
+ ...this.errors,
1523
+ ...this.statements.map(node => node.recursiveErrors()),
1524
+ ].flat();
1525
+ }
1526
+ toJSON() {
1527
+ return {
1528
+ ...super.toJSON(),
1529
+ type: "AST_ERB_ELSE_NODE",
1530
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1531
+ content: this.content ? this.content.toJSON() : null,
1532
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1533
+ statements: this.statements.map(node => node.toJSON()),
1534
+ };
1535
+ }
1536
+ treeInspect() {
1537
+ let output = "";
1538
+ output += `@ ERBElseNode ${this.location.treeInspectWithLabel()}\n`;
1539
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1540
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1541
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1542
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1543
+ output += `└── statements: ${this.inspectArray(this.statements, " ")}`;
1544
+ // output += "\n";
1545
+ return output;
1546
+ }
1547
+ }
1548
+ class ERBIfNode extends Node {
1549
+ tag_opening;
1550
+ content;
1551
+ tag_closing;
1552
+ statements;
1553
+ subsequent;
1554
+ end_node;
1555
+ static from(data) {
1556
+ return new ERBIfNode({
1557
+ type: data.type,
1558
+ location: Location.from(data.location),
1559
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1560
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1561
+ content: data.content ? Token.from(data.content) : null,
1562
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1563
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
1564
+ subsequent: data.subsequent ? fromSerializedNode(data.subsequent) : null,
1565
+ end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
1566
+ });
1567
+ }
1568
+ constructor(props) {
1569
+ super(props.type, props.location, props.errors);
1570
+ this.tag_opening = props.tag_opening;
1571
+ this.content = props.content;
1572
+ this.tag_closing = props.tag_closing;
1573
+ this.statements = props.statements;
1574
+ this.subsequent = props.subsequent;
1575
+ this.end_node = props.end_node;
1576
+ }
1577
+ accept(visitor) {
1578
+ visitor.visitERBIfNode(this);
1579
+ }
1580
+ childNodes() {
1581
+ return [
1582
+ ...this.statements,
1583
+ this.subsequent,
1584
+ this.end_node,
1585
+ ];
1586
+ }
1587
+ compactChildNodes() {
1588
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1589
+ }
1590
+ recursiveErrors() {
1591
+ return [
1592
+ ...this.errors,
1593
+ ...this.statements.map(node => node.recursiveErrors()),
1594
+ this.subsequent ? this.subsequent.recursiveErrors() : [],
1595
+ this.end_node ? this.end_node.recursiveErrors() : [],
1596
+ ].flat();
1597
+ }
1598
+ toJSON() {
1599
+ return {
1600
+ ...super.toJSON(),
1601
+ type: "AST_ERB_IF_NODE",
1602
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1603
+ content: this.content ? this.content.toJSON() : null,
1604
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1605
+ statements: this.statements.map(node => node.toJSON()),
1606
+ subsequent: this.subsequent ? this.subsequent.toJSON() : null,
1607
+ end_node: this.end_node ? this.end_node.toJSON() : null,
1608
+ };
1609
+ }
1610
+ treeInspect() {
1611
+ let output = "";
1612
+ output += `@ ERBIfNode ${this.location.treeInspectWithLabel()}\n`;
1613
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1614
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1615
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1616
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1617
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
1618
+ output += `├── subsequent: ${this.inspectNode(this.subsequent, "│ ")}`;
1619
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
1620
+ // output += "\n";
1621
+ return output;
1622
+ }
1623
+ }
1624
+ class ERBBlockNode extends Node {
1625
+ tag_opening;
1626
+ content;
1627
+ tag_closing;
1628
+ body;
1629
+ end_node;
1630
+ static from(data) {
1631
+ return new ERBBlockNode({
1632
+ type: data.type,
1633
+ location: Location.from(data.location),
1634
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1635
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1636
+ content: data.content ? Token.from(data.content) : null,
1637
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1638
+ body: (data.body || []).map(node => fromSerializedNode(node)),
1639
+ end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
1640
+ });
1641
+ }
1642
+ constructor(props) {
1643
+ super(props.type, props.location, props.errors);
1644
+ this.tag_opening = props.tag_opening;
1645
+ this.content = props.content;
1646
+ this.tag_closing = props.tag_closing;
1647
+ this.body = props.body;
1648
+ this.end_node = props.end_node;
1649
+ }
1650
+ accept(visitor) {
1651
+ visitor.visitERBBlockNode(this);
1652
+ }
1653
+ childNodes() {
1654
+ return [
1655
+ ...this.body,
1656
+ this.end_node,
1657
+ ];
1658
+ }
1659
+ compactChildNodes() {
1660
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1661
+ }
1662
+ recursiveErrors() {
1663
+ return [
1664
+ ...this.errors,
1665
+ ...this.body.map(node => node.recursiveErrors()),
1666
+ this.end_node ? this.end_node.recursiveErrors() : [],
1667
+ ].flat();
1668
+ }
1669
+ toJSON() {
1670
+ return {
1671
+ ...super.toJSON(),
1672
+ type: "AST_ERB_BLOCK_NODE",
1673
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1674
+ content: this.content ? this.content.toJSON() : null,
1675
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1676
+ body: this.body.map(node => node.toJSON()),
1677
+ end_node: this.end_node ? this.end_node.toJSON() : null,
1678
+ };
1679
+ }
1680
+ treeInspect() {
1681
+ let output = "";
1682
+ output += `@ ERBBlockNode ${this.location.treeInspectWithLabel()}\n`;
1683
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1684
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1685
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1686
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1687
+ output += `├── body: ${this.inspectArray(this.body, "│ ")}`;
1688
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
1689
+ // output += "\n";
1690
+ return output;
1691
+ }
1692
+ }
1693
+ class ERBWhenNode extends Node {
1694
+ tag_opening;
1695
+ content;
1696
+ tag_closing;
1697
+ statements;
1698
+ static from(data) {
1699
+ return new ERBWhenNode({
1700
+ type: data.type,
1701
+ location: Location.from(data.location),
1702
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1703
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1704
+ content: data.content ? Token.from(data.content) : null,
1705
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1706
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
1707
+ });
1708
+ }
1709
+ constructor(props) {
1710
+ super(props.type, props.location, props.errors);
1711
+ this.tag_opening = props.tag_opening;
1712
+ this.content = props.content;
1713
+ this.tag_closing = props.tag_closing;
1714
+ this.statements = props.statements;
1715
+ }
1716
+ accept(visitor) {
1717
+ visitor.visitERBWhenNode(this);
1718
+ }
1719
+ childNodes() {
1720
+ return [
1721
+ ...this.statements,
1722
+ ];
1723
+ }
1724
+ compactChildNodes() {
1725
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1726
+ }
1727
+ recursiveErrors() {
1728
+ return [
1729
+ ...this.errors,
1730
+ ...this.statements.map(node => node.recursiveErrors()),
1731
+ ].flat();
1732
+ }
1733
+ toJSON() {
1734
+ return {
1735
+ ...super.toJSON(),
1736
+ type: "AST_ERB_WHEN_NODE",
1737
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1738
+ content: this.content ? this.content.toJSON() : null,
1739
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1740
+ statements: this.statements.map(node => node.toJSON()),
1741
+ };
1742
+ }
1743
+ treeInspect() {
1744
+ let output = "";
1745
+ output += `@ ERBWhenNode ${this.location.treeInspectWithLabel()}\n`;
1746
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1747
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1748
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1749
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1750
+ output += `└── statements: ${this.inspectArray(this.statements, " ")}`;
1751
+ // output += "\n";
1752
+ return output;
1753
+ }
1754
+ }
1755
+ class ERBCaseNode extends Node {
1756
+ tag_opening;
1757
+ content;
1758
+ tag_closing;
1759
+ children;
1760
+ conditions;
1761
+ else_clause;
1762
+ end_node;
1763
+ static from(data) {
1764
+ return new ERBCaseNode({
1765
+ type: data.type,
1766
+ location: Location.from(data.location),
1767
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1768
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1769
+ content: data.content ? Token.from(data.content) : null,
1770
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1771
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1772
+ conditions: (data.conditions || []).map(node => fromSerializedNode(node)),
1773
+ else_clause: data.else_clause ? fromSerializedNode(data.else_clause) : null,
1774
+ end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
1775
+ });
1776
+ }
1777
+ constructor(props) {
1778
+ super(props.type, props.location, props.errors);
1779
+ this.tag_opening = props.tag_opening;
1780
+ this.content = props.content;
1781
+ this.tag_closing = props.tag_closing;
1782
+ this.children = props.children;
1783
+ this.conditions = props.conditions;
1784
+ this.else_clause = props.else_clause;
1785
+ this.end_node = props.end_node;
1786
+ }
1787
+ accept(visitor) {
1788
+ visitor.visitERBCaseNode(this);
1789
+ }
1790
+ childNodes() {
1791
+ return [
1792
+ ...this.children,
1793
+ ...this.conditions,
1794
+ this.else_clause,
1795
+ this.end_node,
1796
+ ];
1797
+ }
1798
+ compactChildNodes() {
1799
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1800
+ }
1801
+ recursiveErrors() {
1802
+ return [
1803
+ ...this.errors,
1804
+ ...this.children.map(node => node.recursiveErrors()),
1805
+ ...this.conditions.map(node => node.recursiveErrors()),
1806
+ this.else_clause ? this.else_clause.recursiveErrors() : [],
1807
+ this.end_node ? this.end_node.recursiveErrors() : [],
1808
+ ].flat();
1809
+ }
1810
+ toJSON() {
1811
+ return {
1812
+ ...super.toJSON(),
1813
+ type: "AST_ERB_CASE_NODE",
1814
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1815
+ content: this.content ? this.content.toJSON() : null,
1816
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1817
+ children: this.children.map(node => node.toJSON()),
1818
+ conditions: this.conditions.map(node => node.toJSON()),
1819
+ else_clause: this.else_clause ? this.else_clause.toJSON() : null,
1820
+ end_node: this.end_node ? this.end_node.toJSON() : null,
1821
+ };
1822
+ }
1823
+ treeInspect() {
1824
+ let output = "";
1825
+ output += `@ ERBCaseNode ${this.location.treeInspectWithLabel()}\n`;
1826
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1827
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1828
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1829
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1830
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1831
+ output += `├── conditions: ${this.inspectArray(this.conditions, "│ ")}`;
1832
+ output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
1833
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
1834
+ // output += "\n";
1835
+ return output;
1836
+ }
1837
+ }
1838
+ class ERBCaseMatchNode extends Node {
1839
+ tag_opening;
1840
+ content;
1841
+ tag_closing;
1842
+ children;
1843
+ conditions;
1844
+ else_clause;
1845
+ end_node;
1846
+ static from(data) {
1847
+ return new ERBCaseMatchNode({
1848
+ type: data.type,
1849
+ location: Location.from(data.location),
1850
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1851
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1852
+ content: data.content ? Token.from(data.content) : null,
1853
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1854
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1855
+ conditions: (data.conditions || []).map(node => fromSerializedNode(node)),
1856
+ else_clause: data.else_clause ? fromSerializedNode(data.else_clause) : null,
1857
+ end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
1858
+ });
1859
+ }
1860
+ constructor(props) {
1861
+ super(props.type, props.location, props.errors);
1862
+ this.tag_opening = props.tag_opening;
1863
+ this.content = props.content;
1864
+ this.tag_closing = props.tag_closing;
1865
+ this.children = props.children;
1866
+ this.conditions = props.conditions;
1867
+ this.else_clause = props.else_clause;
1868
+ this.end_node = props.end_node;
1869
+ }
1870
+ accept(visitor) {
1871
+ visitor.visitERBCaseMatchNode(this);
1872
+ }
1873
+ childNodes() {
1874
+ return [
1875
+ ...this.children,
1876
+ ...this.conditions,
1877
+ this.else_clause,
1878
+ this.end_node,
1879
+ ];
1880
+ }
1881
+ compactChildNodes() {
1882
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1883
+ }
1884
+ recursiveErrors() {
1885
+ return [
1886
+ ...this.errors,
1887
+ ...this.children.map(node => node.recursiveErrors()),
1888
+ ...this.conditions.map(node => node.recursiveErrors()),
1889
+ this.else_clause ? this.else_clause.recursiveErrors() : [],
1890
+ this.end_node ? this.end_node.recursiveErrors() : [],
1891
+ ].flat();
1892
+ }
1893
+ toJSON() {
1894
+ return {
1895
+ ...super.toJSON(),
1896
+ type: "AST_ERB_CASE_MATCH_NODE",
1897
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1898
+ content: this.content ? this.content.toJSON() : null,
1899
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1900
+ children: this.children.map(node => node.toJSON()),
1901
+ conditions: this.conditions.map(node => node.toJSON()),
1902
+ else_clause: this.else_clause ? this.else_clause.toJSON() : null,
1903
+ end_node: this.end_node ? this.end_node.toJSON() : null,
1904
+ };
1905
+ }
1906
+ treeInspect() {
1907
+ let output = "";
1908
+ output += `@ ERBCaseMatchNode ${this.location.treeInspectWithLabel()}\n`;
1909
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1910
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1911
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1912
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1913
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1914
+ output += `├── conditions: ${this.inspectArray(this.conditions, "│ ")}`;
1915
+ output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
1916
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
1917
+ // output += "\n";
1918
+ return output;
1919
+ }
1920
+ }
1921
+ class ERBWhileNode extends Node {
1922
+ tag_opening;
1923
+ content;
1924
+ tag_closing;
1925
+ statements;
1926
+ end_node;
1927
+ static from(data) {
1928
+ return new ERBWhileNode({
1929
+ type: data.type,
1930
+ location: Location.from(data.location),
1931
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1932
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1933
+ content: data.content ? Token.from(data.content) : null,
1934
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1935
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
1936
+ end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
1937
+ });
1938
+ }
1939
+ constructor(props) {
1940
+ super(props.type, props.location, props.errors);
1941
+ this.tag_opening = props.tag_opening;
1942
+ this.content = props.content;
1943
+ this.tag_closing = props.tag_closing;
1944
+ this.statements = props.statements;
1945
+ this.end_node = props.end_node;
1946
+ }
1947
+ accept(visitor) {
1948
+ visitor.visitERBWhileNode(this);
1949
+ }
1950
+ childNodes() {
1951
+ return [
1952
+ ...this.statements,
1953
+ this.end_node,
1954
+ ];
1955
+ }
1956
+ compactChildNodes() {
1957
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1958
+ }
1959
+ recursiveErrors() {
1960
+ return [
1961
+ ...this.errors,
1962
+ ...this.statements.map(node => node.recursiveErrors()),
1963
+ this.end_node ? this.end_node.recursiveErrors() : [],
1964
+ ].flat();
1965
+ }
1966
+ toJSON() {
1967
+ return {
1968
+ ...super.toJSON(),
1969
+ type: "AST_ERB_WHILE_NODE",
1970
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1971
+ content: this.content ? this.content.toJSON() : null,
1972
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1973
+ statements: this.statements.map(node => node.toJSON()),
1974
+ end_node: this.end_node ? this.end_node.toJSON() : null,
1975
+ };
1976
+ }
1977
+ treeInspect() {
1978
+ let output = "";
1979
+ output += `@ ERBWhileNode ${this.location.treeInspectWithLabel()}\n`;
1980
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1981
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1982
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1983
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1984
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
1985
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
1986
+ // output += "\n";
1987
+ return output;
1988
+ }
1989
+ }
1990
+ class ERBUntilNode extends Node {
1991
+ tag_opening;
1992
+ content;
1993
+ tag_closing;
1994
+ statements;
1995
+ end_node;
1996
+ static from(data) {
1997
+ return new ERBUntilNode({
1998
+ type: data.type,
1999
+ location: Location.from(data.location),
2000
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2001
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2002
+ content: data.content ? Token.from(data.content) : null,
2003
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2004
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2005
+ end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
2006
+ });
2007
+ }
2008
+ constructor(props) {
2009
+ super(props.type, props.location, props.errors);
2010
+ this.tag_opening = props.tag_opening;
2011
+ this.content = props.content;
2012
+ this.tag_closing = props.tag_closing;
2013
+ this.statements = props.statements;
2014
+ this.end_node = props.end_node;
2015
+ }
2016
+ accept(visitor) {
2017
+ visitor.visitERBUntilNode(this);
2018
+ }
2019
+ childNodes() {
2020
+ return [
2021
+ ...this.statements,
2022
+ this.end_node,
2023
+ ];
2024
+ }
2025
+ compactChildNodes() {
2026
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2027
+ }
2028
+ recursiveErrors() {
2029
+ return [
2030
+ ...this.errors,
2031
+ ...this.statements.map(node => node.recursiveErrors()),
2032
+ this.end_node ? this.end_node.recursiveErrors() : [],
2033
+ ].flat();
2034
+ }
2035
+ toJSON() {
2036
+ return {
2037
+ ...super.toJSON(),
2038
+ type: "AST_ERB_UNTIL_NODE",
2039
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2040
+ content: this.content ? this.content.toJSON() : null,
2041
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2042
+ statements: this.statements.map(node => node.toJSON()),
2043
+ end_node: this.end_node ? this.end_node.toJSON() : null,
2044
+ };
2045
+ }
2046
+ treeInspect() {
2047
+ let output = "";
2048
+ output += `@ ERBUntilNode ${this.location.treeInspectWithLabel()}\n`;
2049
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2050
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2051
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2052
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2053
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2054
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2055
+ // output += "\n";
2056
+ return output;
2057
+ }
2058
+ }
2059
+ class ERBForNode extends Node {
2060
+ tag_opening;
2061
+ content;
2062
+ tag_closing;
2063
+ statements;
2064
+ end_node;
2065
+ static from(data) {
2066
+ return new ERBForNode({
2067
+ type: data.type,
2068
+ location: Location.from(data.location),
2069
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2070
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2071
+ content: data.content ? Token.from(data.content) : null,
2072
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2073
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2074
+ end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
2075
+ });
2076
+ }
2077
+ constructor(props) {
2078
+ super(props.type, props.location, props.errors);
2079
+ this.tag_opening = props.tag_opening;
2080
+ this.content = props.content;
2081
+ this.tag_closing = props.tag_closing;
2082
+ this.statements = props.statements;
2083
+ this.end_node = props.end_node;
2084
+ }
2085
+ accept(visitor) {
2086
+ visitor.visitERBForNode(this);
2087
+ }
2088
+ childNodes() {
2089
+ return [
2090
+ ...this.statements,
2091
+ this.end_node,
2092
+ ];
2093
+ }
2094
+ compactChildNodes() {
2095
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2096
+ }
2097
+ recursiveErrors() {
2098
+ return [
2099
+ ...this.errors,
2100
+ ...this.statements.map(node => node.recursiveErrors()),
2101
+ this.end_node ? this.end_node.recursiveErrors() : [],
2102
+ ].flat();
2103
+ }
2104
+ toJSON() {
2105
+ return {
2106
+ ...super.toJSON(),
2107
+ type: "AST_ERB_FOR_NODE",
2108
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2109
+ content: this.content ? this.content.toJSON() : null,
2110
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2111
+ statements: this.statements.map(node => node.toJSON()),
2112
+ end_node: this.end_node ? this.end_node.toJSON() : null,
2113
+ };
2114
+ }
2115
+ treeInspect() {
2116
+ let output = "";
2117
+ output += `@ ERBForNode ${this.location.treeInspectWithLabel()}\n`;
2118
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2119
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2120
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2121
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2122
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2123
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2124
+ // output += "\n";
2125
+ return output;
2126
+ }
2127
+ }
2128
+ class ERBRescueNode extends Node {
2129
+ tag_opening;
2130
+ content;
2131
+ tag_closing;
2132
+ statements;
2133
+ subsequent;
2134
+ static from(data) {
2135
+ return new ERBRescueNode({
2136
+ type: data.type,
2137
+ location: Location.from(data.location),
2138
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2139
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2140
+ content: data.content ? Token.from(data.content) : null,
2141
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2142
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2143
+ subsequent: data.subsequent ? fromSerializedNode(data.subsequent) : null,
2144
+ });
2145
+ }
2146
+ constructor(props) {
2147
+ super(props.type, props.location, props.errors);
2148
+ this.tag_opening = props.tag_opening;
2149
+ this.content = props.content;
2150
+ this.tag_closing = props.tag_closing;
2151
+ this.statements = props.statements;
2152
+ this.subsequent = props.subsequent;
2153
+ }
2154
+ accept(visitor) {
2155
+ visitor.visitERBRescueNode(this);
2156
+ }
2157
+ childNodes() {
2158
+ return [
2159
+ ...this.statements,
2160
+ this.subsequent,
2161
+ ];
2162
+ }
2163
+ compactChildNodes() {
2164
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2165
+ }
2166
+ recursiveErrors() {
2167
+ return [
2168
+ ...this.errors,
2169
+ ...this.statements.map(node => node.recursiveErrors()),
2170
+ this.subsequent ? this.subsequent.recursiveErrors() : [],
2171
+ ].flat();
2172
+ }
2173
+ toJSON() {
2174
+ return {
2175
+ ...super.toJSON(),
2176
+ type: "AST_ERB_RESCUE_NODE",
2177
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2178
+ content: this.content ? this.content.toJSON() : null,
2179
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2180
+ statements: this.statements.map(node => node.toJSON()),
2181
+ subsequent: this.subsequent ? this.subsequent.toJSON() : null,
2182
+ };
2183
+ }
2184
+ treeInspect() {
2185
+ let output = "";
2186
+ output += `@ ERBRescueNode ${this.location.treeInspectWithLabel()}\n`;
2187
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2188
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2189
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2190
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2191
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2192
+ output += `└── subsequent: ${this.inspectNode(this.subsequent, " ")}`;
2193
+ // output += "\n";
2194
+ return output;
2195
+ }
2196
+ }
2197
+ class ERBEnsureNode extends Node {
2198
+ tag_opening;
2199
+ content;
2200
+ tag_closing;
2201
+ statements;
2202
+ static from(data) {
2203
+ return new ERBEnsureNode({
2204
+ type: data.type,
2205
+ location: Location.from(data.location),
2206
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2207
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2208
+ content: data.content ? Token.from(data.content) : null,
2209
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2210
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2211
+ });
2212
+ }
2213
+ constructor(props) {
2214
+ super(props.type, props.location, props.errors);
2215
+ this.tag_opening = props.tag_opening;
2216
+ this.content = props.content;
2217
+ this.tag_closing = props.tag_closing;
2218
+ this.statements = props.statements;
2219
+ }
2220
+ accept(visitor) {
2221
+ visitor.visitERBEnsureNode(this);
2222
+ }
2223
+ childNodes() {
2224
+ return [
2225
+ ...this.statements,
2226
+ ];
2227
+ }
2228
+ compactChildNodes() {
2229
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2230
+ }
2231
+ recursiveErrors() {
2232
+ return [
2233
+ ...this.errors,
2234
+ ...this.statements.map(node => node.recursiveErrors()),
2235
+ ].flat();
2236
+ }
2237
+ toJSON() {
2238
+ return {
2239
+ ...super.toJSON(),
2240
+ type: "AST_ERB_ENSURE_NODE",
2241
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2242
+ content: this.content ? this.content.toJSON() : null,
2243
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2244
+ statements: this.statements.map(node => node.toJSON()),
2245
+ };
2246
+ }
2247
+ treeInspect() {
2248
+ let output = "";
2249
+ output += `@ ERBEnsureNode ${this.location.treeInspectWithLabel()}\n`;
2250
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2251
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2252
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2253
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2254
+ output += `└── statements: ${this.inspectArray(this.statements, " ")}`;
2255
+ // output += "\n";
2256
+ return output;
2257
+ }
2258
+ }
2259
+ class ERBBeginNode extends Node {
2260
+ tag_opening;
2261
+ content;
2262
+ tag_closing;
2263
+ statements;
2264
+ rescue_clause;
2265
+ else_clause;
2266
+ ensure_clause;
2267
+ end_node;
2268
+ static from(data) {
2269
+ return new ERBBeginNode({
2270
+ type: data.type,
2271
+ location: Location.from(data.location),
2272
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2273
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2274
+ content: data.content ? Token.from(data.content) : null,
2275
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2276
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2277
+ rescue_clause: data.rescue_clause ? fromSerializedNode(data.rescue_clause) : null,
2278
+ else_clause: data.else_clause ? fromSerializedNode(data.else_clause) : null,
2279
+ ensure_clause: data.ensure_clause ? fromSerializedNode(data.ensure_clause) : null,
2280
+ end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
2281
+ });
2282
+ }
2283
+ constructor(props) {
2284
+ super(props.type, props.location, props.errors);
2285
+ this.tag_opening = props.tag_opening;
2286
+ this.content = props.content;
2287
+ this.tag_closing = props.tag_closing;
2288
+ this.statements = props.statements;
2289
+ this.rescue_clause = props.rescue_clause;
2290
+ this.else_clause = props.else_clause;
2291
+ this.ensure_clause = props.ensure_clause;
2292
+ this.end_node = props.end_node;
2293
+ }
2294
+ accept(visitor) {
2295
+ visitor.visitERBBeginNode(this);
2296
+ }
2297
+ childNodes() {
2298
+ return [
2299
+ ...this.statements,
2300
+ this.rescue_clause,
2301
+ this.else_clause,
2302
+ this.ensure_clause,
2303
+ this.end_node,
2304
+ ];
2305
+ }
2306
+ compactChildNodes() {
2307
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2308
+ }
2309
+ recursiveErrors() {
2310
+ return [
2311
+ ...this.errors,
2312
+ ...this.statements.map(node => node.recursiveErrors()),
2313
+ this.rescue_clause ? this.rescue_clause.recursiveErrors() : [],
2314
+ this.else_clause ? this.else_clause.recursiveErrors() : [],
2315
+ this.ensure_clause ? this.ensure_clause.recursiveErrors() : [],
2316
+ this.end_node ? this.end_node.recursiveErrors() : [],
2317
+ ].flat();
2318
+ }
2319
+ toJSON() {
2320
+ return {
2321
+ ...super.toJSON(),
2322
+ type: "AST_ERB_BEGIN_NODE",
2323
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2324
+ content: this.content ? this.content.toJSON() : null,
2325
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2326
+ statements: this.statements.map(node => node.toJSON()),
2327
+ rescue_clause: this.rescue_clause ? this.rescue_clause.toJSON() : null,
2328
+ else_clause: this.else_clause ? this.else_clause.toJSON() : null,
2329
+ ensure_clause: this.ensure_clause ? this.ensure_clause.toJSON() : null,
2330
+ end_node: this.end_node ? this.end_node.toJSON() : null,
2331
+ };
2332
+ }
2333
+ treeInspect() {
2334
+ let output = "";
2335
+ output += `@ ERBBeginNode ${this.location.treeInspectWithLabel()}\n`;
2336
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2337
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2338
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2339
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2340
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2341
+ output += `├── rescue_clause: ${this.inspectNode(this.rescue_clause, "│ ")}`;
2342
+ output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
2343
+ output += `├── ensure_clause: ${this.inspectNode(this.ensure_clause, "│ ")}`;
2344
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2345
+ // output += "\n";
2346
+ return output;
2347
+ }
2348
+ }
2349
+ class ERBUnlessNode extends Node {
2350
+ tag_opening;
2351
+ content;
2352
+ tag_closing;
2353
+ statements;
2354
+ else_clause;
2355
+ end_node;
2356
+ static from(data) {
2357
+ return new ERBUnlessNode({
2358
+ type: data.type,
2359
+ location: Location.from(data.location),
2360
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2361
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2362
+ content: data.content ? Token.from(data.content) : null,
2363
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2364
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2365
+ else_clause: data.else_clause ? fromSerializedNode(data.else_clause) : null,
2366
+ end_node: data.end_node ? fromSerializedNode(data.end_node) : null,
2367
+ });
2368
+ }
2369
+ constructor(props) {
2370
+ super(props.type, props.location, props.errors);
2371
+ this.tag_opening = props.tag_opening;
2372
+ this.content = props.content;
2373
+ this.tag_closing = props.tag_closing;
2374
+ this.statements = props.statements;
2375
+ this.else_clause = props.else_clause;
2376
+ this.end_node = props.end_node;
2377
+ }
2378
+ accept(visitor) {
2379
+ visitor.visitERBUnlessNode(this);
2380
+ }
2381
+ childNodes() {
2382
+ return [
2383
+ ...this.statements,
2384
+ this.else_clause,
2385
+ this.end_node,
2386
+ ];
2387
+ }
2388
+ compactChildNodes() {
2389
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2390
+ }
2391
+ recursiveErrors() {
2392
+ return [
2393
+ ...this.errors,
2394
+ ...this.statements.map(node => node.recursiveErrors()),
2395
+ this.else_clause ? this.else_clause.recursiveErrors() : [],
2396
+ this.end_node ? this.end_node.recursiveErrors() : [],
2397
+ ].flat();
2398
+ }
2399
+ toJSON() {
2400
+ return {
2401
+ ...super.toJSON(),
2402
+ type: "AST_ERB_UNLESS_NODE",
2403
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2404
+ content: this.content ? this.content.toJSON() : null,
2405
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2406
+ statements: this.statements.map(node => node.toJSON()),
2407
+ else_clause: this.else_clause ? this.else_clause.toJSON() : null,
2408
+ end_node: this.end_node ? this.end_node.toJSON() : null,
2409
+ };
2410
+ }
2411
+ treeInspect() {
2412
+ let output = "";
2413
+ output += `@ ERBUnlessNode ${this.location.treeInspectWithLabel()}\n`;
2414
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2415
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2416
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2417
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2418
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2419
+ output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
2420
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2421
+ // output += "\n";
2422
+ return output;
2423
+ }
2424
+ }
2425
+ class ERBYieldNode extends Node {
2426
+ tag_opening;
2427
+ content;
2428
+ tag_closing;
2429
+ static from(data) {
2430
+ return new ERBYieldNode({
2431
+ type: data.type,
2432
+ location: Location.from(data.location),
2433
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2434
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2435
+ content: data.content ? Token.from(data.content) : null,
2436
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2437
+ });
2438
+ }
2439
+ constructor(props) {
2440
+ super(props.type, props.location, props.errors);
2441
+ this.tag_opening = props.tag_opening;
2442
+ this.content = props.content;
2443
+ this.tag_closing = props.tag_closing;
2444
+ }
2445
+ accept(visitor) {
2446
+ visitor.visitERBYieldNode(this);
2447
+ }
2448
+ childNodes() {
2449
+ return [];
2450
+ }
2451
+ compactChildNodes() {
2452
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2453
+ }
2454
+ recursiveErrors() {
2455
+ return [
2456
+ ...this.errors,
2457
+ ].flat();
2458
+ }
2459
+ toJSON() {
2460
+ return {
2461
+ ...super.toJSON(),
2462
+ type: "AST_ERB_YIELD_NODE",
2463
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2464
+ content: this.content ? this.content.toJSON() : null,
2465
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2466
+ };
2467
+ }
2468
+ treeInspect() {
2469
+ let output = "";
2470
+ output += `@ ERBYieldNode ${this.location.treeInspectWithLabel()}\n`;
2471
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2472
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2473
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2474
+ output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2475
+ // output += "\n";
2476
+ return output;
2477
+ }
2478
+ }
2479
+ class ERBInNode extends Node {
2480
+ tag_opening;
2481
+ content;
2482
+ tag_closing;
2483
+ statements;
2484
+ static from(data) {
2485
+ return new ERBInNode({
2486
+ type: data.type,
2487
+ location: Location.from(data.location),
2488
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2489
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2490
+ content: data.content ? Token.from(data.content) : null,
2491
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2492
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2493
+ });
2494
+ }
2495
+ constructor(props) {
2496
+ super(props.type, props.location, props.errors);
2497
+ this.tag_opening = props.tag_opening;
2498
+ this.content = props.content;
2499
+ this.tag_closing = props.tag_closing;
2500
+ this.statements = props.statements;
2501
+ }
2502
+ accept(visitor) {
2503
+ visitor.visitERBInNode(this);
2504
+ }
2505
+ childNodes() {
2506
+ return [
2507
+ ...this.statements,
2508
+ ];
2509
+ }
2510
+ compactChildNodes() {
2511
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2512
+ }
2513
+ recursiveErrors() {
2514
+ return [
2515
+ ...this.errors,
2516
+ ...this.statements.map(node => node.recursiveErrors()),
2517
+ ].flat();
2518
+ }
2519
+ toJSON() {
2520
+ return {
2521
+ ...super.toJSON(),
2522
+ type: "AST_ERB_IN_NODE",
2523
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2524
+ content: this.content ? this.content.toJSON() : null,
2525
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2526
+ statements: this.statements.map(node => node.toJSON()),
2527
+ };
2528
+ }
2529
+ treeInspect() {
2530
+ let output = "";
2531
+ output += `@ ERBInNode ${this.location.treeInspectWithLabel()}\n`;
2532
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2533
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2534
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2535
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2536
+ output += `└── statements: ${this.inspectArray(this.statements, " ")}`;
2537
+ // output += "\n";
2538
+ return output;
2539
+ }
2540
+ }
2541
+ function fromSerializedNode(node) {
2542
+ switch (node.type) {
2543
+ case "AST_DOCUMENT_NODE": return DocumentNode.from(node);
2544
+ case "AST_LITERAL_NODE": return LiteralNode.from(node);
2545
+ case "AST_HTML_OPEN_TAG_NODE": return HTMLOpenTagNode.from(node);
2546
+ case "AST_HTML_CLOSE_TAG_NODE": return HTMLCloseTagNode.from(node);
2547
+ case "AST_HTML_SELF_CLOSE_TAG_NODE": return HTMLSelfCloseTagNode.from(node);
2548
+ case "AST_HTML_ELEMENT_NODE": return HTMLElementNode.from(node);
2549
+ case "AST_HTML_ATTRIBUTE_VALUE_NODE": return HTMLAttributeValueNode.from(node);
2550
+ case "AST_HTML_ATTRIBUTE_NAME_NODE": return HTMLAttributeNameNode.from(node);
2551
+ case "AST_HTML_ATTRIBUTE_NODE": return HTMLAttributeNode.from(node);
2552
+ case "AST_HTML_TEXT_NODE": return HTMLTextNode.from(node);
2553
+ case "AST_HTML_COMMENT_NODE": return HTMLCommentNode.from(node);
2554
+ case "AST_HTML_DOCTYPE_NODE": return HTMLDoctypeNode.from(node);
2555
+ case "AST_WHITESPACE_NODE": return WhitespaceNode.from(node);
2556
+ case "AST_ERB_CONTENT_NODE": return ERBContentNode.from(node);
2557
+ case "AST_ERB_END_NODE": return ERBEndNode.from(node);
2558
+ case "AST_ERB_ELSE_NODE": return ERBElseNode.from(node);
2559
+ case "AST_ERB_IF_NODE": return ERBIfNode.from(node);
2560
+ case "AST_ERB_BLOCK_NODE": return ERBBlockNode.from(node);
2561
+ case "AST_ERB_WHEN_NODE": return ERBWhenNode.from(node);
2562
+ case "AST_ERB_CASE_NODE": return ERBCaseNode.from(node);
2563
+ case "AST_ERB_CASE_MATCH_NODE": return ERBCaseMatchNode.from(node);
2564
+ case "AST_ERB_WHILE_NODE": return ERBWhileNode.from(node);
2565
+ case "AST_ERB_UNTIL_NODE": return ERBUntilNode.from(node);
2566
+ case "AST_ERB_FOR_NODE": return ERBForNode.from(node);
2567
+ case "AST_ERB_RESCUE_NODE": return ERBRescueNode.from(node);
2568
+ case "AST_ERB_ENSURE_NODE": return ERBEnsureNode.from(node);
2569
+ case "AST_ERB_BEGIN_NODE": return ERBBeginNode.from(node);
2570
+ case "AST_ERB_UNLESS_NODE": return ERBUnlessNode.from(node);
2571
+ case "AST_ERB_YIELD_NODE": return ERBYieldNode.from(node);
2572
+ case "AST_ERB_IN_NODE": return ERBInNode.from(node);
2573
+ default:
2574
+ throw new Error(`Unknown node type: ${node.type}`);
2575
+ }
2576
+ }
2577
+
2578
+ // NOTE: This file is generated by the templates/template.rb script and should not
2579
+ // be modified manually. See /Users/marcoroth/Development/herb-release/templates/javascript/packages/core/src/visitor.ts.erb
2580
+ class Visitor {
2581
+ visit(node) {
2582
+ if (!node)
2583
+ return;
2584
+ node.accept(this);
2585
+ }
2586
+ visitAll(nodes) {
2587
+ nodes.forEach(node => node?.accept(this));
2588
+ }
2589
+ visitChildNodes(node) {
2590
+ node.compactChildNodes().forEach(node => node.accept(this));
2591
+ }
2592
+ visitDocumentNode(node) {
2593
+ this.visitChildNodes(node);
2594
+ }
2595
+ visitLiteralNode(node) {
2596
+ this.visitChildNodes(node);
2597
+ }
2598
+ visitHTMLOpenTagNode(node) {
2599
+ this.visitChildNodes(node);
2600
+ }
2601
+ visitHTMLCloseTagNode(node) {
2602
+ this.visitChildNodes(node);
2603
+ }
2604
+ visitHTMLSelfCloseTagNode(node) {
2605
+ this.visitChildNodes(node);
2606
+ }
2607
+ visitHTMLElementNode(node) {
2608
+ this.visitChildNodes(node);
2609
+ }
2610
+ visitHTMLAttributeValueNode(node) {
2611
+ this.visitChildNodes(node);
2612
+ }
2613
+ visitHTMLAttributeNameNode(node) {
2614
+ this.visitChildNodes(node);
2615
+ }
2616
+ visitHTMLAttributeNode(node) {
2617
+ this.visitChildNodes(node);
2618
+ }
2619
+ visitHTMLTextNode(node) {
2620
+ this.visitChildNodes(node);
2621
+ }
2622
+ visitHTMLCommentNode(node) {
2623
+ this.visitChildNodes(node);
2624
+ }
2625
+ visitHTMLDoctypeNode(node) {
2626
+ this.visitChildNodes(node);
2627
+ }
2628
+ visitWhitespaceNode(node) {
2629
+ this.visitChildNodes(node);
2630
+ }
2631
+ visitERBContentNode(node) {
2632
+ this.visitChildNodes(node);
2633
+ }
2634
+ visitERBEndNode(node) {
2635
+ this.visitChildNodes(node);
2636
+ }
2637
+ visitERBElseNode(node) {
2638
+ this.visitChildNodes(node);
2639
+ }
2640
+ visitERBIfNode(node) {
2641
+ this.visitChildNodes(node);
2642
+ }
2643
+ visitERBBlockNode(node) {
2644
+ this.visitChildNodes(node);
2645
+ }
2646
+ visitERBWhenNode(node) {
2647
+ this.visitChildNodes(node);
2648
+ }
2649
+ visitERBCaseNode(node) {
2650
+ this.visitChildNodes(node);
2651
+ }
2652
+ visitERBCaseMatchNode(node) {
2653
+ this.visitChildNodes(node);
2654
+ }
2655
+ visitERBWhileNode(node) {
2656
+ this.visitChildNodes(node);
2657
+ }
2658
+ visitERBUntilNode(node) {
2659
+ this.visitChildNodes(node);
2660
+ }
2661
+ visitERBForNode(node) {
2662
+ this.visitChildNodes(node);
2663
+ }
2664
+ visitERBRescueNode(node) {
2665
+ this.visitChildNodes(node);
2666
+ }
2667
+ visitERBEnsureNode(node) {
2668
+ this.visitChildNodes(node);
2669
+ }
2670
+ visitERBBeginNode(node) {
2671
+ this.visitChildNodes(node);
2672
+ }
2673
+ visitERBUnlessNode(node) {
2674
+ this.visitChildNodes(node);
2675
+ }
2676
+ visitERBYieldNode(node) {
2677
+ this.visitChildNodes(node);
2678
+ }
2679
+ visitERBInNode(node) {
2680
+ this.visitChildNodes(node);
2681
+ }
2682
+ }
2683
+
2684
+ /**
2685
+ * Printer traverses the Herb AST using the Visitor pattern
2686
+ * and emits a formatted string with proper indentation, line breaks, and attribute wrapping.
2687
+ */
2688
+ class Printer extends Visitor {
2689
+ indentWidth;
2690
+ maxLineLength;
2691
+ source;
2692
+ lines = [];
2693
+ indentLevel = 0;
2694
+ constructor(source, options) {
2695
+ super();
2696
+ this.source = source;
2697
+ this.indentWidth = options.indentWidth;
2698
+ this.maxLineLength = options.maxLineLength;
2699
+ }
2700
+ print(object, indentLevel = 0) {
2701
+ if (object instanceof Token || object.type?.startsWith('TOKEN_')) {
2702
+ return object.value;
2703
+ }
2704
+ const node = object;
2705
+ this.lines = [];
2706
+ this.indentLevel = indentLevel;
2707
+ if (typeof node.accept === 'function') {
2708
+ node.accept(this);
2709
+ }
2710
+ else {
2711
+ this.visit(node);
2712
+ }
2713
+ return this.lines.filter(Boolean).join("\n");
2714
+ }
2715
+ push(line) {
2716
+ this.lines.push(line);
2717
+ }
2718
+ withIndent(callback) {
2719
+ this.indentLevel++;
2720
+ const result = callback();
2721
+ this.indentLevel--;
2722
+ return result;
2723
+ }
2724
+ indent() {
2725
+ return " ".repeat(this.indentLevel * this.indentWidth);
2726
+ }
2727
+ /**
2728
+ * Print an ERB tag (<% %> or <%= %>) with single spaces around inner content.
2729
+ */
2730
+ printERBNode(node) {
2731
+ const indent = this.indent();
2732
+ const open = node.tag_opening?.value ?? "";
2733
+ const close = node.tag_closing?.value ?? "";
2734
+ let inner;
2735
+ if (node.tag_opening && node.tag_closing) {
2736
+ const [, openingEnd] = node.tag_opening.range.toArray();
2737
+ const [closingStart] = node.tag_closing.range.toArray();
2738
+ const rawInner = this.source.slice(openingEnd, closingStart);
2739
+ inner = ` ${rawInner.trim()} `;
2740
+ }
2741
+ else {
2742
+ const txt = node.content?.value ?? "";
2743
+ inner = txt.trim() ? ` ${txt.trim()} ` : "";
2744
+ }
2745
+ this.push(indent + open + inner + close);
2746
+ }
2747
+ // --- Visitor methods ---
2748
+ visitDocumentNode(node) {
2749
+ node.children.forEach(child => this.visit(child));
2750
+ }
2751
+ visitHTMLElementNode(node) {
2752
+ const open = node.open_tag;
2753
+ const tagName = open.tag_name?.value ?? "";
2754
+ const indent = this.indent();
2755
+ const attributes = open.children.filter((child) => child instanceof HTMLAttributeNode || child.type === 'AST_HTML_ATTRIBUTE_NODE');
2756
+ const children = node.body.filter(child => !(child instanceof WhitespaceNode || child.type === 'AST_WHITESPACE_NODE') &&
2757
+ !((child instanceof HTMLTextNode || child.type === 'AST_HTML_TEXT_NODE') && child?.content.trim() === ""));
2758
+ const hasClosing = open.tag_closing?.value === ">" || open.tag_closing?.value === "/>";
2759
+ const isSelfClosing = open.tag_closing?.value === "/>";
2760
+ if (!hasClosing) {
2761
+ this.push(indent + `<${tagName}`);
2762
+ return;
2763
+ }
2764
+ if (attributes.length === 0) {
2765
+ if (children.length === 0) {
2766
+ if (isSelfClosing) {
2767
+ this.push(indent + `<${tagName} />`);
2768
+ }
2769
+ else if (node.is_void) {
2770
+ this.push(indent + `<${tagName}>`);
2771
+ }
2772
+ else {
2773
+ this.push(indent + `<${tagName}></${tagName}>`);
2774
+ }
2775
+ return;
2776
+ }
2777
+ this.push(indent + `<${tagName}>`);
2778
+ this.withIndent(() => {
2779
+ children.forEach(child => this.visit(child));
2780
+ });
2781
+ if (!node.is_void && !isSelfClosing) {
2782
+ this.push(indent + `</${tagName}>`);
2783
+ }
2784
+ return;
2785
+ }
2786
+ const inline = this.renderInlineOpen(tagName, attributes, isSelfClosing);
2787
+ const singleAttribute = attributes[0];
2788
+ const hasEmptyValue = singleAttribute &&
2789
+ (singleAttribute.value instanceof HTMLAttributeValueNode || singleAttribute.value?.type === 'AST_HTML_ATTRIBUTE_VALUE_NODE') &&
2790
+ singleAttribute.value?.children.length === 0;
2791
+ const shouldKeepInline = attributes.length <= 3 &&
2792
+ !hasEmptyValue &&
2793
+ inline.length + indent.length <= this.maxLineLength;
2794
+ if (shouldKeepInline) {
2795
+ if (children.length === 0) {
2796
+ if (isSelfClosing) {
2797
+ this.push(indent + inline);
2798
+ }
2799
+ else if (node.is_void) {
2800
+ this.push(indent + inline);
2801
+ }
2802
+ else {
2803
+ this.push(indent + inline.replace('>', `></${tagName}>`));
2804
+ }
2805
+ return;
2806
+ }
2807
+ if (isSelfClosing) {
2808
+ this.push(indent + inline.replace(' />', '>'));
2809
+ }
2810
+ else {
2811
+ this.push(indent + inline);
2812
+ }
2813
+ this.withIndent(() => {
2814
+ children.forEach(child => this.visit(child));
2815
+ });
2816
+ if (!node.is_void && !isSelfClosing) {
2817
+ this.push(indent + `</${tagName}>`);
2818
+ }
2819
+ return;
2820
+ }
2821
+ this.push(indent + `<${tagName}`);
2822
+ this.withIndent(() => {
2823
+ attributes.forEach(attribute => {
2824
+ this.push(this.indent() + this.renderAttribute(attribute));
2825
+ });
2826
+ });
2827
+ if (isSelfClosing) {
2828
+ this.push(indent + "/>");
2829
+ }
2830
+ else if (node.is_void) {
2831
+ this.push(indent + ">");
2832
+ }
2833
+ else if (children.length === 0) {
2834
+ this.push(indent + ">" + `</${tagName}>`);
2835
+ }
2836
+ else {
2837
+ this.push(indent + ">");
2838
+ this.withIndent(() => {
2839
+ children.forEach(child => this.visit(child));
2840
+ });
2841
+ this.push(indent + `</${tagName}>`);
2842
+ }
2843
+ }
2844
+ visitHTMLOpenTagNode(node) {
2845
+ const tagName = node.tag_name?.value ?? "";
2846
+ const indent = this.indent();
2847
+ const attributes = node.children.filter((attribute) => attribute instanceof HTMLAttributeNode || attribute.type === 'AST_HTML_ATTRIBUTE_NODE');
2848
+ const hasClosing = node.tag_closing?.value === ">";
2849
+ if (!hasClosing) {
2850
+ this.push(indent + `<${tagName}`);
2851
+ return;
2852
+ }
2853
+ const inline = this.renderInlineOpen(tagName, attributes, node.is_void);
2854
+ if (attributes.length === 0 || inline.length + indent.length <= this.maxLineLength) {
2855
+ this.push(indent + inline);
2856
+ return;
2857
+ }
2858
+ this.push(indent + `<${tagName}`);
2859
+ this.withIndent(() => {
2860
+ attributes.forEach(attribute => {
2861
+ this.push(this.indent() + this.renderAttribute(attribute));
2862
+ });
2863
+ });
2864
+ this.push(indent + (node.is_void ? "/>" : ">"));
2865
+ }
2866
+ visitHTMLSelfCloseTagNode(node) {
2867
+ const tagName = node.tag_name?.value ?? "";
2868
+ const indent = this.indent();
2869
+ const attributes = node.attributes.filter((attribute) => attribute instanceof HTMLAttributeNode || attribute.type === 'AST_HTML_ATTRIBUTE_NODE');
2870
+ const inline = this.renderInlineOpen(tagName, attributes, true);
2871
+ const singleAttribute = attributes[0];
2872
+ const hasEmptyValue = singleAttribute &&
2873
+ (singleAttribute.value instanceof HTMLAttributeValueNode || singleAttribute.value?.type === 'AST_HTML_ATTRIBUTE_VALUE_NODE') &&
2874
+ singleAttribute.value?.children.length === 0;
2875
+ const shouldKeepInline = attributes.length <= 3 &&
2876
+ !hasEmptyValue &&
2877
+ inline.length + indent.length <= this.maxLineLength;
2878
+ if (shouldKeepInline) {
2879
+ this.push(indent + inline);
2880
+ return;
2881
+ }
2882
+ this.push(indent + `<${tagName}`);
2883
+ this.withIndent(() => {
2884
+ attributes.forEach(attribute => {
2885
+ this.push(this.indent() + this.renderAttribute(attribute));
2886
+ });
2887
+ });
2888
+ this.push(indent + "/>");
2889
+ }
2890
+ visitHTMLCloseTagNode(node) {
2891
+ const indent = this.indent();
2892
+ const open = node.tag_opening?.value ?? "";
2893
+ const name = node.tag_name?.value ?? "";
2894
+ const close = node.tag_closing?.value ?? "";
2895
+ this.push(indent + open + name + close);
2896
+ }
2897
+ visitHTMLTextNode(node) {
2898
+ const indent = this.indent();
2899
+ let text = node.content.trim();
2900
+ if (!text)
2901
+ return;
2902
+ const wrapWidth = this.maxLineLength - indent.length;
2903
+ const words = text.split(/\s+/);
2904
+ const lines = [];
2905
+ let line = "";
2906
+ for (const word of words) {
2907
+ if ((line + (line ? " " : "") + word).length > wrapWidth && line) {
2908
+ lines.push(indent + line);
2909
+ line = word;
2910
+ }
2911
+ else {
2912
+ line += (line ? " " : "") + word;
2913
+ }
2914
+ }
2915
+ if (line)
2916
+ lines.push(indent + line);
2917
+ lines.forEach(line => this.push(line));
2918
+ }
2919
+ visitHTMLAttributeNode(node) {
2920
+ const indent = this.indent();
2921
+ this.push(indent + this.renderAttribute(node));
2922
+ }
2923
+ visitHTMLAttributeNameNode(node) {
2924
+ const indent = this.indent();
2925
+ const name = node.name?.value ?? "";
2926
+ this.push(indent + name);
2927
+ }
2928
+ visitHTMLAttributeValueNode(node) {
2929
+ const indent = this.indent();
2930
+ const open_quote = node.open_quote?.value ?? "";
2931
+ const close_quote = node.close_quote?.value ?? "";
2932
+ const attribute_value = node.children.map(child => {
2933
+ if (child instanceof HTMLTextNode || child.type === 'AST_HTML_TEXT_NODE' ||
2934
+ child instanceof LiteralNode || child.type === 'AST_LITERAL_NODE') {
2935
+ return child.content;
2936
+ }
2937
+ else if (child instanceof ERBContentNode || child.type === 'AST_ERB_CONTENT_NODE') {
2938
+ const erbChild = child;
2939
+ return (erbChild.tag_opening.value + erbChild.content.value + erbChild.tag_closing.value);
2940
+ }
2941
+ return "";
2942
+ }).join("");
2943
+ this.push(indent + open_quote + attribute_value + close_quote);
2944
+ }
2945
+ visitHTMLCommentNode(node) {
2946
+ const indent = this.indent();
2947
+ const open = node.comment_start?.value ?? "";
2948
+ const close = node.comment_end?.value ?? "";
2949
+ let inner;
2950
+ if (node.comment_start && node.comment_end) {
2951
+ // TODO: use .value
2952
+ const [_, startIndex] = node.comment_start.range.toArray();
2953
+ const [endIndex] = node.comment_end.range.toArray();
2954
+ const rawInner = this.source.slice(startIndex, endIndex);
2955
+ inner = ` ${rawInner.trim()} `;
2956
+ }
2957
+ else {
2958
+ inner = node.children.map(child => {
2959
+ const prevLines = this.lines.length;
2960
+ this.visit(child);
2961
+ return this.lines.slice(prevLines).join("");
2962
+ }).join("");
2963
+ }
2964
+ this.push(indent + open + inner + close);
2965
+ }
2966
+ visitERBCommentNode(node) {
2967
+ const indent = this.indent();
2968
+ const open = node.tag_opening?.value ?? "";
2969
+ const close = node.tag_closing?.value ?? "";
2970
+ let inner;
2971
+ if (node.tag_opening && node.tag_closing) {
2972
+ const [, openingEnd] = node.tag_opening.range.toArray();
2973
+ const [closingStart] = node.tag_closing.range.toArray();
2974
+ const rawInner = this.source.slice(openingEnd, closingStart);
2975
+ const lines = rawInner.split("\n");
2976
+ if (lines.length > 2) {
2977
+ const childIndent = indent + " ".repeat(this.indentWidth);
2978
+ const innerLines = lines.slice(1, -1).map(line => childIndent + line.trim());
2979
+ inner = "\n" + innerLines.join("\n") + "\n";
2980
+ }
2981
+ else {
2982
+ inner = ` ${rawInner.trim()} `;
2983
+ }
2984
+ }
2985
+ else {
2986
+ inner = node.children
2987
+ .map((child) => {
2988
+ const prevLines = this.lines.length;
2989
+ this.visit(child);
2990
+ return this.lines.slice(prevLines).join("");
2991
+ })
2992
+ .join("");
2993
+ }
2994
+ this.push(indent + open + inner + close);
2995
+ }
2996
+ visitHTMLDoctypeNode(node) {
2997
+ const indent = this.indent();
2998
+ const open = node.tag_opening?.value ?? "";
2999
+ let innerDoctype;
3000
+ if (node.tag_opening && node.tag_closing) {
3001
+ // TODO: use .value
3002
+ const [, openingEnd] = node.tag_opening.range.toArray();
3003
+ const [closingStart] = node.tag_closing.range.toArray();
3004
+ innerDoctype = this.source.slice(openingEnd, closingStart);
3005
+ }
3006
+ else {
3007
+ innerDoctype = node.children
3008
+ .map(child => child instanceof HTMLTextNode ? child.content : (() => { const prevLines = this.lines.length; this.visit(child); return this.lines.slice(prevLines).join(""); })())
3009
+ .join("");
3010
+ }
3011
+ const close = node.tag_closing?.value ?? "";
3012
+ this.push(indent + open + innerDoctype + close);
3013
+ }
3014
+ visitERBContentNode(node) {
3015
+ // TODO: this feels hacky
3016
+ if (node.tag_opening?.value === "<%#") {
3017
+ this.visitERBCommentNode(node);
3018
+ }
3019
+ else {
3020
+ this.printERBNode(node);
3021
+ }
3022
+ }
3023
+ visitERBEndNode(node) {
3024
+ this.printERBNode(node);
3025
+ }
3026
+ visitERBYieldNode(node) {
3027
+ this.printERBNode(node);
3028
+ }
3029
+ visitERBInNode(node) {
3030
+ this.printERBNode(node);
3031
+ }
3032
+ visitERBCaseMatchNode(node) {
3033
+ this.printERBNode(node);
3034
+ }
3035
+ visitERBBlockNode(node) {
3036
+ const indent = this.indent();
3037
+ const open = node.tag_opening?.value ?? "";
3038
+ const content = node.content?.value ?? "";
3039
+ const close = node.tag_closing?.value ?? "";
3040
+ this.push(indent + open + content + close);
3041
+ this.withIndent(() => {
3042
+ node.body.forEach(child => this.visit(child));
3043
+ });
3044
+ if (node.end_node) {
3045
+ this.visit(node.end_node);
3046
+ }
3047
+ }
3048
+ visitERBIfNode(node) {
3049
+ this.printERBNode(node);
3050
+ this.withIndent(() => {
3051
+ node.statements.forEach(child => this.visit(child));
3052
+ });
3053
+ if (node.subsequent) {
3054
+ this.visit(node.subsequent);
3055
+ }
3056
+ if (node.end_node) {
3057
+ this.printERBNode(node.end_node);
3058
+ }
3059
+ }
3060
+ visitERBElseNode(node) {
3061
+ this.printERBNode(node);
3062
+ this.withIndent(() => {
3063
+ node.statements.forEach(child => this.visit(child));
3064
+ });
3065
+ }
3066
+ visitERBWhenNode(node) {
3067
+ this.printERBNode(node);
3068
+ this.withIndent(() => {
3069
+ node.statements.forEach(stmt => this.visit(stmt));
3070
+ });
3071
+ }
3072
+ visitERBCaseNode(node) {
3073
+ this.indentLevel;
3074
+ const indent = this.indent();
3075
+ const open = node.tag_opening?.value ?? "";
3076
+ const content = node.content?.value ?? "";
3077
+ const close = node.tag_closing?.value ?? "";
3078
+ this.push(indent + open + content + close);
3079
+ node.conditions.forEach(condition => this.visit(condition));
3080
+ if (node.else_clause)
3081
+ this.visit(node.else_clause);
3082
+ if (node.end_node) {
3083
+ this.visit(node.end_node);
3084
+ }
3085
+ }
3086
+ visitERBBeginNode(node) {
3087
+ const indent = this.indent();
3088
+ const open = node.tag_opening?.value ?? "";
3089
+ const content = node.content?.value ?? "";
3090
+ const close = node.tag_closing?.value ?? "";
3091
+ this.push(indent + open + content + close);
3092
+ this.withIndent(() => {
3093
+ node.statements.forEach(statement => this.visit(statement));
3094
+ });
3095
+ if (node.rescue_clause)
3096
+ this.visit(node.rescue_clause);
3097
+ if (node.else_clause)
3098
+ this.visit(node.else_clause);
3099
+ if (node.ensure_clause)
3100
+ this.visit(node.ensure_clause);
3101
+ if (node.end_node)
3102
+ this.visit(node.end_node);
3103
+ }
3104
+ visitERBWhileNode(node) {
3105
+ this.visitERBGeneric(node);
3106
+ }
3107
+ visitERBUntilNode(node) {
3108
+ this.visitERBGeneric(node);
3109
+ }
3110
+ visitERBForNode(node) {
3111
+ this.visitERBGeneric(node);
3112
+ }
3113
+ visitERBRescueNode(node) {
3114
+ this.visitERBGeneric(node);
3115
+ }
3116
+ visitERBEnsureNode(node) {
3117
+ this.visitERBGeneric(node);
3118
+ }
3119
+ visitERBUnlessNode(node) {
3120
+ this.visitERBGeneric(node);
3121
+ }
3122
+ // TODO: don't use any
3123
+ visitERBGeneric(node) {
3124
+ const indent = this.indent();
3125
+ const open = node.tag_opening?.value ?? "";
3126
+ const content = node.content?.value ?? "";
3127
+ const close = node.tag_closing?.value ?? "";
3128
+ this.push(indent + open + content + close);
3129
+ this.withIndent(() => {
3130
+ const statements = node.statements ?? node.body ?? node.children ?? [];
3131
+ statements.forEach(statement => this.visit(statement));
3132
+ });
3133
+ if (node.end_node)
3134
+ this.visit(node.end_node);
3135
+ }
3136
+ // --- Utility methods ---
3137
+ renderInlineOpen(name, attributes, selfClose) {
3138
+ const parts = attributes.map(attribute => this.renderAttribute(attribute));
3139
+ return `<${name}${parts.length ? " " + parts.join(" ") : ""}${selfClose ? " /" : ""}>`;
3140
+ }
3141
+ renderAttribute(attribute) {
3142
+ const name = attribute.name.name.value ?? "";
3143
+ const equals = attribute.equals?.value ?? "";
3144
+ let value = "";
3145
+ if (attribute.value && (attribute.value instanceof HTMLAttributeValueNode || attribute.value?.type === 'AST_HTML_ATTRIBUTE_VALUE_NODE')) {
3146
+ const attrValue = attribute.value;
3147
+ const open_quote = (attrValue.open_quote?.value ?? "");
3148
+ const close_quote = (attrValue.close_quote?.value ?? "");
3149
+ const attribute_value = attrValue.children.map((attr) => {
3150
+ if (attr instanceof HTMLTextNode || attr.type === 'AST_HTML_TEXT_NODE' ||
3151
+ attr instanceof LiteralNode || attr.type === 'AST_LITERAL_NODE') {
3152
+ return attr.content;
3153
+ }
3154
+ else if (attr instanceof ERBContentNode || attr.type === 'AST_ERB_CONTENT_NODE') {
3155
+ const erbAttr = attr;
3156
+ return (erbAttr.tag_opening.value + erbAttr.content.value + erbAttr.tag_closing.value);
3157
+ }
3158
+ return "";
3159
+ }).join("");
3160
+ value = open_quote + attribute_value + close_quote;
3161
+ }
3162
+ return name + equals + value;
3163
+ }
3164
+ }
3165
+
3166
+ /**
3167
+ * Default values for formatting options.
3168
+ */
3169
+ const defaultFormatOptions = {
3170
+ indentWidth: 2,
3171
+ maxLineLength: 80,
3172
+ };
3173
+ /**
3174
+ * Merge provided options with defaults for any missing values.
3175
+ * @param options partial formatting options
3176
+ * @returns a complete set of formatting options
3177
+ */
3178
+ function resolveFormatOptions(options = {}) {
3179
+ return {
3180
+ indentWidth: options.indentWidth ?? defaultFormatOptions.indentWidth,
3181
+ maxLineLength: options.maxLineLength ?? defaultFormatOptions.maxLineLength,
3182
+ };
3183
+ }
3184
+
3185
+ /**
3186
+ * Formatter uses a Herb Backend to parse the source and then
3187
+ * formats the resulting AST into a well-indented, wrapped string.
3188
+ */
3189
+ class Formatter {
3190
+ herb;
3191
+ options;
3192
+ constructor(herb, options = {}) {
3193
+ this.herb = herb;
3194
+ this.options = resolveFormatOptions(options);
3195
+ }
3196
+ /**
3197
+ * Format a source string, optionally overriding format options per call.
3198
+ */
3199
+ format(source, options = {}) {
3200
+ const result = this.parse(source);
3201
+ if (result.failed)
3202
+ return source;
3203
+ const resolvedOptions = resolveFormatOptions({ ...this.options, ...options });
3204
+ return new Printer(source, resolvedOptions).print(result.value);
3205
+ }
3206
+ parse(source) {
3207
+ this.herb.ensureBackend();
3208
+ return this.herb.parse(source);
3209
+ }
3210
+ }
3211
+
3212
+ exports.Formatter = Formatter;
3213
+ exports.defaultFormatOptions = defaultFormatOptions;
3214
+ exports.resolveFormatOptions = resolveFormatOptions;
3215
+ //# sourceMappingURL=index.cjs.map