@herb-tools/core 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,876 @@
1
+ // NOTE: This file is generated by the templates/template.rb script and should not
2
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.6.0/templates/javascript/packages/core/src/node-type-guards.ts.erb
3
+
4
+ import type { Node, NodeType, ERBNode } from "./nodes.js"
5
+
6
+ import { Token } from "./token.js"
7
+ import { ParseResult } from "./parse-result.js"
8
+
9
+ import {
10
+ DocumentNode,
11
+ LiteralNode,
12
+ HTMLOpenTagNode,
13
+ HTMLCloseTagNode,
14
+ HTMLElementNode,
15
+ HTMLAttributeValueNode,
16
+ HTMLAttributeNameNode,
17
+ HTMLAttributeNode,
18
+ HTMLTextNode,
19
+ HTMLCommentNode,
20
+ HTMLDoctypeNode,
21
+ XMLDeclarationNode,
22
+ CDATANode,
23
+ WhitespaceNode,
24
+ ERBContentNode,
25
+ ERBEndNode,
26
+ ERBElseNode,
27
+ ERBIfNode,
28
+ ERBBlockNode,
29
+ ERBWhenNode,
30
+ ERBCaseNode,
31
+ ERBCaseMatchNode,
32
+ ERBWhileNode,
33
+ ERBUntilNode,
34
+ ERBForNode,
35
+ ERBRescueNode,
36
+ ERBEnsureNode,
37
+ ERBBeginNode,
38
+ ERBUnlessNode,
39
+ ERBYieldNode,
40
+ ERBInNode,
41
+ } from "./nodes.js"
42
+
43
+ /**
44
+ * Type guard functions for AST nodes.
45
+ * These functions provide type checking by combining both instanceof
46
+ * checks and type string comparisons for maximum reliability across different
47
+ * runtime scenarios (e.g., serialized/deserialized nodes).
48
+ */
49
+
50
+ /**
51
+ * Checks if a node is a DocumentNode
52
+ */
53
+ export function isDocumentNode(node: Node): node is DocumentNode {
54
+ return node instanceof DocumentNode || (node as any).type === "AST_DOCUMENT_NODE"
55
+ }
56
+
57
+ /**
58
+ * Checks if a node is a LiteralNode
59
+ */
60
+ export function isLiteralNode(node: Node): node is LiteralNode {
61
+ return node instanceof LiteralNode || (node as any).type === "AST_LITERAL_NODE"
62
+ }
63
+
64
+ /**
65
+ * Checks if a node is a HTMLOpenTagNode
66
+ */
67
+ export function isHTMLOpenTagNode(node: Node): node is HTMLOpenTagNode {
68
+ return node instanceof HTMLOpenTagNode || (node as any).type === "AST_HTML_OPEN_TAG_NODE"
69
+ }
70
+
71
+ /**
72
+ * Checks if a node is a HTMLCloseTagNode
73
+ */
74
+ export function isHTMLCloseTagNode(node: Node): node is HTMLCloseTagNode {
75
+ return node instanceof HTMLCloseTagNode || (node as any).type === "AST_HTML_CLOSE_TAG_NODE"
76
+ }
77
+
78
+ /**
79
+ * Checks if a node is a HTMLElementNode
80
+ */
81
+ export function isHTMLElementNode(node: Node): node is HTMLElementNode {
82
+ return node instanceof HTMLElementNode || (node as any).type === "AST_HTML_ELEMENT_NODE"
83
+ }
84
+
85
+ /**
86
+ * Checks if a node is a HTMLAttributeValueNode
87
+ */
88
+ export function isHTMLAttributeValueNode(node: Node): node is HTMLAttributeValueNode {
89
+ return node instanceof HTMLAttributeValueNode || (node as any).type === "AST_HTML_ATTRIBUTE_VALUE_NODE"
90
+ }
91
+
92
+ /**
93
+ * Checks if a node is a HTMLAttributeNameNode
94
+ */
95
+ export function isHTMLAttributeNameNode(node: Node): node is HTMLAttributeNameNode {
96
+ return node instanceof HTMLAttributeNameNode || (node as any).type === "AST_HTML_ATTRIBUTE_NAME_NODE"
97
+ }
98
+
99
+ /**
100
+ * Checks if a node is a HTMLAttributeNode
101
+ */
102
+ export function isHTMLAttributeNode(node: Node): node is HTMLAttributeNode {
103
+ return node instanceof HTMLAttributeNode || (node as any).type === "AST_HTML_ATTRIBUTE_NODE"
104
+ }
105
+
106
+ /**
107
+ * Checks if a node is a HTMLTextNode
108
+ */
109
+ export function isHTMLTextNode(node: Node): node is HTMLTextNode {
110
+ return node instanceof HTMLTextNode || (node as any).type === "AST_HTML_TEXT_NODE"
111
+ }
112
+
113
+ /**
114
+ * Checks if a node is a HTMLCommentNode
115
+ */
116
+ export function isHTMLCommentNode(node: Node): node is HTMLCommentNode {
117
+ return node instanceof HTMLCommentNode || (node as any).type === "AST_HTML_COMMENT_NODE"
118
+ }
119
+
120
+ /**
121
+ * Checks if a node is a HTMLDoctypeNode
122
+ */
123
+ export function isHTMLDoctypeNode(node: Node): node is HTMLDoctypeNode {
124
+ return node instanceof HTMLDoctypeNode || (node as any).type === "AST_HTML_DOCTYPE_NODE"
125
+ }
126
+
127
+ /**
128
+ * Checks if a node is a XMLDeclarationNode
129
+ */
130
+ export function isXMLDeclarationNode(node: Node): node is XMLDeclarationNode {
131
+ return node instanceof XMLDeclarationNode || (node as any).type === "AST_XML_DECLARATION_NODE"
132
+ }
133
+
134
+ /**
135
+ * Checks if a node is a CDATANode
136
+ */
137
+ export function isCDATANode(node: Node): node is CDATANode {
138
+ return node instanceof CDATANode || (node as any).type === "AST_CDATA_NODE"
139
+ }
140
+
141
+ /**
142
+ * Checks if a node is a WhitespaceNode
143
+ */
144
+ export function isWhitespaceNode(node: Node): node is WhitespaceNode {
145
+ return node instanceof WhitespaceNode || (node as any).type === "AST_WHITESPACE_NODE"
146
+ }
147
+
148
+ /**
149
+ * Checks if a node is a ERBContentNode
150
+ */
151
+ export function isERBContentNode(node: Node): node is ERBContentNode {
152
+ return node instanceof ERBContentNode || (node as any).type === "AST_ERB_CONTENT_NODE"
153
+ }
154
+
155
+ /**
156
+ * Checks if a node is a ERBEndNode
157
+ */
158
+ export function isERBEndNode(node: Node): node is ERBEndNode {
159
+ return node instanceof ERBEndNode || (node as any).type === "AST_ERB_END_NODE"
160
+ }
161
+
162
+ /**
163
+ * Checks if a node is a ERBElseNode
164
+ */
165
+ export function isERBElseNode(node: Node): node is ERBElseNode {
166
+ return node instanceof ERBElseNode || (node as any).type === "AST_ERB_ELSE_NODE"
167
+ }
168
+
169
+ /**
170
+ * Checks if a node is a ERBIfNode
171
+ */
172
+ export function isERBIfNode(node: Node): node is ERBIfNode {
173
+ return node instanceof ERBIfNode || (node as any).type === "AST_ERB_IF_NODE"
174
+ }
175
+
176
+ /**
177
+ * Checks if a node is a ERBBlockNode
178
+ */
179
+ export function isERBBlockNode(node: Node): node is ERBBlockNode {
180
+ return node instanceof ERBBlockNode || (node as any).type === "AST_ERB_BLOCK_NODE"
181
+ }
182
+
183
+ /**
184
+ * Checks if a node is a ERBWhenNode
185
+ */
186
+ export function isERBWhenNode(node: Node): node is ERBWhenNode {
187
+ return node instanceof ERBWhenNode || (node as any).type === "AST_ERB_WHEN_NODE"
188
+ }
189
+
190
+ /**
191
+ * Checks if a node is a ERBCaseNode
192
+ */
193
+ export function isERBCaseNode(node: Node): node is ERBCaseNode {
194
+ return node instanceof ERBCaseNode || (node as any).type === "AST_ERB_CASE_NODE"
195
+ }
196
+
197
+ /**
198
+ * Checks if a node is a ERBCaseMatchNode
199
+ */
200
+ export function isERBCaseMatchNode(node: Node): node is ERBCaseMatchNode {
201
+ return node instanceof ERBCaseMatchNode || (node as any).type === "AST_ERB_CASE_MATCH_NODE"
202
+ }
203
+
204
+ /**
205
+ * Checks if a node is a ERBWhileNode
206
+ */
207
+ export function isERBWhileNode(node: Node): node is ERBWhileNode {
208
+ return node instanceof ERBWhileNode || (node as any).type === "AST_ERB_WHILE_NODE"
209
+ }
210
+
211
+ /**
212
+ * Checks if a node is a ERBUntilNode
213
+ */
214
+ export function isERBUntilNode(node: Node): node is ERBUntilNode {
215
+ return node instanceof ERBUntilNode || (node as any).type === "AST_ERB_UNTIL_NODE"
216
+ }
217
+
218
+ /**
219
+ * Checks if a node is a ERBForNode
220
+ */
221
+ export function isERBForNode(node: Node): node is ERBForNode {
222
+ return node instanceof ERBForNode || (node as any).type === "AST_ERB_FOR_NODE"
223
+ }
224
+
225
+ /**
226
+ * Checks if a node is a ERBRescueNode
227
+ */
228
+ export function isERBRescueNode(node: Node): node is ERBRescueNode {
229
+ return node instanceof ERBRescueNode || (node as any).type === "AST_ERB_RESCUE_NODE"
230
+ }
231
+
232
+ /**
233
+ * Checks if a node is a ERBEnsureNode
234
+ */
235
+ export function isERBEnsureNode(node: Node): node is ERBEnsureNode {
236
+ return node instanceof ERBEnsureNode || (node as any).type === "AST_ERB_ENSURE_NODE"
237
+ }
238
+
239
+ /**
240
+ * Checks if a node is a ERBBeginNode
241
+ */
242
+ export function isERBBeginNode(node: Node): node is ERBBeginNode {
243
+ return node instanceof ERBBeginNode || (node as any).type === "AST_ERB_BEGIN_NODE"
244
+ }
245
+
246
+ /**
247
+ * Checks if a node is a ERBUnlessNode
248
+ */
249
+ export function isERBUnlessNode(node: Node): node is ERBUnlessNode {
250
+ return node instanceof ERBUnlessNode || (node as any).type === "AST_ERB_UNLESS_NODE"
251
+ }
252
+
253
+ /**
254
+ * Checks if a node is a ERBYieldNode
255
+ */
256
+ export function isERBYieldNode(node: Node): node is ERBYieldNode {
257
+ return node instanceof ERBYieldNode || (node as any).type === "AST_ERB_YIELD_NODE"
258
+ }
259
+
260
+ /**
261
+ * Checks if a node is a ERBInNode
262
+ */
263
+ export function isERBInNode(node: Node): node is ERBInNode {
264
+ return node instanceof ERBInNode || (node as any).type === "AST_ERB_IN_NODE"
265
+ }
266
+
267
+ /**
268
+ * Convenience type guards for common node categories
269
+ */
270
+
271
+ /**
272
+ * Checks if a node is any HTML node type
273
+ */
274
+ export function isHTMLNode(node: Node): boolean {
275
+ return isHTMLOpenTagNode(node) ||
276
+ isHTMLCloseTagNode(node) ||
277
+ isHTMLElementNode(node) ||
278
+ isHTMLAttributeValueNode(node) ||
279
+ isHTMLAttributeNameNode(node) ||
280
+ isHTMLAttributeNode(node) ||
281
+ isHTMLTextNode(node) ||
282
+ isHTMLCommentNode(node) ||
283
+ isHTMLDoctypeNode(node)
284
+ }
285
+
286
+ /**
287
+ * Checks if a node is any ERB node type
288
+ */
289
+ export function isERBNode(node: Node): node is ERBNode {
290
+ return isERBContentNode(node) ||
291
+ isERBEndNode(node) ||
292
+ isERBElseNode(node) ||
293
+ isERBIfNode(node) ||
294
+ isERBBlockNode(node) ||
295
+ isERBWhenNode(node) ||
296
+ isERBCaseNode(node) ||
297
+ isERBCaseMatchNode(node) ||
298
+ isERBWhileNode(node) ||
299
+ isERBUntilNode(node) ||
300
+ isERBForNode(node) ||
301
+ isERBRescueNode(node) ||
302
+ isERBEnsureNode(node) ||
303
+ isERBBeginNode(node) ||
304
+ isERBUnlessNode(node) ||
305
+ isERBYieldNode(node) ||
306
+ isERBInNode(node)
307
+ }
308
+
309
+ /**
310
+ * Map of node classes to their corresponding type guard functions
311
+ *
312
+ * @example
313
+ * const guard = NODE_TYPE_GUARDS[HTMLTextNode]
314
+ *
315
+ * if (guard(node)) {
316
+ * // node is HTMLTextNode
317
+ * }
318
+ */
319
+ export const NODE_TYPE_GUARDS = new Map<new (...args: any[]) => Node, (node: Node) => boolean>([
320
+ [DocumentNode, isDocumentNode],
321
+ [LiteralNode, isLiteralNode],
322
+ [HTMLOpenTagNode, isHTMLOpenTagNode],
323
+ [HTMLCloseTagNode, isHTMLCloseTagNode],
324
+ [HTMLElementNode, isHTMLElementNode],
325
+ [HTMLAttributeValueNode, isHTMLAttributeValueNode],
326
+ [HTMLAttributeNameNode, isHTMLAttributeNameNode],
327
+ [HTMLAttributeNode, isHTMLAttributeNode],
328
+ [HTMLTextNode, isHTMLTextNode],
329
+ [HTMLCommentNode, isHTMLCommentNode],
330
+ [HTMLDoctypeNode, isHTMLDoctypeNode],
331
+ [XMLDeclarationNode, isXMLDeclarationNode],
332
+ [CDATANode, isCDATANode],
333
+ [WhitespaceNode, isWhitespaceNode],
334
+ [ERBContentNode, isERBContentNode],
335
+ [ERBEndNode, isERBEndNode],
336
+ [ERBElseNode, isERBElseNode],
337
+ [ERBIfNode, isERBIfNode],
338
+ [ERBBlockNode, isERBBlockNode],
339
+ [ERBWhenNode, isERBWhenNode],
340
+ [ERBCaseNode, isERBCaseNode],
341
+ [ERBCaseMatchNode, isERBCaseMatchNode],
342
+ [ERBWhileNode, isERBWhileNode],
343
+ [ERBUntilNode, isERBUntilNode],
344
+ [ERBForNode, isERBForNode],
345
+ [ERBRescueNode, isERBRescueNode],
346
+ [ERBEnsureNode, isERBEnsureNode],
347
+ [ERBBeginNode, isERBBeginNode],
348
+ [ERBUnlessNode, isERBUnlessNode],
349
+ [ERBYieldNode, isERBYieldNode],
350
+ [ERBInNode, isERBInNode],
351
+ ])
352
+
353
+ /**
354
+ * Map of AST node type strings to their corresponding type guard functions
355
+ *
356
+ * @example
357
+ * const guard = AST_TYPE_GUARDS["AST_HTML_TEXT_NODE"]
358
+ *
359
+ * if (guard(node)) {
360
+ * // node is HTMLTextNode
361
+ * }
362
+ */
363
+ export const AST_TYPE_GUARDS = new Map<string, (node: Node) => boolean>([
364
+ ["AST_DOCUMENT_NODE", isDocumentNode],
365
+ ["AST_LITERAL_NODE", isLiteralNode],
366
+ ["AST_HTML_OPEN_TAG_NODE", isHTMLOpenTagNode],
367
+ ["AST_HTML_CLOSE_TAG_NODE", isHTMLCloseTagNode],
368
+ ["AST_HTML_ELEMENT_NODE", isHTMLElementNode],
369
+ ["AST_HTML_ATTRIBUTE_VALUE_NODE", isHTMLAttributeValueNode],
370
+ ["AST_HTML_ATTRIBUTE_NAME_NODE", isHTMLAttributeNameNode],
371
+ ["AST_HTML_ATTRIBUTE_NODE", isHTMLAttributeNode],
372
+ ["AST_HTML_TEXT_NODE", isHTMLTextNode],
373
+ ["AST_HTML_COMMENT_NODE", isHTMLCommentNode],
374
+ ["AST_HTML_DOCTYPE_NODE", isHTMLDoctypeNode],
375
+ ["AST_XML_DECLARATION_NODE", isXMLDeclarationNode],
376
+ ["AST_CDATA_NODE", isCDATANode],
377
+ ["AST_WHITESPACE_NODE", isWhitespaceNode],
378
+ ["AST_ERB_CONTENT_NODE", isERBContentNode],
379
+ ["AST_ERB_END_NODE", isERBEndNode],
380
+ ["AST_ERB_ELSE_NODE", isERBElseNode],
381
+ ["AST_ERB_IF_NODE", isERBIfNode],
382
+ ["AST_ERB_BLOCK_NODE", isERBBlockNode],
383
+ ["AST_ERB_WHEN_NODE", isERBWhenNode],
384
+ ["AST_ERB_CASE_NODE", isERBCaseNode],
385
+ ["AST_ERB_CASE_MATCH_NODE", isERBCaseMatchNode],
386
+ ["AST_ERB_WHILE_NODE", isERBWhileNode],
387
+ ["AST_ERB_UNTIL_NODE", isERBUntilNode],
388
+ ["AST_ERB_FOR_NODE", isERBForNode],
389
+ ["AST_ERB_RESCUE_NODE", isERBRescueNode],
390
+ ["AST_ERB_ENSURE_NODE", isERBEnsureNode],
391
+ ["AST_ERB_BEGIN_NODE", isERBBeginNode],
392
+ ["AST_ERB_UNLESS_NODE", isERBUnlessNode],
393
+ ["AST_ERB_YIELD_NODE", isERBYieldNode],
394
+ ["AST_ERB_IN_NODE", isERBInNode],
395
+ ])
396
+
397
+ type NodeTypeToClass = {
398
+ "AST_DOCUMENT_NODE": DocumentNode;
399
+ "AST_LITERAL_NODE": LiteralNode;
400
+ "AST_HTML_OPEN_TAG_NODE": HTMLOpenTagNode;
401
+ "AST_HTML_CLOSE_TAG_NODE": HTMLCloseTagNode;
402
+ "AST_HTML_ELEMENT_NODE": HTMLElementNode;
403
+ "AST_HTML_ATTRIBUTE_VALUE_NODE": HTMLAttributeValueNode;
404
+ "AST_HTML_ATTRIBUTE_NAME_NODE": HTMLAttributeNameNode;
405
+ "AST_HTML_ATTRIBUTE_NODE": HTMLAttributeNode;
406
+ "AST_HTML_TEXT_NODE": HTMLTextNode;
407
+ "AST_HTML_COMMENT_NODE": HTMLCommentNode;
408
+ "AST_HTML_DOCTYPE_NODE": HTMLDoctypeNode;
409
+ "AST_XML_DECLARATION_NODE": XMLDeclarationNode;
410
+ "AST_CDATA_NODE": CDATANode;
411
+ "AST_WHITESPACE_NODE": WhitespaceNode;
412
+ "AST_ERB_CONTENT_NODE": ERBContentNode;
413
+ "AST_ERB_END_NODE": ERBEndNode;
414
+ "AST_ERB_ELSE_NODE": ERBElseNode;
415
+ "AST_ERB_IF_NODE": ERBIfNode;
416
+ "AST_ERB_BLOCK_NODE": ERBBlockNode;
417
+ "AST_ERB_WHEN_NODE": ERBWhenNode;
418
+ "AST_ERB_CASE_NODE": ERBCaseNode;
419
+ "AST_ERB_CASE_MATCH_NODE": ERBCaseMatchNode;
420
+ "AST_ERB_WHILE_NODE": ERBWhileNode;
421
+ "AST_ERB_UNTIL_NODE": ERBUntilNode;
422
+ "AST_ERB_FOR_NODE": ERBForNode;
423
+ "AST_ERB_RESCUE_NODE": ERBRescueNode;
424
+ "AST_ERB_ENSURE_NODE": ERBEnsureNode;
425
+ "AST_ERB_BEGIN_NODE": ERBBeginNode;
426
+ "AST_ERB_UNLESS_NODE": ERBUnlessNode;
427
+ "AST_ERB_YIELD_NODE": ERBYieldNode;
428
+ "AST_ERB_IN_NODE": ERBInNode;
429
+ }
430
+
431
+ type ClassToInstance<T> = T extends new (...args: any[]) => infer R ? R : never
432
+
433
+ /**
434
+ * Checks if a node matches any of the provided type identifiers with proper type narrowing
435
+ * Supports AST type strings, node classes, or type guard functions
436
+ *
437
+ * @example
438
+ * if (isAnyOf(node, "AST_HTML_TEXT_NODE", "AST_LITERAL_NODE")) {
439
+ * // node is narrowed to HTMLTextNode | LiteralNode
440
+ * }
441
+ *
442
+ * @example
443
+ * if (isAnyOf(node, HTMLTextNode, LiteralNode)) {
444
+ * // node is narrowed to HTMLTextNode | LiteralNode
445
+ * }
446
+ */
447
+
448
+ export function isAnyOf(node: Node, ...types: Array<NodeType | (new (...args: any[]) => Node) | ((node: Node) => boolean)>): boolean {
449
+ return types.some(type => {
450
+ if (typeof type === 'string') {
451
+ return isNode(node, type)
452
+ } else if (typeof type === 'function' && type.prototype && type.prototype.constructor === type && NODE_TYPE_GUARDS.has(type as new (...args: any[]) => Node)) {
453
+ return isNode(node, type as new (...args: any[]) => Node)
454
+ } else if (typeof type === 'function') {
455
+ return (type as (node: Node) => boolean)(node)
456
+ } else {
457
+ return false
458
+ }
459
+ })
460
+ }
461
+
462
+ /**
463
+ * Checks if a node does NOT match any of the provided type identifiers
464
+ * Supports AST type strings, node classes, or type guard functions
465
+ * This is the logical inverse of isAnyOf
466
+ *
467
+ * @example
468
+ * if (isNoneOf(node, "AST_HTML_TEXT_NODE", "AST_LITERAL_NODE")) {
469
+ * // node is neither HTMLTextNode nor LiteralNode
470
+ * }
471
+ *
472
+ * @example
473
+ * if (isNoneOf(node, HTMLTextNode, LiteralNode)) {
474
+ * // node is neither HTMLTextNode nor LiteralNode
475
+ * }
476
+ *
477
+ * @example
478
+ * if (isNoneOf(node, isHTMLTextNode, isLiteralNode)) {
479
+ * // node is neither HTMLTextNode nor LiteralNode
480
+ * }
481
+ */
482
+ export function isNoneOf(
483
+ node: Node,
484
+ ...types: Array<NodeType | (new (...args: any[]) => Node) | ((node: Node) => boolean)>
485
+ ): boolean {
486
+ return !isAnyOf(node, ...types)
487
+ }
488
+
489
+ /**
490
+ * Checks if ALL nodes in an array match any of the provided type identifiers
491
+ * Supports AST type strings, node classes, or type guard functions
492
+ * Provides type narrowing for the array when true
493
+ *
494
+ * @example
495
+ * if (areAllOfType(nodes, "AST_HTML_TEXT_NODE", "AST_LITERAL_NODE")) {
496
+ * // nodes is narrowed to (HTMLTextNode | LiteralNode)[]
497
+ * }
498
+ *
499
+ * @example
500
+ * if (areAllOfType(nodes, HTMLTextNode, LiteralNode)) {
501
+ * // nodes is narrowed to (HTMLTextNode | LiteralNode)[]
502
+ * }
503
+ *
504
+ * @example
505
+ * if (areAllOfType(nodes, isHTMLTextNode, isLiteralNode)) {
506
+ * // all nodes are either HTMLTextNode or LiteralNode
507
+ * }
508
+ */
509
+
510
+ export function areAllOfType<T extends NodeType[]>(
511
+ nodes: Node[],
512
+ ...types: T
513
+ ): nodes is NodeTypeToClass[T[number]][]
514
+
515
+ export function areAllOfType<T extends (new (...args: any[]) => Node)[]>(
516
+ nodes: Node[],
517
+ ...types: T
518
+ ): nodes is ClassToInstance<T[number]>[]
519
+
520
+ export function areAllOfType(
521
+ nodes: Node[],
522
+ ...types: Array<(node: Node) => boolean>
523
+ ): boolean
524
+
525
+ export function areAllOfType(
526
+ nodes: Node[],
527
+ ...types: Array<NodeType | (new (...args: any[]) => Node) | ((node: Node) => boolean)>
528
+ ): boolean {
529
+ return nodes.every(node => isAnyOf(node, ...types))
530
+ }
531
+
532
+ /**
533
+ * Filters an array of nodes to only include nodes matching any of the provided type identifiers
534
+ * Supports AST type strings, node classes, or type guard functions
535
+ * Returns a properly typed array of the filtered nodes
536
+ *
537
+ * @example
538
+ * const filtered = filterNodes(nodes, "AST_HTML_TEXT_NODE", "AST_LITERAL_NODE")
539
+ * // filtered is typed as (HTMLTextNode | LiteralNode)[]
540
+ *
541
+ * @example
542
+ * const filtered = filterNodes(nodes, HTMLTextNode, LiteralNode)
543
+ * // filtered is typed as (HTMLTextNode | LiteralNode)[]
544
+ *
545
+ * @example
546
+ * const filtered = filterNodes(nodes, isHTMLTextNode, isLiteralNode)
547
+ * // filtered contains only HTMLTextNode or LiteralNode instances
548
+ *
549
+ * @example
550
+ * const filtered = filterNodes(nodes, "AST_LITERAL_NODE", HTMLTextNode, isERBContentNode)
551
+ * // filtered contains only LiteralNode, HTMLTextNode, or ERBContentNode instances
552
+ */
553
+
554
+ export function filterNodes<T extends NodeType[]>(
555
+ nodes: Node[] | undefined | null,
556
+ ...types: T
557
+ ): NodeTypeToClass[T[number]][]
558
+
559
+ export function filterNodes<T extends (new (...args: any[]) => Node)[]>(
560
+ nodes: Node[] | undefined | null,
561
+ ...types: T
562
+ ): ClassToInstance<T[number]>[]
563
+
564
+ export function filterNodes(
565
+ nodes: Node[] | undefined | null,
566
+ ...types: Array<(node: Node) => boolean>
567
+ ): Node[]
568
+
569
+ export function filterNodes(
570
+ nodes: Node[] | undefined | null,
571
+ ...types: Array<NodeType | (new (...args: any[]) => Node) | ((node: Node) => boolean)>
572
+ ): Node[] {
573
+ if (!nodes) return []
574
+
575
+ return nodes.filter(node => isAnyOf(node, ...types))
576
+ }
577
+
578
+ /**
579
+ * Checks if a node matches a specific type identifier with proper type narrowing
580
+ * Supports AST type strings or node classes
581
+ *
582
+ * @example
583
+ * if (isNode(node, "AST_HTML_TEXT_NODE")) {
584
+ * // node is narrowed to HTMLTextNode
585
+ * }
586
+ *
587
+ * @example
588
+ * if (isNode(node, HTMLTextNode)) {
589
+ * // node is narrowed to HTMLTextNode
590
+ * }
591
+ */
592
+
593
+ export function isNode<T extends NodeType>(
594
+ node: Node,
595
+ type: T
596
+ ): node is NodeTypeToClass[T]
597
+
598
+ export function isNode<T extends new (...args: any[]) => Node>(
599
+ node: Node,
600
+ type: T
601
+ ): node is ClassToInstance<T>
602
+
603
+ export function isNode(
604
+ node: Node,
605
+ type: NodeType | (new (...args: any[]) => Node)
606
+ ): boolean {
607
+ if (typeof type === 'string') {
608
+ const guard = AST_TYPE_GUARDS.get(type)
609
+
610
+ return guard ? guard(node) : false
611
+ } else if (typeof type === 'function') {
612
+ const guard = NODE_TYPE_GUARDS.get(type as new (...args: any[]) => Node)
613
+
614
+ return guard ? guard(node) : false
615
+ } else {
616
+ return false
617
+ }
618
+ }
619
+
620
+ export function isToken(object: any): object is Token {
621
+ return (object instanceof Token) || (object?.constructor?.name === "Token" && "value" in object) || (object as any).type?.startsWith('TOKEN_')
622
+ }
623
+
624
+ export function isParseResult(object: any): object is ParseResult {
625
+ return (object instanceof ParseResult) || (object?.constructor?.name === "ParseResult" && "value" in object)
626
+ }
627
+
628
+ /**
629
+ * Checks if a node has children (contains other nodes)
630
+ */
631
+ export function hasChildren(node: Node): boolean {
632
+ return isDocumentNode(node) ||
633
+ isHTMLOpenTagNode(node) ||
634
+ isHTMLCloseTagNode(node) ||
635
+ isHTMLElementNode(node) ||
636
+ isHTMLAttributeValueNode(node) ||
637
+ isHTMLAttributeNameNode(node) ||
638
+ isHTMLCommentNode(node) ||
639
+ isHTMLDoctypeNode(node) ||
640
+ isERBElseNode(node) ||
641
+ isERBIfNode(node) ||
642
+ isERBBlockNode(node) ||
643
+ isERBWhenNode(node) ||
644
+ isERBCaseNode(node) ||
645
+ isERBCaseMatchNode(node) ||
646
+ isERBWhileNode(node) ||
647
+ isERBUntilNode(node) ||
648
+ isERBForNode(node) ||
649
+ isERBRescueNode(node) ||
650
+ isERBEnsureNode(node) ||
651
+ isERBBeginNode(node) ||
652
+ isERBUnlessNode(node) ||
653
+ isERBInNode(node)
654
+ }
655
+
656
+ /**
657
+ * Filter functions for extracting specific node types from arrays
658
+ */
659
+
660
+ /**
661
+ * Filters an array of nodes to only include DocumentNode nodes
662
+ */
663
+ export function filterDocumentNodes(nodes: Node[]): DocumentNode[] {
664
+ return nodes.filter(isDocumentNode) as DocumentNode[]
665
+ }
666
+
667
+ /**
668
+ * Filters an array of nodes to only include LiteralNode nodes
669
+ */
670
+ export function filterLiteralNodes(nodes: Node[]): LiteralNode[] {
671
+ return nodes.filter(isLiteralNode) as LiteralNode[]
672
+ }
673
+
674
+ /**
675
+ * Filters an array of nodes to only include HTMLOpenTagNode nodes
676
+ */
677
+ export function filterHTMLOpenTagNodes(nodes: Node[]): HTMLOpenTagNode[] {
678
+ return nodes.filter(isHTMLOpenTagNode) as HTMLOpenTagNode[]
679
+ }
680
+
681
+ /**
682
+ * Filters an array of nodes to only include HTMLCloseTagNode nodes
683
+ */
684
+ export function filterHTMLCloseTagNodes(nodes: Node[]): HTMLCloseTagNode[] {
685
+ return nodes.filter(isHTMLCloseTagNode) as HTMLCloseTagNode[]
686
+ }
687
+
688
+ /**
689
+ * Filters an array of nodes to only include HTMLElementNode nodes
690
+ */
691
+ export function filterHTMLElementNodes(nodes: Node[]): HTMLElementNode[] {
692
+ return nodes.filter(isHTMLElementNode) as HTMLElementNode[]
693
+ }
694
+
695
+ /**
696
+ * Filters an array of nodes to only include HTMLAttributeValueNode nodes
697
+ */
698
+ export function filterHTMLAttributeValueNodes(nodes: Node[]): HTMLAttributeValueNode[] {
699
+ return nodes.filter(isHTMLAttributeValueNode) as HTMLAttributeValueNode[]
700
+ }
701
+
702
+ /**
703
+ * Filters an array of nodes to only include HTMLAttributeNameNode nodes
704
+ */
705
+ export function filterHTMLAttributeNameNodes(nodes: Node[]): HTMLAttributeNameNode[] {
706
+ return nodes.filter(isHTMLAttributeNameNode) as HTMLAttributeNameNode[]
707
+ }
708
+
709
+ /**
710
+ * Filters an array of nodes to only include HTMLAttributeNode nodes
711
+ */
712
+ export function filterHTMLAttributeNodes(nodes: Node[]): HTMLAttributeNode[] {
713
+ return nodes.filter(isHTMLAttributeNode) as HTMLAttributeNode[]
714
+ }
715
+
716
+ /**
717
+ * Filters an array of nodes to only include HTMLTextNode nodes
718
+ */
719
+ export function filterHTMLTextNodes(nodes: Node[]): HTMLTextNode[] {
720
+ return nodes.filter(isHTMLTextNode) as HTMLTextNode[]
721
+ }
722
+
723
+ /**
724
+ * Filters an array of nodes to only include HTMLCommentNode nodes
725
+ */
726
+ export function filterHTMLCommentNodes(nodes: Node[]): HTMLCommentNode[] {
727
+ return nodes.filter(isHTMLCommentNode) as HTMLCommentNode[]
728
+ }
729
+
730
+ /**
731
+ * Filters an array of nodes to only include HTMLDoctypeNode nodes
732
+ */
733
+ export function filterHTMLDoctypeNodes(nodes: Node[]): HTMLDoctypeNode[] {
734
+ return nodes.filter(isHTMLDoctypeNode) as HTMLDoctypeNode[]
735
+ }
736
+
737
+ /**
738
+ * Filters an array of nodes to only include XMLDeclarationNode nodes
739
+ */
740
+ export function filterXMLDeclarationNodes(nodes: Node[]): XMLDeclarationNode[] {
741
+ return nodes.filter(isXMLDeclarationNode) as XMLDeclarationNode[]
742
+ }
743
+
744
+ /**
745
+ * Filters an array of nodes to only include CDATANode nodes
746
+ */
747
+ export function filterCDATANodes(nodes: Node[]): CDATANode[] {
748
+ return nodes.filter(isCDATANode) as CDATANode[]
749
+ }
750
+
751
+ /**
752
+ * Filters an array of nodes to only include WhitespaceNode nodes
753
+ */
754
+ export function filterWhitespaceNodes(nodes: Node[]): WhitespaceNode[] {
755
+ return nodes.filter(isWhitespaceNode) as WhitespaceNode[]
756
+ }
757
+
758
+ /**
759
+ * Filters an array of nodes to only include ERBContentNode nodes
760
+ */
761
+ export function filterERBContentNodes(nodes: Node[]): ERBContentNode[] {
762
+ return nodes.filter(isERBContentNode) as ERBContentNode[]
763
+ }
764
+
765
+ /**
766
+ * Filters an array of nodes to only include ERBEndNode nodes
767
+ */
768
+ export function filterERBEndNodes(nodes: Node[]): ERBEndNode[] {
769
+ return nodes.filter(isERBEndNode) as ERBEndNode[]
770
+ }
771
+
772
+ /**
773
+ * Filters an array of nodes to only include ERBElseNode nodes
774
+ */
775
+ export function filterERBElseNodes(nodes: Node[]): ERBElseNode[] {
776
+ return nodes.filter(isERBElseNode) as ERBElseNode[]
777
+ }
778
+
779
+ /**
780
+ * Filters an array of nodes to only include ERBIfNode nodes
781
+ */
782
+ export function filterERBIfNodes(nodes: Node[]): ERBIfNode[] {
783
+ return nodes.filter(isERBIfNode) as ERBIfNode[]
784
+ }
785
+
786
+ /**
787
+ * Filters an array of nodes to only include ERBBlockNode nodes
788
+ */
789
+ export function filterERBBlockNodes(nodes: Node[]): ERBBlockNode[] {
790
+ return nodes.filter(isERBBlockNode) as ERBBlockNode[]
791
+ }
792
+
793
+ /**
794
+ * Filters an array of nodes to only include ERBWhenNode nodes
795
+ */
796
+ export function filterERBWhenNodes(nodes: Node[]): ERBWhenNode[] {
797
+ return nodes.filter(isERBWhenNode) as ERBWhenNode[]
798
+ }
799
+
800
+ /**
801
+ * Filters an array of nodes to only include ERBCaseNode nodes
802
+ */
803
+ export function filterERBCaseNodes(nodes: Node[]): ERBCaseNode[] {
804
+ return nodes.filter(isERBCaseNode) as ERBCaseNode[]
805
+ }
806
+
807
+ /**
808
+ * Filters an array of nodes to only include ERBCaseMatchNode nodes
809
+ */
810
+ export function filterERBCaseMatchNodes(nodes: Node[]): ERBCaseMatchNode[] {
811
+ return nodes.filter(isERBCaseMatchNode) as ERBCaseMatchNode[]
812
+ }
813
+
814
+ /**
815
+ * Filters an array of nodes to only include ERBWhileNode nodes
816
+ */
817
+ export function filterERBWhileNodes(nodes: Node[]): ERBWhileNode[] {
818
+ return nodes.filter(isERBWhileNode) as ERBWhileNode[]
819
+ }
820
+
821
+ /**
822
+ * Filters an array of nodes to only include ERBUntilNode nodes
823
+ */
824
+ export function filterERBUntilNodes(nodes: Node[]): ERBUntilNode[] {
825
+ return nodes.filter(isERBUntilNode) as ERBUntilNode[]
826
+ }
827
+
828
+ /**
829
+ * Filters an array of nodes to only include ERBForNode nodes
830
+ */
831
+ export function filterERBForNodes(nodes: Node[]): ERBForNode[] {
832
+ return nodes.filter(isERBForNode) as ERBForNode[]
833
+ }
834
+
835
+ /**
836
+ * Filters an array of nodes to only include ERBRescueNode nodes
837
+ */
838
+ export function filterERBRescueNodes(nodes: Node[]): ERBRescueNode[] {
839
+ return nodes.filter(isERBRescueNode) as ERBRescueNode[]
840
+ }
841
+
842
+ /**
843
+ * Filters an array of nodes to only include ERBEnsureNode nodes
844
+ */
845
+ export function filterERBEnsureNodes(nodes: Node[]): ERBEnsureNode[] {
846
+ return nodes.filter(isERBEnsureNode) as ERBEnsureNode[]
847
+ }
848
+
849
+ /**
850
+ * Filters an array of nodes to only include ERBBeginNode nodes
851
+ */
852
+ export function filterERBBeginNodes(nodes: Node[]): ERBBeginNode[] {
853
+ return nodes.filter(isERBBeginNode) as ERBBeginNode[]
854
+ }
855
+
856
+ /**
857
+ * Filters an array of nodes to only include ERBUnlessNode nodes
858
+ */
859
+ export function filterERBUnlessNodes(nodes: Node[]): ERBUnlessNode[] {
860
+ return nodes.filter(isERBUnlessNode) as ERBUnlessNode[]
861
+ }
862
+
863
+ /**
864
+ * Filters an array of nodes to only include ERBYieldNode nodes
865
+ */
866
+ export function filterERBYieldNodes(nodes: Node[]): ERBYieldNode[] {
867
+ return nodes.filter(isERBYieldNode) as ERBYieldNode[]
868
+ }
869
+
870
+ /**
871
+ * Filters an array of nodes to only include ERBInNode nodes
872
+ */
873
+ export function filterERBInNodes(nodes: Node[]): ERBInNode[] {
874
+ return nodes.filter(isERBInNode) as ERBInNode[]
875
+ }
876
+