@herb-tools/rewriter 0.8.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,3793 @@
1
+ /**
2
+ * Base class for AST rewriters that transform AST nodes before formatting
3
+ *
4
+ * AST rewriters receive a Node and can mutate it in place or return a modified Node.
5
+ * They run before the formatting step.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { ASTRewriter, asMutable } from "@herb-tools/rewriter"
10
+ * import { Visitor } from "@herb-tools/core"
11
+ *
12
+ * class MyRewriter extends ASTRewriter {
13
+ * get name() { return "my-rewriter" }
14
+ * get description() { return "My custom AST rewriter" }
15
+ *
16
+ * async initialize(context) {
17
+ * // Load config, initialize dependencies, etc.
18
+ * }
19
+ *
20
+ * rewrite(node, context) {
21
+ * // Use visitor pattern to traverse and modify AST
22
+ * const visitor = new MyVisitor()
23
+ * visitor.visit(node)
24
+ *
25
+ * return node
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ class ASTRewriter {
31
+ /**
32
+ * Optional async initialization hook
33
+ *
34
+ * Called once before the first rewrite operation. Use this to:
35
+ * - Load configuration files
36
+ * - Initialize dependencies
37
+ * - Perform expensive setup operations
38
+ *
39
+ * @param context - Context with baseDir and optional filePath
40
+ */
41
+ async initialize(_context) {
42
+ // Override in subclass if needed
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Base class for string rewriters that transform the formatted output
48
+ *
49
+ * String rewriters receive the formatted string and can modify it before
50
+ * returning the final output. They run after the formatting step.
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * import { StringRewriter } from "@herb-tools/rewriter"
55
+ *
56
+ * class AddTrailingNewline extends StringRewriter {
57
+ * get name() { return "add-trailing-newline" }
58
+ * get description() { return "Ensures file ends with a newline" }
59
+ *
60
+ * rewrite(formatted, context) {
61
+ * return formatted.endsWith("\n") ? formatted : formatted + "\n"
62
+ * }
63
+ * }
64
+ * ```
65
+ */
66
+ class StringRewriter {
67
+ /**
68
+ * Optional async initialization hook
69
+ *
70
+ * Called once before the first rewrite operation. Use this to:
71
+ * - Load configuration files
72
+ * - Initialize dependencies
73
+ * - Perform expensive setup operations
74
+ *
75
+ * @param context - Context with baseDir and optional filePath
76
+ */
77
+ async initialize(_context) {
78
+ // Override in subclass if needed
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Cast a readonly value to a mutable version
84
+ *
85
+ * @example
86
+ * const literalNode = asMutable(node)
87
+ * literalNode.content = "new value"
88
+ */
89
+ function asMutable(node) {
90
+ return node;
91
+ }
92
+
93
+ /**
94
+ * Type guard to check if a class is an ASTRewriter
95
+ * Uses duck typing to work across module boundaries
96
+ */
97
+ function isASTRewriterClass(obj) {
98
+ if (typeof obj !== 'function')
99
+ return false;
100
+ if (obj.prototype instanceof ASTRewriter)
101
+ return true;
102
+ let proto = obj.prototype;
103
+ while (proto) {
104
+ if (proto.constructor?.name === 'ASTRewriter')
105
+ return true;
106
+ proto = Object.getPrototypeOf(proto);
107
+ }
108
+ return false;
109
+ }
110
+ /**
111
+ * Type guard to check if a class is a StringRewriter
112
+ * Uses duck typing to work across module boundaries
113
+ */
114
+ function isStringRewriterClass(obj) {
115
+ if (typeof obj !== 'function')
116
+ return false;
117
+ if (obj.prototype instanceof StringRewriter)
118
+ return true;
119
+ let proto = obj.prototype;
120
+ while (proto) {
121
+ if (proto.constructor?.name === 'StringRewriter')
122
+ return true;
123
+ proto = Object.getPrototypeOf(proto);
124
+ }
125
+ return false;
126
+ }
127
+ /**
128
+ * Type guard to check if a class is any kind of rewriter
129
+ */
130
+ function isRewriterClass(obj) {
131
+ return isASTRewriterClass(obj) || isStringRewriterClass(obj);
132
+ }
133
+
134
+ class Position {
135
+ line;
136
+ column;
137
+ static from(positionOrLine, column) {
138
+ if (typeof positionOrLine === "number") {
139
+ return new Position(positionOrLine, column);
140
+ }
141
+ else {
142
+ return new Position(positionOrLine.line, positionOrLine.column);
143
+ }
144
+ }
145
+ static get zero() {
146
+ return new Position(0, 0);
147
+ }
148
+ constructor(line, column) {
149
+ this.line = line;
150
+ this.column = column;
151
+ }
152
+ toHash() {
153
+ return { line: this.line, column: this.column };
154
+ }
155
+ toJSON() {
156
+ return this.toHash();
157
+ }
158
+ treeInspect() {
159
+ return `(${this.line}:${this.column})`;
160
+ }
161
+ inspect() {
162
+ return `#<Herb::Position ${this.treeInspect()}>`;
163
+ }
164
+ toString() {
165
+ return this.inspect();
166
+ }
167
+ }
168
+
169
+ class Location {
170
+ start;
171
+ end;
172
+ static from(locationOrLine, column, endLine, endColumn) {
173
+ if (typeof locationOrLine === "number") {
174
+ const start = Position.from(locationOrLine, column);
175
+ const end = Position.from(endLine, endColumn);
176
+ return new Location(start, end);
177
+ }
178
+ else {
179
+ const start = Position.from(locationOrLine.start);
180
+ const end = Position.from(locationOrLine.end);
181
+ return new Location(start, end);
182
+ }
183
+ }
184
+ static get zero() {
185
+ return new Location(Position.zero, Position.zero);
186
+ }
187
+ constructor(start, end) {
188
+ this.start = start;
189
+ this.end = end;
190
+ }
191
+ toHash() {
192
+ return {
193
+ start: this.start.toHash(),
194
+ end: this.end.toHash(),
195
+ };
196
+ }
197
+ toJSON() {
198
+ return this.toHash();
199
+ }
200
+ treeInspect() {
201
+ return `${this.start.treeInspect()}-${this.end.treeInspect()}`;
202
+ }
203
+ treeInspectWithLabel() {
204
+ return `(location: ${this.treeInspect()})`;
205
+ }
206
+ inspect() {
207
+ return `#<Herb::Location ${this.treeInspect()}>`;
208
+ }
209
+ toString() {
210
+ return this.inspect();
211
+ }
212
+ }
213
+
214
+ class Range {
215
+ start;
216
+ end;
217
+ static from(rangeOrStart, end) {
218
+ if (typeof rangeOrStart === "number") {
219
+ return new Range(rangeOrStart, end);
220
+ }
221
+ else {
222
+ return new Range(rangeOrStart[0], rangeOrStart[1]);
223
+ }
224
+ }
225
+ static get zero() {
226
+ return new Range(0, 0);
227
+ }
228
+ constructor(start, end) {
229
+ this.start = start;
230
+ this.end = end;
231
+ }
232
+ toArray() {
233
+ return [this.start, this.end];
234
+ }
235
+ toJSON() {
236
+ return this.toArray();
237
+ }
238
+ treeInspect() {
239
+ return `[${this.start}, ${this.end}]`;
240
+ }
241
+ inspect() {
242
+ return `#<Herb::Range ${this.toArray()}>`;
243
+ }
244
+ toString() {
245
+ return this.inspect();
246
+ }
247
+ }
248
+
249
+ class Token {
250
+ value;
251
+ range;
252
+ location;
253
+ type;
254
+ static from(token) {
255
+ return new Token(token.value, Range.from(token.range), Location.from(token.location), token.type);
256
+ }
257
+ constructor(value, range, location, type) {
258
+ this.value = value;
259
+ this.range = range;
260
+ this.location = location;
261
+ this.type = type;
262
+ }
263
+ toHash() {
264
+ return {
265
+ value: this.value,
266
+ range: this.range?.toArray(),
267
+ location: this.location?.toHash(),
268
+ type: this.type,
269
+ };
270
+ }
271
+ toJSON() {
272
+ return this.toHash();
273
+ }
274
+ treeInspect() {
275
+ return `"${this.value}" ${this.location.treeInspectWithLabel()}`;
276
+ }
277
+ valueInspect() {
278
+ return this.type === "TOKEN_EOF"
279
+ ? JSON.stringify("<EOF>")
280
+ : JSON.stringify(this.value);
281
+ }
282
+ inspect() {
283
+ return `#<Herb::Token type="${this.type}" value=${this.valueInspect()} range=${this.range.treeInspect()} start=${this.location.start.treeInspect()} end=${this.location.end.treeInspect()}>`;
284
+ }
285
+ toString() {
286
+ return this.inspect();
287
+ }
288
+ }
289
+
290
+ // NOTE: This file is generated by the templates/template.rb script and should not
291
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/errors.ts.erb
292
+ class HerbError {
293
+ type;
294
+ message;
295
+ location;
296
+ severity = "error";
297
+ source = "parser";
298
+ get code() {
299
+ return this.type;
300
+ }
301
+ static from(error) {
302
+ return fromSerializedError(error);
303
+ }
304
+ constructor(type, message, location) {
305
+ this.type = type;
306
+ this.message = message;
307
+ this.location = location;
308
+ }
309
+ toJSON() {
310
+ return {
311
+ type: this.type,
312
+ message: this.message,
313
+ location: this.location.toJSON(),
314
+ };
315
+ }
316
+ inspect() {
317
+ return this.treeInspect(0);
318
+ }
319
+ }
320
+ class UnexpectedError extends HerbError {
321
+ description;
322
+ expected;
323
+ found;
324
+ static from(data) {
325
+ return new UnexpectedError({
326
+ type: data.type,
327
+ message: data.message,
328
+ location: Location.from(data.location),
329
+ description: data.description,
330
+ expected: data.expected,
331
+ found: data.found,
332
+ });
333
+ }
334
+ constructor(props) {
335
+ super(props.type, props.message, props.location);
336
+ this.description = props.description;
337
+ this.expected = props.expected;
338
+ this.found = props.found;
339
+ }
340
+ toJSON() {
341
+ return {
342
+ ...super.toJSON(),
343
+ type: "UNEXPECTED_ERROR",
344
+ description: this.description,
345
+ expected: this.expected,
346
+ found: this.found,
347
+ };
348
+ }
349
+ toMonacoDiagnostic() {
350
+ return {
351
+ line: this.location.start.line,
352
+ column: this.location.start.column,
353
+ endLine: this.location.end.line,
354
+ endColumn: this.location.end.column,
355
+ message: this.message,
356
+ severity: 'error'
357
+ };
358
+ }
359
+ treeInspect() {
360
+ let output = "";
361
+ output += `@ UnexpectedError ${this.location.treeInspectWithLabel()}\n`;
362
+ output += `├── message: "${this.message}"\n`;
363
+ output += `├── description: ${JSON.stringify(this.description)}\n`;
364
+ output += `├── expected: ${JSON.stringify(this.expected)}\n`;
365
+ output += `└── found: ${JSON.stringify(this.found)}\n`;
366
+ return output;
367
+ }
368
+ }
369
+ class UnexpectedTokenError extends HerbError {
370
+ expected_type;
371
+ found;
372
+ static from(data) {
373
+ return new UnexpectedTokenError({
374
+ type: data.type,
375
+ message: data.message,
376
+ location: Location.from(data.location),
377
+ expected_type: data.expected_type,
378
+ found: data.found ? Token.from(data.found) : null,
379
+ });
380
+ }
381
+ constructor(props) {
382
+ super(props.type, props.message, props.location);
383
+ this.expected_type = props.expected_type;
384
+ this.found = props.found;
385
+ }
386
+ toJSON() {
387
+ return {
388
+ ...super.toJSON(),
389
+ type: "UNEXPECTED_TOKEN_ERROR",
390
+ expected_type: this.expected_type,
391
+ found: this.found ? this.found.toJSON() : null,
392
+ };
393
+ }
394
+ toMonacoDiagnostic() {
395
+ return {
396
+ line: this.location.start.line,
397
+ column: this.location.start.column,
398
+ endLine: this.location.end.line,
399
+ endColumn: this.location.end.column,
400
+ message: this.message,
401
+ severity: 'error'
402
+ };
403
+ }
404
+ treeInspect() {
405
+ let output = "";
406
+ output += `@ UnexpectedTokenError ${this.location.treeInspectWithLabel()}\n`;
407
+ output += `├── message: "${this.message}"\n`;
408
+ output += `├── expected_type: ${JSON.stringify(this.expected_type)}\n`;
409
+ output += `└── found: ${this.found ? this.found.treeInspect() : "∅"}\n`;
410
+ return output;
411
+ }
412
+ }
413
+ class MissingOpeningTagError extends HerbError {
414
+ closing_tag;
415
+ static from(data) {
416
+ return new MissingOpeningTagError({
417
+ type: data.type,
418
+ message: data.message,
419
+ location: Location.from(data.location),
420
+ closing_tag: data.closing_tag ? Token.from(data.closing_tag) : null,
421
+ });
422
+ }
423
+ constructor(props) {
424
+ super(props.type, props.message, props.location);
425
+ this.closing_tag = props.closing_tag;
426
+ }
427
+ toJSON() {
428
+ return {
429
+ ...super.toJSON(),
430
+ type: "MISSING_OPENING_TAG_ERROR",
431
+ closing_tag: this.closing_tag ? this.closing_tag.toJSON() : null,
432
+ };
433
+ }
434
+ toMonacoDiagnostic() {
435
+ return {
436
+ line: this.location.start.line,
437
+ column: this.location.start.column,
438
+ endLine: this.location.end.line,
439
+ endColumn: this.location.end.column,
440
+ message: this.message,
441
+ severity: 'error'
442
+ };
443
+ }
444
+ treeInspect() {
445
+ let output = "";
446
+ output += `@ MissingOpeningTagError ${this.location.treeInspectWithLabel()}\n`;
447
+ output += `├── message: "${this.message}"\n`;
448
+ output += `└── closing_tag: ${this.closing_tag ? this.closing_tag.treeInspect() : "∅"}\n`;
449
+ return output;
450
+ }
451
+ }
452
+ class MissingClosingTagError extends HerbError {
453
+ opening_tag;
454
+ static from(data) {
455
+ return new MissingClosingTagError({
456
+ type: data.type,
457
+ message: data.message,
458
+ location: Location.from(data.location),
459
+ opening_tag: data.opening_tag ? Token.from(data.opening_tag) : null,
460
+ });
461
+ }
462
+ constructor(props) {
463
+ super(props.type, props.message, props.location);
464
+ this.opening_tag = props.opening_tag;
465
+ }
466
+ toJSON() {
467
+ return {
468
+ ...super.toJSON(),
469
+ type: "MISSING_CLOSING_TAG_ERROR",
470
+ opening_tag: this.opening_tag ? this.opening_tag.toJSON() : null,
471
+ };
472
+ }
473
+ toMonacoDiagnostic() {
474
+ return {
475
+ line: this.location.start.line,
476
+ column: this.location.start.column,
477
+ endLine: this.location.end.line,
478
+ endColumn: this.location.end.column,
479
+ message: this.message,
480
+ severity: 'error'
481
+ };
482
+ }
483
+ treeInspect() {
484
+ let output = "";
485
+ output += `@ MissingClosingTagError ${this.location.treeInspectWithLabel()}\n`;
486
+ output += `├── message: "${this.message}"\n`;
487
+ output += `└── opening_tag: ${this.opening_tag ? this.opening_tag.treeInspect() : "∅"}\n`;
488
+ return output;
489
+ }
490
+ }
491
+ class TagNamesMismatchError extends HerbError {
492
+ opening_tag;
493
+ closing_tag;
494
+ static from(data) {
495
+ return new TagNamesMismatchError({
496
+ type: data.type,
497
+ message: data.message,
498
+ location: Location.from(data.location),
499
+ opening_tag: data.opening_tag ? Token.from(data.opening_tag) : null,
500
+ closing_tag: data.closing_tag ? Token.from(data.closing_tag) : null,
501
+ });
502
+ }
503
+ constructor(props) {
504
+ super(props.type, props.message, props.location);
505
+ this.opening_tag = props.opening_tag;
506
+ this.closing_tag = props.closing_tag;
507
+ }
508
+ toJSON() {
509
+ return {
510
+ ...super.toJSON(),
511
+ type: "TAG_NAMES_MISMATCH_ERROR",
512
+ opening_tag: this.opening_tag ? this.opening_tag.toJSON() : null,
513
+ closing_tag: this.closing_tag ? this.closing_tag.toJSON() : null,
514
+ };
515
+ }
516
+ toMonacoDiagnostic() {
517
+ return {
518
+ line: this.location.start.line,
519
+ column: this.location.start.column,
520
+ endLine: this.location.end.line,
521
+ endColumn: this.location.end.column,
522
+ message: this.message,
523
+ severity: 'error'
524
+ };
525
+ }
526
+ treeInspect() {
527
+ let output = "";
528
+ output += `@ TagNamesMismatchError ${this.location.treeInspectWithLabel()}\n`;
529
+ output += `├── message: "${this.message}"\n`;
530
+ output += `├── opening_tag: ${this.opening_tag ? this.opening_tag.treeInspect() : "∅"}\n`;
531
+ output += `└── closing_tag: ${this.closing_tag ? this.closing_tag.treeInspect() : "∅"}\n`;
532
+ return output;
533
+ }
534
+ }
535
+ class QuotesMismatchError extends HerbError {
536
+ opening_quote;
537
+ closing_quote;
538
+ static from(data) {
539
+ return new QuotesMismatchError({
540
+ type: data.type,
541
+ message: data.message,
542
+ location: Location.from(data.location),
543
+ opening_quote: data.opening_quote ? Token.from(data.opening_quote) : null,
544
+ closing_quote: data.closing_quote ? Token.from(data.closing_quote) : null,
545
+ });
546
+ }
547
+ constructor(props) {
548
+ super(props.type, props.message, props.location);
549
+ this.opening_quote = props.opening_quote;
550
+ this.closing_quote = props.closing_quote;
551
+ }
552
+ toJSON() {
553
+ return {
554
+ ...super.toJSON(),
555
+ type: "QUOTES_MISMATCH_ERROR",
556
+ opening_quote: this.opening_quote ? this.opening_quote.toJSON() : null,
557
+ closing_quote: this.closing_quote ? this.closing_quote.toJSON() : null,
558
+ };
559
+ }
560
+ toMonacoDiagnostic() {
561
+ return {
562
+ line: this.location.start.line,
563
+ column: this.location.start.column,
564
+ endLine: this.location.end.line,
565
+ endColumn: this.location.end.column,
566
+ message: this.message,
567
+ severity: 'error'
568
+ };
569
+ }
570
+ treeInspect() {
571
+ let output = "";
572
+ output += `@ QuotesMismatchError ${this.location.treeInspectWithLabel()}\n`;
573
+ output += `├── message: "${this.message}"\n`;
574
+ output += `├── opening_quote: ${this.opening_quote ? this.opening_quote.treeInspect() : "∅"}\n`;
575
+ output += `└── closing_quote: ${this.closing_quote ? this.closing_quote.treeInspect() : "∅"}\n`;
576
+ return output;
577
+ }
578
+ }
579
+ class VoidElementClosingTagError extends HerbError {
580
+ tag_name;
581
+ expected;
582
+ found;
583
+ static from(data) {
584
+ return new VoidElementClosingTagError({
585
+ type: data.type,
586
+ message: data.message,
587
+ location: Location.from(data.location),
588
+ tag_name: data.tag_name ? Token.from(data.tag_name) : null,
589
+ expected: data.expected,
590
+ found: data.found,
591
+ });
592
+ }
593
+ constructor(props) {
594
+ super(props.type, props.message, props.location);
595
+ this.tag_name = props.tag_name;
596
+ this.expected = props.expected;
597
+ this.found = props.found;
598
+ }
599
+ toJSON() {
600
+ return {
601
+ ...super.toJSON(),
602
+ type: "VOID_ELEMENT_CLOSING_TAG_ERROR",
603
+ tag_name: this.tag_name ? this.tag_name.toJSON() : null,
604
+ expected: this.expected,
605
+ found: this.found,
606
+ };
607
+ }
608
+ toMonacoDiagnostic() {
609
+ return {
610
+ line: this.location.start.line,
611
+ column: this.location.start.column,
612
+ endLine: this.location.end.line,
613
+ endColumn: this.location.end.column,
614
+ message: this.message,
615
+ severity: 'error'
616
+ };
617
+ }
618
+ treeInspect() {
619
+ let output = "";
620
+ output += `@ VoidElementClosingTagError ${this.location.treeInspectWithLabel()}\n`;
621
+ output += `├── message: "${this.message}"\n`;
622
+ output += `├── tag_name: ${this.tag_name ? this.tag_name.treeInspect() : "∅"}\n`;
623
+ output += `├── expected: ${JSON.stringify(this.expected)}\n`;
624
+ output += `└── found: ${JSON.stringify(this.found)}\n`;
625
+ return output;
626
+ }
627
+ }
628
+ class UnclosedElementError extends HerbError {
629
+ opening_tag;
630
+ static from(data) {
631
+ return new UnclosedElementError({
632
+ type: data.type,
633
+ message: data.message,
634
+ location: Location.from(data.location),
635
+ opening_tag: data.opening_tag ? Token.from(data.opening_tag) : null,
636
+ });
637
+ }
638
+ constructor(props) {
639
+ super(props.type, props.message, props.location);
640
+ this.opening_tag = props.opening_tag;
641
+ }
642
+ toJSON() {
643
+ return {
644
+ ...super.toJSON(),
645
+ type: "UNCLOSED_ELEMENT_ERROR",
646
+ opening_tag: this.opening_tag ? this.opening_tag.toJSON() : null,
647
+ };
648
+ }
649
+ toMonacoDiagnostic() {
650
+ return {
651
+ line: this.location.start.line,
652
+ column: this.location.start.column,
653
+ endLine: this.location.end.line,
654
+ endColumn: this.location.end.column,
655
+ message: this.message,
656
+ severity: 'error'
657
+ };
658
+ }
659
+ treeInspect() {
660
+ let output = "";
661
+ output += `@ UnclosedElementError ${this.location.treeInspectWithLabel()}\n`;
662
+ output += `├── message: "${this.message}"\n`;
663
+ output += `└── opening_tag: ${this.opening_tag ? this.opening_tag.treeInspect() : "∅"}\n`;
664
+ return output;
665
+ }
666
+ }
667
+ class RubyParseError extends HerbError {
668
+ error_message;
669
+ diagnostic_id;
670
+ level;
671
+ static from(data) {
672
+ return new RubyParseError({
673
+ type: data.type,
674
+ message: data.message,
675
+ location: Location.from(data.location),
676
+ error_message: data.error_message,
677
+ diagnostic_id: data.diagnostic_id,
678
+ level: data.level,
679
+ });
680
+ }
681
+ constructor(props) {
682
+ super(props.type, props.message, props.location);
683
+ this.error_message = props.error_message;
684
+ this.diagnostic_id = props.diagnostic_id;
685
+ this.level = props.level;
686
+ }
687
+ toJSON() {
688
+ return {
689
+ ...super.toJSON(),
690
+ type: "RUBY_PARSE_ERROR",
691
+ error_message: this.error_message,
692
+ diagnostic_id: this.diagnostic_id,
693
+ level: this.level,
694
+ };
695
+ }
696
+ toMonacoDiagnostic() {
697
+ return {
698
+ line: this.location.start.line,
699
+ column: this.location.start.column,
700
+ endLine: this.location.end.line,
701
+ endColumn: this.location.end.column,
702
+ message: this.message,
703
+ severity: 'error'
704
+ };
705
+ }
706
+ treeInspect() {
707
+ let output = "";
708
+ output += `@ RubyParseError ${this.location.treeInspectWithLabel()}\n`;
709
+ output += `├── message: "${this.message}"\n`;
710
+ output += `├── error_message: ${JSON.stringify(this.error_message)}\n`;
711
+ output += `├── diagnostic_id: ${JSON.stringify(this.diagnostic_id)}\n`;
712
+ output += `└── level: ${JSON.stringify(this.level)}\n`;
713
+ return output;
714
+ }
715
+ }
716
+ class ERBControlFlowScopeError extends HerbError {
717
+ keyword;
718
+ static from(data) {
719
+ return new ERBControlFlowScopeError({
720
+ type: data.type,
721
+ message: data.message,
722
+ location: Location.from(data.location),
723
+ keyword: data.keyword,
724
+ });
725
+ }
726
+ constructor(props) {
727
+ super(props.type, props.message, props.location);
728
+ this.keyword = props.keyword;
729
+ }
730
+ toJSON() {
731
+ return {
732
+ ...super.toJSON(),
733
+ type: "ERB_CONTROL_FLOW_SCOPE_ERROR",
734
+ keyword: this.keyword,
735
+ };
736
+ }
737
+ toMonacoDiagnostic() {
738
+ return {
739
+ line: this.location.start.line,
740
+ column: this.location.start.column,
741
+ endLine: this.location.end.line,
742
+ endColumn: this.location.end.column,
743
+ message: this.message,
744
+ severity: 'error'
745
+ };
746
+ }
747
+ treeInspect() {
748
+ let output = "";
749
+ output += `@ ERBControlFlowScopeError ${this.location.treeInspectWithLabel()}\n`;
750
+ output += `├── message: "${this.message}"\n`;
751
+ output += `└── keyword: ${JSON.stringify(this.keyword)}\n`;
752
+ return output;
753
+ }
754
+ }
755
+ class MissingERBEndTagError extends HerbError {
756
+ keyword;
757
+ static from(data) {
758
+ return new MissingERBEndTagError({
759
+ type: data.type,
760
+ message: data.message,
761
+ location: Location.from(data.location),
762
+ keyword: data.keyword,
763
+ });
764
+ }
765
+ constructor(props) {
766
+ super(props.type, props.message, props.location);
767
+ this.keyword = props.keyword;
768
+ }
769
+ toJSON() {
770
+ return {
771
+ ...super.toJSON(),
772
+ type: "MISSINGERB_END_TAG_ERROR",
773
+ keyword: this.keyword,
774
+ };
775
+ }
776
+ toMonacoDiagnostic() {
777
+ return {
778
+ line: this.location.start.line,
779
+ column: this.location.start.column,
780
+ endLine: this.location.end.line,
781
+ endColumn: this.location.end.column,
782
+ message: this.message,
783
+ severity: 'error'
784
+ };
785
+ }
786
+ treeInspect() {
787
+ let output = "";
788
+ output += `@ MissingERBEndTagError ${this.location.treeInspectWithLabel()}\n`;
789
+ output += `├── message: "${this.message}"\n`;
790
+ output += `└── keyword: ${JSON.stringify(this.keyword)}\n`;
791
+ return output;
792
+ }
793
+ }
794
+ function fromSerializedError(error) {
795
+ switch (error.type) {
796
+ case "UNEXPECTED_ERROR": return UnexpectedError.from(error);
797
+ case "UNEXPECTED_TOKEN_ERROR": return UnexpectedTokenError.from(error);
798
+ case "MISSING_OPENING_TAG_ERROR": return MissingOpeningTagError.from(error);
799
+ case "MISSING_CLOSING_TAG_ERROR": return MissingClosingTagError.from(error);
800
+ case "TAG_NAMES_MISMATCH_ERROR": return TagNamesMismatchError.from(error);
801
+ case "QUOTES_MISMATCH_ERROR": return QuotesMismatchError.from(error);
802
+ case "VOID_ELEMENT_CLOSING_TAG_ERROR": return VoidElementClosingTagError.from(error);
803
+ case "UNCLOSED_ELEMENT_ERROR": return UnclosedElementError.from(error);
804
+ case "RUBY_PARSE_ERROR": return RubyParseError.from(error);
805
+ case "ERB_CONTROL_FLOW_SCOPE_ERROR": return ERBControlFlowScopeError.from(error);
806
+ case "MISSINGERB_END_TAG_ERROR": return MissingERBEndTagError.from(error);
807
+ default:
808
+ throw new Error(`Unknown node type: ${error.type}`);
809
+ }
810
+ }
811
+ function convertToUTF8(string) {
812
+ const bytes = [];
813
+ for (let i = 0; i < string.length; i++) {
814
+ bytes.push(string.charCodeAt(i));
815
+ }
816
+ const decoder = new TextDecoder("utf-8");
817
+ return decoder.decode(new Uint8Array(bytes));
818
+ }
819
+
820
+ // NOTE: This file is generated by the templates/template.rb script and should not
821
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/nodes.ts.erb
822
+ class Node {
823
+ type;
824
+ location;
825
+ errors;
826
+ static from(node) {
827
+ return fromSerializedNode(node);
828
+ }
829
+ static get type() {
830
+ throw new Error("AST_NODE");
831
+ }
832
+ constructor(type, location, errors) {
833
+ this.type = type;
834
+ this.location = location;
835
+ this.errors = errors;
836
+ }
837
+ toJSON() {
838
+ return {
839
+ type: this.type,
840
+ location: this.location.toJSON(),
841
+ errors: this.errors,
842
+ };
843
+ }
844
+ inspect() {
845
+ return this.treeInspect(0);
846
+ }
847
+ is(nodeClass) {
848
+ return this.type === nodeClass.type;
849
+ }
850
+ isOfType(type) {
851
+ return this.type === type;
852
+ }
853
+ get isSingleLine() {
854
+ return this.location.start.line === this.location.end.line;
855
+ }
856
+ inspectArray(array, prefix) {
857
+ if (!array)
858
+ return "∅\n";
859
+ if (array.length === 0)
860
+ return "[]\n";
861
+ let output = `(${array.length} item${array.length === 1 ? "" : "s"})\n`;
862
+ array.forEach((item, index) => {
863
+ const isLast = index === array.length - 1;
864
+ if (item instanceof Node || item instanceof HerbError) {
865
+ output += this.inspectNode(item, prefix, isLast ? " " : "│ ", isLast, false);
866
+ }
867
+ else {
868
+ const symbol = isLast ? "└── " : "├── ";
869
+ output += `${prefix}${symbol} ${item}\n`;
870
+ }
871
+ });
872
+ output += `${prefix}\n`;
873
+ return output;
874
+ }
875
+ inspectNode(node, prefix, prefix2 = " ", last = true, trailingNewline = true) {
876
+ if (!node)
877
+ return "∅\n";
878
+ let output = trailingNewline ? "\n" : "";
879
+ output += `${prefix}`;
880
+ output += last ? "└── " : "├── ";
881
+ output += node
882
+ .treeInspect()
883
+ .trimStart()
884
+ .split("\n")
885
+ .map((line, index) => index === 0 ? line.trimStart() : `${prefix}${prefix2}${line}`)
886
+ .join("\n")
887
+ .trimStart();
888
+ output += `\n`;
889
+ return output;
890
+ }
891
+ }
892
+ class DocumentNode extends Node {
893
+ children;
894
+ static get type() {
895
+ return "AST_DOCUMENT_NODE";
896
+ }
897
+ static from(data) {
898
+ return new DocumentNode({
899
+ type: data.type,
900
+ location: Location.from(data.location),
901
+ errors: (data.errors || []).map(error => HerbError.from(error)),
902
+ children: (data.children || []).map(node => fromSerializedNode(node)),
903
+ });
904
+ }
905
+ constructor(props) {
906
+ super(props.type, props.location, props.errors);
907
+ this.children = props.children;
908
+ }
909
+ accept(visitor) {
910
+ visitor.visitDocumentNode(this);
911
+ }
912
+ childNodes() {
913
+ return [
914
+ ...this.children,
915
+ ];
916
+ }
917
+ compactChildNodes() {
918
+ return this.childNodes().filter(node => node !== null && node !== undefined);
919
+ }
920
+ recursiveErrors() {
921
+ return [
922
+ ...this.errors,
923
+ ...this.children.map(node => node.recursiveErrors()),
924
+ ].flat();
925
+ }
926
+ toJSON() {
927
+ return {
928
+ ...super.toJSON(),
929
+ type: "AST_DOCUMENT_NODE",
930
+ children: this.children.map(node => node.toJSON()),
931
+ };
932
+ }
933
+ treeInspect() {
934
+ let output = "";
935
+ output += `@ DocumentNode ${this.location.treeInspectWithLabel()}\n`;
936
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
937
+ output += `└── children: ${this.inspectArray(this.children, " ")}`;
938
+ return output;
939
+ }
940
+ }
941
+ class LiteralNode extends Node {
942
+ content;
943
+ static get type() {
944
+ return "AST_LITERAL_NODE";
945
+ }
946
+ static from(data) {
947
+ return new LiteralNode({
948
+ type: data.type,
949
+ location: Location.from(data.location),
950
+ errors: (data.errors || []).map(error => HerbError.from(error)),
951
+ content: data.content,
952
+ });
953
+ }
954
+ constructor(props) {
955
+ super(props.type, props.location, props.errors);
956
+ this.content = convertToUTF8(props.content);
957
+ }
958
+ accept(visitor) {
959
+ visitor.visitLiteralNode(this);
960
+ }
961
+ childNodes() {
962
+ return [];
963
+ }
964
+ compactChildNodes() {
965
+ return this.childNodes().filter(node => node !== null && node !== undefined);
966
+ }
967
+ recursiveErrors() {
968
+ return [
969
+ ...this.errors,
970
+ ].flat();
971
+ }
972
+ toJSON() {
973
+ return {
974
+ ...super.toJSON(),
975
+ type: "AST_LITERAL_NODE",
976
+ content: this.content,
977
+ };
978
+ }
979
+ treeInspect() {
980
+ let output = "";
981
+ output += `@ LiteralNode ${this.location.treeInspectWithLabel()}\n`;
982
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
983
+ output += `└── content: ${this.content ? JSON.stringify(this.content) : "∅"}\n`;
984
+ return output;
985
+ }
986
+ }
987
+ class HTMLOpenTagNode extends Node {
988
+ tag_opening;
989
+ tag_name;
990
+ tag_closing;
991
+ children;
992
+ is_void;
993
+ static get type() {
994
+ return "AST_HTML_OPEN_TAG_NODE";
995
+ }
996
+ static from(data) {
997
+ return new HTMLOpenTagNode({
998
+ type: data.type,
999
+ location: Location.from(data.location),
1000
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1001
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1002
+ tag_name: data.tag_name ? Token.from(data.tag_name) : null,
1003
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1004
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1005
+ is_void: data.is_void,
1006
+ });
1007
+ }
1008
+ constructor(props) {
1009
+ super(props.type, props.location, props.errors);
1010
+ this.tag_opening = props.tag_opening;
1011
+ this.tag_name = props.tag_name;
1012
+ this.tag_closing = props.tag_closing;
1013
+ this.children = props.children;
1014
+ this.is_void = props.is_void;
1015
+ }
1016
+ accept(visitor) {
1017
+ visitor.visitHTMLOpenTagNode(this);
1018
+ }
1019
+ childNodes() {
1020
+ return [
1021
+ ...this.children,
1022
+ ];
1023
+ }
1024
+ compactChildNodes() {
1025
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1026
+ }
1027
+ recursiveErrors() {
1028
+ return [
1029
+ ...this.errors,
1030
+ ...this.children.map(node => node.recursiveErrors()),
1031
+ ].flat();
1032
+ }
1033
+ toJSON() {
1034
+ return {
1035
+ ...super.toJSON(),
1036
+ type: "AST_HTML_OPEN_TAG_NODE",
1037
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1038
+ tag_name: this.tag_name ? this.tag_name.toJSON() : null,
1039
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1040
+ children: this.children.map(node => node.toJSON()),
1041
+ is_void: this.is_void,
1042
+ };
1043
+ }
1044
+ treeInspect() {
1045
+ let output = "";
1046
+ output += `@ HTMLOpenTagNode ${this.location.treeInspectWithLabel()}\n`;
1047
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1048
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1049
+ output += `├── tag_name: ${this.tag_name ? this.tag_name.treeInspect() : "∅"}\n`;
1050
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1051
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1052
+ output += `└── is_void: ${typeof this.is_void === 'boolean' ? String(this.is_void) : "∅"}\n`;
1053
+ return output;
1054
+ }
1055
+ }
1056
+ class HTMLCloseTagNode extends Node {
1057
+ tag_opening;
1058
+ tag_name;
1059
+ children;
1060
+ tag_closing;
1061
+ static get type() {
1062
+ return "AST_HTML_CLOSE_TAG_NODE";
1063
+ }
1064
+ static from(data) {
1065
+ return new HTMLCloseTagNode({
1066
+ type: data.type,
1067
+ location: Location.from(data.location),
1068
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1069
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1070
+ tag_name: data.tag_name ? Token.from(data.tag_name) : null,
1071
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1072
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1073
+ });
1074
+ }
1075
+ constructor(props) {
1076
+ super(props.type, props.location, props.errors);
1077
+ this.tag_opening = props.tag_opening;
1078
+ this.tag_name = props.tag_name;
1079
+ this.children = props.children;
1080
+ this.tag_closing = props.tag_closing;
1081
+ }
1082
+ accept(visitor) {
1083
+ visitor.visitHTMLCloseTagNode(this);
1084
+ }
1085
+ childNodes() {
1086
+ return [
1087
+ ...this.children,
1088
+ ];
1089
+ }
1090
+ compactChildNodes() {
1091
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1092
+ }
1093
+ recursiveErrors() {
1094
+ return [
1095
+ ...this.errors,
1096
+ ...this.children.map(node => node.recursiveErrors()),
1097
+ ].flat();
1098
+ }
1099
+ toJSON() {
1100
+ return {
1101
+ ...super.toJSON(),
1102
+ type: "AST_HTML_CLOSE_TAG_NODE",
1103
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1104
+ tag_name: this.tag_name ? this.tag_name.toJSON() : null,
1105
+ children: this.children.map(node => node.toJSON()),
1106
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1107
+ };
1108
+ }
1109
+ treeInspect() {
1110
+ let output = "";
1111
+ output += `@ HTMLCloseTagNode ${this.location.treeInspectWithLabel()}\n`;
1112
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1113
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1114
+ output += `├── tag_name: ${this.tag_name ? this.tag_name.treeInspect() : "∅"}\n`;
1115
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1116
+ output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1117
+ return output;
1118
+ }
1119
+ }
1120
+ class HTMLElementNode extends Node {
1121
+ open_tag;
1122
+ tag_name;
1123
+ body;
1124
+ close_tag;
1125
+ is_void;
1126
+ source;
1127
+ static get type() {
1128
+ return "AST_HTML_ELEMENT_NODE";
1129
+ }
1130
+ static from(data) {
1131
+ return new HTMLElementNode({
1132
+ type: data.type,
1133
+ location: Location.from(data.location),
1134
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1135
+ open_tag: data.open_tag ? fromSerializedNode((data.open_tag)) : null,
1136
+ tag_name: data.tag_name ? Token.from(data.tag_name) : null,
1137
+ body: (data.body || []).map(node => fromSerializedNode(node)),
1138
+ close_tag: data.close_tag ? fromSerializedNode((data.close_tag)) : null,
1139
+ is_void: data.is_void,
1140
+ source: data.source,
1141
+ });
1142
+ }
1143
+ constructor(props) {
1144
+ super(props.type, props.location, props.errors);
1145
+ this.open_tag = props.open_tag;
1146
+ this.tag_name = props.tag_name;
1147
+ this.body = props.body;
1148
+ this.close_tag = props.close_tag;
1149
+ this.is_void = props.is_void;
1150
+ this.source = props.source;
1151
+ }
1152
+ accept(visitor) {
1153
+ visitor.visitHTMLElementNode(this);
1154
+ }
1155
+ childNodes() {
1156
+ return [
1157
+ this.open_tag,
1158
+ ...this.body,
1159
+ this.close_tag,
1160
+ ];
1161
+ }
1162
+ compactChildNodes() {
1163
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1164
+ }
1165
+ recursiveErrors() {
1166
+ return [
1167
+ ...this.errors,
1168
+ this.open_tag ? this.open_tag.recursiveErrors() : [],
1169
+ ...this.body.map(node => node.recursiveErrors()),
1170
+ this.close_tag ? this.close_tag.recursiveErrors() : [],
1171
+ ].flat();
1172
+ }
1173
+ toJSON() {
1174
+ return {
1175
+ ...super.toJSON(),
1176
+ type: "AST_HTML_ELEMENT_NODE",
1177
+ open_tag: this.open_tag ? this.open_tag.toJSON() : null,
1178
+ tag_name: this.tag_name ? this.tag_name.toJSON() : null,
1179
+ body: this.body.map(node => node.toJSON()),
1180
+ close_tag: this.close_tag ? this.close_tag.toJSON() : null,
1181
+ is_void: this.is_void,
1182
+ source: this.source,
1183
+ };
1184
+ }
1185
+ treeInspect() {
1186
+ let output = "";
1187
+ output += `@ HTMLElementNode ${this.location.treeInspectWithLabel()}\n`;
1188
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1189
+ output += `├── open_tag: ${this.inspectNode(this.open_tag, "│ ")}`;
1190
+ output += `├── tag_name: ${this.tag_name ? this.tag_name.treeInspect() : "∅"}\n`;
1191
+ output += `├── body: ${this.inspectArray(this.body, "│ ")}`;
1192
+ output += `├── close_tag: ${this.inspectNode(this.close_tag, "│ ")}`;
1193
+ output += `├── is_void: ${typeof this.is_void === 'boolean' ? String(this.is_void) : "∅"}\n`;
1194
+ output += `└── source: ${this.source ? JSON.stringify(this.source) : "∅"}\n`;
1195
+ return output;
1196
+ }
1197
+ }
1198
+ class HTMLAttributeValueNode extends Node {
1199
+ open_quote;
1200
+ children;
1201
+ close_quote;
1202
+ quoted;
1203
+ static get type() {
1204
+ return "AST_HTML_ATTRIBUTE_VALUE_NODE";
1205
+ }
1206
+ static from(data) {
1207
+ return new HTMLAttributeValueNode({
1208
+ type: data.type,
1209
+ location: Location.from(data.location),
1210
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1211
+ open_quote: data.open_quote ? Token.from(data.open_quote) : null,
1212
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1213
+ close_quote: data.close_quote ? Token.from(data.close_quote) : null,
1214
+ quoted: data.quoted,
1215
+ });
1216
+ }
1217
+ constructor(props) {
1218
+ super(props.type, props.location, props.errors);
1219
+ this.open_quote = props.open_quote;
1220
+ this.children = props.children;
1221
+ this.close_quote = props.close_quote;
1222
+ this.quoted = props.quoted;
1223
+ }
1224
+ accept(visitor) {
1225
+ visitor.visitHTMLAttributeValueNode(this);
1226
+ }
1227
+ childNodes() {
1228
+ return [
1229
+ ...this.children,
1230
+ ];
1231
+ }
1232
+ compactChildNodes() {
1233
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1234
+ }
1235
+ recursiveErrors() {
1236
+ return [
1237
+ ...this.errors,
1238
+ ...this.children.map(node => node.recursiveErrors()),
1239
+ ].flat();
1240
+ }
1241
+ toJSON() {
1242
+ return {
1243
+ ...super.toJSON(),
1244
+ type: "AST_HTML_ATTRIBUTE_VALUE_NODE",
1245
+ open_quote: this.open_quote ? this.open_quote.toJSON() : null,
1246
+ children: this.children.map(node => node.toJSON()),
1247
+ close_quote: this.close_quote ? this.close_quote.toJSON() : null,
1248
+ quoted: this.quoted,
1249
+ };
1250
+ }
1251
+ treeInspect() {
1252
+ let output = "";
1253
+ output += `@ HTMLAttributeValueNode ${this.location.treeInspectWithLabel()}\n`;
1254
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1255
+ output += `├── open_quote: ${this.open_quote ? this.open_quote.treeInspect() : "∅"}\n`;
1256
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1257
+ output += `├── close_quote: ${this.close_quote ? this.close_quote.treeInspect() : "∅"}\n`;
1258
+ output += `└── quoted: ${typeof this.quoted === 'boolean' ? String(this.quoted) : "∅"}\n`;
1259
+ return output;
1260
+ }
1261
+ }
1262
+ class HTMLAttributeNameNode extends Node {
1263
+ children;
1264
+ static get type() {
1265
+ return "AST_HTML_ATTRIBUTE_NAME_NODE";
1266
+ }
1267
+ static from(data) {
1268
+ return new HTMLAttributeNameNode({
1269
+ type: data.type,
1270
+ location: Location.from(data.location),
1271
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1272
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1273
+ });
1274
+ }
1275
+ constructor(props) {
1276
+ super(props.type, props.location, props.errors);
1277
+ this.children = props.children;
1278
+ }
1279
+ accept(visitor) {
1280
+ visitor.visitHTMLAttributeNameNode(this);
1281
+ }
1282
+ childNodes() {
1283
+ return [
1284
+ ...this.children,
1285
+ ];
1286
+ }
1287
+ compactChildNodes() {
1288
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1289
+ }
1290
+ recursiveErrors() {
1291
+ return [
1292
+ ...this.errors,
1293
+ ...this.children.map(node => node.recursiveErrors()),
1294
+ ].flat();
1295
+ }
1296
+ toJSON() {
1297
+ return {
1298
+ ...super.toJSON(),
1299
+ type: "AST_HTML_ATTRIBUTE_NAME_NODE",
1300
+ children: this.children.map(node => node.toJSON()),
1301
+ };
1302
+ }
1303
+ treeInspect() {
1304
+ let output = "";
1305
+ output += `@ HTMLAttributeNameNode ${this.location.treeInspectWithLabel()}\n`;
1306
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1307
+ output += `└── children: ${this.inspectArray(this.children, " ")}`;
1308
+ return output;
1309
+ }
1310
+ }
1311
+ class HTMLAttributeNode extends Node {
1312
+ name;
1313
+ equals;
1314
+ value;
1315
+ static get type() {
1316
+ return "AST_HTML_ATTRIBUTE_NODE";
1317
+ }
1318
+ static from(data) {
1319
+ return new HTMLAttributeNode({
1320
+ type: data.type,
1321
+ location: Location.from(data.location),
1322
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1323
+ name: data.name ? fromSerializedNode((data.name)) : null,
1324
+ equals: data.equals ? Token.from(data.equals) : null,
1325
+ value: data.value ? fromSerializedNode((data.value)) : null,
1326
+ });
1327
+ }
1328
+ constructor(props) {
1329
+ super(props.type, props.location, props.errors);
1330
+ this.name = props.name;
1331
+ this.equals = props.equals;
1332
+ this.value = props.value;
1333
+ }
1334
+ accept(visitor) {
1335
+ visitor.visitHTMLAttributeNode(this);
1336
+ }
1337
+ childNodes() {
1338
+ return [
1339
+ this.name,
1340
+ this.value,
1341
+ ];
1342
+ }
1343
+ compactChildNodes() {
1344
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1345
+ }
1346
+ recursiveErrors() {
1347
+ return [
1348
+ ...this.errors,
1349
+ this.name ? this.name.recursiveErrors() : [],
1350
+ this.value ? this.value.recursiveErrors() : [],
1351
+ ].flat();
1352
+ }
1353
+ toJSON() {
1354
+ return {
1355
+ ...super.toJSON(),
1356
+ type: "AST_HTML_ATTRIBUTE_NODE",
1357
+ name: this.name ? this.name.toJSON() : null,
1358
+ equals: this.equals ? this.equals.toJSON() : null,
1359
+ value: this.value ? this.value.toJSON() : null,
1360
+ };
1361
+ }
1362
+ treeInspect() {
1363
+ let output = "";
1364
+ output += `@ HTMLAttributeNode ${this.location.treeInspectWithLabel()}\n`;
1365
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1366
+ output += `├── name: ${this.inspectNode(this.name, "│ ")}`;
1367
+ output += `├── equals: ${this.equals ? this.equals.treeInspect() : "∅"}\n`;
1368
+ output += `└── value: ${this.inspectNode(this.value, " ")}`;
1369
+ return output;
1370
+ }
1371
+ }
1372
+ class HTMLTextNode extends Node {
1373
+ content;
1374
+ static get type() {
1375
+ return "AST_HTML_TEXT_NODE";
1376
+ }
1377
+ static from(data) {
1378
+ return new HTMLTextNode({
1379
+ type: data.type,
1380
+ location: Location.from(data.location),
1381
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1382
+ content: data.content,
1383
+ });
1384
+ }
1385
+ constructor(props) {
1386
+ super(props.type, props.location, props.errors);
1387
+ this.content = convertToUTF8(props.content);
1388
+ }
1389
+ accept(visitor) {
1390
+ visitor.visitHTMLTextNode(this);
1391
+ }
1392
+ childNodes() {
1393
+ return [];
1394
+ }
1395
+ compactChildNodes() {
1396
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1397
+ }
1398
+ recursiveErrors() {
1399
+ return [
1400
+ ...this.errors,
1401
+ ].flat();
1402
+ }
1403
+ toJSON() {
1404
+ return {
1405
+ ...super.toJSON(),
1406
+ type: "AST_HTML_TEXT_NODE",
1407
+ content: this.content,
1408
+ };
1409
+ }
1410
+ treeInspect() {
1411
+ let output = "";
1412
+ output += `@ HTMLTextNode ${this.location.treeInspectWithLabel()}\n`;
1413
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1414
+ output += `└── content: ${this.content ? JSON.stringify(this.content) : "∅"}\n`;
1415
+ return output;
1416
+ }
1417
+ }
1418
+ class HTMLCommentNode extends Node {
1419
+ comment_start;
1420
+ children;
1421
+ comment_end;
1422
+ static get type() {
1423
+ return "AST_HTML_COMMENT_NODE";
1424
+ }
1425
+ static from(data) {
1426
+ return new HTMLCommentNode({
1427
+ type: data.type,
1428
+ location: Location.from(data.location),
1429
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1430
+ comment_start: data.comment_start ? Token.from(data.comment_start) : null,
1431
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1432
+ comment_end: data.comment_end ? Token.from(data.comment_end) : null,
1433
+ });
1434
+ }
1435
+ constructor(props) {
1436
+ super(props.type, props.location, props.errors);
1437
+ this.comment_start = props.comment_start;
1438
+ this.children = props.children;
1439
+ this.comment_end = props.comment_end;
1440
+ }
1441
+ accept(visitor) {
1442
+ visitor.visitHTMLCommentNode(this);
1443
+ }
1444
+ childNodes() {
1445
+ return [
1446
+ ...this.children,
1447
+ ];
1448
+ }
1449
+ compactChildNodes() {
1450
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1451
+ }
1452
+ recursiveErrors() {
1453
+ return [
1454
+ ...this.errors,
1455
+ ...this.children.map(node => node.recursiveErrors()),
1456
+ ].flat();
1457
+ }
1458
+ toJSON() {
1459
+ return {
1460
+ ...super.toJSON(),
1461
+ type: "AST_HTML_COMMENT_NODE",
1462
+ comment_start: this.comment_start ? this.comment_start.toJSON() : null,
1463
+ children: this.children.map(node => node.toJSON()),
1464
+ comment_end: this.comment_end ? this.comment_end.toJSON() : null,
1465
+ };
1466
+ }
1467
+ treeInspect() {
1468
+ let output = "";
1469
+ output += `@ HTMLCommentNode ${this.location.treeInspectWithLabel()}\n`;
1470
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1471
+ output += `├── comment_start: ${this.comment_start ? this.comment_start.treeInspect() : "∅"}\n`;
1472
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1473
+ output += `└── comment_end: ${this.comment_end ? this.comment_end.treeInspect() : "∅"}\n`;
1474
+ return output;
1475
+ }
1476
+ }
1477
+ class HTMLDoctypeNode extends Node {
1478
+ tag_opening;
1479
+ children;
1480
+ tag_closing;
1481
+ static get type() {
1482
+ return "AST_HTML_DOCTYPE_NODE";
1483
+ }
1484
+ static from(data) {
1485
+ return new HTMLDoctypeNode({
1486
+ type: data.type,
1487
+ location: Location.from(data.location),
1488
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1489
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1490
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1491
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1492
+ });
1493
+ }
1494
+ constructor(props) {
1495
+ super(props.type, props.location, props.errors);
1496
+ this.tag_opening = props.tag_opening;
1497
+ this.children = props.children;
1498
+ this.tag_closing = props.tag_closing;
1499
+ }
1500
+ accept(visitor) {
1501
+ visitor.visitHTMLDoctypeNode(this);
1502
+ }
1503
+ childNodes() {
1504
+ return [
1505
+ ...this.children,
1506
+ ];
1507
+ }
1508
+ compactChildNodes() {
1509
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1510
+ }
1511
+ recursiveErrors() {
1512
+ return [
1513
+ ...this.errors,
1514
+ ...this.children.map(node => node.recursiveErrors()),
1515
+ ].flat();
1516
+ }
1517
+ toJSON() {
1518
+ return {
1519
+ ...super.toJSON(),
1520
+ type: "AST_HTML_DOCTYPE_NODE",
1521
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1522
+ children: this.children.map(node => node.toJSON()),
1523
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1524
+ };
1525
+ }
1526
+ treeInspect() {
1527
+ let output = "";
1528
+ output += `@ HTMLDoctypeNode ${this.location.treeInspectWithLabel()}\n`;
1529
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1530
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1531
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1532
+ output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1533
+ return output;
1534
+ }
1535
+ }
1536
+ class XMLDeclarationNode extends Node {
1537
+ tag_opening;
1538
+ children;
1539
+ tag_closing;
1540
+ static get type() {
1541
+ return "AST_XML_DECLARATION_NODE";
1542
+ }
1543
+ static from(data) {
1544
+ return new XMLDeclarationNode({
1545
+ type: data.type,
1546
+ location: Location.from(data.location),
1547
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1548
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1549
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1550
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1551
+ });
1552
+ }
1553
+ constructor(props) {
1554
+ super(props.type, props.location, props.errors);
1555
+ this.tag_opening = props.tag_opening;
1556
+ this.children = props.children;
1557
+ this.tag_closing = props.tag_closing;
1558
+ }
1559
+ accept(visitor) {
1560
+ visitor.visitXMLDeclarationNode(this);
1561
+ }
1562
+ childNodes() {
1563
+ return [
1564
+ ...this.children,
1565
+ ];
1566
+ }
1567
+ compactChildNodes() {
1568
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1569
+ }
1570
+ recursiveErrors() {
1571
+ return [
1572
+ ...this.errors,
1573
+ ...this.children.map(node => node.recursiveErrors()),
1574
+ ].flat();
1575
+ }
1576
+ toJSON() {
1577
+ return {
1578
+ ...super.toJSON(),
1579
+ type: "AST_XML_DECLARATION_NODE",
1580
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1581
+ children: this.children.map(node => node.toJSON()),
1582
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1583
+ };
1584
+ }
1585
+ treeInspect() {
1586
+ let output = "";
1587
+ output += `@ XMLDeclarationNode ${this.location.treeInspectWithLabel()}\n`;
1588
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1589
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1590
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1591
+ output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1592
+ return output;
1593
+ }
1594
+ }
1595
+ class CDATANode extends Node {
1596
+ tag_opening;
1597
+ children;
1598
+ tag_closing;
1599
+ static get type() {
1600
+ return "AST_CDATA_NODE";
1601
+ }
1602
+ static from(data) {
1603
+ return new CDATANode({
1604
+ type: data.type,
1605
+ location: Location.from(data.location),
1606
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1607
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1608
+ children: (data.children || []).map(node => fromSerializedNode(node)),
1609
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1610
+ });
1611
+ }
1612
+ constructor(props) {
1613
+ super(props.type, props.location, props.errors);
1614
+ this.tag_opening = props.tag_opening;
1615
+ this.children = props.children;
1616
+ this.tag_closing = props.tag_closing;
1617
+ }
1618
+ accept(visitor) {
1619
+ visitor.visitCDATANode(this);
1620
+ }
1621
+ childNodes() {
1622
+ return [
1623
+ ...this.children,
1624
+ ];
1625
+ }
1626
+ compactChildNodes() {
1627
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1628
+ }
1629
+ recursiveErrors() {
1630
+ return [
1631
+ ...this.errors,
1632
+ ...this.children.map(node => node.recursiveErrors()),
1633
+ ].flat();
1634
+ }
1635
+ toJSON() {
1636
+ return {
1637
+ ...super.toJSON(),
1638
+ type: "AST_CDATA_NODE",
1639
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1640
+ children: this.children.map(node => node.toJSON()),
1641
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1642
+ };
1643
+ }
1644
+ treeInspect() {
1645
+ let output = "";
1646
+ output += `@ CDATANode ${this.location.treeInspectWithLabel()}\n`;
1647
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1648
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1649
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
1650
+ output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1651
+ return output;
1652
+ }
1653
+ }
1654
+ class WhitespaceNode extends Node {
1655
+ value;
1656
+ static get type() {
1657
+ return "AST_WHITESPACE_NODE";
1658
+ }
1659
+ static from(data) {
1660
+ return new WhitespaceNode({
1661
+ type: data.type,
1662
+ location: Location.from(data.location),
1663
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1664
+ value: data.value ? Token.from(data.value) : null,
1665
+ });
1666
+ }
1667
+ constructor(props) {
1668
+ super(props.type, props.location, props.errors);
1669
+ this.value = props.value;
1670
+ }
1671
+ accept(visitor) {
1672
+ visitor.visitWhitespaceNode(this);
1673
+ }
1674
+ childNodes() {
1675
+ return [];
1676
+ }
1677
+ compactChildNodes() {
1678
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1679
+ }
1680
+ recursiveErrors() {
1681
+ return [
1682
+ ...this.errors,
1683
+ ].flat();
1684
+ }
1685
+ toJSON() {
1686
+ return {
1687
+ ...super.toJSON(),
1688
+ type: "AST_WHITESPACE_NODE",
1689
+ value: this.value ? this.value.toJSON() : null,
1690
+ };
1691
+ }
1692
+ treeInspect() {
1693
+ let output = "";
1694
+ output += `@ WhitespaceNode ${this.location.treeInspectWithLabel()}\n`;
1695
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1696
+ output += `└── value: ${this.value ? this.value.treeInspect() : "∅"}\n`;
1697
+ return output;
1698
+ }
1699
+ }
1700
+ class ERBContentNode extends Node {
1701
+ tag_opening;
1702
+ content;
1703
+ tag_closing;
1704
+ // no-op for analyzed_ruby
1705
+ parsed;
1706
+ valid;
1707
+ static get type() {
1708
+ return "AST_ERB_CONTENT_NODE";
1709
+ }
1710
+ static from(data) {
1711
+ return new ERBContentNode({
1712
+ type: data.type,
1713
+ location: Location.from(data.location),
1714
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1715
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1716
+ content: data.content ? Token.from(data.content) : null,
1717
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1718
+ // no-op for analyzed_ruby
1719
+ parsed: data.parsed,
1720
+ valid: data.valid,
1721
+ });
1722
+ }
1723
+ constructor(props) {
1724
+ super(props.type, props.location, props.errors);
1725
+ this.tag_opening = props.tag_opening;
1726
+ this.content = props.content;
1727
+ this.tag_closing = props.tag_closing;
1728
+ // no-op for analyzed_ruby
1729
+ this.parsed = props.parsed;
1730
+ this.valid = props.valid;
1731
+ }
1732
+ accept(visitor) {
1733
+ visitor.visitERBContentNode(this);
1734
+ }
1735
+ childNodes() {
1736
+ return [];
1737
+ }
1738
+ compactChildNodes() {
1739
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1740
+ }
1741
+ recursiveErrors() {
1742
+ return [
1743
+ ...this.errors,
1744
+ ].flat();
1745
+ }
1746
+ toJSON() {
1747
+ return {
1748
+ ...super.toJSON(),
1749
+ type: "AST_ERB_CONTENT_NODE",
1750
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1751
+ content: this.content ? this.content.toJSON() : null,
1752
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1753
+ // no-op for analyzed_ruby
1754
+ parsed: this.parsed,
1755
+ valid: this.valid,
1756
+ };
1757
+ }
1758
+ treeInspect() {
1759
+ let output = "";
1760
+ output += `@ ERBContentNode ${this.location.treeInspectWithLabel()}\n`;
1761
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1762
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1763
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1764
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1765
+ // no-op for analyzed_ruby
1766
+ output += `├── parsed: ${typeof this.parsed === 'boolean' ? String(this.parsed) : "∅"}\n`;
1767
+ output += `└── valid: ${typeof this.valid === 'boolean' ? String(this.valid) : "∅"}\n`;
1768
+ return output;
1769
+ }
1770
+ }
1771
+ class ERBEndNode extends Node {
1772
+ tag_opening;
1773
+ content;
1774
+ tag_closing;
1775
+ static get type() {
1776
+ return "AST_ERB_END_NODE";
1777
+ }
1778
+ static from(data) {
1779
+ return new ERBEndNode({
1780
+ type: data.type,
1781
+ location: Location.from(data.location),
1782
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1783
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1784
+ content: data.content ? Token.from(data.content) : null,
1785
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1786
+ });
1787
+ }
1788
+ constructor(props) {
1789
+ super(props.type, props.location, props.errors);
1790
+ this.tag_opening = props.tag_opening;
1791
+ this.content = props.content;
1792
+ this.tag_closing = props.tag_closing;
1793
+ }
1794
+ accept(visitor) {
1795
+ visitor.visitERBEndNode(this);
1796
+ }
1797
+ childNodes() {
1798
+ return [];
1799
+ }
1800
+ compactChildNodes() {
1801
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1802
+ }
1803
+ recursiveErrors() {
1804
+ return [
1805
+ ...this.errors,
1806
+ ].flat();
1807
+ }
1808
+ toJSON() {
1809
+ return {
1810
+ ...super.toJSON(),
1811
+ type: "AST_ERB_END_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
+ };
1816
+ }
1817
+ treeInspect() {
1818
+ let output = "";
1819
+ output += `@ ERBEndNode ${this.location.treeInspectWithLabel()}\n`;
1820
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1821
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1822
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1823
+ output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1824
+ return output;
1825
+ }
1826
+ }
1827
+ class ERBElseNode extends Node {
1828
+ tag_opening;
1829
+ content;
1830
+ tag_closing;
1831
+ statements;
1832
+ static get type() {
1833
+ return "AST_ERB_ELSE_NODE";
1834
+ }
1835
+ static from(data) {
1836
+ return new ERBElseNode({
1837
+ type: data.type,
1838
+ location: Location.from(data.location),
1839
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1840
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1841
+ content: data.content ? Token.from(data.content) : null,
1842
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1843
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
1844
+ });
1845
+ }
1846
+ constructor(props) {
1847
+ super(props.type, props.location, props.errors);
1848
+ this.tag_opening = props.tag_opening;
1849
+ this.content = props.content;
1850
+ this.tag_closing = props.tag_closing;
1851
+ this.statements = props.statements;
1852
+ }
1853
+ accept(visitor) {
1854
+ visitor.visitERBElseNode(this);
1855
+ }
1856
+ childNodes() {
1857
+ return [
1858
+ ...this.statements,
1859
+ ];
1860
+ }
1861
+ compactChildNodes() {
1862
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1863
+ }
1864
+ recursiveErrors() {
1865
+ return [
1866
+ ...this.errors,
1867
+ ...this.statements.map(node => node.recursiveErrors()),
1868
+ ].flat();
1869
+ }
1870
+ toJSON() {
1871
+ return {
1872
+ ...super.toJSON(),
1873
+ type: "AST_ERB_ELSE_NODE",
1874
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1875
+ content: this.content ? this.content.toJSON() : null,
1876
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1877
+ statements: this.statements.map(node => node.toJSON()),
1878
+ };
1879
+ }
1880
+ treeInspect() {
1881
+ let output = "";
1882
+ output += `@ ERBElseNode ${this.location.treeInspectWithLabel()}\n`;
1883
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1884
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1885
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1886
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1887
+ output += `└── statements: ${this.inspectArray(this.statements, " ")}`;
1888
+ return output;
1889
+ }
1890
+ }
1891
+ class ERBIfNode extends Node {
1892
+ tag_opening;
1893
+ content;
1894
+ tag_closing;
1895
+ statements;
1896
+ subsequent;
1897
+ end_node;
1898
+ static get type() {
1899
+ return "AST_ERB_IF_NODE";
1900
+ }
1901
+ static from(data) {
1902
+ return new ERBIfNode({
1903
+ type: data.type,
1904
+ location: Location.from(data.location),
1905
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1906
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1907
+ content: data.content ? Token.from(data.content) : null,
1908
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1909
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
1910
+ subsequent: data.subsequent ? fromSerializedNode((data.subsequent)) : null,
1911
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
1912
+ });
1913
+ }
1914
+ constructor(props) {
1915
+ super(props.type, props.location, props.errors);
1916
+ this.tag_opening = props.tag_opening;
1917
+ this.content = props.content;
1918
+ this.tag_closing = props.tag_closing;
1919
+ this.statements = props.statements;
1920
+ this.subsequent = props.subsequent;
1921
+ this.end_node = props.end_node;
1922
+ }
1923
+ accept(visitor) {
1924
+ visitor.visitERBIfNode(this);
1925
+ }
1926
+ childNodes() {
1927
+ return [
1928
+ ...this.statements,
1929
+ this.subsequent,
1930
+ this.end_node,
1931
+ ];
1932
+ }
1933
+ compactChildNodes() {
1934
+ return this.childNodes().filter(node => node !== null && node !== undefined);
1935
+ }
1936
+ recursiveErrors() {
1937
+ return [
1938
+ ...this.errors,
1939
+ ...this.statements.map(node => node.recursiveErrors()),
1940
+ this.subsequent ? this.subsequent.recursiveErrors() : [],
1941
+ this.end_node ? this.end_node.recursiveErrors() : [],
1942
+ ].flat();
1943
+ }
1944
+ toJSON() {
1945
+ return {
1946
+ ...super.toJSON(),
1947
+ type: "AST_ERB_IF_NODE",
1948
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
1949
+ content: this.content ? this.content.toJSON() : null,
1950
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
1951
+ statements: this.statements.map(node => node.toJSON()),
1952
+ subsequent: this.subsequent ? this.subsequent.toJSON() : null,
1953
+ end_node: this.end_node ? this.end_node.toJSON() : null,
1954
+ };
1955
+ }
1956
+ treeInspect() {
1957
+ let output = "";
1958
+ output += `@ ERBIfNode ${this.location.treeInspectWithLabel()}\n`;
1959
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
1960
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
1961
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
1962
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
1963
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
1964
+ output += `├── subsequent: ${this.inspectNode(this.subsequent, "│ ")}`;
1965
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
1966
+ return output;
1967
+ }
1968
+ }
1969
+ class ERBBlockNode extends Node {
1970
+ tag_opening;
1971
+ content;
1972
+ tag_closing;
1973
+ body;
1974
+ end_node;
1975
+ static get type() {
1976
+ return "AST_ERB_BLOCK_NODE";
1977
+ }
1978
+ static from(data) {
1979
+ return new ERBBlockNode({
1980
+ type: data.type,
1981
+ location: Location.from(data.location),
1982
+ errors: (data.errors || []).map(error => HerbError.from(error)),
1983
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
1984
+ content: data.content ? Token.from(data.content) : null,
1985
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
1986
+ body: (data.body || []).map(node => fromSerializedNode(node)),
1987
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
1988
+ });
1989
+ }
1990
+ constructor(props) {
1991
+ super(props.type, props.location, props.errors);
1992
+ this.tag_opening = props.tag_opening;
1993
+ this.content = props.content;
1994
+ this.tag_closing = props.tag_closing;
1995
+ this.body = props.body;
1996
+ this.end_node = props.end_node;
1997
+ }
1998
+ accept(visitor) {
1999
+ visitor.visitERBBlockNode(this);
2000
+ }
2001
+ childNodes() {
2002
+ return [
2003
+ ...this.body,
2004
+ this.end_node,
2005
+ ];
2006
+ }
2007
+ compactChildNodes() {
2008
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2009
+ }
2010
+ recursiveErrors() {
2011
+ return [
2012
+ ...this.errors,
2013
+ ...this.body.map(node => node.recursiveErrors()),
2014
+ this.end_node ? this.end_node.recursiveErrors() : [],
2015
+ ].flat();
2016
+ }
2017
+ toJSON() {
2018
+ return {
2019
+ ...super.toJSON(),
2020
+ type: "AST_ERB_BLOCK_NODE",
2021
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2022
+ content: this.content ? this.content.toJSON() : null,
2023
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2024
+ body: this.body.map(node => node.toJSON()),
2025
+ end_node: this.end_node ? this.end_node.toJSON() : null,
2026
+ };
2027
+ }
2028
+ treeInspect() {
2029
+ let output = "";
2030
+ output += `@ ERBBlockNode ${this.location.treeInspectWithLabel()}\n`;
2031
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2032
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2033
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2034
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2035
+ output += `├── body: ${this.inspectArray(this.body, "│ ")}`;
2036
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2037
+ return output;
2038
+ }
2039
+ }
2040
+ class ERBWhenNode extends Node {
2041
+ tag_opening;
2042
+ content;
2043
+ tag_closing;
2044
+ statements;
2045
+ static get type() {
2046
+ return "AST_ERB_WHEN_NODE";
2047
+ }
2048
+ static from(data) {
2049
+ return new ERBWhenNode({
2050
+ type: data.type,
2051
+ location: Location.from(data.location),
2052
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2053
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2054
+ content: data.content ? Token.from(data.content) : null,
2055
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2056
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2057
+ });
2058
+ }
2059
+ constructor(props) {
2060
+ super(props.type, props.location, props.errors);
2061
+ this.tag_opening = props.tag_opening;
2062
+ this.content = props.content;
2063
+ this.tag_closing = props.tag_closing;
2064
+ this.statements = props.statements;
2065
+ }
2066
+ accept(visitor) {
2067
+ visitor.visitERBWhenNode(this);
2068
+ }
2069
+ childNodes() {
2070
+ return [
2071
+ ...this.statements,
2072
+ ];
2073
+ }
2074
+ compactChildNodes() {
2075
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2076
+ }
2077
+ recursiveErrors() {
2078
+ return [
2079
+ ...this.errors,
2080
+ ...this.statements.map(node => node.recursiveErrors()),
2081
+ ].flat();
2082
+ }
2083
+ toJSON() {
2084
+ return {
2085
+ ...super.toJSON(),
2086
+ type: "AST_ERB_WHEN_NODE",
2087
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2088
+ content: this.content ? this.content.toJSON() : null,
2089
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2090
+ statements: this.statements.map(node => node.toJSON()),
2091
+ };
2092
+ }
2093
+ treeInspect() {
2094
+ let output = "";
2095
+ output += `@ ERBWhenNode ${this.location.treeInspectWithLabel()}\n`;
2096
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2097
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2098
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2099
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2100
+ output += `└── statements: ${this.inspectArray(this.statements, " ")}`;
2101
+ return output;
2102
+ }
2103
+ }
2104
+ class ERBCaseNode extends Node {
2105
+ tag_opening;
2106
+ content;
2107
+ tag_closing;
2108
+ children;
2109
+ conditions;
2110
+ else_clause;
2111
+ end_node;
2112
+ static get type() {
2113
+ return "AST_ERB_CASE_NODE";
2114
+ }
2115
+ static from(data) {
2116
+ return new ERBCaseNode({
2117
+ type: data.type,
2118
+ location: Location.from(data.location),
2119
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2120
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2121
+ content: data.content ? Token.from(data.content) : null,
2122
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2123
+ children: (data.children || []).map(node => fromSerializedNode(node)),
2124
+ conditions: (data.conditions || []).map(node => fromSerializedNode(node)),
2125
+ else_clause: data.else_clause ? fromSerializedNode((data.else_clause)) : null,
2126
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
2127
+ });
2128
+ }
2129
+ constructor(props) {
2130
+ super(props.type, props.location, props.errors);
2131
+ this.tag_opening = props.tag_opening;
2132
+ this.content = props.content;
2133
+ this.tag_closing = props.tag_closing;
2134
+ this.children = props.children;
2135
+ this.conditions = props.conditions;
2136
+ this.else_clause = props.else_clause;
2137
+ this.end_node = props.end_node;
2138
+ }
2139
+ accept(visitor) {
2140
+ visitor.visitERBCaseNode(this);
2141
+ }
2142
+ childNodes() {
2143
+ return [
2144
+ ...this.children,
2145
+ ...this.conditions,
2146
+ this.else_clause,
2147
+ this.end_node,
2148
+ ];
2149
+ }
2150
+ compactChildNodes() {
2151
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2152
+ }
2153
+ recursiveErrors() {
2154
+ return [
2155
+ ...this.errors,
2156
+ ...this.children.map(node => node.recursiveErrors()),
2157
+ ...this.conditions.map(node => node.recursiveErrors()),
2158
+ this.else_clause ? this.else_clause.recursiveErrors() : [],
2159
+ this.end_node ? this.end_node.recursiveErrors() : [],
2160
+ ].flat();
2161
+ }
2162
+ toJSON() {
2163
+ return {
2164
+ ...super.toJSON(),
2165
+ type: "AST_ERB_CASE_NODE",
2166
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2167
+ content: this.content ? this.content.toJSON() : null,
2168
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2169
+ children: this.children.map(node => node.toJSON()),
2170
+ conditions: this.conditions.map(node => node.toJSON()),
2171
+ else_clause: this.else_clause ? this.else_clause.toJSON() : null,
2172
+ end_node: this.end_node ? this.end_node.toJSON() : null,
2173
+ };
2174
+ }
2175
+ treeInspect() {
2176
+ let output = "";
2177
+ output += `@ ERBCaseNode ${this.location.treeInspectWithLabel()}\n`;
2178
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2179
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2180
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2181
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2182
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
2183
+ output += `├── conditions: ${this.inspectArray(this.conditions, "│ ")}`;
2184
+ output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
2185
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2186
+ return output;
2187
+ }
2188
+ }
2189
+ class ERBCaseMatchNode extends Node {
2190
+ tag_opening;
2191
+ content;
2192
+ tag_closing;
2193
+ children;
2194
+ conditions;
2195
+ else_clause;
2196
+ end_node;
2197
+ static get type() {
2198
+ return "AST_ERB_CASE_MATCH_NODE";
2199
+ }
2200
+ static from(data) {
2201
+ return new ERBCaseMatchNode({
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
+ children: (data.children || []).map(node => fromSerializedNode(node)),
2209
+ conditions: (data.conditions || []).map(node => fromSerializedNode(node)),
2210
+ else_clause: data.else_clause ? fromSerializedNode((data.else_clause)) : null,
2211
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
2212
+ });
2213
+ }
2214
+ constructor(props) {
2215
+ super(props.type, props.location, props.errors);
2216
+ this.tag_opening = props.tag_opening;
2217
+ this.content = props.content;
2218
+ this.tag_closing = props.tag_closing;
2219
+ this.children = props.children;
2220
+ this.conditions = props.conditions;
2221
+ this.else_clause = props.else_clause;
2222
+ this.end_node = props.end_node;
2223
+ }
2224
+ accept(visitor) {
2225
+ visitor.visitERBCaseMatchNode(this);
2226
+ }
2227
+ childNodes() {
2228
+ return [
2229
+ ...this.children,
2230
+ ...this.conditions,
2231
+ this.else_clause,
2232
+ this.end_node,
2233
+ ];
2234
+ }
2235
+ compactChildNodes() {
2236
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2237
+ }
2238
+ recursiveErrors() {
2239
+ return [
2240
+ ...this.errors,
2241
+ ...this.children.map(node => node.recursiveErrors()),
2242
+ ...this.conditions.map(node => node.recursiveErrors()),
2243
+ this.else_clause ? this.else_clause.recursiveErrors() : [],
2244
+ this.end_node ? this.end_node.recursiveErrors() : [],
2245
+ ].flat();
2246
+ }
2247
+ toJSON() {
2248
+ return {
2249
+ ...super.toJSON(),
2250
+ type: "AST_ERB_CASE_MATCH_NODE",
2251
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2252
+ content: this.content ? this.content.toJSON() : null,
2253
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2254
+ children: this.children.map(node => node.toJSON()),
2255
+ conditions: this.conditions.map(node => node.toJSON()),
2256
+ else_clause: this.else_clause ? this.else_clause.toJSON() : null,
2257
+ end_node: this.end_node ? this.end_node.toJSON() : null,
2258
+ };
2259
+ }
2260
+ treeInspect() {
2261
+ let output = "";
2262
+ output += `@ ERBCaseMatchNode ${this.location.treeInspectWithLabel()}\n`;
2263
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2264
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2265
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2266
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2267
+ output += `├── children: ${this.inspectArray(this.children, "│ ")}`;
2268
+ output += `├── conditions: ${this.inspectArray(this.conditions, "│ ")}`;
2269
+ output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
2270
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2271
+ return output;
2272
+ }
2273
+ }
2274
+ class ERBWhileNode extends Node {
2275
+ tag_opening;
2276
+ content;
2277
+ tag_closing;
2278
+ statements;
2279
+ end_node;
2280
+ static get type() {
2281
+ return "AST_ERB_WHILE_NODE";
2282
+ }
2283
+ static from(data) {
2284
+ return new ERBWhileNode({
2285
+ type: data.type,
2286
+ location: Location.from(data.location),
2287
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2288
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2289
+ content: data.content ? Token.from(data.content) : null,
2290
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2291
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2292
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
2293
+ });
2294
+ }
2295
+ constructor(props) {
2296
+ super(props.type, props.location, props.errors);
2297
+ this.tag_opening = props.tag_opening;
2298
+ this.content = props.content;
2299
+ this.tag_closing = props.tag_closing;
2300
+ this.statements = props.statements;
2301
+ this.end_node = props.end_node;
2302
+ }
2303
+ accept(visitor) {
2304
+ visitor.visitERBWhileNode(this);
2305
+ }
2306
+ childNodes() {
2307
+ return [
2308
+ ...this.statements,
2309
+ this.end_node,
2310
+ ];
2311
+ }
2312
+ compactChildNodes() {
2313
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2314
+ }
2315
+ recursiveErrors() {
2316
+ return [
2317
+ ...this.errors,
2318
+ ...this.statements.map(node => node.recursiveErrors()),
2319
+ this.end_node ? this.end_node.recursiveErrors() : [],
2320
+ ].flat();
2321
+ }
2322
+ toJSON() {
2323
+ return {
2324
+ ...super.toJSON(),
2325
+ type: "AST_ERB_WHILE_NODE",
2326
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2327
+ content: this.content ? this.content.toJSON() : null,
2328
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2329
+ statements: this.statements.map(node => node.toJSON()),
2330
+ end_node: this.end_node ? this.end_node.toJSON() : null,
2331
+ };
2332
+ }
2333
+ treeInspect() {
2334
+ let output = "";
2335
+ output += `@ ERBWhileNode ${this.location.treeInspectWithLabel()}\n`;
2336
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2337
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2338
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2339
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2340
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2341
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2342
+ return output;
2343
+ }
2344
+ }
2345
+ class ERBUntilNode extends Node {
2346
+ tag_opening;
2347
+ content;
2348
+ tag_closing;
2349
+ statements;
2350
+ end_node;
2351
+ static get type() {
2352
+ return "AST_ERB_UNTIL_NODE";
2353
+ }
2354
+ static from(data) {
2355
+ return new ERBUntilNode({
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
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
2364
+ });
2365
+ }
2366
+ constructor(props) {
2367
+ super(props.type, props.location, props.errors);
2368
+ this.tag_opening = props.tag_opening;
2369
+ this.content = props.content;
2370
+ this.tag_closing = props.tag_closing;
2371
+ this.statements = props.statements;
2372
+ this.end_node = props.end_node;
2373
+ }
2374
+ accept(visitor) {
2375
+ visitor.visitERBUntilNode(this);
2376
+ }
2377
+ childNodes() {
2378
+ return [
2379
+ ...this.statements,
2380
+ this.end_node,
2381
+ ];
2382
+ }
2383
+ compactChildNodes() {
2384
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2385
+ }
2386
+ recursiveErrors() {
2387
+ return [
2388
+ ...this.errors,
2389
+ ...this.statements.map(node => node.recursiveErrors()),
2390
+ this.end_node ? this.end_node.recursiveErrors() : [],
2391
+ ].flat();
2392
+ }
2393
+ toJSON() {
2394
+ return {
2395
+ ...super.toJSON(),
2396
+ type: "AST_ERB_UNTIL_NODE",
2397
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2398
+ content: this.content ? this.content.toJSON() : null,
2399
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2400
+ statements: this.statements.map(node => node.toJSON()),
2401
+ end_node: this.end_node ? this.end_node.toJSON() : null,
2402
+ };
2403
+ }
2404
+ treeInspect() {
2405
+ let output = "";
2406
+ output += `@ ERBUntilNode ${this.location.treeInspectWithLabel()}\n`;
2407
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2408
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2409
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2410
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2411
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2412
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2413
+ return output;
2414
+ }
2415
+ }
2416
+ class ERBForNode extends Node {
2417
+ tag_opening;
2418
+ content;
2419
+ tag_closing;
2420
+ statements;
2421
+ end_node;
2422
+ static get type() {
2423
+ return "AST_ERB_FOR_NODE";
2424
+ }
2425
+ static from(data) {
2426
+ return new ERBForNode({
2427
+ type: data.type,
2428
+ location: Location.from(data.location),
2429
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2430
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2431
+ content: data.content ? Token.from(data.content) : null,
2432
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2433
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2434
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : 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
+ this.statements = props.statements;
2443
+ this.end_node = props.end_node;
2444
+ }
2445
+ accept(visitor) {
2446
+ visitor.visitERBForNode(this);
2447
+ }
2448
+ childNodes() {
2449
+ return [
2450
+ ...this.statements,
2451
+ this.end_node,
2452
+ ];
2453
+ }
2454
+ compactChildNodes() {
2455
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2456
+ }
2457
+ recursiveErrors() {
2458
+ return [
2459
+ ...this.errors,
2460
+ ...this.statements.map(node => node.recursiveErrors()),
2461
+ this.end_node ? this.end_node.recursiveErrors() : [],
2462
+ ].flat();
2463
+ }
2464
+ toJSON() {
2465
+ return {
2466
+ ...super.toJSON(),
2467
+ type: "AST_ERB_FOR_NODE",
2468
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2469
+ content: this.content ? this.content.toJSON() : null,
2470
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2471
+ statements: this.statements.map(node => node.toJSON()),
2472
+ end_node: this.end_node ? this.end_node.toJSON() : null,
2473
+ };
2474
+ }
2475
+ treeInspect() {
2476
+ let output = "";
2477
+ output += `@ ERBForNode ${this.location.treeInspectWithLabel()}\n`;
2478
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2479
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2480
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2481
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2482
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2483
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2484
+ return output;
2485
+ }
2486
+ }
2487
+ class ERBRescueNode extends Node {
2488
+ tag_opening;
2489
+ content;
2490
+ tag_closing;
2491
+ statements;
2492
+ subsequent;
2493
+ static get type() {
2494
+ return "AST_ERB_RESCUE_NODE";
2495
+ }
2496
+ static from(data) {
2497
+ return new ERBRescueNode({
2498
+ type: data.type,
2499
+ location: Location.from(data.location),
2500
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2501
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2502
+ content: data.content ? Token.from(data.content) : null,
2503
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2504
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2505
+ subsequent: data.subsequent ? fromSerializedNode((data.subsequent)) : null,
2506
+ });
2507
+ }
2508
+ constructor(props) {
2509
+ super(props.type, props.location, props.errors);
2510
+ this.tag_opening = props.tag_opening;
2511
+ this.content = props.content;
2512
+ this.tag_closing = props.tag_closing;
2513
+ this.statements = props.statements;
2514
+ this.subsequent = props.subsequent;
2515
+ }
2516
+ accept(visitor) {
2517
+ visitor.visitERBRescueNode(this);
2518
+ }
2519
+ childNodes() {
2520
+ return [
2521
+ ...this.statements,
2522
+ this.subsequent,
2523
+ ];
2524
+ }
2525
+ compactChildNodes() {
2526
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2527
+ }
2528
+ recursiveErrors() {
2529
+ return [
2530
+ ...this.errors,
2531
+ ...this.statements.map(node => node.recursiveErrors()),
2532
+ this.subsequent ? this.subsequent.recursiveErrors() : [],
2533
+ ].flat();
2534
+ }
2535
+ toJSON() {
2536
+ return {
2537
+ ...super.toJSON(),
2538
+ type: "AST_ERB_RESCUE_NODE",
2539
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2540
+ content: this.content ? this.content.toJSON() : null,
2541
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2542
+ statements: this.statements.map(node => node.toJSON()),
2543
+ subsequent: this.subsequent ? this.subsequent.toJSON() : null,
2544
+ };
2545
+ }
2546
+ treeInspect() {
2547
+ let output = "";
2548
+ output += `@ ERBRescueNode ${this.location.treeInspectWithLabel()}\n`;
2549
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2550
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2551
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2552
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2553
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2554
+ output += `└── subsequent: ${this.inspectNode(this.subsequent, " ")}`;
2555
+ return output;
2556
+ }
2557
+ }
2558
+ class ERBEnsureNode extends Node {
2559
+ tag_opening;
2560
+ content;
2561
+ tag_closing;
2562
+ statements;
2563
+ static get type() {
2564
+ return "AST_ERB_ENSURE_NODE";
2565
+ }
2566
+ static from(data) {
2567
+ return new ERBEnsureNode({
2568
+ type: data.type,
2569
+ location: Location.from(data.location),
2570
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2571
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2572
+ content: data.content ? Token.from(data.content) : null,
2573
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2574
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2575
+ });
2576
+ }
2577
+ constructor(props) {
2578
+ super(props.type, props.location, props.errors);
2579
+ this.tag_opening = props.tag_opening;
2580
+ this.content = props.content;
2581
+ this.tag_closing = props.tag_closing;
2582
+ this.statements = props.statements;
2583
+ }
2584
+ accept(visitor) {
2585
+ visitor.visitERBEnsureNode(this);
2586
+ }
2587
+ childNodes() {
2588
+ return [
2589
+ ...this.statements,
2590
+ ];
2591
+ }
2592
+ compactChildNodes() {
2593
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2594
+ }
2595
+ recursiveErrors() {
2596
+ return [
2597
+ ...this.errors,
2598
+ ...this.statements.map(node => node.recursiveErrors()),
2599
+ ].flat();
2600
+ }
2601
+ toJSON() {
2602
+ return {
2603
+ ...super.toJSON(),
2604
+ type: "AST_ERB_ENSURE_NODE",
2605
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2606
+ content: this.content ? this.content.toJSON() : null,
2607
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2608
+ statements: this.statements.map(node => node.toJSON()),
2609
+ };
2610
+ }
2611
+ treeInspect() {
2612
+ let output = "";
2613
+ output += `@ ERBEnsureNode ${this.location.treeInspectWithLabel()}\n`;
2614
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2615
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2616
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2617
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2618
+ output += `└── statements: ${this.inspectArray(this.statements, " ")}`;
2619
+ return output;
2620
+ }
2621
+ }
2622
+ class ERBBeginNode extends Node {
2623
+ tag_opening;
2624
+ content;
2625
+ tag_closing;
2626
+ statements;
2627
+ rescue_clause;
2628
+ else_clause;
2629
+ ensure_clause;
2630
+ end_node;
2631
+ static get type() {
2632
+ return "AST_ERB_BEGIN_NODE";
2633
+ }
2634
+ static from(data) {
2635
+ return new ERBBeginNode({
2636
+ type: data.type,
2637
+ location: Location.from(data.location),
2638
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2639
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2640
+ content: data.content ? Token.from(data.content) : null,
2641
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2642
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2643
+ rescue_clause: data.rescue_clause ? fromSerializedNode((data.rescue_clause)) : null,
2644
+ else_clause: data.else_clause ? fromSerializedNode((data.else_clause)) : null,
2645
+ ensure_clause: data.ensure_clause ? fromSerializedNode((data.ensure_clause)) : null,
2646
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
2647
+ });
2648
+ }
2649
+ constructor(props) {
2650
+ super(props.type, props.location, props.errors);
2651
+ this.tag_opening = props.tag_opening;
2652
+ this.content = props.content;
2653
+ this.tag_closing = props.tag_closing;
2654
+ this.statements = props.statements;
2655
+ this.rescue_clause = props.rescue_clause;
2656
+ this.else_clause = props.else_clause;
2657
+ this.ensure_clause = props.ensure_clause;
2658
+ this.end_node = props.end_node;
2659
+ }
2660
+ accept(visitor) {
2661
+ visitor.visitERBBeginNode(this);
2662
+ }
2663
+ childNodes() {
2664
+ return [
2665
+ ...this.statements,
2666
+ this.rescue_clause,
2667
+ this.else_clause,
2668
+ this.ensure_clause,
2669
+ this.end_node,
2670
+ ];
2671
+ }
2672
+ compactChildNodes() {
2673
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2674
+ }
2675
+ recursiveErrors() {
2676
+ return [
2677
+ ...this.errors,
2678
+ ...this.statements.map(node => node.recursiveErrors()),
2679
+ this.rescue_clause ? this.rescue_clause.recursiveErrors() : [],
2680
+ this.else_clause ? this.else_clause.recursiveErrors() : [],
2681
+ this.ensure_clause ? this.ensure_clause.recursiveErrors() : [],
2682
+ this.end_node ? this.end_node.recursiveErrors() : [],
2683
+ ].flat();
2684
+ }
2685
+ toJSON() {
2686
+ return {
2687
+ ...super.toJSON(),
2688
+ type: "AST_ERB_BEGIN_NODE",
2689
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2690
+ content: this.content ? this.content.toJSON() : null,
2691
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2692
+ statements: this.statements.map(node => node.toJSON()),
2693
+ rescue_clause: this.rescue_clause ? this.rescue_clause.toJSON() : null,
2694
+ else_clause: this.else_clause ? this.else_clause.toJSON() : null,
2695
+ ensure_clause: this.ensure_clause ? this.ensure_clause.toJSON() : null,
2696
+ end_node: this.end_node ? this.end_node.toJSON() : null,
2697
+ };
2698
+ }
2699
+ treeInspect() {
2700
+ let output = "";
2701
+ output += `@ ERBBeginNode ${this.location.treeInspectWithLabel()}\n`;
2702
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2703
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2704
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2705
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2706
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2707
+ output += `├── rescue_clause: ${this.inspectNode(this.rescue_clause, "│ ")}`;
2708
+ output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
2709
+ output += `├── ensure_clause: ${this.inspectNode(this.ensure_clause, "│ ")}`;
2710
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2711
+ return output;
2712
+ }
2713
+ }
2714
+ class ERBUnlessNode extends Node {
2715
+ tag_opening;
2716
+ content;
2717
+ tag_closing;
2718
+ statements;
2719
+ else_clause;
2720
+ end_node;
2721
+ static get type() {
2722
+ return "AST_ERB_UNLESS_NODE";
2723
+ }
2724
+ static from(data) {
2725
+ return new ERBUnlessNode({
2726
+ type: data.type,
2727
+ location: Location.from(data.location),
2728
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2729
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2730
+ content: data.content ? Token.from(data.content) : null,
2731
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2732
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2733
+ else_clause: data.else_clause ? fromSerializedNode((data.else_clause)) : null,
2734
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
2735
+ });
2736
+ }
2737
+ constructor(props) {
2738
+ super(props.type, props.location, props.errors);
2739
+ this.tag_opening = props.tag_opening;
2740
+ this.content = props.content;
2741
+ this.tag_closing = props.tag_closing;
2742
+ this.statements = props.statements;
2743
+ this.else_clause = props.else_clause;
2744
+ this.end_node = props.end_node;
2745
+ }
2746
+ accept(visitor) {
2747
+ visitor.visitERBUnlessNode(this);
2748
+ }
2749
+ childNodes() {
2750
+ return [
2751
+ ...this.statements,
2752
+ this.else_clause,
2753
+ this.end_node,
2754
+ ];
2755
+ }
2756
+ compactChildNodes() {
2757
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2758
+ }
2759
+ recursiveErrors() {
2760
+ return [
2761
+ ...this.errors,
2762
+ ...this.statements.map(node => node.recursiveErrors()),
2763
+ this.else_clause ? this.else_clause.recursiveErrors() : [],
2764
+ this.end_node ? this.end_node.recursiveErrors() : [],
2765
+ ].flat();
2766
+ }
2767
+ toJSON() {
2768
+ return {
2769
+ ...super.toJSON(),
2770
+ type: "AST_ERB_UNLESS_NODE",
2771
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2772
+ content: this.content ? this.content.toJSON() : null,
2773
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2774
+ statements: this.statements.map(node => node.toJSON()),
2775
+ else_clause: this.else_clause ? this.else_clause.toJSON() : null,
2776
+ end_node: this.end_node ? this.end_node.toJSON() : null,
2777
+ };
2778
+ }
2779
+ treeInspect() {
2780
+ let output = "";
2781
+ output += `@ ERBUnlessNode ${this.location.treeInspectWithLabel()}\n`;
2782
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2783
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2784
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2785
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2786
+ output += `├── statements: ${this.inspectArray(this.statements, "│ ")}`;
2787
+ output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
2788
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
2789
+ return output;
2790
+ }
2791
+ }
2792
+ class ERBYieldNode extends Node {
2793
+ tag_opening;
2794
+ content;
2795
+ tag_closing;
2796
+ static get type() {
2797
+ return "AST_ERB_YIELD_NODE";
2798
+ }
2799
+ static from(data) {
2800
+ return new ERBYieldNode({
2801
+ type: data.type,
2802
+ location: Location.from(data.location),
2803
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2804
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2805
+ content: data.content ? Token.from(data.content) : null,
2806
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2807
+ });
2808
+ }
2809
+ constructor(props) {
2810
+ super(props.type, props.location, props.errors);
2811
+ this.tag_opening = props.tag_opening;
2812
+ this.content = props.content;
2813
+ this.tag_closing = props.tag_closing;
2814
+ }
2815
+ accept(visitor) {
2816
+ visitor.visitERBYieldNode(this);
2817
+ }
2818
+ childNodes() {
2819
+ return [];
2820
+ }
2821
+ compactChildNodes() {
2822
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2823
+ }
2824
+ recursiveErrors() {
2825
+ return [
2826
+ ...this.errors,
2827
+ ].flat();
2828
+ }
2829
+ toJSON() {
2830
+ return {
2831
+ ...super.toJSON(),
2832
+ type: "AST_ERB_YIELD_NODE",
2833
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2834
+ content: this.content ? this.content.toJSON() : null,
2835
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2836
+ };
2837
+ }
2838
+ treeInspect() {
2839
+ let output = "";
2840
+ output += `@ ERBYieldNode ${this.location.treeInspectWithLabel()}\n`;
2841
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2842
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2843
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2844
+ output += `└── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2845
+ return output;
2846
+ }
2847
+ }
2848
+ class ERBInNode extends Node {
2849
+ tag_opening;
2850
+ content;
2851
+ tag_closing;
2852
+ statements;
2853
+ static get type() {
2854
+ return "AST_ERB_IN_NODE";
2855
+ }
2856
+ static from(data) {
2857
+ return new ERBInNode({
2858
+ type: data.type,
2859
+ location: Location.from(data.location),
2860
+ errors: (data.errors || []).map(error => HerbError.from(error)),
2861
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
2862
+ content: data.content ? Token.from(data.content) : null,
2863
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
2864
+ statements: (data.statements || []).map(node => fromSerializedNode(node)),
2865
+ });
2866
+ }
2867
+ constructor(props) {
2868
+ super(props.type, props.location, props.errors);
2869
+ this.tag_opening = props.tag_opening;
2870
+ this.content = props.content;
2871
+ this.tag_closing = props.tag_closing;
2872
+ this.statements = props.statements;
2873
+ }
2874
+ accept(visitor) {
2875
+ visitor.visitERBInNode(this);
2876
+ }
2877
+ childNodes() {
2878
+ return [
2879
+ ...this.statements,
2880
+ ];
2881
+ }
2882
+ compactChildNodes() {
2883
+ return this.childNodes().filter(node => node !== null && node !== undefined);
2884
+ }
2885
+ recursiveErrors() {
2886
+ return [
2887
+ ...this.errors,
2888
+ ...this.statements.map(node => node.recursiveErrors()),
2889
+ ].flat();
2890
+ }
2891
+ toJSON() {
2892
+ return {
2893
+ ...super.toJSON(),
2894
+ type: "AST_ERB_IN_NODE",
2895
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
2896
+ content: this.content ? this.content.toJSON() : null,
2897
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
2898
+ statements: this.statements.map(node => node.toJSON()),
2899
+ };
2900
+ }
2901
+ treeInspect() {
2902
+ let output = "";
2903
+ output += `@ ERBInNode ${this.location.treeInspectWithLabel()}\n`;
2904
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
2905
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
2906
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
2907
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
2908
+ output += `└── statements: ${this.inspectArray(this.statements, " ")}`;
2909
+ return output;
2910
+ }
2911
+ }
2912
+ function fromSerializedNode(node) {
2913
+ switch (node.type) {
2914
+ case "AST_DOCUMENT_NODE": return DocumentNode.from(node);
2915
+ case "AST_LITERAL_NODE": return LiteralNode.from(node);
2916
+ case "AST_HTML_OPEN_TAG_NODE": return HTMLOpenTagNode.from(node);
2917
+ case "AST_HTML_CLOSE_TAG_NODE": return HTMLCloseTagNode.from(node);
2918
+ case "AST_HTML_ELEMENT_NODE": return HTMLElementNode.from(node);
2919
+ case "AST_HTML_ATTRIBUTE_VALUE_NODE": return HTMLAttributeValueNode.from(node);
2920
+ case "AST_HTML_ATTRIBUTE_NAME_NODE": return HTMLAttributeNameNode.from(node);
2921
+ case "AST_HTML_ATTRIBUTE_NODE": return HTMLAttributeNode.from(node);
2922
+ case "AST_HTML_TEXT_NODE": return HTMLTextNode.from(node);
2923
+ case "AST_HTML_COMMENT_NODE": return HTMLCommentNode.from(node);
2924
+ case "AST_HTML_DOCTYPE_NODE": return HTMLDoctypeNode.from(node);
2925
+ case "AST_XML_DECLARATION_NODE": return XMLDeclarationNode.from(node);
2926
+ case "AST_CDATA_NODE": return CDATANode.from(node);
2927
+ case "AST_WHITESPACE_NODE": return WhitespaceNode.from(node);
2928
+ case "AST_ERB_CONTENT_NODE": return ERBContentNode.from(node);
2929
+ case "AST_ERB_END_NODE": return ERBEndNode.from(node);
2930
+ case "AST_ERB_ELSE_NODE": return ERBElseNode.from(node);
2931
+ case "AST_ERB_IF_NODE": return ERBIfNode.from(node);
2932
+ case "AST_ERB_BLOCK_NODE": return ERBBlockNode.from(node);
2933
+ case "AST_ERB_WHEN_NODE": return ERBWhenNode.from(node);
2934
+ case "AST_ERB_CASE_NODE": return ERBCaseNode.from(node);
2935
+ case "AST_ERB_CASE_MATCH_NODE": return ERBCaseMatchNode.from(node);
2936
+ case "AST_ERB_WHILE_NODE": return ERBWhileNode.from(node);
2937
+ case "AST_ERB_UNTIL_NODE": return ERBUntilNode.from(node);
2938
+ case "AST_ERB_FOR_NODE": return ERBForNode.from(node);
2939
+ case "AST_ERB_RESCUE_NODE": return ERBRescueNode.from(node);
2940
+ case "AST_ERB_ENSURE_NODE": return ERBEnsureNode.from(node);
2941
+ case "AST_ERB_BEGIN_NODE": return ERBBeginNode.from(node);
2942
+ case "AST_ERB_UNLESS_NODE": return ERBUnlessNode.from(node);
2943
+ case "AST_ERB_YIELD_NODE": return ERBYieldNode.from(node);
2944
+ case "AST_ERB_IN_NODE": return ERBInNode.from(node);
2945
+ default:
2946
+ throw new Error(`Unknown node type: ${node.type}`);
2947
+ }
2948
+ }
2949
+
2950
+ class Result {
2951
+ source;
2952
+ warnings;
2953
+ errors;
2954
+ constructor(source, warnings = [], errors = []) {
2955
+ this.source = source;
2956
+ this.warnings = warnings || [];
2957
+ this.errors = errors || [];
2958
+ }
2959
+ /**
2960
+ * Determines if the parsing was successful.
2961
+ * @returns `true` if there are no errors, otherwise `false`.
2962
+ */
2963
+ get successful() {
2964
+ return this.errors.length === 0;
2965
+ }
2966
+ /**
2967
+ * Determines if the parsing failed.
2968
+ * @returns `true` if there are errors, otherwise `false`.
2969
+ */
2970
+ get failed() {
2971
+ return this.errors.length > 0;
2972
+ }
2973
+ }
2974
+
2975
+ class HerbWarning {
2976
+ message;
2977
+ location;
2978
+ static from(warning) {
2979
+ return new HerbWarning(warning.message, Location.from(warning.location));
2980
+ }
2981
+ constructor(message, location) {
2982
+ this.message = message;
2983
+ this.location = location;
2984
+ }
2985
+ }
2986
+
2987
+ /**
2988
+ * Represents the result of a parsing operation, extending the base `Result` class.
2989
+ * It contains the parsed document node, source code, warnings, and errors.
2990
+ */
2991
+ class ParseResult extends Result {
2992
+ /** The document node generated from the source code. */
2993
+ value;
2994
+ /**
2995
+ * Creates a `ParseResult` instance from a serialized result.
2996
+ * @param result - The serialized parse result containing the value and source.
2997
+ * @returns A new `ParseResult` instance.
2998
+ */
2999
+ static from(result) {
3000
+ return new ParseResult(DocumentNode.from(result.value), result.source, result.warnings.map((warning) => HerbWarning.from(warning)), result.errors.map((error) => HerbError.from(error)));
3001
+ }
3002
+ /**
3003
+ * Constructs a new `ParseResult`.
3004
+ * @param value - The document node.
3005
+ * @param source - The source code that was parsed.
3006
+ * @param warnings - An array of warnings encountered during parsing.
3007
+ * @param errors - An array of errors encountered during parsing.
3008
+ */
3009
+ constructor(value, source, warnings = [], errors = []) {
3010
+ super(source, warnings, errors);
3011
+ this.value = value;
3012
+ }
3013
+ /**
3014
+ * Determines if the parsing failed.
3015
+ * @returns `true` if there are errors, otherwise `false`.
3016
+ */
3017
+ get failed() {
3018
+ // Consider errors on this result and recursively in the document tree
3019
+ return this.recursiveErrors().length > 0;
3020
+ }
3021
+ /**
3022
+ * Determines if the parsing was successful.
3023
+ * @returns `true` if there are no errors, otherwise `false`.
3024
+ */
3025
+ get successful() {
3026
+ return !this.failed;
3027
+ }
3028
+ /**
3029
+ * Returns a pretty-printed JSON string of the errors.
3030
+ * @returns A string representation of the errors.
3031
+ */
3032
+ prettyErrors() {
3033
+ return JSON.stringify([...this.errors, ...this.value.errors], null, 2);
3034
+ }
3035
+ recursiveErrors() {
3036
+ return [...this.errors, ...this.value.recursiveErrors()];
3037
+ }
3038
+ /**
3039
+ * Returns a pretty-printed string of the parse result.
3040
+ * @returns A string representation of the parse result.
3041
+ */
3042
+ inspect() {
3043
+ return this.value.inspect();
3044
+ }
3045
+ /**
3046
+ * Accepts a visitor to traverse the document node.
3047
+ * @param visitor - The visitor instance.
3048
+ */
3049
+ visit(visitor) {
3050
+ visitor.visit(this.value);
3051
+ }
3052
+ }
3053
+ function isToken(object) {
3054
+ return (object instanceof Token) || (object?.constructor?.name === "Token" && "value" in object) || object.type?.startsWith('TOKEN_');
3055
+ }
3056
+ function isParseResult(object) {
3057
+ return (object instanceof ParseResult) || (object?.constructor?.name === "ParseResult" && "value" in object);
3058
+ }
3059
+ /**
3060
+ * Compares two positions to determine if the first comes before the second
3061
+ * Returns true if pos1 comes before pos2 in source order
3062
+ * @param inclusive - If true, returns true when positions are equal
3063
+ */
3064
+ function isPositionBefore(position1, position2, inclusive = false) {
3065
+ if (position1.line < position2.line)
3066
+ return true;
3067
+ if (position1.line > position2.line)
3068
+ return false;
3069
+ return inclusive ? position1.column <= position2.column : position1.column < position2.column;
3070
+ }
3071
+ /**
3072
+ * Compares two positions to determine if the first comes after the second
3073
+ * Returns true if pos1 comes after pos2 in source order
3074
+ * @param inclusive - If true, returns true when positions are equal
3075
+ */
3076
+ function isPositionAfter(position1, position2, inclusive = false) {
3077
+ if (position1.line > position2.line)
3078
+ return true;
3079
+ if (position1.line < position2.line)
3080
+ return false;
3081
+ return inclusive ? position1.column >= position2.column : position1.column > position2.column;
3082
+ }
3083
+ /**
3084
+ * Gets nodes that end before the specified position
3085
+ * @param inclusive - If true, includes nodes that end exactly at the position (default: false, matching half-open interval semantics)
3086
+ */
3087
+ function getNodesBeforePosition(nodes, position, inclusive = false) {
3088
+ return nodes.filter(node => node.location && isPositionBefore(node.location.end, position, inclusive));
3089
+ }
3090
+ /**
3091
+ * Gets nodes that start after the specified position
3092
+ * @param inclusive - If true, includes nodes that start exactly at the position (default: true, matching typical boundary behavior)
3093
+ */
3094
+ function getNodesAfterPosition(nodes, position, inclusive = true) {
3095
+ return nodes.filter(node => node.location && isPositionAfter(node.location.start, position, inclusive));
3096
+ }
3097
+
3098
+ // NOTE: This file is generated by the templates/template.rb script and should not
3099
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/visitor.ts.erb
3100
+ class Visitor {
3101
+ visit(node) {
3102
+ if (!node)
3103
+ return;
3104
+ node.accept(this);
3105
+ }
3106
+ visitAll(nodes) {
3107
+ nodes.forEach(node => node?.accept(this));
3108
+ }
3109
+ visitChildNodes(node) {
3110
+ node.compactChildNodes().forEach(node => node.accept(this));
3111
+ }
3112
+ visitNode(_node) {
3113
+ // Default implementation does nothing
3114
+ }
3115
+ visitERBNode(_node) {
3116
+ // Default implementation does nothing
3117
+ }
3118
+ visitDocumentNode(node) {
3119
+ this.visitNode(node);
3120
+ this.visitChildNodes(node);
3121
+ }
3122
+ visitLiteralNode(node) {
3123
+ this.visitNode(node);
3124
+ this.visitChildNodes(node);
3125
+ }
3126
+ visitHTMLOpenTagNode(node) {
3127
+ this.visitNode(node);
3128
+ this.visitChildNodes(node);
3129
+ }
3130
+ visitHTMLCloseTagNode(node) {
3131
+ this.visitNode(node);
3132
+ this.visitChildNodes(node);
3133
+ }
3134
+ visitHTMLElementNode(node) {
3135
+ this.visitNode(node);
3136
+ this.visitChildNodes(node);
3137
+ }
3138
+ visitHTMLAttributeValueNode(node) {
3139
+ this.visitNode(node);
3140
+ this.visitChildNodes(node);
3141
+ }
3142
+ visitHTMLAttributeNameNode(node) {
3143
+ this.visitNode(node);
3144
+ this.visitChildNodes(node);
3145
+ }
3146
+ visitHTMLAttributeNode(node) {
3147
+ this.visitNode(node);
3148
+ this.visitChildNodes(node);
3149
+ }
3150
+ visitHTMLTextNode(node) {
3151
+ this.visitNode(node);
3152
+ this.visitChildNodes(node);
3153
+ }
3154
+ visitHTMLCommentNode(node) {
3155
+ this.visitNode(node);
3156
+ this.visitChildNodes(node);
3157
+ }
3158
+ visitHTMLDoctypeNode(node) {
3159
+ this.visitNode(node);
3160
+ this.visitChildNodes(node);
3161
+ }
3162
+ visitXMLDeclarationNode(node) {
3163
+ this.visitNode(node);
3164
+ this.visitChildNodes(node);
3165
+ }
3166
+ visitCDATANode(node) {
3167
+ this.visitNode(node);
3168
+ this.visitChildNodes(node);
3169
+ }
3170
+ visitWhitespaceNode(node) {
3171
+ this.visitNode(node);
3172
+ this.visitChildNodes(node);
3173
+ }
3174
+ visitERBContentNode(node) {
3175
+ this.visitNode(node);
3176
+ this.visitERBNode(node);
3177
+ this.visitChildNodes(node);
3178
+ }
3179
+ visitERBEndNode(node) {
3180
+ this.visitNode(node);
3181
+ this.visitERBNode(node);
3182
+ this.visitChildNodes(node);
3183
+ }
3184
+ visitERBElseNode(node) {
3185
+ this.visitNode(node);
3186
+ this.visitERBNode(node);
3187
+ this.visitChildNodes(node);
3188
+ }
3189
+ visitERBIfNode(node) {
3190
+ this.visitNode(node);
3191
+ this.visitERBNode(node);
3192
+ this.visitChildNodes(node);
3193
+ }
3194
+ visitERBBlockNode(node) {
3195
+ this.visitNode(node);
3196
+ this.visitERBNode(node);
3197
+ this.visitChildNodes(node);
3198
+ }
3199
+ visitERBWhenNode(node) {
3200
+ this.visitNode(node);
3201
+ this.visitERBNode(node);
3202
+ this.visitChildNodes(node);
3203
+ }
3204
+ visitERBCaseNode(node) {
3205
+ this.visitNode(node);
3206
+ this.visitERBNode(node);
3207
+ this.visitChildNodes(node);
3208
+ }
3209
+ visitERBCaseMatchNode(node) {
3210
+ this.visitNode(node);
3211
+ this.visitERBNode(node);
3212
+ this.visitChildNodes(node);
3213
+ }
3214
+ visitERBWhileNode(node) {
3215
+ this.visitNode(node);
3216
+ this.visitERBNode(node);
3217
+ this.visitChildNodes(node);
3218
+ }
3219
+ visitERBUntilNode(node) {
3220
+ this.visitNode(node);
3221
+ this.visitERBNode(node);
3222
+ this.visitChildNodes(node);
3223
+ }
3224
+ visitERBForNode(node) {
3225
+ this.visitNode(node);
3226
+ this.visitERBNode(node);
3227
+ this.visitChildNodes(node);
3228
+ }
3229
+ visitERBRescueNode(node) {
3230
+ this.visitNode(node);
3231
+ this.visitERBNode(node);
3232
+ this.visitChildNodes(node);
3233
+ }
3234
+ visitERBEnsureNode(node) {
3235
+ this.visitNode(node);
3236
+ this.visitERBNode(node);
3237
+ this.visitChildNodes(node);
3238
+ }
3239
+ visitERBBeginNode(node) {
3240
+ this.visitNode(node);
3241
+ this.visitERBNode(node);
3242
+ this.visitChildNodes(node);
3243
+ }
3244
+ visitERBUnlessNode(node) {
3245
+ this.visitNode(node);
3246
+ this.visitERBNode(node);
3247
+ this.visitChildNodes(node);
3248
+ }
3249
+ visitERBYieldNode(node) {
3250
+ this.visitNode(node);
3251
+ this.visitERBNode(node);
3252
+ this.visitChildNodes(node);
3253
+ }
3254
+ visitERBInNode(node) {
3255
+ this.visitNode(node);
3256
+ this.visitERBNode(node);
3257
+ this.visitChildNodes(node);
3258
+ }
3259
+ }
3260
+
3261
+ class PrintContext {
3262
+ output = "";
3263
+ indentLevel = 0;
3264
+ currentColumn = 0;
3265
+ preserveStack = [];
3266
+ /**
3267
+ * Write text to the output
3268
+ */
3269
+ write(text) {
3270
+ this.output += text;
3271
+ this.currentColumn += text.length;
3272
+ }
3273
+ /**
3274
+ * Write text and update column tracking for newlines
3275
+ */
3276
+ writeWithColumnTracking(text) {
3277
+ this.output += text;
3278
+ const lines = text.split('\n');
3279
+ if (lines.length > 1) {
3280
+ this.currentColumn = lines[lines.length - 1].length;
3281
+ }
3282
+ else {
3283
+ this.currentColumn += text.length;
3284
+ }
3285
+ }
3286
+ /**
3287
+ * Increase indentation level
3288
+ */
3289
+ indent() {
3290
+ this.indentLevel++;
3291
+ }
3292
+ /**
3293
+ * Decrease indentation level
3294
+ */
3295
+ dedent() {
3296
+ if (this.indentLevel > 0) {
3297
+ this.indentLevel--;
3298
+ }
3299
+ }
3300
+ /**
3301
+ * Enter a tag that may preserve whitespace
3302
+ */
3303
+ enterTag(tagName) {
3304
+ this.preserveStack.push(tagName.toLowerCase());
3305
+ }
3306
+ /**
3307
+ * Exit the current tag
3308
+ */
3309
+ exitTag() {
3310
+ this.preserveStack.pop();
3311
+ }
3312
+ /**
3313
+ * Check if we're at the start of a line
3314
+ */
3315
+ isAtStartOfLine() {
3316
+ return this.currentColumn === 0;
3317
+ }
3318
+ /**
3319
+ * Get current indentation level
3320
+ */
3321
+ getCurrentIndentLevel() {
3322
+ return this.indentLevel;
3323
+ }
3324
+ /**
3325
+ * Get current column position
3326
+ */
3327
+ getCurrentColumn() {
3328
+ return this.currentColumn;
3329
+ }
3330
+ /**
3331
+ * Get the current tag stack (for debugging)
3332
+ */
3333
+ getTagStack() {
3334
+ return [...this.preserveStack];
3335
+ }
3336
+ /**
3337
+ * Get the complete output string
3338
+ */
3339
+ getOutput() {
3340
+ return this.output;
3341
+ }
3342
+ /**
3343
+ * Reset the context for reuse
3344
+ */
3345
+ reset() {
3346
+ this.output = "";
3347
+ this.indentLevel = 0;
3348
+ this.currentColumn = 0;
3349
+ this.preserveStack = [];
3350
+ }
3351
+ }
3352
+
3353
+ /**
3354
+ * Default print options used when none are provided
3355
+ */
3356
+ const DEFAULT_PRINT_OPTIONS = {
3357
+ ignoreErrors: false
3358
+ };
3359
+ class Printer extends Visitor {
3360
+ context = new PrintContext();
3361
+ /**
3362
+ * Static method to print a node without creating an instance
3363
+ *
3364
+ * @param input - The AST Node, Token, or ParseResult to print
3365
+ * @param options - Print options to control behavior
3366
+ * @returns The printed string representation of the input
3367
+ * @throws {Error} When node has parse errors and ignoreErrors is false
3368
+ */
3369
+ static print(input, options = DEFAULT_PRINT_OPTIONS) {
3370
+ const printer = new this();
3371
+ return printer.print(input, options);
3372
+ }
3373
+ /**
3374
+ * Print a node, token, or parse result to a string
3375
+ *
3376
+ * @param input - The AST Node, Token, or ParseResult to print
3377
+ * @param options - Print options to control behavior
3378
+ * @returns The printed string representation of the input
3379
+ * @throws {Error} When node has parse errors and ignoreErrors is false
3380
+ */
3381
+ print(input, options = DEFAULT_PRINT_OPTIONS) {
3382
+ if (!input)
3383
+ return "";
3384
+ if (isToken(input)) {
3385
+ return input.value;
3386
+ }
3387
+ if (Array.isArray(input)) {
3388
+ this.context.reset();
3389
+ input.forEach(node => this.visit(node));
3390
+ return this.context.getOutput();
3391
+ }
3392
+ const node = isParseResult(input) ? input.value : input;
3393
+ if (options.ignoreErrors === false && node.recursiveErrors().length > 0) {
3394
+ throw new Error(`Cannot print the node (${node.type}) since it or any of its children has parse errors. Either pass in a valid Node or call \`print()\` using \`print(node, { ignoreErrors: true })\``);
3395
+ }
3396
+ this.context.reset();
3397
+ this.visit(node);
3398
+ return this.context.getOutput();
3399
+ }
3400
+ write(content) {
3401
+ this.context.write(content);
3402
+ }
3403
+ }
3404
+
3405
+ /**
3406
+ * IdentityPrinter - Provides lossless reconstruction of the original source
3407
+ *
3408
+ * This printer aims to reconstruct the original input as faithfully as possible,
3409
+ * preserving all whitespace, formatting, and structure. It's useful for:
3410
+ * - Testing parser accuracy (input should equal output)
3411
+ * - Baseline printing before applying transformations
3412
+ * - Verifying AST round-trip fidelity
3413
+ */
3414
+ class IdentityPrinter extends Printer {
3415
+ static printERBNode(node) {
3416
+ const printer = new IdentityPrinter();
3417
+ printer.printERBNode(node);
3418
+ return printer.context.getOutput();
3419
+ }
3420
+ visitLiteralNode(node) {
3421
+ this.write(node.content);
3422
+ }
3423
+ visitHTMLTextNode(node) {
3424
+ this.write(node.content);
3425
+ }
3426
+ visitWhitespaceNode(node) {
3427
+ if (node.value) {
3428
+ this.write(node.value.value);
3429
+ }
3430
+ }
3431
+ visitHTMLOpenTagNode(node) {
3432
+ if (node.tag_opening) {
3433
+ this.write(node.tag_opening.value);
3434
+ }
3435
+ if (node.tag_name) {
3436
+ this.write(node.tag_name.value);
3437
+ }
3438
+ this.visitChildNodes(node);
3439
+ if (node.tag_closing) {
3440
+ this.write(node.tag_closing.value);
3441
+ }
3442
+ }
3443
+ visitHTMLCloseTagNode(node) {
3444
+ if (node.tag_opening) {
3445
+ this.write(node.tag_opening.value);
3446
+ }
3447
+ if (node.tag_name) {
3448
+ const before = getNodesBeforePosition(node.children, node.tag_name.location.start, true);
3449
+ const after = getNodesAfterPosition(node.children, node.tag_name.location.end);
3450
+ this.visitAll(before);
3451
+ this.write(node.tag_name.value);
3452
+ this.visitAll(after);
3453
+ }
3454
+ else {
3455
+ this.visitAll(node.children);
3456
+ }
3457
+ if (node.tag_closing) {
3458
+ this.write(node.tag_closing.value);
3459
+ }
3460
+ }
3461
+ visitHTMLElementNode(node) {
3462
+ const tagName = node.tag_name?.value;
3463
+ if (tagName) {
3464
+ this.context.enterTag(tagName);
3465
+ }
3466
+ if (node.open_tag) {
3467
+ this.visit(node.open_tag);
3468
+ }
3469
+ if (node.body) {
3470
+ node.body.forEach(child => this.visit(child));
3471
+ }
3472
+ if (node.close_tag) {
3473
+ this.visit(node.close_tag);
3474
+ }
3475
+ if (tagName) {
3476
+ this.context.exitTag();
3477
+ }
3478
+ }
3479
+ visitHTMLAttributeNode(node) {
3480
+ if (node.name) {
3481
+ this.visit(node.name);
3482
+ }
3483
+ if (node.equals) {
3484
+ this.write(node.equals.value);
3485
+ }
3486
+ if (node.equals && node.value) {
3487
+ this.visit(node.value);
3488
+ }
3489
+ }
3490
+ visitHTMLAttributeNameNode(node) {
3491
+ this.visitChildNodes(node);
3492
+ }
3493
+ visitHTMLAttributeValueNode(node) {
3494
+ if (node.quoted && node.open_quote) {
3495
+ this.write(node.open_quote.value);
3496
+ }
3497
+ this.visitChildNodes(node);
3498
+ if (node.quoted && node.close_quote) {
3499
+ this.write(node.close_quote.value);
3500
+ }
3501
+ }
3502
+ visitHTMLCommentNode(node) {
3503
+ if (node.comment_start) {
3504
+ this.write(node.comment_start.value);
3505
+ }
3506
+ this.visitChildNodes(node);
3507
+ if (node.comment_end) {
3508
+ this.write(node.comment_end.value);
3509
+ }
3510
+ }
3511
+ visitHTMLDoctypeNode(node) {
3512
+ if (node.tag_opening) {
3513
+ this.write(node.tag_opening.value);
3514
+ }
3515
+ this.visitChildNodes(node);
3516
+ if (node.tag_closing) {
3517
+ this.write(node.tag_closing.value);
3518
+ }
3519
+ }
3520
+ visitXMLDeclarationNode(node) {
3521
+ if (node.tag_opening) {
3522
+ this.write(node.tag_opening.value);
3523
+ }
3524
+ this.visitChildNodes(node);
3525
+ if (node.tag_closing) {
3526
+ this.write(node.tag_closing.value);
3527
+ }
3528
+ }
3529
+ visitCDATANode(node) {
3530
+ if (node.tag_opening) {
3531
+ this.write(node.tag_opening.value);
3532
+ }
3533
+ this.visitChildNodes(node);
3534
+ if (node.tag_closing) {
3535
+ this.write(node.tag_closing.value);
3536
+ }
3537
+ }
3538
+ visitERBContentNode(node) {
3539
+ this.printERBNode(node);
3540
+ }
3541
+ visitERBIfNode(node) {
3542
+ this.printERBNode(node);
3543
+ if (node.statements) {
3544
+ node.statements.forEach(statement => this.visit(statement));
3545
+ }
3546
+ if (node.subsequent) {
3547
+ this.visit(node.subsequent);
3548
+ }
3549
+ if (node.end_node) {
3550
+ this.visit(node.end_node);
3551
+ }
3552
+ }
3553
+ visitERBElseNode(node) {
3554
+ this.printERBNode(node);
3555
+ if (node.statements) {
3556
+ node.statements.forEach(statement => this.visit(statement));
3557
+ }
3558
+ }
3559
+ visitERBEndNode(node) {
3560
+ this.printERBNode(node);
3561
+ }
3562
+ visitERBBlockNode(node) {
3563
+ this.printERBNode(node);
3564
+ if (node.body) {
3565
+ node.body.forEach(child => this.visit(child));
3566
+ }
3567
+ if (node.end_node) {
3568
+ this.visit(node.end_node);
3569
+ }
3570
+ }
3571
+ visitERBCaseNode(node) {
3572
+ this.printERBNode(node);
3573
+ if (node.children) {
3574
+ node.children.forEach(child => this.visit(child));
3575
+ }
3576
+ if (node.conditions) {
3577
+ node.conditions.forEach(condition => this.visit(condition));
3578
+ }
3579
+ if (node.else_clause) {
3580
+ this.visit(node.else_clause);
3581
+ }
3582
+ if (node.end_node) {
3583
+ this.visit(node.end_node);
3584
+ }
3585
+ }
3586
+ visitERBWhenNode(node) {
3587
+ this.printERBNode(node);
3588
+ if (node.statements) {
3589
+ node.statements.forEach(statement => this.visit(statement));
3590
+ }
3591
+ }
3592
+ visitERBWhileNode(node) {
3593
+ this.printERBNode(node);
3594
+ if (node.statements) {
3595
+ node.statements.forEach(statement => this.visit(statement));
3596
+ }
3597
+ if (node.end_node) {
3598
+ this.visit(node.end_node);
3599
+ }
3600
+ }
3601
+ visitERBUntilNode(node) {
3602
+ this.printERBNode(node);
3603
+ if (node.statements) {
3604
+ node.statements.forEach(statement => this.visit(statement));
3605
+ }
3606
+ if (node.end_node) {
3607
+ this.visit(node.end_node);
3608
+ }
3609
+ }
3610
+ visitERBForNode(node) {
3611
+ this.printERBNode(node);
3612
+ if (node.statements) {
3613
+ node.statements.forEach(statement => this.visit(statement));
3614
+ }
3615
+ if (node.end_node) {
3616
+ this.visit(node.end_node);
3617
+ }
3618
+ }
3619
+ visitERBBeginNode(node) {
3620
+ this.printERBNode(node);
3621
+ if (node.statements) {
3622
+ node.statements.forEach(statement => this.visit(statement));
3623
+ }
3624
+ if (node.rescue_clause) {
3625
+ this.visit(node.rescue_clause);
3626
+ }
3627
+ if (node.else_clause) {
3628
+ this.visit(node.else_clause);
3629
+ }
3630
+ if (node.ensure_clause) {
3631
+ this.visit(node.ensure_clause);
3632
+ }
3633
+ if (node.end_node) {
3634
+ this.visit(node.end_node);
3635
+ }
3636
+ }
3637
+ visitERBRescueNode(node) {
3638
+ this.printERBNode(node);
3639
+ if (node.statements) {
3640
+ node.statements.forEach(statement => this.visit(statement));
3641
+ }
3642
+ if (node.subsequent) {
3643
+ this.visit(node.subsequent);
3644
+ }
3645
+ }
3646
+ visitERBEnsureNode(node) {
3647
+ this.printERBNode(node);
3648
+ if (node.statements) {
3649
+ node.statements.forEach(statement => this.visit(statement));
3650
+ }
3651
+ }
3652
+ visitERBUnlessNode(node) {
3653
+ this.printERBNode(node);
3654
+ if (node.statements) {
3655
+ node.statements.forEach(statement => this.visit(statement));
3656
+ }
3657
+ if (node.else_clause) {
3658
+ this.visit(node.else_clause);
3659
+ }
3660
+ if (node.end_node) {
3661
+ this.visit(node.end_node);
3662
+ }
3663
+ }
3664
+ visitERBYieldNode(node) {
3665
+ this.printERBNode(node);
3666
+ }
3667
+ visitERBInNode(node) {
3668
+ this.printERBNode(node);
3669
+ if (node.statements) {
3670
+ node.statements.forEach(statement => this.visit(statement));
3671
+ }
3672
+ }
3673
+ visitERBCaseMatchNode(node) {
3674
+ this.printERBNode(node);
3675
+ if (node.children) {
3676
+ node.children.forEach(child => this.visit(child));
3677
+ }
3678
+ if (node.conditions) {
3679
+ node.conditions.forEach(condition => this.visit(condition));
3680
+ }
3681
+ if (node.else_clause) {
3682
+ this.visit(node.else_clause);
3683
+ }
3684
+ if (node.end_node) {
3685
+ this.visit(node.end_node);
3686
+ }
3687
+ }
3688
+ /**
3689
+ * Print ERB node tags and content
3690
+ */
3691
+ printERBNode(node) {
3692
+ if (node.tag_opening) {
3693
+ this.write(node.tag_opening.value);
3694
+ }
3695
+ if (node.content) {
3696
+ this.write(node.content.value);
3697
+ }
3698
+ if (node.tag_closing) {
3699
+ this.write(node.tag_closing.value);
3700
+ }
3701
+ }
3702
+ }
3703
+
3704
+ ({
3705
+ ...DEFAULT_PRINT_OPTIONS});
3706
+
3707
+ /**
3708
+ * Rewrite an AST Node using the provided rewriters
3709
+ *
3710
+ * This is the main rewrite function that operates on AST nodes.
3711
+ * For string input, use `rewriteString()` instead.
3712
+ *
3713
+ * @example
3714
+ * ```typescript
3715
+ * import { Herb } from '@herb-tools/node-wasm'
3716
+ * import { rewrite } from '@herb-tools/rewriter'
3717
+ * import { tailwindClassSorter } from '@herb-tools/rewriter/loader'
3718
+ *
3719
+ * await Herb.load()
3720
+ *
3721
+ * const template = '<div class="text-red-500 p-4 mt-2"></div>'
3722
+ * const parseResult = Herb.parse(template)
3723
+ * const { output, node } = rewrite(parseResult.value, [tailwindClassSorter()])
3724
+ * ```
3725
+ *
3726
+ * @param node - The AST Node to rewrite
3727
+ * @param rewriters - Array of rewriter instances to apply
3728
+ * @param options - Optional configuration for the rewrite operation
3729
+ * @returns Object containing the rewritten string and Node
3730
+ */
3731
+ function rewrite(node, rewriters, options = {}) {
3732
+ const { baseDir = process.cwd(), filePath } = options;
3733
+ const context = { baseDir, filePath };
3734
+ let currentNode = node;
3735
+ const astRewriters = rewriters.filter(rewriter => rewriter instanceof ASTRewriter);
3736
+ const stringRewriters = rewriters.filter(rewriter => rewriter instanceof StringRewriter);
3737
+ for (const rewriter of astRewriters) {
3738
+ try {
3739
+ currentNode = rewriter.rewrite(currentNode, context);
3740
+ }
3741
+ catch (error) {
3742
+ console.error(`AST rewriter "${rewriter.name}" failed:`, error);
3743
+ }
3744
+ }
3745
+ let result = IdentityPrinter.print(currentNode);
3746
+ for (const rewriter of stringRewriters) {
3747
+ try {
3748
+ result = rewriter.rewrite(result, context);
3749
+ }
3750
+ catch (error) {
3751
+ console.error(`String rewriter "${rewriter.name}" failed:`, error);
3752
+ }
3753
+ }
3754
+ return {
3755
+ output: result,
3756
+ node: currentNode
3757
+ };
3758
+ }
3759
+ /**
3760
+ * Rewrite an HTML+ERB template string
3761
+ *
3762
+ * Convenience wrapper around `rewrite()` that parses the string first.
3763
+ *
3764
+ * @example
3765
+ * ```typescript
3766
+ * import { Herb } from '@herb-tools/node-wasm'
3767
+ * import { rewriteString } from '@herb-tools/rewriter'
3768
+ * import { tailwindClassSorter } from '@herb-tools/rewriter/loader'
3769
+ *
3770
+ * await Herb.load()
3771
+ *
3772
+ * const template = '<div class="text-red-500 p-4 mt-2"></div>'
3773
+ * const output = rewriteString(Herb, template, [tailwindClassSorter()])
3774
+ * // output: '<div class="mt-2 p-4 text-red-500"></div>'
3775
+ * ```
3776
+ *
3777
+ * @param herb - The Herb backend instance for parsing
3778
+ * @param template - The HTML+ERB template string to rewrite
3779
+ * @param rewriters - Array of rewriter instances to apply
3780
+ * @param options - Optional configuration for the rewrite operation
3781
+ * @returns The rewritten template string
3782
+ */
3783
+ function rewriteString(herb, template, rewriters, options = {}) {
3784
+ const parseResult = herb.parse(template, { track_whitespace: true });
3785
+ if (parseResult.failed) {
3786
+ return template;
3787
+ }
3788
+ const { output } = rewrite(parseResult.value, rewriters, options);
3789
+ return output;
3790
+ }
3791
+
3792
+ export { ASTRewriter, StringRewriter, asMutable, isASTRewriterClass, isRewriterClass, isStringRewriterClass, rewrite, rewriteString };
3793
+ //# sourceMappingURL=index.esm.js.map