@herb-tools/rewriter 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/loader.cjs CHANGED
@@ -209,7 +209,7 @@ class Token {
209
209
  }
210
210
 
211
211
  // NOTE: This file is generated by the templates/template.rb script and should not
212
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.3/templates/javascript/packages/core/src/errors.ts.erb
212
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.5/templates/javascript/packages/core/src/errors.ts.erb
213
213
  class HerbError {
214
214
  type;
215
215
  message;
@@ -20357,6 +20357,17 @@ function inspectPrismSerialized(bytes, source, prefix = "") {
20357
20357
  }
20358
20358
  }
20359
20359
 
20360
+ /**
20361
+ * Checks if a Prism node is of a specific type by comparing constructor names.
20362
+ *
20363
+ * This is preferred over `instanceof` because `@ruby/prism` classes may be
20364
+ * duplicated across bundled packages, causing `instanceof` checks to fail.
20365
+ */
20366
+ function isPrismNodeType(node, type) {
20367
+ if (!node)
20368
+ return false;
20369
+ return node.constructor?.name === type;
20370
+ }
20360
20371
  /**
20361
20372
  * Deserialize a Prism parse result from the raw bytes produced by pm_serialize().
20362
20373
  *
@@ -20388,7 +20399,7 @@ function deserializePrismNode(bytes, source) {
20388
20399
  }
20389
20400
 
20390
20401
  // NOTE: This file is generated by the templates/template.rb script and should not
20391
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.3/templates/javascript/packages/core/src/nodes.ts.erb
20402
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.5/templates/javascript/packages/core/src/nodes.ts.erb
20392
20403
  class Node {
20393
20404
  type;
20394
20405
  location;
@@ -22009,6 +22020,7 @@ class ERBBlockNode extends Node {
22009
22020
  tag_closing;
22010
22021
  prism_node;
22011
22022
  body;
22023
+ block_arguments;
22012
22024
  rescue_clause;
22013
22025
  else_clause;
22014
22026
  ensure_clause;
@@ -22026,6 +22038,7 @@ class ERBBlockNode extends Node {
22026
22038
  tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
22027
22039
  prism_node: data.prism_node ? new Uint8Array(data.prism_node) : null,
22028
22040
  body: (data.body || []).map(node => fromSerializedNode(node)),
22041
+ block_arguments: (data.block_arguments || []).map(node => fromSerializedNode(node)),
22029
22042
  rescue_clause: data.rescue_clause ? fromSerializedNode((data.rescue_clause)) : null,
22030
22043
  else_clause: data.else_clause ? fromSerializedNode((data.else_clause)) : null,
22031
22044
  ensure_clause: data.ensure_clause ? fromSerializedNode((data.ensure_clause)) : null,
@@ -22039,6 +22052,7 @@ class ERBBlockNode extends Node {
22039
22052
  this.tag_closing = props.tag_closing;
22040
22053
  this.prism_node = props.prism_node;
22041
22054
  this.body = props.body;
22055
+ this.block_arguments = props.block_arguments;
22042
22056
  this.rescue_clause = props.rescue_clause;
22043
22057
  this.else_clause = props.else_clause;
22044
22058
  this.ensure_clause = props.ensure_clause;
@@ -22050,6 +22064,7 @@ class ERBBlockNode extends Node {
22050
22064
  childNodes() {
22051
22065
  return [
22052
22066
  ...this.body,
22067
+ ...this.block_arguments,
22053
22068
  this.rescue_clause,
22054
22069
  this.else_clause,
22055
22070
  this.ensure_clause,
@@ -22068,6 +22083,7 @@ class ERBBlockNode extends Node {
22068
22083
  return [
22069
22084
  ...this.errors,
22070
22085
  ...this.body.map(node => node.recursiveErrors()),
22086
+ ...this.block_arguments.map(node => node.recursiveErrors()),
22071
22087
  this.rescue_clause ? this.rescue_clause.recursiveErrors() : [],
22072
22088
  this.else_clause ? this.else_clause.recursiveErrors() : [],
22073
22089
  this.ensure_clause ? this.ensure_clause.recursiveErrors() : [],
@@ -22083,6 +22099,7 @@ class ERBBlockNode extends Node {
22083
22099
  tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
22084
22100
  prism_node: this.prism_node ? Array.from(this.prism_node) : null,
22085
22101
  body: this.body.map(node => node.toJSON()),
22102
+ block_arguments: this.block_arguments.map(node => node.toJSON()),
22086
22103
  rescue_clause: this.rescue_clause ? this.rescue_clause.toJSON() : null,
22087
22104
  else_clause: this.else_clause ? this.else_clause.toJSON() : null,
22088
22105
  ensure_clause: this.ensure_clause ? this.ensure_clause.toJSON() : null,
@@ -22100,6 +22117,7 @@ class ERBBlockNode extends Node {
22100
22117
  output += `├── prism_node: ${this.source ? inspectPrismSerialized(this.prism_node, this.source, "│ ") : `(${this.prism_node.length} bytes)`}\n`;
22101
22118
  }
22102
22119
  output += `├── body: ${this.inspectArray(this.body, "│ ")}`;
22120
+ output += `├── block_arguments: ${this.inspectArray(this.block_arguments, "│ ")}`;
22103
22121
  output += `├── rescue_clause: ${this.inspectNode(this.rescue_clause, "│ ")}`;
22104
22122
  output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
22105
22123
  output += `├── ensure_clause: ${this.inspectNode(this.ensure_clause, "│ ")}`;
@@ -23007,12 +23025,7 @@ class RubyRenderLocalNode extends Node {
23007
23025
  return output;
23008
23026
  }
23009
23027
  }
23010
- class ERBRenderNode extends Node {
23011
- tag_opening;
23012
- content;
23013
- tag_closing;
23014
- // no-op for analyzed_ruby
23015
- prism_node;
23028
+ class RubyRenderKeywordsNode extends Node {
23016
23029
  partial;
23017
23030
  template_path;
23018
23031
  layout;
@@ -23032,18 +23045,13 @@ class ERBRenderNode extends Node {
23032
23045
  content_type;
23033
23046
  locals;
23034
23047
  static get type() {
23035
- return "AST_ERB_RENDER_NODE";
23048
+ return "AST_RUBY_RENDER_KEYWORDS_NODE";
23036
23049
  }
23037
23050
  static from(data) {
23038
- return new ERBRenderNode({
23051
+ return new RubyRenderKeywordsNode({
23039
23052
  type: data.type,
23040
23053
  location: Location.from(data.location),
23041
23054
  errors: (data.errors || []).map(error => HerbError.from(error)),
23042
- tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
23043
- content: data.content ? Token.from(data.content) : null,
23044
- tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
23045
- // no-op for analyzed_ruby
23046
- prism_node: data.prism_node ? new Uint8Array(data.prism_node) : null,
23047
23055
  partial: data.partial ? Token.from(data.partial) : null,
23048
23056
  template_path: data.template_path ? Token.from(data.template_path) : null,
23049
23057
  layout: data.layout ? Token.from(data.layout) : null,
@@ -23066,11 +23074,6 @@ class ERBRenderNode extends Node {
23066
23074
  }
23067
23075
  constructor(props) {
23068
23076
  super(props.type, props.location, props.errors);
23069
- this.tag_opening = props.tag_opening;
23070
- this.content = props.content;
23071
- this.tag_closing = props.tag_closing;
23072
- // no-op for analyzed_ruby
23073
- this.prism_node = props.prism_node;
23074
23077
  this.partial = props.partial;
23075
23078
  this.template_path = props.template_path;
23076
23079
  this.layout = props.layout;
@@ -23091,7 +23094,7 @@ class ERBRenderNode extends Node {
23091
23094
  this.locals = props.locals;
23092
23095
  }
23093
23096
  accept(visitor) {
23094
- visitor.visitERBRenderNode(this);
23097
+ visitor.visitRubyRenderKeywordsNode(this);
23095
23098
  }
23096
23099
  childNodes() {
23097
23100
  return [
@@ -23101,11 +23104,6 @@ class ERBRenderNode extends Node {
23101
23104
  compactChildNodes() {
23102
23105
  return this.childNodes().filter(node => node !== null && node !== undefined);
23103
23106
  }
23104
- get prismNode() {
23105
- if (!this.prism_node || !this.source)
23106
- return null;
23107
- return deserializePrismNode(this.prism_node, this.source);
23108
- }
23109
23107
  recursiveErrors() {
23110
23108
  return [
23111
23109
  ...this.errors,
@@ -23115,12 +23113,7 @@ class ERBRenderNode extends Node {
23115
23113
  toJSON() {
23116
23114
  return {
23117
23115
  ...super.toJSON(),
23118
- type: "AST_ERB_RENDER_NODE",
23119
- tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
23120
- content: this.content ? this.content.toJSON() : null,
23121
- tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
23122
- // no-op for analyzed_ruby
23123
- prism_node: this.prism_node ? Array.from(this.prism_node) : null,
23116
+ type: "AST_RUBY_RENDER_KEYWORDS_NODE",
23124
23117
  partial: this.partial ? this.partial.toJSON() : null,
23125
23118
  template_path: this.template_path ? this.template_path.toJSON() : null,
23126
23119
  layout: this.layout ? this.layout.toJSON() : null,
@@ -23143,14 +23136,8 @@ class ERBRenderNode extends Node {
23143
23136
  }
23144
23137
  treeInspect() {
23145
23138
  let output = "";
23146
- output += `@ ERBRenderNode ${this.location.treeInspectWithLabel()}\n`;
23139
+ output += `@ RubyRenderKeywordsNode ${this.location.treeInspectWithLabel()}\n`;
23147
23140
  output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
23148
- output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
23149
- output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
23150
- output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
23151
- if (this.prism_node) {
23152
- output += `├── prism_node: ${this.source ? inspectPrismSerialized(this.prism_node, this.source, "│ ") : `(${this.prism_node.length} bytes)`}\n`;
23153
- }
23154
23141
  output += `├── partial: ${this.partial ? this.partial.treeInspect() : "∅"}\n`;
23155
23142
  output += `├── template_path: ${this.template_path ? this.template_path.treeInspect() : "∅"}\n`;
23156
23143
  output += `├── layout: ${this.layout ? this.layout.treeInspect() : "∅"}\n`;
@@ -23172,34 +23159,156 @@ class ERBRenderNode extends Node {
23172
23159
  return output;
23173
23160
  }
23174
23161
  }
23175
- class RubyStrictLocalNode extends Node {
23162
+ class ERBRenderNode extends Node {
23163
+ tag_opening;
23164
+ content;
23165
+ tag_closing;
23166
+ // no-op for analyzed_ruby
23167
+ prism_node;
23168
+ keywords;
23169
+ body;
23170
+ block_arguments;
23171
+ rescue_clause;
23172
+ else_clause;
23173
+ ensure_clause;
23174
+ end_node;
23175
+ static get type() {
23176
+ return "AST_ERB_RENDER_NODE";
23177
+ }
23178
+ static from(data) {
23179
+ return new ERBRenderNode({
23180
+ type: data.type,
23181
+ location: Location.from(data.location),
23182
+ errors: (data.errors || []).map(error => HerbError.from(error)),
23183
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
23184
+ content: data.content ? Token.from(data.content) : null,
23185
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
23186
+ // no-op for analyzed_ruby
23187
+ prism_node: data.prism_node ? new Uint8Array(data.prism_node) : null,
23188
+ keywords: data.keywords ? fromSerializedNode((data.keywords)) : null,
23189
+ body: (data.body || []).map(node => fromSerializedNode(node)),
23190
+ block_arguments: (data.block_arguments || []).map(node => fromSerializedNode(node)),
23191
+ rescue_clause: data.rescue_clause ? fromSerializedNode((data.rescue_clause)) : null,
23192
+ else_clause: data.else_clause ? fromSerializedNode((data.else_clause)) : null,
23193
+ ensure_clause: data.ensure_clause ? fromSerializedNode((data.ensure_clause)) : null,
23194
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
23195
+ });
23196
+ }
23197
+ constructor(props) {
23198
+ super(props.type, props.location, props.errors);
23199
+ this.tag_opening = props.tag_opening;
23200
+ this.content = props.content;
23201
+ this.tag_closing = props.tag_closing;
23202
+ // no-op for analyzed_ruby
23203
+ this.prism_node = props.prism_node;
23204
+ this.keywords = props.keywords;
23205
+ this.body = props.body;
23206
+ this.block_arguments = props.block_arguments;
23207
+ this.rescue_clause = props.rescue_clause;
23208
+ this.else_clause = props.else_clause;
23209
+ this.ensure_clause = props.ensure_clause;
23210
+ this.end_node = props.end_node;
23211
+ }
23212
+ accept(visitor) {
23213
+ visitor.visitERBRenderNode(this);
23214
+ }
23215
+ childNodes() {
23216
+ return [
23217
+ this.keywords,
23218
+ ...this.body,
23219
+ ...this.block_arguments,
23220
+ this.rescue_clause,
23221
+ this.else_clause,
23222
+ this.ensure_clause,
23223
+ this.end_node,
23224
+ ];
23225
+ }
23226
+ compactChildNodes() {
23227
+ return this.childNodes().filter(node => node !== null && node !== undefined);
23228
+ }
23229
+ get prismNode() {
23230
+ if (!this.prism_node || !this.source)
23231
+ return null;
23232
+ return deserializePrismNode(this.prism_node, this.source);
23233
+ }
23234
+ recursiveErrors() {
23235
+ return [
23236
+ ...this.errors,
23237
+ this.keywords ? this.keywords.recursiveErrors() : [],
23238
+ ...this.body.map(node => node.recursiveErrors()),
23239
+ ...this.block_arguments.map(node => node.recursiveErrors()),
23240
+ this.rescue_clause ? this.rescue_clause.recursiveErrors() : [],
23241
+ this.else_clause ? this.else_clause.recursiveErrors() : [],
23242
+ this.ensure_clause ? this.ensure_clause.recursiveErrors() : [],
23243
+ this.end_node ? this.end_node.recursiveErrors() : [],
23244
+ ].flat();
23245
+ }
23246
+ toJSON() {
23247
+ return {
23248
+ ...super.toJSON(),
23249
+ type: "AST_ERB_RENDER_NODE",
23250
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
23251
+ content: this.content ? this.content.toJSON() : null,
23252
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
23253
+ // no-op for analyzed_ruby
23254
+ prism_node: this.prism_node ? Array.from(this.prism_node) : null,
23255
+ keywords: this.keywords ? this.keywords.toJSON() : null,
23256
+ body: this.body.map(node => node.toJSON()),
23257
+ block_arguments: this.block_arguments.map(node => node.toJSON()),
23258
+ rescue_clause: this.rescue_clause ? this.rescue_clause.toJSON() : null,
23259
+ else_clause: this.else_clause ? this.else_clause.toJSON() : null,
23260
+ ensure_clause: this.ensure_clause ? this.ensure_clause.toJSON() : null,
23261
+ end_node: this.end_node ? this.end_node.toJSON() : null,
23262
+ };
23263
+ }
23264
+ treeInspect() {
23265
+ let output = "";
23266
+ output += `@ ERBRenderNode ${this.location.treeInspectWithLabel()}\n`;
23267
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
23268
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
23269
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
23270
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
23271
+ if (this.prism_node) {
23272
+ output += `├── prism_node: ${this.source ? inspectPrismSerialized(this.prism_node, this.source, "│ ") : `(${this.prism_node.length} bytes)`}\n`;
23273
+ }
23274
+ output += `├── keywords: ${this.inspectNode(this.keywords, "│ ")}`;
23275
+ output += `├── body: ${this.inspectArray(this.body, "│ ")}`;
23276
+ output += `├── block_arguments: ${this.inspectArray(this.block_arguments, "│ ")}`;
23277
+ output += `├── rescue_clause: ${this.inspectNode(this.rescue_clause, "│ ")}`;
23278
+ output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
23279
+ output += `├── ensure_clause: ${this.inspectNode(this.ensure_clause, "│ ")}`;
23280
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
23281
+ return output;
23282
+ }
23283
+ }
23284
+ class RubyParameterNode extends Node {
23176
23285
  name;
23177
23286
  default_value;
23287
+ kind;
23178
23288
  required;
23179
- double_splat;
23180
23289
  static get type() {
23181
- return "AST_RUBY_STRICT_LOCAL_NODE";
23290
+ return "AST_RUBY_PARAMETER_NODE";
23182
23291
  }
23183
23292
  static from(data) {
23184
- return new RubyStrictLocalNode({
23293
+ return new RubyParameterNode({
23185
23294
  type: data.type,
23186
23295
  location: Location.from(data.location),
23187
23296
  errors: (data.errors || []).map(error => HerbError.from(error)),
23188
23297
  name: data.name ? Token.from(data.name) : null,
23189
23298
  default_value: data.default_value ? fromSerializedNode((data.default_value)) : null,
23299
+ kind: data.kind,
23190
23300
  required: data.required,
23191
- double_splat: data.double_splat,
23192
23301
  });
23193
23302
  }
23194
23303
  constructor(props) {
23195
23304
  super(props.type, props.location, props.errors);
23196
23305
  this.name = props.name;
23197
23306
  this.default_value = props.default_value;
23307
+ this.kind = props.kind;
23198
23308
  this.required = props.required;
23199
- this.double_splat = props.double_splat;
23200
23309
  }
23201
23310
  accept(visitor) {
23202
- visitor.visitRubyStrictLocalNode(this);
23311
+ visitor.visitRubyParameterNode(this);
23203
23312
  }
23204
23313
  childNodes() {
23205
23314
  return [
@@ -23218,21 +23327,21 @@ class RubyStrictLocalNode extends Node {
23218
23327
  toJSON() {
23219
23328
  return {
23220
23329
  ...super.toJSON(),
23221
- type: "AST_RUBY_STRICT_LOCAL_NODE",
23330
+ type: "AST_RUBY_PARAMETER_NODE",
23222
23331
  name: this.name ? this.name.toJSON() : null,
23223
23332
  default_value: this.default_value ? this.default_value.toJSON() : null,
23333
+ kind: this.kind,
23224
23334
  required: this.required,
23225
- double_splat: this.double_splat,
23226
23335
  };
23227
23336
  }
23228
23337
  treeInspect() {
23229
23338
  let output = "";
23230
- output += `@ RubyStrictLocalNode ${this.location.treeInspectWithLabel()}\n`;
23339
+ output += `@ RubyParameterNode ${this.location.treeInspectWithLabel()}\n`;
23231
23340
  output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
23232
23341
  output += `├── name: ${this.name ? this.name.treeInspect() : "∅"}\n`;
23233
23342
  output += `├── default_value: ${this.inspectNode(this.default_value, "│ ")}`;
23234
- output += `├── required: ${typeof this.required === 'boolean' ? String(this.required) : "∅"}\n`;
23235
- output += `└── double_splat: ${typeof this.double_splat === 'boolean' ? String(this.double_splat) : "∅"}\n`;
23343
+ output += `├── kind: ${this.kind ? JSON.stringify(this.kind) : "∅"}\n`;
23344
+ output += `└── required: ${typeof this.required === 'boolean' ? String(this.required) : "∅"}\n`;
23236
23345
  return output;
23237
23346
  }
23238
23347
  }
@@ -23480,8 +23589,9 @@ function fromSerializedNode(node) {
23480
23589
  case "AST_ERB_BEGIN_NODE": return ERBBeginNode.from(node);
23481
23590
  case "AST_ERB_UNLESS_NODE": return ERBUnlessNode.from(node);
23482
23591
  case "AST_RUBY_RENDER_LOCAL_NODE": return RubyRenderLocalNode.from(node);
23592
+ case "AST_RUBY_RENDER_KEYWORDS_NODE": return RubyRenderKeywordsNode.from(node);
23483
23593
  case "AST_ERB_RENDER_NODE": return ERBRenderNode.from(node);
23484
- case "AST_RUBY_STRICT_LOCAL_NODE": return RubyStrictLocalNode.from(node);
23594
+ case "AST_RUBY_PARAMETER_NODE": return RubyParameterNode.from(node);
23485
23595
  case "AST_ERB_STRICT_LOCALS_NODE": return ERBStrictLocalsNode.from(node);
23486
23596
  case "AST_ERB_YIELD_NODE": return ERBYieldNode.from(node);
23487
23597
  case "AST_ERB_IN_NODE": return ERBInNode.from(node);
@@ -23657,7 +23767,7 @@ class ParseResult extends Result {
23657
23767
  }
23658
23768
 
23659
23769
  // NOTE: This file is generated by the templates/template.rb script and should not
23660
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.3/templates/javascript/packages/core/src/node-type-guards.ts.erb
23770
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.5/templates/javascript/packages/core/src/node-type-guards.ts.erb
23661
23771
  /**
23662
23772
  * Type guard functions for AST nodes.
23663
23773
  * These functions provide type checking by combining both instanceof
@@ -23960,6 +24070,14 @@ function isRubyRenderLocalNode(node) {
23960
24070
  return false;
23961
24071
  return node instanceof RubyRenderLocalNode || node.type === "AST_RUBY_RENDER_LOCAL_NODE" || node.constructor.type === "AST_RUBY_RENDER_LOCAL_NODE";
23962
24072
  }
24073
+ /**
24074
+ * Checks if a node is a RubyRenderKeywordsNode
24075
+ */
24076
+ function isRubyRenderKeywordsNode(node) {
24077
+ if (!node)
24078
+ return false;
24079
+ return node instanceof RubyRenderKeywordsNode || node.type === "AST_RUBY_RENDER_KEYWORDS_NODE" || node.constructor.type === "AST_RUBY_RENDER_KEYWORDS_NODE";
24080
+ }
23963
24081
  /**
23964
24082
  * Checks if a node is a ERBRenderNode
23965
24083
  */
