@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/index.cjs CHANGED
@@ -205,7 +205,7 @@ class Token {
205
205
  }
206
206
 
207
207
  // NOTE: This file is generated by the templates/template.rb script and should not
208
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.3/templates/javascript/packages/core/src/errors.ts.erb
208
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.5/templates/javascript/packages/core/src/errors.ts.erb
209
209
  class HerbError {
210
210
  type;
211
211
  message;
@@ -20353,6 +20353,17 @@ function inspectPrismSerialized(bytes, source, prefix = "") {
20353
20353
  }
20354
20354
  }
20355
20355
 
20356
+ /**
20357
+ * Checks if a Prism node is of a specific type by comparing constructor names.
20358
+ *
20359
+ * This is preferred over `instanceof` because `@ruby/prism` classes may be
20360
+ * duplicated across bundled packages, causing `instanceof` checks to fail.
20361
+ */
20362
+ function isPrismNodeType(node, type) {
20363
+ if (!node)
20364
+ return false;
20365
+ return node.constructor?.name === type;
20366
+ }
20356
20367
  /**
20357
20368
  * Deserialize a Prism parse result from the raw bytes produced by pm_serialize().
20358
20369
  *
@@ -20384,7 +20395,7 @@ function deserializePrismNode(bytes, source) {
20384
20395
  }
20385
20396
 
20386
20397
  // NOTE: This file is generated by the templates/template.rb script and should not
20387
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.3/templates/javascript/packages/core/src/nodes.ts.erb
20398
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.5/templates/javascript/packages/core/src/nodes.ts.erb
20388
20399
  class Node {
20389
20400
  type;
20390
20401
  location;
@@ -22005,6 +22016,7 @@ class ERBBlockNode extends Node {
22005
22016
  tag_closing;
22006
22017
  prism_node;
22007
22018
  body;
22019
+ block_arguments;
22008
22020
  rescue_clause;
22009
22021
  else_clause;
22010
22022
  ensure_clause;
@@ -22022,6 +22034,7 @@ class ERBBlockNode extends Node {
22022
22034
  tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
22023
22035
  prism_node: data.prism_node ? new Uint8Array(data.prism_node) : null,
22024
22036
  body: (data.body || []).map(node => fromSerializedNode(node)),
22037
+ block_arguments: (data.block_arguments || []).map(node => fromSerializedNode(node)),
22025
22038
  rescue_clause: data.rescue_clause ? fromSerializedNode((data.rescue_clause)) : null,
22026
22039
  else_clause: data.else_clause ? fromSerializedNode((data.else_clause)) : null,
22027
22040
  ensure_clause: data.ensure_clause ? fromSerializedNode((data.ensure_clause)) : null,
@@ -22035,6 +22048,7 @@ class ERBBlockNode extends Node {
22035
22048
  this.tag_closing = props.tag_closing;
22036
22049
  this.prism_node = props.prism_node;
22037
22050
  this.body = props.body;
22051
+ this.block_arguments = props.block_arguments;
22038
22052
  this.rescue_clause = props.rescue_clause;
22039
22053
  this.else_clause = props.else_clause;
22040
22054
  this.ensure_clause = props.ensure_clause;
@@ -22046,6 +22060,7 @@ class ERBBlockNode extends Node {
22046
22060
  childNodes() {
22047
22061
  return [
22048
22062
  ...this.body,
22063
+ ...this.block_arguments,
22049
22064
  this.rescue_clause,
22050
22065
  this.else_clause,
22051
22066
  this.ensure_clause,
@@ -22064,6 +22079,7 @@ class ERBBlockNode extends Node {
22064
22079
  return [
22065
22080
  ...this.errors,
22066
22081
  ...this.body.map(node => node.recursiveErrors()),
22082
+ ...this.block_arguments.map(node => node.recursiveErrors()),
22067
22083
  this.rescue_clause ? this.rescue_clause.recursiveErrors() : [],
22068
22084
  this.else_clause ? this.else_clause.recursiveErrors() : [],
22069
22085
  this.ensure_clause ? this.ensure_clause.recursiveErrors() : [],
@@ -22079,6 +22095,7 @@ class ERBBlockNode extends Node {
22079
22095
  tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
22080
22096
  prism_node: this.prism_node ? Array.from(this.prism_node) : null,
22081
22097
  body: this.body.map(node => node.toJSON()),
22098
+ block_arguments: this.block_arguments.map(node => node.toJSON()),
22082
22099
  rescue_clause: this.rescue_clause ? this.rescue_clause.toJSON() : null,
22083
22100
  else_clause: this.else_clause ? this.else_clause.toJSON() : null,
22084
22101
  ensure_clause: this.ensure_clause ? this.ensure_clause.toJSON() : null,
@@ -22096,6 +22113,7 @@ class ERBBlockNode extends Node {
22096
22113
  output += `├── prism_node: ${this.source ? inspectPrismSerialized(this.prism_node, this.source, "│ ") : `(${this.prism_node.length} bytes)`}\n`;
22097
22114
  }
22098
22115
  output += `├── body: ${this.inspectArray(this.body, "│ ")}`;
22116
+ output += `├── block_arguments: ${this.inspectArray(this.block_arguments, "│ ")}`;
22099
22117
  output += `├── rescue_clause: ${this.inspectNode(this.rescue_clause, "│ ")}`;
22100
22118
  output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
22101
22119
  output += `├── ensure_clause: ${this.inspectNode(this.ensure_clause, "│ ")}`;
@@ -23003,12 +23021,7 @@ class RubyRenderLocalNode extends Node {
23003
23021
  return output;
23004
23022
  }
23005
23023
  }
23006
- class ERBRenderNode extends Node {
23007
- tag_opening;
23008
- content;
23009
- tag_closing;
23010
- // no-op for analyzed_ruby
23011
- prism_node;
23024
+ class RubyRenderKeywordsNode extends Node {
23012
23025
  partial;
23013
23026
  template_path;
23014
23027
  layout;
@@ -23028,18 +23041,13 @@ class ERBRenderNode extends Node {
23028
23041
  content_type;
23029
23042
  locals;
23030
23043
  static get type() {
23031
- return "AST_ERB_RENDER_NODE";
23044
+ return "AST_RUBY_RENDER_KEYWORDS_NODE";
23032
23045
  }
23033
23046
  static from(data) {
23034
- return new ERBRenderNode({
23047
+ return new RubyRenderKeywordsNode({
23035
23048
  type: data.type,
23036
23049
  location: Location.from(data.location),
23037
23050
  errors: (data.errors || []).map(error => HerbError.from(error)),
23038
- tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
23039
- content: data.content ? Token.from(data.content) : null,
23040
- tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
23041
- // no-op for analyzed_ruby
23042
- prism_node: data.prism_node ? new Uint8Array(data.prism_node) : null,
23043
23051
  partial: data.partial ? Token.from(data.partial) : null,
23044
23052
  template_path: data.template_path ? Token.from(data.template_path) : null,
23045
23053
  layout: data.layout ? Token.from(data.layout) : null,
@@ -23062,11 +23070,6 @@ class ERBRenderNode extends Node {
23062
23070
  }
23063
23071
  constructor(props) {
23064
23072
  super(props.type, props.location, props.errors);
23065
- this.tag_opening = props.tag_opening;
23066
- this.content = props.content;
23067
- this.tag_closing = props.tag_closing;
23068
- // no-op for analyzed_ruby
23069
- this.prism_node = props.prism_node;
23070
23073
  this.partial = props.partial;
23071
23074
  this.template_path = props.template_path;
23072
23075
  this.layout = props.layout;
@@ -23087,7 +23090,7 @@ class ERBRenderNode extends Node {
23087
23090
  this.locals = props.locals;
23088
23091
  }
23089
23092
  accept(visitor) {
23090
- visitor.visitERBRenderNode(this);
23093
+ visitor.visitRubyRenderKeywordsNode(this);
23091
23094
  }
23092
23095
  childNodes() {
23093
23096
  return [
@@ -23097,11 +23100,6 @@ class ERBRenderNode extends Node {
23097
23100
  compactChildNodes() {
23098
23101
  return this.childNodes().filter(node => node !== null && node !== undefined);
23099
23102
  }
23100
- get prismNode() {
23101
- if (!this.prism_node || !this.source)
23102
- return null;
23103
- return deserializePrismNode(this.prism_node, this.source);
23104
- }
23105
23103
  recursiveErrors() {
23106
23104
  return [
23107
23105
  ...this.errors,
@@ -23111,12 +23109,7 @@ class ERBRenderNode extends Node {
23111
23109
  toJSON() {
23112
23110
  return {
23113
23111
  ...super.toJSON(),
23114
- type: "AST_ERB_RENDER_NODE",
23115
- tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
23116
- content: this.content ? this.content.toJSON() : null,
23117
- tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
23118
- // no-op for analyzed_ruby
23119
- prism_node: this.prism_node ? Array.from(this.prism_node) : null,
23112
+ type: "AST_RUBY_RENDER_KEYWORDS_NODE",
23120
23113
  partial: this.partial ? this.partial.toJSON() : null,
23121
23114
  template_path: this.template_path ? this.template_path.toJSON() : null,
23122
23115
  layout: this.layout ? this.layout.toJSON() : null,
@@ -23139,14 +23132,8 @@ class ERBRenderNode extends Node {
23139
23132
  }
23140
23133
  treeInspect() {
23141
23134
  let output = "";
23142
- output += `@ ERBRenderNode ${this.location.treeInspectWithLabel()}\n`;
23135
+ output += `@ RubyRenderKeywordsNode ${this.location.treeInspectWithLabel()}\n`;
23143
23136
  output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
23144
- output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
23145
- output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
23146
- output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
23147
- if (this.prism_node) {
23148
- output += `├── prism_node: ${this.source ? inspectPrismSerialized(this.prism_node, this.source, "│ ") : `(${this.prism_node.length} bytes)`}\n`;
23149
- }
23150
23137
  output += `├── partial: ${this.partial ? this.partial.treeInspect() : "∅"}\n`;
23151
23138
  output += `├── template_path: ${this.template_path ? this.template_path.treeInspect() : "∅"}\n`;
23152
23139
  output += `├── layout: ${this.layout ? this.layout.treeInspect() : "∅"}\n`;
@@ -23168,34 +23155,156 @@ class ERBRenderNode extends Node {
23168
23155
  return output;
23169
23156
  }
23170
23157
  }
23171
- class RubyStrictLocalNode extends Node {
23158
+ class ERBRenderNode extends Node {
23159
+ tag_opening;
23160
+ content;
23161
+ tag_closing;
23162
+ // no-op for analyzed_ruby
23163
+ prism_node;
23164
+ keywords;
23165
+ body;
23166
+ block_arguments;
23167
+ rescue_clause;
23168
+ else_clause;
23169
+ ensure_clause;
23170
+ end_node;
23171
+ static get type() {
23172
+ return "AST_ERB_RENDER_NODE";
23173
+ }
23174
+ static from(data) {
23175
+ return new ERBRenderNode({
23176
+ type: data.type,
23177
+ location: Location.from(data.location),
23178
+ errors: (data.errors || []).map(error => HerbError.from(error)),
23179
+ tag_opening: data.tag_opening ? Token.from(data.tag_opening) : null,
23180
+ content: data.content ? Token.from(data.content) : null,
23181
+ tag_closing: data.tag_closing ? Token.from(data.tag_closing) : null,
23182
+ // no-op for analyzed_ruby
23183
+ prism_node: data.prism_node ? new Uint8Array(data.prism_node) : null,
23184
+ keywords: data.keywords ? fromSerializedNode((data.keywords)) : null,
23185
+ body: (data.body || []).map(node => fromSerializedNode(node)),
23186
+ block_arguments: (data.block_arguments || []).map(node => fromSerializedNode(node)),
23187
+ rescue_clause: data.rescue_clause ? fromSerializedNode((data.rescue_clause)) : null,
23188
+ else_clause: data.else_clause ? fromSerializedNode((data.else_clause)) : null,
23189
+ ensure_clause: data.ensure_clause ? fromSerializedNode((data.ensure_clause)) : null,
23190
+ end_node: data.end_node ? fromSerializedNode((data.end_node)) : null,
23191
+ });
23192
+ }
23193
+ constructor(props) {
23194
+ super(props.type, props.location, props.errors);
23195
+ this.tag_opening = props.tag_opening;
23196
+ this.content = props.content;
23197
+ this.tag_closing = props.tag_closing;
23198
+ // no-op for analyzed_ruby
23199
+ this.prism_node = props.prism_node;
23200
+ this.keywords = props.keywords;
23201
+ this.body = props.body;
23202
+ this.block_arguments = props.block_arguments;
23203
+ this.rescue_clause = props.rescue_clause;
23204
+ this.else_clause = props.else_clause;
23205
+ this.ensure_clause = props.ensure_clause;
23206
+ this.end_node = props.end_node;
23207
+ }
23208
+ accept(visitor) {
23209
+ visitor.visitERBRenderNode(this);
23210
+ }
23211
+ childNodes() {
23212
+ return [
23213
+ this.keywords,
23214
+ ...this.body,
23215
+ ...this.block_arguments,
23216
+ this.rescue_clause,
23217
+ this.else_clause,
23218
+ this.ensure_clause,
23219
+ this.end_node,
23220
+ ];
23221
+ }
23222
+ compactChildNodes() {
23223
+ return this.childNodes().filter(node => node !== null && node !== undefined);
23224
+ }
23225
+ get prismNode() {
23226
+ if (!this.prism_node || !this.source)
23227
+ return null;
23228
+ return deserializePrismNode(this.prism_node, this.source);
23229
+ }
23230
+ recursiveErrors() {
23231
+ return [
23232
+ ...this.errors,
23233
+ this.keywords ? this.keywords.recursiveErrors() : [],
23234
+ ...this.body.map(node => node.recursiveErrors()),
23235
+ ...this.block_arguments.map(node => node.recursiveErrors()),
23236
+ this.rescue_clause ? this.rescue_clause.recursiveErrors() : [],
23237
+ this.else_clause ? this.else_clause.recursiveErrors() : [],
23238
+ this.ensure_clause ? this.ensure_clause.recursiveErrors() : [],
23239
+ this.end_node ? this.end_node.recursiveErrors() : [],
23240
+ ].flat();
23241
+ }
23242
+ toJSON() {
23243
+ return {
23244
+ ...super.toJSON(),
23245
+ type: "AST_ERB_RENDER_NODE",
23246
+ tag_opening: this.tag_opening ? this.tag_opening.toJSON() : null,
23247
+ content: this.content ? this.content.toJSON() : null,
23248
+ tag_closing: this.tag_closing ? this.tag_closing.toJSON() : null,
23249
+ // no-op for analyzed_ruby
23250
+ prism_node: this.prism_node ? Array.from(this.prism_node) : null,
23251
+ keywords: this.keywords ? this.keywords.toJSON() : null,
23252
+ body: this.body.map(node => node.toJSON()),
23253
+ block_arguments: this.block_arguments.map(node => node.toJSON()),
23254
+ rescue_clause: this.rescue_clause ? this.rescue_clause.toJSON() : null,
23255
+ else_clause: this.else_clause ? this.else_clause.toJSON() : null,
23256
+ ensure_clause: this.ensure_clause ? this.ensure_clause.toJSON() : null,
23257
+ end_node: this.end_node ? this.end_node.toJSON() : null,
23258
+ };
23259
+ }
23260
+ treeInspect() {
23261
+ let output = "";
23262
+ output += `@ ERBRenderNode ${this.location.treeInspectWithLabel()}\n`;
23263
+ output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
23264
+ output += `├── tag_opening: ${this.tag_opening ? this.tag_opening.treeInspect() : "∅"}\n`;
23265
+ output += `├── content: ${this.content ? this.content.treeInspect() : "∅"}\n`;
23266
+ output += `├── tag_closing: ${this.tag_closing ? this.tag_closing.treeInspect() : "∅"}\n`;
23267
+ if (this.prism_node) {
23268
+ output += `├── prism_node: ${this.source ? inspectPrismSerialized(this.prism_node, this.source, "│ ") : `(${this.prism_node.length} bytes)`}\n`;
23269
+ }
23270
+ output += `├── keywords: ${this.inspectNode(this.keywords, "│ ")}`;
23271
+ output += `├── body: ${this.inspectArray(this.body, "│ ")}`;
23272
+ output += `├── block_arguments: ${this.inspectArray(this.block_arguments, "│ ")}`;
23273
+ output += `├── rescue_clause: ${this.inspectNode(this.rescue_clause, "│ ")}`;
23274
+ output += `├── else_clause: ${this.inspectNode(this.else_clause, "│ ")}`;
23275
+ output += `├── ensure_clause: ${this.inspectNode(this.ensure_clause, "│ ")}`;
23276
+ output += `└── end_node: ${this.inspectNode(this.end_node, " ")}`;
23277
+ return output;
23278
+ }
23279
+ }
23280
+ class RubyParameterNode extends Node {
23172
23281
  name;
23173
23282
  default_value;
23283
+ kind;
23174
23284
  required;
23175
- double_splat;
23176
23285
  static get type() {
23177
- return "AST_RUBY_STRICT_LOCAL_NODE";
23286
+ return "AST_RUBY_PARAMETER_NODE";
23178
23287
  }
23179
23288
  static from(data) {
23180
- return new RubyStrictLocalNode({
23289
+ return new RubyParameterNode({
23181
23290
  type: data.type,
23182
23291
  location: Location.from(data.location),
23183
23292
  errors: (data.errors || []).map(error => HerbError.from(error)),
23184
23293
  name: data.name ? Token.from(data.name) : null,
23185
23294
  default_value: data.default_value ? fromSerializedNode((data.default_value)) : null,
23295
+ kind: data.kind,
23186
23296
  required: data.required,
23187
- double_splat: data.double_splat,
23188
23297
  });
23189
23298
  }
23190
23299
  constructor(props) {
23191
23300
  super(props.type, props.location, props.errors);
23192
23301
  this.name = props.name;
23193
23302
  this.default_value = props.default_value;
23303
+ this.kind = props.kind;
23194
23304
  this.required = props.required;
23195
- this.double_splat = props.double_splat;
23196
23305
  }
23197
23306
  accept(visitor) {
23198
- visitor.visitRubyStrictLocalNode(this);
23307
+ visitor.visitRubyParameterNode(this);
23199
23308
  }
23200
23309
  childNodes() {
23201
23310
  return [
@@ -23214,21 +23323,21 @@ class RubyStrictLocalNode extends Node {
23214
23323
  toJSON() {
23215
23324
  return {
23216
23325
  ...super.toJSON(),
23217
- type: "AST_RUBY_STRICT_LOCAL_NODE",
23326
+ type: "AST_RUBY_PARAMETER_NODE",
23218
23327
  name: this.name ? this.name.toJSON() : null,
23219
23328
  default_value: this.default_value ? this.default_value.toJSON() : null,
23329
+ kind: this.kind,
23220
23330
  required: this.required,
23221
- double_splat: this.double_splat,
23222
23331
  };
23223
23332
  }
23224
23333
  treeInspect() {
23225
23334
  let output = "";
23226
- output += `@ RubyStrictLocalNode ${this.location.treeInspectWithLabel()}\n`;
23335
+ output += `@ RubyParameterNode ${this.location.treeInspectWithLabel()}\n`;
23227
23336
  output += `├── errors: ${this.inspectArray(this.errors, "│ ")}`;
23228
23337
  output += `├── name: ${this.name ? this.name.treeInspect() : "∅"}\n`;
23229
23338
  output += `├── default_value: ${this.inspectNode(this.default_value, "│ ")}`;
23230
- output += `├── required: ${typeof this.required === 'boolean' ? String(this.required) : "∅"}\n`;
23231
- output += `└── double_splat: ${typeof this.double_splat === 'boolean' ? String(this.double_splat) : "∅"}\n`;
23339
+ output += `├── kind: ${this.kind ? JSON.stringify(this.kind) : "∅"}\n`;
23340
+ output += `└── required: ${typeof this.required === 'boolean' ? String(this.required) : "∅"}\n`;
23232
23341
  return output;
23233
23342
  }
23234
23343
  }
@@ -23476,8 +23585,9 @@ function fromSerializedNode(node) {
23476
23585
  case "AST_ERB_BEGIN_NODE": return ERBBeginNode.from(node);
23477
23586
  case "AST_ERB_UNLESS_NODE": return ERBUnlessNode.from(node);
23478
23587
  case "AST_RUBY_RENDER_LOCAL_NODE": return RubyRenderLocalNode.from(node);
23588
+ case "AST_RUBY_RENDER_KEYWORDS_NODE": return RubyRenderKeywordsNode.from(node);
23479
23589
  case "AST_ERB_RENDER_NODE": return ERBRenderNode.from(node);
23480
- case "AST_RUBY_STRICT_LOCAL_NODE": return RubyStrictLocalNode.from(node);
23590
+ case "AST_RUBY_PARAMETER_NODE": return RubyParameterNode.from(node);
23481
23591
  case "AST_ERB_STRICT_LOCALS_NODE": return ERBStrictLocalsNode.from(node);
23482
23592
  case "AST_ERB_YIELD_NODE": return ERBYieldNode.from(node);
23483
23593
  case "AST_ERB_IN_NODE": return ERBInNode.from(node);
@@ -23653,7 +23763,7 @@ class ParseResult extends Result {
23653
23763
  }
23654
23764
 
23655
23765
  // NOTE: This file is generated by the templates/template.rb script and should not
23656
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.3/templates/javascript/packages/core/src/node-type-guards.ts.erb
23766
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.5/templates/javascript/packages/core/src/node-type-guards.ts.erb
23657
23767
  /**
23658
23768
  * Type guard functions for AST nodes.
23659
23769
  * These functions provide type checking by combining both instanceof
@@ -23956,6 +24066,14 @@ function isRubyRenderLocalNode(node) {
23956
24066
  return false;
23957
24067
  return node instanceof RubyRenderLocalNode || node.type === "AST_RUBY_RENDER_LOCAL_NODE" || node.constructor.type === "AST_RUBY_RENDER_LOCAL_NODE";
23958
24068
  }
24069
+ /**
24070
+ * Checks if a node is a RubyRenderKeywordsNode
24071
+ */
24072
+ function isRubyRenderKeywordsNode(node) {
24073
+ if (!node)
24074
+ return false;
24075
+ return node instanceof RubyRenderKeywordsNode || node.type === "AST_RUBY_RENDER_KEYWORDS_NODE" || node.constructor.type === "AST_RUBY_RENDER_KEYWORDS_NODE";
24076
+ }
23959
24077
  /**
23960
24078
  * Checks if a node is a ERBRenderNode
23961
24079
  */
