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