@@ -23969,12 +24087,12 @@ function isERBRenderNode(node) {
23969
24087
  return node instanceof ERBRenderNode || node.type === "AST_ERB_RENDER_NODE" || node.constructor.type === "AST_ERB_RENDER_NODE";
23970
24088
  }
23971
24089
  /**
23972
- * Checks if a node is a RubyStrictLocalNode
24090
+ * Checks if a node is a RubyParameterNode
23973
24091
  */
23974
- function isRubyStrictLocalNode(node) {
24092
+ function isRubyParameterNode(node) {
23975
24093
  if (!node)
23976
24094
  return false;
23977
- return node instanceof RubyStrictLocalNode || node.type === "AST_RUBY_STRICT_LOCAL_NODE" || node.constructor.type === "AST_RUBY_STRICT_LOCAL_NODE";
24095
+ return node instanceof RubyParameterNode || node.type === "AST_RUBY_PARAMETER_NODE" || node.constructor.type === "AST_RUBY_PARAMETER_NODE";
23978
24096
  }
23979
24097
  /**
23980
24098
  * Checks if a node is a ERBStrictLocalsNode
@@ -24000,6 +24118,31 @@ function isERBInNode(node) {
24000
24118
  return false;
24001
24119
  return node instanceof ERBInNode || node.type === "AST_ERB_IN_NODE" || node.constructor.type === "AST_ERB_IN_NODE";
24002
24120
  }
24121
+ /**
24122
+ * Checks if a node is any ERB node type
24123
+ */
24124
+ function isERBNode(node) {
24125
+ return isERBOpenTagNode(node) ||
24126
+ isERBContentNode(node) ||
24127
+ isERBEndNode(node) ||
24128
+ isERBElseNode(node) ||
24129
+ isERBIfNode(node) ||
24130
+ isERBBlockNode(node) ||
24131
+ isERBWhenNode(node) ||
24132
+ isERBCaseNode(node) ||
24133
+ isERBCaseMatchNode(node) ||
24134
+ isERBWhileNode(node) ||
24135
+ isERBUntilNode(node) ||
24136
+ isERBForNode(node) ||
24137
+ isERBRescueNode(node) ||
24138
+ isERBEnsureNode(node) ||
24139
+ isERBBeginNode(node) ||
24140
+ isERBUnlessNode(node) ||
24141
+ isERBRenderNode(node) ||
24142
+ isERBStrictLocalsNode(node) ||
24143
+ isERBYieldNode(node) ||
24144
+ isERBInNode(node);
24145
+ }
24003
24146
  /**
24004
24147
  * Map of node classes to their corresponding type guard functions
24005
24148
  *
@@ -24048,8 +24191,9 @@ const NODE_TYPE_GUARDS = new Map([
24048
24191
  [ERBBeginNode, isERBBeginNode],
24049
24192
  [ERBUnlessNode, isERBUnlessNode],
24050
24193
  [RubyRenderLocalNode, isRubyRenderLocalNode],
24194
+ [RubyRenderKeywordsNode, isRubyRenderKeywordsNode],
24051
24195
  [ERBRenderNode, isERBRenderNode],
24052
- [RubyStrictLocalNode, isRubyStrictLocalNode],
24196
+ [RubyParameterNode, isRubyParameterNode],
24053
24197
  [ERBStrictLocalsNode, isERBStrictLocalsNode],
24054
24198
  [ERBYieldNode, isERBYieldNode],
24055
24199
  [ERBInNode, isERBInNode],
@@ -24102,8 +24246,9 @@ const AST_TYPE_GUARDS = new Map([
24102
24246
  ["AST_ERB_BEGIN_NODE", isERBBeginNode],
24103
24247
  ["AST_ERB_UNLESS_NODE", isERBUnlessNode],
24104
24248
  ["AST_RUBY_RENDER_LOCAL_NODE", isRubyRenderLocalNode],
24249
+ ["AST_RUBY_RENDER_KEYWORDS_NODE", isRubyRenderKeywordsNode],
24105
24250
  ["AST_ERB_RENDER_NODE", isERBRenderNode],
24106
- ["AST_RUBY_STRICT_LOCAL_NODE", isRubyStrictLocalNode],
24251
+ ["AST_RUBY_PARAMETER_NODE", isRubyParameterNode],
24107
24252
  ["AST_ERB_STRICT_LOCALS_NODE", isERBStrictLocalsNode],
24108
24253
  ["AST_ERB_YIELD_NODE", isERBYieldNode],
24109
24254
  ["AST_ERB_IN_NODE", isERBInNode],
@@ -24162,6 +24307,17 @@ function isToken(object) {
24162
24307
  function isParseResult(object) {
24163
24308
  return (object instanceof ParseResult) || (object?.constructor?.name === "ParseResult" && "value" in object);
24164
24309
  }
24310
+
24311
+ /**
24312
+ * Checks if a node is an ERB output node (generates content: <%= %> or <%== %>)
24313
+ */
24314
+ function isERBOutputNode(node) {
24315
+ if (!isERBNode(node))
24316
+ return false;
24317
+ if (!node.tag_opening?.value)
24318
+ return false;
24319
+ return ["<%=", "<%=="].includes(node.tag_opening?.value);
24320
+ }
24165
24321
  /**
24166
24322
  * Extracts a static string from an array of literal nodes
24167
24323
  * Returns null if any node is not a literal node
@@ -24326,12 +24482,88 @@ function getNodesBeforePosition(nodes, position, inclusive = false) {
24326
24482
  function getNodesAfterPosition(nodes, position, inclusive = true) {
24327
24483
  return nodes.filter(node => node.location && isPositionAfter(node.location.start, position, inclusive));
24328
24484
  }
24485
+ // --- AST Mutation Utilities ---
24486
+ const CHILD_ARRAY_PROPS = ["children", "body", "statements", "conditions"];
24487
+ const LINKED_NODE_PROPS = ["subsequent", "else_clause"];
24488
+ /**
24489
+ * Finds the array containing a target node in the AST, along with its index.
24490
+ * Traverses child arrays and linked node properties (e.g., `subsequent`, `else_clause`).
24491
+ *
24492
+ * Useful for autofix operations that need to splice nodes in/out of their parent array.
24493
+ *
24494
+ * @param root - The root node to search from
24495
+ * @param target - The node to find
24496
+ * @returns The containing array and the target's index, or null if not found
24497
+ */
24498
+ function findParentArray(root, target) {
24499
+ const search = (node) => {
24500
+ const record = node;
24501
+ for (const prop of CHILD_ARRAY_PROPS) {
24502
+ const array = record[prop];
24503
+ if (Array.isArray(array)) {
24504
+ const index = array.indexOf(target);
24505
+ if (index !== -1) {
24506
+ return { array, index };
24507
+ }
24508
+ }
24509
+ }
24510
+ for (const prop of CHILD_ARRAY_PROPS) {
24511
+ const array = record[prop];
24512
+ if (Array.isArray(array)) {
24513
+ for (const child of array) {
24514
+ if (child && typeof child === 'object' && 'type' in child) {
24515
+ const result = search(child);
24516
+ if (result) {
24517
+ return result;
24518
+ }
24519
+ }
24520
+ }
24521
+ }
24522
+ }
24523
+ for (const prop of LINKED_NODE_PROPS) {
24524
+ const value = record[prop];
24525
+ if (value && typeof value === 'object' && 'type' in value) {
24526
+ const result = search(value);
24527
+ if (result) {
24528
+ return result;
24529
+ }
24530
+ }
24531
+ }
24532
+ return null;
24533
+ };
24534
+ return search(root);
24535
+ }
24536
+ /**
24537
+ * Creates a synthetic LiteralNode with the given content and zero location.
24538
+ * Useful for inserting whitespace or newlines during AST mutations.
24539
+ */
24540
+ function createLiteral(content) {
24541
+ return new LiteralNode({
24542
+ type: "AST_LITERAL_NODE",
24543
+ content,
24544
+ location: Location.zero,
24545
+ errors: [],
24546
+ });
24547
+ }
24329
24548
  function createSyntheticToken(value, type = "TOKEN_SYNTHETIC") {
24330
24549
  return new Token(value, Range.zero, Location.zero, type);
24331
24550
  }
24551
+ function createERBOutputNode(expression, tagOpening = "<%=", tagClosing = "%>") {
24552
+ return new ERBContentNode({
24553
+ type: "AST_ERB_CONTENT_NODE",
24554
+ tag_opening: createSyntheticToken(tagOpening),
24555
+ content: createSyntheticToken(expression),
24556
+ tag_closing: createSyntheticToken(tagClosing),
24557
+ parsed: false,
24558
+ valid: true,
24559
+ prism_node: null,
24560
+ location: Location.zero,
24561
+ errors: [],
24562
+ });
24563
+ }
24332
24564
 
24333
24565
  // NOTE: This file is generated by the templates/template.rb script and should not
24334
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.3/templates/javascript/packages/core/src/visitor.ts.erb
24566
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.5/templates/javascript/packages/core/src/visitor.ts.erb
24335
24567
  class Visitor {
24336
24568
  visit(node) {
24337
24569
  if (!node)
@@ -24514,12 +24746,16 @@ class Visitor {
24514
24746
  this.visitNode(node);
24515
24747
  this.visitChildNodes(node);
24516
24748
  }
24749
+ visitRubyRenderKeywordsNode(node) {
24750
+ this.visitNode(node);
24751
+ this.visitChildNodes(node);
24752
+ }
24517
24753
  visitERBRenderNode(node) {
24518
24754
  this.visitNode(node);
24519
24755
  this.visitERBNode(node);
24520
24756
  this.visitChildNodes(node);
24521
24757
  }
24522
- visitRubyStrictLocalNode(node) {
24758
+ visitRubyParameterNode(node) {
24523
24759
  this.visitNode(node);
24524
24760
  this.visitChildNodes(node);
24525
24761
  }
@@ -24726,6 +24962,124 @@ class ActionViewTagHelperToHTMLRewriter extends ASTRewriter {
24726
24962
  }
24727
24963
  }
24728
24964
 
24965
+ const STRING_NODE_TYPE = "StringNode";
24966
+ const INTERPOLATED_STRING_NODE_TYPE = "InterpolatedStringNode";
24967
+ const EMBEDDED_STATEMENTS_NODE_TYPE = "EmbeddedStatementsNode";
24968
+ class ERBStringToDirectOutputVisitor extends Visitor {
24969
+ root;
24970
+ constructor(root) {
24971
+ super();
24972
+ this.root = root;
24973
+ }
24974
+ visitERBContentNode(node) {
24975
+ if (!isERBOutputNode(node)) {
24976
+ this.visitChildNodes(node);
24977
+ return;
24978
+ }
24979
+ const prismNode = node.prismNode;
24980
+ if (!prismNode) {
24981
+ this.visitChildNodes(node);
24982
+ return;
24983
+ }
24984
+ const source = node.source;
24985
+ if (!source) {
24986
+ this.visitChildNodes(node);
24987
+ return;
24988
+ }
24989
+ if (!ERBStringToDirectOutputRewriter.isStringOutputNode(prismNode)) {
24990
+ this.visitChildNodes(node);
24991
+ return;
24992
+ }
24993
+ const replacementParts = ERBStringToDirectOutputRewriter.extractReplacementParts(prismNode, source);
24994
+ if (!replacementParts) {
24995
+ this.visitChildNodes(node);
24996
+ return;
24997
+ }
24998
+ const tagOpening = node.tag_opening?.value ?? "<%=";
24999
+ const tagClosing = node.tag_closing?.value ?? "%>";
25000
+ const parentInfo = findParentArray(this.root, node);
25001
+ if (!parentInfo) {
25002
+ this.visitChildNodes(node);
25003
+ return;
25004
+ }
25005
+ const { array: parentArray, index: nodeIndex } = parentInfo;
25006
+ const replacementNodes = [];
25007
+ for (const part of replacementParts) {
25008
+ if (part.type === "text") {
25009
+ replacementNodes.push(createLiteral(part.content));
25010
+ }
25011
+ else {
25012
+ replacementNodes.push(createERBOutputNode(` ${part.expression.trim()} `, tagOpening, tagClosing));
25013
+ }
25014
+ }
25015
+ parentArray.splice(nodeIndex, 1, ...replacementNodes);
25016
+ }
25017
+ }
25018
+ class ERBStringToDirectOutputRewriter extends ASTRewriter {
25019
+ get name() {
25020
+ return "erb-string-to-direct-output";
25021
+ }
25022
+ get description() {
25023
+ return "Replaces ERB string output with direct text and expression tags";
25024
+ }
25025
+ rewrite(node, _context) {
25026
+ const visitor = new ERBStringToDirectOutputVisitor(node);
25027
+ visitor.visit(node);
25028
+ return node;
25029
+ }
25030
+ static isStringOutputNode(prismNode) {
25031
+ return isPrismNodeType(prismNode, STRING_NODE_TYPE) || isPrismNodeType(prismNode, INTERPOLATED_STRING_NODE_TYPE);
25032
+ }
25033
+ static extractStringContent(stringNode, source) {
25034
+ const unescapedValue = stringNode.unescaped?.value;
25035
+ if (typeof unescapedValue === "string") {
25036
+ return unescapedValue;
25037
+ }
25038
+ const location = stringNode.contentLoc;
25039
+ if (location) {
25040
+ return source.substring(location.startOffset, location.startOffset + location.length);
25041
+ }
25042
+ return "";
25043
+ }
25044
+ static extractExpressionSource(embeddedNode, source) {
25045
+ const openingLocation = embeddedNode.openingLoc;
25046
+ const closingLocation = embeddedNode.closingLoc;
25047
+ if (!openingLocation || !closingLocation)
25048
+ return null;
25049
+ const expressionStart = openingLocation.startOffset + openingLocation.length;
25050
+ const expressionEnd = closingLocation.startOffset;
25051
+ return source.substring(expressionStart, expressionEnd);
25052
+ }
25053
+ static extractReplacementParts(prismNode, source) {
25054
+ if (isPrismNodeType(prismNode, STRING_NODE_TYPE)) {
25055
+ const textContent = this.extractStringContent(prismNode, source);
25056
+ return [{ type: "text", content: textContent }];
25057
+ }
25058
+ if (isPrismNodeType(prismNode, INTERPOLATED_STRING_NODE_TYPE)) {
25059
+ const parts = prismNode.parts;
25060
+ if (!parts || parts.length === 0)
25061
+ return null;
25062
+ const replacementParts = [];
25063
+ for (const part of parts) {
25064
+ if (isPrismNodeType(part, STRING_NODE_TYPE)) {
25065
+ const textContent = this.extractStringContent(part, source);
25066
+ if (textContent) {
25067
+ replacementParts.push({ type: "text", content: textContent });
25068
+ }
25069
+ }
25070
+ else if (isPrismNodeType(part, EMBEDDED_STATEMENTS_NODE_TYPE)) {
25071
+ const expression = this.extractExpressionSource(part, source);
25072
+ if (expression) {
25073
+ replacementParts.push({ type: "expression", expression });
25074
+ }
25075
+ }
25076
+ }
25077
+ return replacementParts.length > 0 ? replacementParts : null;
25078
+ }
25079
+ return null;
25080
+ }
25081
+ }
25082
+
24729
25083
  function serializeAttributeValue(value) {
24730
25084
  const hasERB = value.children.some(child => isERBContentNode(child));
24731
25085
  if (hasERB && value.children.length === 1 && isERBContentNode(value.children[0])) {
@@ -25319,12 +25673,12 @@ class IdentityPrinter extends Printer {
25319
25673
  this.visitChildNodes(node);
25320
25674
  }
25321
25675
  visitHTMLAttributeValueNode(node) {
25322
- if (node.quoted && node.open_quote) {
25323
- this.write(node.open_quote.value);
25676
+ if (node.quoted) {
25677
+ this.write(node.open_quote?.value ?? '"');
25324
25678
  }
25325
25679
  this.visitChildNodes(node);
25326
- if (node.quoted && node.close_quote) {
25327
- this.write(node.close_quote.value);
25680
+ if (node.quoted) {
25681
+ this.write(node.close_quote?.value ?? '"');
25328
25682
  }
25329
25683
  }
25330
25684
  visitRubyLiteralNode(node) {
@@ -25509,6 +25863,24 @@ class IdentityPrinter extends Printer {
25509
25863
  }
25510
25864
  visitERBRenderNode(node) {
25511
25865
  this.printERBNode(node);
25866
+ if (node.end_node) {
25867
+ if (node.body) {
25868
+ node.body.forEach(child => this.visit(child));
25869
+ }
25870
+ if (node.rescue_clause) {
25871
+ this.visit(node.rescue_clause);
25872
+ }
25873
+ if (node.else_clause) {
25874
+ this.visit(node.else_clause);
25875
+ }
25876
+ if (node.ensure_clause) {
25877
+ this.visit(node.ensure_clause);
25878
+ }
25879
+ this.visit(node.end_node);
25880
+ }
25881
+ }
25882
+ visitRubyRenderKeywordsNode(_node) {
25883
+ // no-op: extracted metadata, nothing to print
25512
25884
  }
25513
25885
  visitRubyRenderLocalNode(_node) {
25514
25886
  // extracted metadata, nothing to print
@@ -25516,7 +25888,7 @@ class IdentityPrinter extends Printer {
25516
25888
  visitERBStrictLocalsNode(node) {
25517
25889
  this.printERBNode(node);
25518
25890
  }
25519
- visitRubyStrictLocalNode(_node) {
25891
+ visitRubyParameterNode(_node) {
25520
25892
  // extracted metadata, nothing to print
25521
25893
  }
25522
25894
  visitERBYieldNode(node) {
@@ -26067,6 +26439,7 @@ class TailwindClassSorterRewriter extends ASTRewriter {
26067
26439
  */
26068
26440
  const builtinRewriters = [
26069
26441
  ActionViewTagHelperToHTMLRewriter,
26442
+ ERBStringToDirectOutputRewriter,
26070
26443
  HTMLToActionViewTagHelperRewriter,
26071
26444
  TailwindClassSorterRewriter
26072
26445
  ];
@@ -26118,6 +26491,7 @@ async function tailwindClassSorter(options = {}) {
26118
26491
  exports.ASTRewriter = ASTRewriter;
26119
26492
  exports.ActionViewTagHelperToHTMLRewriter = ActionViewTagHelperToHTMLRewriter;
26120
26493
  exports.CustomRewriterLoader = CustomRewriterLoader;
26494
+ exports.ERBStringToDirectOutputRewriter = ERBStringToDirectOutputRewriter;
26121
26495
  exports.HTMLToActionViewTagHelperRewriter = HTMLToActionViewTagHelperRewriter;
26122
26496
  exports.StringRewriter = StringRewriter;
26123
26497
  exports.TailwindClassSorterRewriter = TailwindClassSorterRewriter;