@@ -23965,12 +24083,12 @@ function isERBRenderNode(node) {
23965
24083
  return node instanceof ERBRenderNode || node.type === "AST_ERB_RENDER_NODE" || node.constructor.type === "AST_ERB_RENDER_NODE";
23966
24084
  }
23967
24085
  /**
23968
- * Checks if a node is a RubyStrictLocalNode
24086
+ * Checks if a node is a RubyParameterNode
23969
24087
  */
23970
- function isRubyStrictLocalNode(node) {
24088
+ function isRubyParameterNode(node) {
23971
24089
  if (!node)
23972
24090
  return false;
23973
- return node instanceof RubyStrictLocalNode || node.type === "AST_RUBY_STRICT_LOCAL_NODE" || node.constructor.type === "AST_RUBY_STRICT_LOCAL_NODE";
24091
+ return node instanceof RubyParameterNode || node.type === "AST_RUBY_PARAMETER_NODE" || node.constructor.type === "AST_RUBY_PARAMETER_NODE";
23974
24092
  }
23975
24093
  /**
23976
24094
  * Checks if a node is a ERBStrictLocalsNode
@@ -23996,6 +24114,31 @@ function isERBInNode(node) {
23996
24114
  return false;
23997
24115
  return node instanceof ERBInNode || node.type === "AST_ERB_IN_NODE" || node.constructor.type === "AST_ERB_IN_NODE";
23998
24116
  }
24117
+ /**
24118
+ * Checks if a node is any ERB node type
24119
+ */
24120
+ function isERBNode(node) {
24121
+ return isERBOpenTagNode(node) ||
24122
+ isERBContentNode(node) ||
24123
+ isERBEndNode(node) ||
24124
+ isERBElseNode(node) ||
24125
+ isERBIfNode(node) ||
24126
+ isERBBlockNode(node) ||
24127
+ isERBWhenNode(node) ||
24128
+ isERBCaseNode(node) ||
24129
+ isERBCaseMatchNode(node) ||
24130
+ isERBWhileNode(node) ||
24131
+ isERBUntilNode(node) ||
24132
+ isERBForNode(node) ||
24133
+ isERBRescueNode(node) ||
24134
+ isERBEnsureNode(node) ||
24135
+ isERBBeginNode(node) ||
24136
+ isERBUnlessNode(node) ||
24137
+ isERBRenderNode(node) ||
24138
+ isERBStrictLocalsNode(node) ||
24139
+ isERBYieldNode(node) ||
24140
+ isERBInNode(node);
24141
+ }
23999
24142
  /**
24000
24143
  * Map of node classes to their corresponding type guard functions
24001
24144
  *
@@ -24044,8 +24187,9 @@ const NODE_TYPE_GUARDS = new Map([
24044
24187
  [ERBBeginNode, isERBBeginNode],
24045
24188
  [ERBUnlessNode, isERBUnlessNode],
24046
24189
  [RubyRenderLocalNode, isRubyRenderLocalNode],
24190
+ [RubyRenderKeywordsNode, isRubyRenderKeywordsNode],
24047
24191
  [ERBRenderNode, isERBRenderNode],
24048
- [RubyStrictLocalNode, isRubyStrictLocalNode],
24192
+ [RubyParameterNode, isRubyParameterNode],
24049
24193
  [ERBStrictLocalsNode, isERBStrictLocalsNode],
24050
24194
  [ERBYieldNode, isERBYieldNode],
24051
24195
  [ERBInNode, isERBInNode],
@@ -24098,8 +24242,9 @@ const AST_TYPE_GUARDS = new Map([
24098
24242
  ["AST_ERB_BEGIN_NODE", isERBBeginNode],
24099
24243
  ["AST_ERB_UNLESS_NODE", isERBUnlessNode],
24100
24244
  ["AST_RUBY_RENDER_LOCAL_NODE", isRubyRenderLocalNode],
24245
+ ["AST_RUBY_RENDER_KEYWORDS_NODE", isRubyRenderKeywordsNode],
24101
24246
  ["AST_ERB_RENDER_NODE", isERBRenderNode],
24102
- ["AST_RUBY_STRICT_LOCAL_NODE", isRubyStrictLocalNode],
24247
+ ["AST_RUBY_PARAMETER_NODE", isRubyParameterNode],
24103
24248
  ["AST_ERB_STRICT_LOCALS_NODE", isERBStrictLocalsNode],
24104
24249
  ["AST_ERB_YIELD_NODE", isERBYieldNode],
24105
24250
  ["AST_ERB_IN_NODE", isERBInNode],
@@ -24158,6 +24303,17 @@ function isToken(object) {
24158
24303
  function isParseResult(object) {
24159
24304
  return (object instanceof ParseResult) || (object?.constructor?.name === "ParseResult" && "value" in object);
24160
24305
  }
24306
+
24307
+ /**
24308
+ * Checks if a node is an ERB output node (generates content: <%= %> or <%== %>)
24309
+ */
24310
+ function isERBOutputNode(node) {
24311
+ if (!isERBNode(node))
24312
+ return false;
24313
+ if (!node.tag_opening?.value)
24314
+ return false;
24315
+ return ["<%=", "<%=="].includes(node.tag_opening?.value);
24316
+ }
24161
24317
  /**
24162
24318
  * Extracts a static string from an array of literal nodes
24163
24319
  * Returns null if any node is not a literal node
@@ -24217,12 +24373,88 @@ function getNodesBeforePosition(nodes, position, inclusive = false) {
24217
24373
  function getNodesAfterPosition(nodes, position, inclusive = true) {
24218
24374
  return nodes.filter(node => node.location && isPositionAfter(node.location.start, position, inclusive));
24219
24375
  }
24376
+ // --- AST Mutation Utilities ---
24377
+ const CHILD_ARRAY_PROPS = ["children", "body", "statements", "conditions"];
24378
+ const LINKED_NODE_PROPS = ["subsequent", "else_clause"];
24379
+ /**
24380
+ * Finds the array containing a target node in the AST, along with its index.
24381
+ * Traverses child arrays and linked node properties (e.g., `subsequent`, `else_clause`).
24382
+ *
24383
+ * Useful for autofix operations that need to splice nodes in/out of their parent array.
24384
+ *
24385
+ * @param root - The root node to search from
24386
+ * @param target - The node to find
24387
+ * @returns The containing array and the target's index, or null if not found
24388
+ */
24389
+ function findParentArray(root, target) {
24390
+ const search = (node) => {
24391
+ const record = node;
24392
+ for (const prop of CHILD_ARRAY_PROPS) {
24393
+ const array = record[prop];
24394
+ if (Array.isArray(array)) {
24395
+ const index = array.indexOf(target);
24396
+ if (index !== -1) {
24397
+ return { array, index };
24398
+ }
24399
+ }
24400
+ }
24401
+ for (const prop of CHILD_ARRAY_PROPS) {
24402
+ const array = record[prop];
24403
+ if (Array.isArray(array)) {
24404
+ for (const child of array) {
24405
+ if (child && typeof child === 'object' && 'type' in child) {
24406
+ const result = search(child);
24407
+ if (result) {
24408
+ return result;
24409
+ }
24410
+ }
24411
+ }
24412
+ }
24413
+ }
24414
+ for (const prop of LINKED_NODE_PROPS) {
24415
+ const value = record[prop];
24416
+ if (value && typeof value === 'object' && 'type' in value) {
24417
+ const result = search(value);
24418
+ if (result) {
24419
+ return result;
24420
+ }
24421
+ }
24422
+ }
24423
+ return null;
24424
+ };
24425
+ return search(root);
24426
+ }
24427
+ /**
24428
+ * Creates a synthetic LiteralNode with the given content and zero location.
24429
+ * Useful for inserting whitespace or newlines during AST mutations.
24430
+ */
24431
+ function createLiteral(content) {
24432
+ return new LiteralNode({
24433
+ type: "AST_LITERAL_NODE",
24434
+ content,
24435
+ location: Location.zero,
24436
+ errors: [],
24437
+ });
24438
+ }
24220
24439
  function createSyntheticToken(value, type = "TOKEN_SYNTHETIC") {
24221
24440
  return new Token(value, Range.zero, Location.zero, type);
24222
24441
  }
24442
+ function createERBOutputNode(expression, tagOpening = "<%=", tagClosing = "%>") {
24443
+ return new ERBContentNode({
24444
+ type: "AST_ERB_CONTENT_NODE",
24445
+ tag_opening: createSyntheticToken(tagOpening),
24446
+ content: createSyntheticToken(expression),
24447
+ tag_closing: createSyntheticToken(tagClosing),
24448
+ parsed: false,
24449
+ valid: true,
24450
+ prism_node: null,
24451
+ location: Location.zero,
24452
+ errors: [],
24453
+ });
24454
+ }
24223
24455
 
24224
24456
  // NOTE: This file is generated by the templates/template.rb script and should not
24225
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.3/templates/javascript/packages/core/src/visitor.ts.erb
24457
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.9.5/templates/javascript/packages/core/src/visitor.ts.erb
24226
24458
  class Visitor {
24227
24459
  visit(node) {
24228
24460
  if (!node)
@@ -24405,12 +24637,16 @@ class Visitor {
24405
24637
  this.visitNode(node);
24406
24638
  this.visitChildNodes(node);
24407
24639
  }
24640
+ visitRubyRenderKeywordsNode(node) {
24641
+ this.visitNode(node);
24642
+ this.visitChildNodes(node);
24643
+ }
24408
24644
  visitERBRenderNode(node) {
24409
24645
  this.visitNode(node);
24410
24646
  this.visitERBNode(node);
24411
24647
  this.visitChildNodes(node);
24412
24648
  }
24413
- visitRubyStrictLocalNode(node) {
24649
+ visitRubyParameterNode(node) {
24414
24650
  this.visitNode(node);
24415
24651
  this.visitChildNodes(node);
24416
24652
  }
@@ -24617,6 +24853,124 @@ class ActionViewTagHelperToHTMLRewriter extends ASTRewriter {
24617
24853
  }
24618
24854
  }
24619
24855
 
24856
+ const STRING_NODE_TYPE = "StringNode";
24857
+ const INTERPOLATED_STRING_NODE_TYPE = "InterpolatedStringNode";
24858
+ const EMBEDDED_STATEMENTS_NODE_TYPE = "EmbeddedStatementsNode";
24859
+ class ERBStringToDirectOutputVisitor extends Visitor {
24860
+ root;
24861
+ constructor(root) {
24862
+ super();
24863
+ this.root = root;
24864
+ }
24865
+ visitERBContentNode(node) {
24866
+ if (!isERBOutputNode(node)) {
24867
+ this.visitChildNodes(node);
24868
+ return;
24869
+ }
24870
+ const prismNode = node.prismNode;
24871
+ if (!prismNode) {
24872
+ this.visitChildNodes(node);
24873
+ return;
24874
+ }
24875
+ const source = node.source;
24876
+ if (!source) {
24877
+ this.visitChildNodes(node);
24878
+ return;
24879
+ }
24880
+ if (!ERBStringToDirectOutputRewriter.isStringOutputNode(prismNode)) {
24881
+ this.visitChildNodes(node);
24882
+ return;
24883
+ }
24884
+ const replacementParts = ERBStringToDirectOutputRewriter.extractReplacementParts(prismNode, source);
24885
+ if (!replacementParts) {
24886
+ this.visitChildNodes(node);
24887
+ return;
24888
+ }
24889
+ const tagOpening = node.tag_opening?.value ?? "<%=";
24890
+ const tagClosing = node.tag_closing?.value ?? "%>";
24891
+ const parentInfo = findParentArray(this.root, node);
24892
+ if (!parentInfo) {
24893
+ this.visitChildNodes(node);
24894
+ return;
24895
+ }
24896
+ const { array: parentArray, index: nodeIndex } = parentInfo;
24897
+ const replacementNodes = [];
24898
+ for (const part of replacementParts) {
24899
+ if (part.type === "text") {
24900
+ replacementNodes.push(createLiteral(part.content));
24901
+ }
24902
+ else {
24903
+ replacementNodes.push(createERBOutputNode(` ${part.expression.trim()} `, tagOpening, tagClosing));
24904
+ }
24905
+ }
24906
+ parentArray.splice(nodeIndex, 1, ...replacementNodes);
24907
+ }
24908
+ }
24909
+ class ERBStringToDirectOutputRewriter extends ASTRewriter {
24910
+ get name() {
24911
+ return "erb-string-to-direct-output";
24912
+ }
24913
+ get description() {
24914
+ return "Replaces ERB string output with direct text and expression tags";
24915
+ }
24916
+ rewrite(node, _context) {
24917
+ const visitor = new ERBStringToDirectOutputVisitor(node);
24918
+ visitor.visit(node);
24919
+ return node;
24920
+ }
24921
+ static isStringOutputNode(prismNode) {
24922
+ return isPrismNodeType(prismNode, STRING_NODE_TYPE) || isPrismNodeType(prismNode, INTERPOLATED_STRING_NODE_TYPE);
24923
+ }
24924
+ static extractStringContent(stringNode, source) {
24925
+ const unescapedValue = stringNode.unescaped?.value;
24926
+ if (typeof unescapedValue === "string") {
24927
+ return unescapedValue;
24928
+ }
24929
+ const location = stringNode.contentLoc;
24930
+ if (location) {
24931
+ return source.substring(location.startOffset, location.startOffset + location.length);
24932
+ }
24933
+ return "";
24934
+ }
24935
+ static extractExpressionSource(embeddedNode, source) {
24936
+ const openingLocation = embeddedNode.openingLoc;
24937
+ const closingLocation = embeddedNode.closingLoc;
24938
+ if (!openingLocation || !closingLocation)
24939
+ return null;
24940
+ const expressionStart = openingLocation.startOffset + openingLocation.length;
24941
+ const expressionEnd = closingLocation.startOffset;
24942
+ return source.substring(expressionStart, expressionEnd);
24943
+ }
24944
+ static extractReplacementParts(prismNode, source) {
24945
+ if (isPrismNodeType(prismNode, STRING_NODE_TYPE)) {
24946
+ const textContent = this.extractStringContent(prismNode, source);
24947
+ return [{ type: "text", content: textContent }];
24948
+ }
24949
+ if (isPrismNodeType(prismNode, INTERPOLATED_STRING_NODE_TYPE)) {
24950
+ const parts = prismNode.parts;
24951
+ if (!parts || parts.length === 0)
24952
+ return null;
24953
+ const replacementParts = [];
24954
+ for (const part of parts) {
24955
+ if (isPrismNodeType(part, STRING_NODE_TYPE)) {
24956
+ const textContent = this.extractStringContent(part, source);
24957
+ if (textContent) {
24958
+ replacementParts.push({ type: "text", content: textContent });
24959
+ }
24960
+ }
24961
+ else if (isPrismNodeType(part, EMBEDDED_STATEMENTS_NODE_TYPE)) {
24962
+ const expression = this.extractExpressionSource(part, source);
24963
+ if (expression) {
24964
+ replacementParts.push({ type: "expression", expression });
24965
+ }
24966
+ }
24967
+ }
24968
+ return replacementParts.length > 0 ? replacementParts : null;
24969
+ }
24970
+ return null;
24971
+ }
24972
+ }
24973
+
24620
24974
  function serializeAttributeValue(value) {
24621
24975
  const hasERB = value.children.some(child => isERBContentNode(child));
24622
24976
  if (hasERB && value.children.length === 1 && isERBContentNode(value.children[0])) {
@@ -25210,12 +25564,12 @@ class IdentityPrinter extends Printer {
25210
25564
  this.visitChildNodes(node);
25211
25565
  }
25212
25566
  visitHTMLAttributeValueNode(node) {
25213
- if (node.quoted && node.open_quote) {
25214
- this.write(node.open_quote.value);
25567
+ if (node.quoted) {
25568
+ this.write(node.open_quote?.value ?? '"');
25215
25569
  }
25216
25570
  this.visitChildNodes(node);
25217
- if (node.quoted && node.close_quote) {
25218
- this.write(node.close_quote.value);
25571
+ if (node.quoted) {
25572
+ this.write(node.close_quote?.value ?? '"');
25219
25573
  }
25220
25574
  }
25221
25575
  visitRubyLiteralNode(node) {
@@ -25400,6 +25754,24 @@ class IdentityPrinter extends Printer {
25400
25754
  }
25401
25755
  visitERBRenderNode(node) {
25402
25756
  this.printERBNode(node);
25757
+ if (node.end_node) {
25758
+ if (node.body) {
25759
+ node.body.forEach(child => this.visit(child));
25760
+ }
25761
+ if (node.rescue_clause) {
25762
+ this.visit(node.rescue_clause);
25763
+ }
25764
+ if (node.else_clause) {
25765
+ this.visit(node.else_clause);
25766
+ }
25767
+ if (node.ensure_clause) {
25768
+ this.visit(node.ensure_clause);
25769
+ }
25770
+ this.visit(node.end_node);
25771
+ }
25772
+ }
25773
+ visitRubyRenderKeywordsNode(_node) {
25774
+ // no-op: extracted metadata, nothing to print
25403
25775
  }
25404
25776
  visitRubyRenderLocalNode(_node) {
25405
25777
  // extracted metadata, nothing to print
@@ -25407,7 +25779,7 @@ class IdentityPrinter extends Printer {
25407
25779
  visitERBStrictLocalsNode(node) {
25408
25780
  this.printERBNode(node);
25409
25781
  }
25410
- visitRubyStrictLocalNode(_node) {
25782
+ visitRubyParameterNode(_node) {
25411
25783
  // extracted metadata, nothing to print
25412
25784
  }
25413
25785
  visitERBYieldNode(node) {
@@ -25540,6 +25912,7 @@ function rewriteString(herb, template, rewriters, options = {}) {
25540
25912
 
25541
25913
  exports.ASTRewriter = ASTRewriter;
25542
25914
  exports.ActionViewTagHelperToHTMLRewriter = ActionViewTagHelperToHTMLRewriter;
25915
+ exports.ERBStringToDirectOutputRewriter = ERBStringToDirectOutputRewriter;
25543
25916
  exports.HTMLToActionViewTagHelperRewriter = HTMLToActionViewTagHelperRewriter;
25544
25917
  exports.StringRewriter = StringRewriter;
25545
25918
  exports.asMutable = asMutable;