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