@herb-tools/core 0.5.0 → 0.6.1

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,878 @@
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.1/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.type === "AST_DOCUMENT_NODE" || (node.constructor 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.type === "AST_LITERAL_NODE" || (node.constructor 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.type === "AST_HTML_OPEN_TAG_NODE" || (node.constructor 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.type === "AST_HTML_CLOSE_TAG_NODE" || (node.constructor 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.type === "AST_HTML_ELEMENT_NODE" || (node.constructor 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.type === "AST_HTML_ATTRIBUTE_VALUE_NODE" || (node.constructor 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.type === "AST_HTML_ATTRIBUTE_NAME_NODE" || (node.constructor 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.type === "AST_HTML_ATTRIBUTE_NODE" || (node.constructor 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.type === "AST_HTML_TEXT_NODE" || (node.constructor 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.type === "AST_HTML_COMMENT_NODE" || (node.constructor 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.type === "AST_HTML_DOCTYPE_NODE" || (node.constructor 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.type === "AST_XML_DECLARATION_NODE" || (node.constructor 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.type === "AST_CDATA_NODE" || (node.constructor 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.type === "AST_WHITESPACE_NODE" || (node.constructor 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.type === "AST_ERB_CONTENT_NODE" || (node.constructor 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.type === "AST_ERB_END_NODE" || (node.constructor 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.type === "AST_ERB_ELSE_NODE" || (node.constructor 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.type === "AST_ERB_IF_NODE" || (node.constructor 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.type === "AST_ERB_BLOCK_NODE" || (node.constructor 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.type === "AST_ERB_WHEN_NODE" || (node.constructor 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.type === "AST_ERB_CASE_NODE" || (node.constructor 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.type === "AST_ERB_CASE_MATCH_NODE" || (node.constructor 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.type === "AST_ERB_WHILE_NODE" || (node.constructor 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.type === "AST_ERB_UNTIL_NODE" || (node.constructor 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.type === "AST_ERB_FOR_NODE" || (node.constructor 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.type === "AST_ERB_RESCUE_NODE" || (node.constructor 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.type === "AST_ERB_ENSURE_NODE" || (node.constructor 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.type === "AST_ERB_BEGIN_NODE" || (node.constructor 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.type === "AST_ERB_UNLESS_NODE" || (node.constructor 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.type === "AST_ERB_YIELD_NODE" || (node.constructor 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.type === "AST_ERB_IN_NODE" || (node.constructor 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<NodeType, (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 | null | undefined,
595
+ type: T
596
+ ): node is NodeTypeToClass[T]
597
+
598
+ export function isNode<T extends new (...args: any[]) => Node>(
599
+ node: Node | null | undefined,
600
+ type: T
601
+ ): node is ClassToInstance<T>
602
+
603
+ export function isNode(
604
+ node: Node | null | undefined,
605
+ type: NodeType | (new (...args: any[]) => Node)
606
+ ): boolean {
607
+ if (!node) return false
608
+
609
+ if (typeof type === 'string') {
610
+ const guard = AST_TYPE_GUARDS.get(type)
611
+
612
+ return guard ? guard(node) : false
613
+ } else if (typeof type === 'function') {
614
+ const guard = NODE_TYPE_GUARDS.get(type as new (...args: any[]) => Node)
615
+
616
+ return guard ? guard(node) : false
617
+ } else {
618
+ return false
619
+ }
620
+ }
621
+
622
+ export function isToken(object: any): object is Token {
623
+ return (object instanceof Token) || (object?.constructor?.name === "Token" && "value" in object) || (object as any).type?.startsWith('TOKEN_')
624
+ }
625
+
626
+ export function isParseResult(object: any): object is ParseResult {
627
+ return (object instanceof ParseResult) || (object?.constructor?.name === "ParseResult" && "value" in object)
628
+ }
629
+
630
+ /**
631
+ * Checks if a node has children (contains other nodes)
632
+ */
633
+ export function hasChildren(node: Node): boolean {
634
+ return isDocumentNode(node) ||
635
+ isHTMLOpenTagNode(node) ||
636
+ isHTMLCloseTagNode(node) ||
637
+ isHTMLElementNode(node) ||
638
+ isHTMLAttributeValueNode(node) ||
639
+ isHTMLAttributeNameNode(node) ||
640
+ isHTMLCommentNode(node) ||
641
+ isHTMLDoctypeNode(node) ||
642
+ isERBElseNode(node) ||
643
+ isERBIfNode(node) ||
644
+ isERBBlockNode(node) ||
645
+ isERBWhenNode(node) ||
646
+ isERBCaseNode(node) ||
647
+ isERBCaseMatchNode(node) ||
648
+ isERBWhileNode(node) ||
649
+ isERBUntilNode(node) ||
650
+ isERBForNode(node) ||
651
+ isERBRescueNode(node) ||
652
+ isERBEnsureNode(node) ||
653
+ isERBBeginNode(node) ||
654
+ isERBUnlessNode(node) ||
655
+ isERBInNode(node)
656
+ }
657
+
658
+ /**
659
+ * Filter functions for extracting specific node types from arrays
660
+ */
661
+
662
+ /**
663
+ * Filters an array of nodes to only include DocumentNode nodes
664
+ */
665
+ export function filterDocumentNodes(nodes: Node[]): DocumentNode[] {
666
+ return nodes.filter(isDocumentNode) as DocumentNode[]
667
+ }
668
+
669
+ /**
670
+ * Filters an array of nodes to only include LiteralNode nodes
671
+ */
672
+ export function filterLiteralNodes(nodes: Node[]): LiteralNode[] {
673
+ return nodes.filter(isLiteralNode) as LiteralNode[]
674
+ }
675
+
676
+ /**
677
+ * Filters an array of nodes to only include HTMLOpenTagNode nodes
678
+ */
679
+ export function filterHTMLOpenTagNodes(nodes: Node[]): HTMLOpenTagNode[] {
680
+ return nodes.filter(isHTMLOpenTagNode) as HTMLOpenTagNode[]
681
+ }
682
+
683
+ /**
684
+ * Filters an array of nodes to only include HTMLCloseTagNode nodes
685
+ */
686
+ export function filterHTMLCloseTagNodes(nodes: Node[]): HTMLCloseTagNode[] {
687
+ return nodes.filter(isHTMLCloseTagNode) as HTMLCloseTagNode[]
688
+ }
689
+
690
+ /**
691
+ * Filters an array of nodes to only include HTMLElementNode nodes
692
+ */
693
+ export function filterHTMLElementNodes(nodes: Node[]): HTMLElementNode[] {
694
+ return nodes.filter(isHTMLElementNode) as HTMLElementNode[]
695
+ }
696
+
697
+ /**
698
+ * Filters an array of nodes to only include HTMLAttributeValueNode nodes
699
+ */
700
+ export function filterHTMLAttributeValueNodes(nodes: Node[]): HTMLAttributeValueNode[] {
701
+ return nodes.filter(isHTMLAttributeValueNode) as HTMLAttributeValueNode[]
702
+ }
703
+
704
+ /**
705
+ * Filters an array of nodes to only include HTMLAttributeNameNode nodes
706
+ */
707
+ export function filterHTMLAttributeNameNodes(nodes: Node[]): HTMLAttributeNameNode[] {
708
+ return nodes.filter(isHTMLAttributeNameNode) as HTMLAttributeNameNode[]
709
+ }
710
+
711
+ /**
712
+ * Filters an array of nodes to only include HTMLAttributeNode nodes
713
+ */
714
+ export function filterHTMLAttributeNodes(nodes: Node[]): HTMLAttributeNode[] {
715
+ return nodes.filter(isHTMLAttributeNode) as HTMLAttributeNode[]
716
+ }
717
+
718
+ /**
719
+ * Filters an array of nodes to only include HTMLTextNode nodes
720
+ */
721
+ export function filterHTMLTextNodes(nodes: Node[]): HTMLTextNode[] {
722
+ return nodes.filter(isHTMLTextNode) as HTMLTextNode[]
723
+ }
724
+
725
+ /**
726
+ * Filters an array of nodes to only include HTMLCommentNode nodes
727
+ */
728
+ export function filterHTMLCommentNodes(nodes: Node[]): HTMLCommentNode[] {
729
+ return nodes.filter(isHTMLCommentNode) as HTMLCommentNode[]
730
+ }
731
+
732
+ /**
733
+ * Filters an array of nodes to only include HTMLDoctypeNode nodes
734
+ */
735
+ export function filterHTMLDoctypeNodes(nodes: Node[]): HTMLDoctypeNode[] {
736
+ return nodes.filter(isHTMLDoctypeNode) as HTMLDoctypeNode[]
737
+ }
738
+
739
+ /**
740
+ * Filters an array of nodes to only include XMLDeclarationNode nodes
741
+ */
742
+ export function filterXMLDeclarationNodes(nodes: Node[]): XMLDeclarationNode[] {
743
+ return nodes.filter(isXMLDeclarationNode) as XMLDeclarationNode[]
744
+ }
745
+
746
+ /**
747
+ * Filters an array of nodes to only include CDATANode nodes
748
+ */
749
+ export function filterCDATANodes(nodes: Node[]): CDATANode[] {
750
+ return nodes.filter(isCDATANode) as CDATANode[]
751
+ }
752
+
753
+ /**
754
+ * Filters an array of nodes to only include WhitespaceNode nodes
755
+ */
756
+ export function filterWhitespaceNodes(nodes: Node[]): WhitespaceNode[] {
757
+ return nodes.filter(isWhitespaceNode) as WhitespaceNode[]
758
+ }
759
+
760
+ /**
761
+ * Filters an array of nodes to only include ERBContentNode nodes
762
+ */
763
+ export function filterERBContentNodes(nodes: Node[]): ERBContentNode[] {
764
+ return nodes.filter(isERBContentNode) as ERBContentNode[]
765
+ }
766
+
767
+ /**
768
+ * Filters an array of nodes to only include ERBEndNode nodes
769
+ */
770
+ export function filterERBEndNodes(nodes: Node[]): ERBEndNode[] {
771
+ return nodes.filter(isERBEndNode) as ERBEndNode[]
772
+ }
773
+
774
+ /**
775
+ * Filters an array of nodes to only include ERBElseNode nodes
776
+ */
777
+ export function filterERBElseNodes(nodes: Node[]): ERBElseNode[] {
778
+ return nodes.filter(isERBElseNode) as ERBElseNode[]
779
+ }
780
+
781
+ /**
782
+ * Filters an array of nodes to only include ERBIfNode nodes
783
+ */
784
+ export function filterERBIfNodes(nodes: Node[]): ERBIfNode[] {
785
+ return nodes.filter(isERBIfNode) as ERBIfNode[]
786
+ }
787
+
788
+ /**
789
+ * Filters an array of nodes to only include ERBBlockNode nodes
790
+ */
791
+ export function filterERBBlockNodes(nodes: Node[]): ERBBlockNode[] {
792
+ return nodes.filter(isERBBlockNode) as ERBBlockNode[]
793
+ }
794
+
795
+ /**
796
+ * Filters an array of nodes to only include ERBWhenNode nodes
797
+ */
798
+ export function filterERBWhenNodes(nodes: Node[]): ERBWhenNode[] {
799
+ return nodes.filter(isERBWhenNode) as ERBWhenNode[]
800
+ }
801
+
802
+ /**
803
+ * Filters an array of nodes to only include ERBCaseNode nodes
804
+ */
805
+ export function filterERBCaseNodes(nodes: Node[]): ERBCaseNode[] {
806
+ return nodes.filter(isERBCaseNode) as ERBCaseNode[]
807
+ }
808
+
809
+ /**
810
+ * Filters an array of nodes to only include ERBCaseMatchNode nodes
811
+ */
812
+ export function filterERBCaseMatchNodes(nodes: Node[]): ERBCaseMatchNode[] {
813
+ return nodes.filter(isERBCaseMatchNode) as ERBCaseMatchNode[]
814
+ }
815
+
816
+ /**
817
+ * Filters an array of nodes to only include ERBWhileNode nodes
818
+ */
819
+ export function filterERBWhileNodes(nodes: Node[]): ERBWhileNode[] {
820
+ return nodes.filter(isERBWhileNode) as ERBWhileNode[]
821
+ }
822
+
823
+ /**
824
+ * Filters an array of nodes to only include ERBUntilNode nodes
825
+ */
826
+ export function filterERBUntilNodes(nodes: Node[]): ERBUntilNode[] {
827
+ return nodes.filter(isERBUntilNode) as ERBUntilNode[]
828
+ }
829
+
830
+ /**
831
+ * Filters an array of nodes to only include ERBForNode nodes
832
+ */
833
+ export function filterERBForNodes(nodes: Node[]): ERBForNode[] {
834
+ return nodes.filter(isERBForNode) as ERBForNode[]
835
+ }
836
+
837
+ /**
838
+ * Filters an array of nodes to only include ERBRescueNode nodes
839
+ */
840
+ export function filterERBRescueNodes(nodes: Node[]): ERBRescueNode[] {
841
+ return nodes.filter(isERBRescueNode) as ERBRescueNode[]
842
+ }
843
+
844
+ /**
845
+ * Filters an array of nodes to only include ERBEnsureNode nodes
846
+ */
847
+ export function filterERBEnsureNodes(nodes: Node[]): ERBEnsureNode[] {
848
+ return nodes.filter(isERBEnsureNode) as ERBEnsureNode[]
849
+ }
850
+
851
+ /**
852
+ * Filters an array of nodes to only include ERBBeginNode nodes
853
+ */
854
+ export function filterERBBeginNodes(nodes: Node[]): ERBBeginNode[] {
855
+ return nodes.filter(isERBBeginNode) as ERBBeginNode[]
856
+ }
857
+
858
+ /**
859
+ * Filters an array of nodes to only include ERBUnlessNode nodes
860
+ */
861
+ export function filterERBUnlessNodes(nodes: Node[]): ERBUnlessNode[] {
862
+ return nodes.filter(isERBUnlessNode) as ERBUnlessNode[]
863
+ }
864
+
865
+ /**
866
+ * Filters an array of nodes to only include ERBYieldNode nodes
867
+ */
868
+ export function filterERBYieldNodes(nodes: Node[]): ERBYieldNode[] {
869
+ return nodes.filter(isERBYieldNode) as ERBYieldNode[]
870
+ }
871
+
872
+ /**
873
+ * Filters an array of nodes to only include ERBInNode nodes
874
+ */
875
+ export function filterERBInNodes(nodes: Node[]): ERBInNode[] {
876
+ return nodes.filter(isERBInNode) as ERBInNode[]
877
+ }
878
+