@herb-tools/core 0.8.10 → 0.9.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,11 @@
1
+ export interface ExtractRubyOptions {
2
+ semicolons?: boolean
3
+ comments?: boolean
4
+ preserve_positions?: boolean
5
+ }
6
+
7
+ export const DEFAULT_EXTRACT_RUBY_OPTIONS: ExtractRubyOptions = {
8
+ semicolons: true,
9
+ comments: false,
10
+ preserve_positions: true,
11
+ }
@@ -4,9 +4,13 @@ import { ensureString } from "./util.js"
4
4
  import { LexResult } from "./lex-result.js"
5
5
  import { ParseResult } from "./parse-result.js"
6
6
  import { DEFAULT_PARSER_OPTIONS } from "./parser-options.js"
7
+ import { DEFAULT_EXTRACT_RUBY_OPTIONS } from "./extract-ruby-options.js"
8
+ import { deserializePrismParseResult } from "./prism/index.js"
7
9
 
8
10
  import type { LibHerbBackend, BackendPromise } from "./backend.js"
9
- import type { ParserOptions } from "./parser-options.js"
11
+ import type { ParseOptions } from "./parser-options.js"
12
+ import type { ExtractRubyOptions } from "./extract-ruby-options.js"
13
+ import type { PrismParseResult } from "./prism/index.js"
10
14
 
11
15
  /**
12
16
  * The main Herb parser interface, providing methods to lex and parse input.
@@ -55,13 +59,8 @@ export abstract class HerbBackend {
55
59
  * Lexes a file.
56
60
  * @param path - The file path to lex.
57
61
  * @returns A `LexResult` instance.
58
- * @throws Error if the backend is not loaded.
59
62
  */
60
- lexFile(path: string): LexResult {
61
- this.ensureBackend()
62
-
63
- return LexResult.from(this.backend.lexFile(ensureString(path)))
64
- }
63
+ abstract lexFile(path: string): LexResult
65
64
 
66
65
  /**
67
66
  * Parses the given source string into a `ParseResult`.
@@ -70,7 +69,7 @@ export abstract class HerbBackend {
70
69
  * @returns A `ParseResult` instance.
71
70
  * @throws Error if the backend is not loaded.
72
71
  */
