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