73
- parse(source: string, options?: ParserOptions): ParseResult {
72
+ parse(source: string, options?: ParseOptions): ParseResult {
74
73
  this.ensureBackend()
75
74
 
76
75
  const mergedOptions = { ...DEFAULT_PARSER_OPTIONS, ...options }
@@ -82,24 +81,40 @@ export abstract class HerbBackend {
82
81
  * Parses a file.
83
82
  * @param path - The file path to parse.
84
83
  * @returns A `ParseResult` instance.
84
+ */
85
+ abstract parseFile(path: string): ParseResult
86
+
87
+ /**
88
+ * Extracts embedded Ruby code from the given source.
89
+ * @param source - The source code to extract Ruby from.
90
+ * @param options - Optional extraction options.
91
+ * @returns The extracted Ruby code as a string.
85
92
  * @throws Error if the backend is not loaded.
86
93
  */
87
- parseFile(path: string): ParseResult {
94
+ extractRuby(source: string, options?: ExtractRubyOptions): string {
88
95
  this.ensureBackend()
89
96
 
90
- return ParseResult.from(this.backend.parseFile(ensureString(path)))
97
+ const mergedOptions = { ...DEFAULT_EXTRACT_RUBY_OPTIONS, ...options }
98
+
99
+ return this.backend.extractRuby(ensureString(source), mergedOptions)
91
100
  }
92
101
 
93
102
  /**
94
- * Extracts embedded Ruby code from the given source.
95
- * @param source - The source code to extract Ruby from.
96
- * @returns The extracted Ruby code as a string.
103
+ * Parses a Ruby source string using Prism via the libherb backend.
104
+ * @param source - The Ruby source code to parse.
105
+ * @returns A Prism ParseResult containing the AST.
97
106
  * @throws Error if the backend is not loaded.
98
107
  */
99
- extractRuby(source: string): string {
108
+ parseRuby(source: string): PrismParseResult {
100
109
  this.ensureBackend()
101
110
 
102
- return this.backend.extractRuby(ensureString(source))
111
+ const bytes = this.backend.parseRuby(ensureString(source))
112
+
113
+ if (!bytes) {
114
+ throw new Error("Failed to parse Ruby source")
115
+ }
116
+
117
+ return deserializePrismParseResult(bytes, source)
103
118
  }
104
119
 
105
120
  /**
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ export * from "./backend.js"
3
3
  export * from "./diagnostic.js"
4
4
  export * from "./didyoumean.js"
5
5
  export * from "./errors.js"
6
+ export * from "./extract-ruby-options.js"
6
7
  export * from "./herb-backend.js"
7
8
  export * from "./levenshtein.js"
8
9
  export * from "./lex-result.js"
@@ -19,3 +20,4 @@ export * from "./token.js"
19
20
  export * from "./util.js"
20
21
  export * from "./visitor.js"
21
22
  export * from "./warning.js"
23
+ export * from "./prism"
@@ -1,5 +1,5 @@
1
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.8.10/templates/javascript/packages/core/src/node-type-guards.ts.erb
2
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.0/templates/javascript/packages/core/src/node-type-guards.ts.erb
3
3
 
4
4
  import type { Node, NodeType, ERBNode } from "./nodes.js"
5
5
 
@@ -10,11 +10,18 @@ import {
10
10
  DocumentNode,
11
11
  LiteralNode,
12
12
  HTMLOpenTagNode,
13
+ HTMLConditionalOpenTagNode,
13
14
  HTMLCloseTagNode,
15
+ HTMLOmittedCloseTagNode,
16
+ HTMLVirtualCloseTagNode,
14
17
  HTMLElementNode,
18
+ HTMLConditionalElementNode,
15
19
  HTMLAttributeValueNode,
16
20
  HTMLAttributeNameNode,
17
21
  HTMLAttributeNode,
22
+ RubyLiteralNode,
23
+ RubyHTMLAttributesSplatNode,
24
+ ERBOpenTagNode,
18
25
  HTMLTextNode,
19
26
  HTMLCommentNode,
20
27
  HTMLDoctypeNode,
@@ -50,217 +57,342 @@ import {
50
57
  /**
51
58
  * Checks if a node is a DocumentNode
52
59
  */
53
- export function isDocumentNode(node: Node): node is DocumentNode {
60
+ export function isDocumentNode(node: Node | null | undefined): node is DocumentNode {
61
+ if (!node) return false
62
+
54
63
  return node instanceof DocumentNode || node.type === "AST_DOCUMENT_NODE" || (node.constructor as any).type === "AST_DOCUMENT_NODE"
55
64
  }
56
65
 
57
66
  /**
58
67
  * Checks if a node is a LiteralNode
59
68
  */
60
- export function isLiteralNode(node: Node): node is LiteralNode {
69
+ export function isLiteralNode(node: Node | null | undefined): node is LiteralNode {
70
+ if (!node) return false
71
+
61
72
  return node instanceof LiteralNode || node.type === "AST_LITERAL_NODE" || (node.constructor as any).type === "AST_LITERAL_NODE"
62
73
  }
63
74
 
64
75
  /**
65
76
  * Checks if a node is a HTMLOpenTagNode
66
77
  */
67
- export function isHTMLOpenTagNode(node: Node): node is HTMLOpenTagNode {
78
+ export function isHTMLOpenTagNode(node: Node | null | undefined): node is HTMLOpenTagNode {
79
+ if (!node) return false
80
+
68
81
  return node instanceof HTMLOpenTagNode || node.type === "AST_HTML_OPEN_TAG_NODE" || (node.constructor as any).type === "AST_HTML_OPEN_TAG_NODE"
69
82
  }
70
83
 
84
+ /**
85
+ * Checks if a node is a HTMLConditionalOpenTagNode
86
+ */
87
+ export function isHTMLConditionalOpenTagNode(node: Node | null | undefined): node is HTMLConditionalOpenTagNode {
88
+ if (!node) return false
89
+
90
+ return node instanceof HTMLConditionalOpenTagNode || node.type === "AST_HTML_CONDITIONAL_OPEN_TAG_NODE" || (node.constructor as any).type === "AST_HTML_CONDITIONAL_OPEN_TAG_NODE"
91
+ }
92
+
71
93
  /**
72
94
  * Checks if a node is a HTMLCloseTagNode
73
95
  */
74
- export function isHTMLCloseTagNode(node: Node): node is HTMLCloseTagNode {
96
+ export function isHTMLCloseTagNode(node: Node | null | undefined): node is HTMLCloseTagNode {
97
+ if (!node) return false
98
+
75
99
  return node instanceof HTMLCloseTagNode || node.type === "AST_HTML_CLOSE_TAG_NODE" || (node.constructor as any).type === "AST_HTML_CLOSE_TAG_NODE"
76
100
  }
77
101
 
102
+ /**
103
+ * Checks if a node is a HTMLOmittedCloseTagNode
104
+ */
105
+ export function isHTMLOmittedCloseTagNode(node: Node | null | undefined): node is HTMLOmittedCloseTagNode {
106
+ if (!node) return false
107
+
108
+ return node instanceof HTMLOmittedCloseTagNode || node.type === "AST_HTML_OMITTED_CLOSE_TAG_NODE" || (node.constructor as any).type === "AST_HTML_OMITTED_CLOSE_TAG_NODE"
109
+ }
110
+
111
+ /**
112
+ * Checks if a node is a HTMLVirtualCloseTagNode
113
+ */
114
+ export function isHTMLVirtualCloseTagNode(node: Node | null | undefined): node is HTMLVirtualCloseTagNode {
115
+ if (!node) return false
116
+
117
+ return node instanceof HTMLVirtualCloseTagNode || node.type === "AST_HTML_VIRTUAL_CLOSE_TAG_NODE" || (node.constructor as any).type === "AST_HTML_VIRTUAL_CLOSE_TAG_NODE"
118
+ }
119
+
78
120
  /**
79
121
  * Checks if a node is a HTMLElementNode
80
122
  */
81
- export function isHTMLElementNode(node: Node): node is HTMLElementNode {
123
+ export function isHTMLElementNode(node: Node | null | undefined): node is HTMLElementNode {
124
+ if (!node) return false
125
+
82
126
  return node instanceof HTMLElementNode || node.type === "AST_HTML_ELEMENT_NODE" || (node.constructor as any).type === "AST_HTML_ELEMENT_NODE"
83
127
  }
84
128
 
129
+ /**
130
+ * Checks if a node is a HTMLConditionalElementNode
131
+ */
132
+ export function isHTMLConditionalElementNode(node: Node | null | undefined): node is HTMLConditionalElementNode {
133
+ if (!node) return false
134
+
135
+ return node instanceof HTMLConditionalElementNode || node.type === "AST_HTML_CONDITIONAL_ELEMENT_NODE" || (node.constructor as any).type === "AST_HTML_CONDITIONAL_ELEMENT_NODE"
136
+ }
137
+
85
138
  /**
86
139
  * Checks if a node is a HTMLAttributeValueNode
87
140
  */
88
- export function isHTMLAttributeValueNode(node: Node): node is HTMLAttributeValueNode {
141
+ export function isHTMLAttributeValueNode(node: Node | null | undefined): node is HTMLAttributeValueNode {
142
+ if (!node) return false
143
+
89
144
  return node instanceof HTMLAttributeValueNode || node.type === "AST_HTML_ATTRIBUTE_VALUE_NODE" || (node.constructor as any).type === "AST_HTML_ATTRIBUTE_VALUE_NODE"
90
145
  }
91
146
 
92
147
  /**
93
148
  * Checks if a node is a HTMLAttributeNameNode
94
149
  */
95
- export function isHTMLAttributeNameNode(node: Node): node is HTMLAttributeNameNode {
150
+ export function isHTMLAttributeNameNode(node: Node | null | undefined): node is HTMLAttributeNameNode {
151
+ if (!node) return false
152
+
96
153
  return node instanceof HTMLAttributeNameNode || node.type === "AST_HTML_ATTRIBUTE_NAME_NODE" || (node.constructor as any).type === "AST_HTML_ATTRIBUTE_NAME_NODE"
97
154
  }
98
155
 
99
156
  /**
100
157
  * Checks if a node is a HTMLAttributeNode
101
158
  */
102
- export function isHTMLAttributeNode(node: Node): node is HTMLAttributeNode {
159
+ export function isHTMLAttributeNode(node: Node | null | undefined): node is HTMLAttributeNode {
160
+ if (!node) return false
161
+
103
162
  return node instanceof HTMLAttributeNode || node.type === "AST_HTML_ATTRIBUTE_NODE" || (node.constructor as any).type === "AST_HTML_ATTRIBUTE_NODE"
104
163
  }
105
164
 
165
+ /**
166
+ * Checks if a node is a RubyLiteralNode
167
+ */
168
+ export function isRubyLiteralNode(node: Node | null | undefined): node is RubyLiteralNode {
169
+ if (!node) return false
170
+
171
+ return node instanceof RubyLiteralNode || node.type === "AST_RUBY_LITERAL_NODE" || (node.constructor as any).type === "AST_RUBY_LITERAL_NODE"
172
+ }
173
+
174
+ /**
175
+ * Checks if a node is a RubyHTMLAttributesSplatNode
176
+ */
177
+ export function isRubyHTMLAttributesSplatNode(node: Node | null | undefined): node is RubyHTMLAttributesSplatNode {
178
+ if (!node) return false
179
+
180
+ return node instanceof RubyHTMLAttributesSplatNode || node.type === "AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE" || (node.constructor as any).type === "AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE"
181
+ }
182
+
183
+ /**
184
+ * Checks if a node is a ERBOpenTagNode
185
+ */
186
+ export function isERBOpenTagNode(node: Node | null | undefined): node is ERBOpenTagNode {
187
+ if (!node) return false
188
+
189
+ return node instanceof ERBOpenTagNode || node.type === "AST_ERB_OPEN_TAG_NODE" || (node.constructor as any).type === "AST_ERB_OPEN_TAG_NODE"
190
+ }
191
+
106
192
  /**
107
193
  * Checks if a node is a HTMLTextNode
108
194
  */
109
- export function isHTMLTextNode(node: Node): node is HTMLTextNode {
195
+ export function isHTMLTextNode(node: Node | null | undefined): node is HTMLTextNode {
196
+ if (!node) return false
197
+
110
198
  return node instanceof HTMLTextNode || node.type === "AST_HTML_TEXT_NODE" || (node.constructor as any).type === "AST_HTML_TEXT_NODE"
111
199
  }
112
200
 
113
201
  /**
114
202
  * Checks if a node is a HTMLCommentNode
115
203
  */
116
- export function isHTMLCommentNode(node: Node): node is HTMLCommentNode {
204
+ export function isHTMLCommentNode(node: Node | null | undefined): node is HTMLCommentNode {
205
+ if (!node) return false
206
+
117
207
  return node instanceof HTMLCommentNode || node.type === "AST_HTML_COMMENT_NODE" || (node.constructor as any).type === "AST_HTML_COMMENT_NODE"
118
208
  }
119
209
 
120
210
  /**
121
211
  * Checks if a node is a HTMLDoctypeNode
122
212
  */
123
- export function isHTMLDoctypeNode(node: Node): node is HTMLDoctypeNode {
213
+ export function isHTMLDoctypeNode(node: Node | null | undefined): node is HTMLDoctypeNode {
214
+ if (!node) return false
215
+
124
216
  return node instanceof HTMLDoctypeNode || node.type === "AST_HTML_DOCTYPE_NODE" || (node.constructor as any).type === "AST_HTML_DOCTYPE_NODE"
125
217
  }
126
218
 
127
219
  /**
128
220
  * Checks if a node is a XMLDeclarationNode
129
221
  */
130
- export function isXMLDeclarationNode(node: Node): node is XMLDeclarationNode {
222
+ export function isXMLDeclarationNode(node: Node | null | undefined): node is XMLDeclarationNode {
223
+ if (!node) return false
224
+
131
225
  return node instanceof XMLDeclarationNode || node.type === "AST_XML_DECLARATION_NODE" || (node.constructor as any).type === "AST_XML_DECLARATION_NODE"
132
226
  }
133
227
 
134
228
  /**
135
229
  * Checks if a node is a CDATANode
136
230
  */
137
- export function isCDATANode(node: Node): node is CDATANode {
231
+ export function isCDATANode(node: Node | null | undefined): node is CDATANode {
232
+ if (!node) return false
233
+
138
234
  return node instanceof CDATANode || node.type === "AST_CDATA_NODE" || (node.constructor as any).type === "AST_CDATA_NODE"
139
235
  }
140
236
 
141
237
  /**
142
238
  * Checks if a node is a WhitespaceNode
143
239
  */
144
- export function isWhitespaceNode(node: Node): node is WhitespaceNode {
240
+ export function isWhitespaceNode(node: Node | null | undefined): node is WhitespaceNode {
241
+ if (!node) return false
242
+
145
243
  return node instanceof WhitespaceNode || node.type === "AST_WHITESPACE_NODE" || (node.constructor as any).type === "AST_WHITESPACE_NODE"
146
244
  }
147
245
 
148
246
  /**
149
247
  * Checks if a node is a ERBContentNode
150
248
  */
151
- export function isERBContentNode(node: Node): node is ERBContentNode {
249
+ export function isERBContentNode(node: Node | null | undefined): node is ERBContentNode {
250
+ if (!node) return false
251
+
152
252
  return node instanceof ERBContentNode || node.type === "AST_ERB_CONTENT_NODE" || (node.constructor as any).type === "AST_ERB_CONTENT_NODE"
153
253
  }
154
254
 
155
255
  /**
156
256
  * Checks if a node is a ERBEndNode
157
257
  */
158
- export function isERBEndNode(node: Node): node is ERBEndNode {
258
+ export function isERBEndNode(node: Node | null | undefined): node is ERBEndNode {
259
+ if (!node) return false
260
+
159
261
  return node instanceof ERBEndNode || node.type === "AST_ERB_END_NODE" || (node.constructor as any).type === "AST_ERB_END_NODE"
160
262
  }
161
263
 
162
264
  /**
163
265
  * Checks if a node is a ERBElseNode
164
266
  */
165
- export function isERBElseNode(node: Node): node is ERBElseNode {
267
+ export function isERBElseNode(node: Node | null | undefined): node is ERBElseNode {
268
+ if (!node) return false
269
+
166
270
  return node instanceof ERBElseNode || node.type === "AST_ERB_ELSE_NODE" || (node.constructor as any).type === "AST_ERB_ELSE_NODE"
167
271
  }
168
272
 
169
273
  /**
170
274
  * Checks if a node is a ERBIfNode
171
275
  */
172
- export function isERBIfNode(node: Node): node is ERBIfNode {
276
+ export function isERBIfNode(node: Node | null | undefined): node is ERBIfNode {
277
+ if (!node) return false
278
+
173
279
  return node instanceof ERBIfNode || node.type === "AST_ERB_IF_NODE" || (node.constructor as any).type === "AST_ERB_IF_NODE"
174
280
  }
175
281
 
176
282
  /**
177
283
  * Checks if a node is a ERBBlockNode
178
284
  */
179
- export function isERBBlockNode(node: Node): node is ERBBlockNode {
285
+ export function isERBBlockNode(node: Node | null | undefined): node is ERBBlockNode {
286
+ if (!node) return false
287
+
180
288
  return node instanceof ERBBlockNode || node.type === "AST_ERB_BLOCK_NODE" || (node.constructor as any).type === "AST_ERB_BLOCK_NODE"
181
289
  }
182
290
 
183
291
  /**
184
292
  * Checks if a node is a ERBWhenNode
185
293
  */
186
- export function isERBWhenNode(node: Node): node is ERBWhenNode {
294
+ export function isERBWhenNode(node: Node | null | undefined): node is ERBWhenNode {
295
+ if (!node) return false
296
+
187
297
  return node instanceof ERBWhenNode || node.type === "AST_ERB_WHEN_NODE" || (node.constructor as any).type === "AST_ERB_WHEN_NODE"
188
298
  }
189
299
 
190
300
  /**
191
301
  * Checks if a node is a ERBCaseNode
192
302
  */
193
- export function isERBCaseNode(node: Node): node is ERBCaseNode {
303
+ export function isERBCaseNode(node: Node | null | undefined): node is ERBCaseNode {
304
+ if (!node) return false
305
+
194
306
  return node instanceof ERBCaseNode || node.type === "AST_ERB_CASE_NODE" || (node.constructor as any).type === "AST_ERB_CASE_NODE"
195
307
  }
196
308
 
197
309
  /**
198
310
  * Checks if a node is a ERBCaseMatchNode
199
311
  */
200
- export function isERBCaseMatchNode(node: Node): node is ERBCaseMatchNode {
312
+ export function isERBCaseMatchNode(node: Node | null | undefined): node is ERBCaseMatchNode {
313
+ if (!node) return false
314
+
201
315
  return node instanceof ERBCaseMatchNode || node.type === "AST_ERB_CASE_MATCH_NODE" || (node.constructor as any).type === "AST_ERB_CASE_MATCH_NODE"
202
316
  }
203
317
 
204
318
  /**
205
319
  * Checks if a node is a ERBWhileNode
206
320
  */
207
- export function isERBWhileNode(node: Node): node is ERBWhileNode {
321
+ export function isERBWhileNode(node: Node | null | undefined): node is ERBWhileNode {
322
+ if (!node) return false
323
+
208
324
  return node instanceof ERBWhileNode || node.type === "AST_ERB_WHILE_NODE" || (node.constructor as any).type === "AST_ERB_WHILE_NODE"
209
325
  }
210
326
 
211
327
  /**
212
328
  * Checks if a node is a ERBUntilNode
213
329
  */
214
- export function isERBUntilNode(node: Node): node is ERBUntilNode {
330
+ export function isERBUntilNode(node: Node | null | undefined): node is ERBUntilNode {
331
+ if (!node) return false
332
+
215
333
  return node instanceof ERBUntilNode || node.type === "AST_ERB_UNTIL_NODE" || (node.constructor as any).type === "AST_ERB_UNTIL_NODE"
216
334
  }
217
335
 
218
336
  /**
219
337
  * Checks if a node is a ERBForNode
220
338
  */
221
- export function isERBForNode(node: Node): node is ERBForNode {
339
+ export function isERBForNode(node: Node | null | undefined): node is ERBForNode {
340
+ if (!node) return false
341
+
222
342
  return node instanceof ERBForNode || node.type === "AST_ERB_FOR_NODE" || (node.constructor as any).type === "AST_ERB_FOR_NODE"
223
343
  }
224
344
 
225
345
  /**
226
346
  * Checks if a node is a ERBRescueNode
227
347
  */
228
- export function isERBRescueNode(node: Node): node is ERBRescueNode {
348
+ export function isERBRescueNode(node: Node | null | undefined): node is ERBRescueNode {
349
+ if (!node) return false
350
+
229
351
  return node instanceof ERBRescueNode || node.type === "AST_ERB_RESCUE_NODE" || (node.constructor as any).type === "AST_ERB_RESCUE_NODE"
230
352
  }
231
353
 
232
354
  /**
233
355
  * Checks if a node is a ERBEnsureNode
234
356
  */
235
- export function isERBEnsureNode(node: Node): node is ERBEnsureNode {
357
+ export function isERBEnsureNode(node: Node | null | undefined): node is ERBEnsureNode {
358
+ if (!node) return false
359
+
236
360
  return node instanceof ERBEnsureNode || node.type === "AST_ERB_ENSURE_NODE" || (node.constructor as any).type === "AST_ERB_ENSURE_NODE"
237
361
  }
238
362
 
239
363
  /**
240
364
  * Checks if a node is a ERBBeginNode
241
365
  */
242
- export function isERBBeginNode(node: Node): node is ERBBeginNode {
366
+ export function isERBBeginNode(node: Node | null | undefined): node is ERBBeginNode {
367
+ if (!node) return false
368
+
243
369
  return node instanceof ERBBeginNode || node.type === "AST_ERB_BEGIN_NODE" || (node.constructor as any).type === "AST_ERB_BEGIN_NODE"
244
370
  }
245
371
 
246
372
  /**
247
373
  * Checks if a node is a ERBUnlessNode
248
374
  */
249
- export function isERBUnlessNode(node: Node): node is ERBUnlessNode {
375
+ export function isERBUnlessNode(node: Node | null | undefined): node is ERBUnlessNode {
376
+ if (!node) return false
377
+
250
378
  return node instanceof ERBUnlessNode || node.type === "AST_ERB_UNLESS_NODE" || (node.constructor as any).type === "AST_ERB_UNLESS_NODE"
251
379
  }
252
380
 
253
381
  /**
254
382
  * Checks if a node is a ERBYieldNode
255
383
  */
256
- export function isERBYieldNode(node: Node): node is ERBYieldNode {
384
+ export function isERBYieldNode(node: Node | null | undefined): node is ERBYieldNode {
385
+ if (!node) return false
386
+
257
387
  return node instanceof ERBYieldNode || node.type === "AST_ERB_YIELD_NODE" || (node.constructor as any).type === "AST_ERB_YIELD_NODE"
258
388
  }
259
389
 
260
390
  /**
261
391
  * Checks if a node is a ERBInNode
262
392
  */
263
- export function isERBInNode(node: Node): node is ERBInNode {
393
+ export function isERBInNode(node: Node | null | undefined): node is ERBInNode {
394
+ if (!node) return false
395
+
264
396
  return node instanceof ERBInNode || node.type === "AST_ERB_IN_NODE" || (node.constructor as any).type === "AST_ERB_IN_NODE"
265
397
  }
266
398
 
@@ -273,8 +405,12 @@ export function isERBInNode(node: Node): node is ERBInNode {
273
405
  */
274
406
  export function isHTMLNode(node: Node): boolean {
275
407
  return isHTMLOpenTagNode(node) ||
408
+ isHTMLConditionalOpenTagNode(node) ||
276
409
  isHTMLCloseTagNode(node) ||
410
+ isHTMLOmittedCloseTagNode(node) ||
411
+ isHTMLVirtualCloseTagNode(node) ||
277
412
  isHTMLElementNode(node) ||
413
+ isHTMLConditionalElementNode(node) ||
278
414
  isHTMLAttributeValueNode(node) ||
279
415
  isHTMLAttributeNameNode(node) ||
280
416
  isHTMLAttributeNode(node) ||
@@ -287,7 +423,8 @@ export function isHTMLNode(node: Node): boolean {
287
423
  * Checks if a node is any ERB node type
288
424
  */
289
425
  export function isERBNode(node: Node): node is ERBNode {
290
- return isERBContentNode(node) ||
426
+ return isERBOpenTagNode(node) ||
427
+ isERBContentNode(node) ||
291
428
  isERBEndNode(node) ||
292
429
  isERBElseNode(node) ||
293
430
  isERBIfNode(node) ||
@@ -320,11 +457,18 @@ export const NODE_TYPE_GUARDS = new Map<new (...args: any[]) => Node, (node: Nod
320
457
  [DocumentNode, isDocumentNode],
321
458
  [LiteralNode, isLiteralNode],
322
459
  [HTMLOpenTagNode, isHTMLOpenTagNode],
460
+ [HTMLConditionalOpenTagNode, isHTMLConditionalOpenTagNode],
323
461
  [HTMLCloseTagNode, isHTMLCloseTagNode],
462
+ [HTMLOmittedCloseTagNode, isHTMLOmittedCloseTagNode],
463
+ [HTMLVirtualCloseTagNode, isHTMLVirtualCloseTagNode],
324
464
  [HTMLElementNode, isHTMLElementNode],
465
+ [HTMLConditionalElementNode, isHTMLConditionalElementNode],
325
466
  [HTMLAttributeValueNode, isHTMLAttributeValueNode],
326
467
  [HTMLAttributeNameNode, isHTMLAttributeNameNode],
327
468
  [HTMLAttributeNode, isHTMLAttributeNode],
469
+ [RubyLiteralNode, isRubyLiteralNode],
470
+ [RubyHTMLAttributesSplatNode, isRubyHTMLAttributesSplatNode],
471
+ [ERBOpenTagNode, isERBOpenTagNode],
328
472
  [HTMLTextNode, isHTMLTextNode],
329
473
  [HTMLCommentNode, isHTMLCommentNode],
330
474
  [HTMLDoctypeNode, isHTMLDoctypeNode],
@@ -364,11 +508,18 @@ export const AST_TYPE_GUARDS = new Map<NodeType, (node: Node) => boolean>([
364
508
  ["AST_DOCUMENT_NODE", isDocumentNode],
365
509
  ["AST_LITERAL_NODE", isLiteralNode],
366
510
  ["AST_HTML_OPEN_TAG_NODE", isHTMLOpenTagNode],
511
+ ["AST_HTML_CONDITIONAL_OPEN_TAG_NODE", isHTMLConditionalOpenTagNode],
367
512
  ["AST_HTML_CLOSE_TAG_NODE", isHTMLCloseTagNode],
513
+ ["AST_HTML_OMITTED_CLOSE_TAG_NODE", isHTMLOmittedCloseTagNode],
514
+ ["AST_HTML_VIRTUAL_CLOSE_TAG_NODE", isHTMLVirtualCloseTagNode],
368
515
  ["AST_HTML_ELEMENT_NODE", isHTMLElementNode],
516
+ ["AST_HTML_CONDITIONAL_ELEMENT_NODE", isHTMLConditionalElementNode],
369
517
  ["AST_HTML_ATTRIBUTE_VALUE_NODE", isHTMLAttributeValueNode],
370
518
  ["AST_HTML_ATTRIBUTE_NAME_NODE", isHTMLAttributeNameNode],
371
519
  ["AST_HTML_ATTRIBUTE_NODE", isHTMLAttributeNode],
520
+ ["AST_RUBY_LITERAL_NODE", isRubyLiteralNode],
521
+ ["AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE", isRubyHTMLAttributesSplatNode],
522
+ ["AST_ERB_OPEN_TAG_NODE", isERBOpenTagNode],
372
523
  ["AST_HTML_TEXT_NODE", isHTMLTextNode],
373
524
  ["AST_HTML_COMMENT_NODE", isHTMLCommentNode],
374
525
  ["AST_HTML_DOCTYPE_NODE", isHTMLDoctypeNode],
@@ -398,11 +549,18 @@ type NodeTypeToClass = {
398
549
  "AST_DOCUMENT_NODE": DocumentNode;
399
550
  "AST_LITERAL_NODE": LiteralNode;
400
551
  "AST_HTML_OPEN_TAG_NODE": HTMLOpenTagNode;
552
+ "AST_HTML_CONDITIONAL_OPEN_TAG_NODE": HTMLConditionalOpenTagNode;
401
553
  "AST_HTML_CLOSE_TAG_NODE": HTMLCloseTagNode;
554
+ "AST_HTML_OMITTED_CLOSE_TAG_NODE": HTMLOmittedCloseTagNode;
555
+ "AST_HTML_VIRTUAL_CLOSE_TAG_NODE": HTMLVirtualCloseTagNode;
402
556
  "AST_HTML_ELEMENT_NODE": HTMLElementNode;
557
+ "AST_HTML_CONDITIONAL_ELEMENT_NODE": HTMLConditionalElementNode;
403
558
  "AST_HTML_ATTRIBUTE_VALUE_NODE": HTMLAttributeValueNode;
404
559
  "AST_HTML_ATTRIBUTE_NAME_NODE": HTMLAttributeNameNode;
405
560
  "AST_HTML_ATTRIBUTE_NODE": HTMLAttributeNode;
561
+ "AST_RUBY_LITERAL_NODE": RubyLiteralNode;
562
+ "AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE": RubyHTMLAttributesSplatNode;
563
+ "AST_ERB_OPEN_TAG_NODE": ERBOpenTagNode;
406
564
  "AST_HTML_TEXT_NODE": HTMLTextNode;
407
565
  "AST_HTML_COMMENT_NODE": HTMLCommentNode;
408
566
  "AST_HTML_DOCTYPE_NODE": HTMLDoctypeNode;
@@ -680,6 +838,13 @@ export function filterHTMLOpenTagNodes(nodes: Node[]): HTMLOpenTagNode[] {
680
838
  return nodes.filter(isHTMLOpenTagNode) as HTMLOpenTagNode[]
681
839
  }
682
840
 
841
+ /**
842
+ * Filters an array of nodes to only include HTMLConditionalOpenTagNode nodes
843
+ */
844
+ export function filterHTMLConditionalOpenTagNodes(nodes: Node[]): HTMLConditionalOpenTagNode[] {
845
+ return nodes.filter(isHTMLConditionalOpenTagNode) as HTMLConditionalOpenTagNode[]
846
+ }
847
+
683
848
  /**
684
849
  * Filters an array of nodes to only include HTMLCloseTagNode nodes
685
850
  */
@@ -687,6 +852,20 @@ export function filterHTMLCloseTagNodes(nodes: Node[]): HTMLCloseTagNode[] {
687
852
  return nodes.filter(isHTMLCloseTagNode) as HTMLCloseTagNode[]
688
853
  }
689
854
 
855
+ /**
856
+ * Filters an array of nodes to only include HTMLOmittedCloseTagNode nodes
857
+ */
858
+ export function filterHTMLOmittedCloseTagNodes(nodes: Node[]): HTMLOmittedCloseTagNode[] {
859
+ return nodes.filter(isHTMLOmittedCloseTagNode) as HTMLOmittedCloseTagNode[]
860
+ }
861
+
862
+ /**
863
+ * Filters an array of nodes to only include HTMLVirtualCloseTagNode nodes
864
+ */
865
+ export function filterHTMLVirtualCloseTagNodes(nodes: Node[]): HTMLVirtualCloseTagNode[] {
866
+ return nodes.filter(isHTMLVirtualCloseTagNode) as HTMLVirtualCloseTagNode[]
867
+ }
868
+
690
869
  /**
691
870
  * Filters an array of nodes to only include HTMLElementNode nodes
692
871
  */
@@ -694,6 +873,13 @@ export function filterHTMLElementNodes(nodes: Node[]): HTMLElementNode[] {
694
873
  return nodes.filter(isHTMLElementNode) as HTMLElementNode[]
695
874
  }
696
875
 
876
+ /**
877
+ * Filters an array of nodes to only include HTMLConditionalElementNode nodes
878
+ */
879
+ export function filterHTMLConditionalElementNodes(nodes: Node[]): HTMLConditionalElementNode[] {
880
+ return nodes.filter(isHTMLConditionalElementNode) as HTMLConditionalElementNode[]
881
+ }
882
+
697
883
  /**
698
884
  * Filters an array of nodes to only include HTMLAttributeValueNode nodes
699
885
  */
@@ -715,6 +901,27 @@ export function filterHTMLAttributeNodes(nodes: Node[]): HTMLAttributeNode[] {
715
901
  return nodes.filter(isHTMLAttributeNode) as HTMLAttributeNode[]
716
902
  }
717
903
 
904
+ /**
905
+ * Filters an array of nodes to only include RubyLiteralNode nodes
906
+ */
907
+ export function filterRubyLiteralNodes(nodes: Node[]): RubyLiteralNode[] {
908
+ return nodes.filter(isRubyLiteralNode) as RubyLiteralNode[]
909
+ }
910
+
911
+ /**
912
+ * Filters an array of nodes to only include RubyHTMLAttributesSplatNode nodes
913
+ */
914
+ export function filterRubyHTMLAttributesSplatNodes(nodes: Node[]): RubyHTMLAttributesSplatNode[] {
915
+ return nodes.filter(isRubyHTMLAttributesSplatNode) as RubyHTMLAttributesSplatNode[]
916
+ }
917
+
918
+ /**
919
+ * Filters an array of nodes to only include ERBOpenTagNode nodes
920
+ */
921
+ export function filterERBOpenTagNodes(nodes: Node[]): ERBOpenTagNode[] {
922
+ return nodes.filter(isERBOpenTagNode) as ERBOpenTagNode[]
923
+ }
924
+
718
925
  /**
719
926
  * Filters an array of nodes to only include HTMLTextNode nodes
720
927
  */