@openrewrite/rewrite 8.66.0 → 8.66.2

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.
Files changed (113) hide show
  1. package/dist/java/tree.d.ts +10 -1
  2. package/dist/java/tree.d.ts.map +1 -1
  3. package/dist/java/tree.js +21 -5
  4. package/dist/java/tree.js.map +1 -1
  5. package/dist/java/type-visitor.d.ts +1 -1
  6. package/dist/java/type-visitor.d.ts.map +1 -1
  7. package/dist/java/visitor.d.ts +2 -2
  8. package/dist/java/visitor.d.ts.map +1 -1
  9. package/dist/java/visitor.js +8 -2
  10. package/dist/java/visitor.js.map +1 -1
  11. package/dist/javascript/assertions.d.ts +6 -0
  12. package/dist/javascript/assertions.d.ts.map +1 -1
  13. package/dist/javascript/assertions.js +14 -6
  14. package/dist/javascript/assertions.js.map +1 -1
  15. package/dist/javascript/comparator.d.ts +217 -7
  16. package/dist/javascript/comparator.d.ts.map +1 -1
  17. package/dist/javascript/comparator.js +1020 -2848
  18. package/dist/javascript/comparator.js.map +1 -1
  19. package/dist/javascript/format.d.ts +5 -3
  20. package/dist/javascript/format.d.ts.map +1 -1
  21. package/dist/javascript/format.js +87 -44
  22. package/dist/javascript/format.js.map +1 -1
  23. package/dist/javascript/index.d.ts +2 -1
  24. package/dist/javascript/index.d.ts.map +1 -1
  25. package/dist/javascript/index.js +2 -1
  26. package/dist/javascript/index.js.map +1 -1
  27. package/dist/javascript/parser.d.ts +2 -1
  28. package/dist/javascript/parser.d.ts.map +1 -1
  29. package/dist/javascript/parser.js +54 -43
  30. package/dist/javascript/parser.js.map +1 -1
  31. package/dist/javascript/templating/capture.d.ts +293 -0
  32. package/dist/javascript/templating/capture.d.ts.map +1 -0
  33. package/dist/javascript/templating/capture.js +461 -0
  34. package/dist/javascript/templating/capture.js.map +1 -0
  35. package/dist/javascript/templating/comparator.d.ts +171 -0
  36. package/dist/javascript/templating/comparator.d.ts.map +1 -0
  37. package/dist/javascript/templating/comparator.js +1221 -0
  38. package/dist/javascript/templating/comparator.js.map +1 -0
  39. package/dist/javascript/templating/engine.d.ts +108 -0
  40. package/dist/javascript/templating/engine.d.ts.map +1 -0
  41. package/dist/javascript/templating/engine.js +661 -0
  42. package/dist/javascript/templating/engine.js.map +1 -0
  43. package/dist/javascript/templating/index.d.ts +6 -0
  44. package/dist/javascript/templating/index.d.ts.map +1 -0
  45. package/dist/javascript/templating/index.js +44 -0
  46. package/dist/javascript/templating/index.js.map +1 -0
  47. package/dist/javascript/templating/pattern.d.ts +276 -0
  48. package/dist/javascript/templating/pattern.d.ts.map +1 -0
  49. package/dist/javascript/templating/pattern.js +952 -0
  50. package/dist/javascript/templating/pattern.js.map +1 -0
  51. package/dist/javascript/templating/placeholder-replacement.d.ts +83 -0
  52. package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -0
  53. package/dist/javascript/templating/placeholder-replacement.js +467 -0
  54. package/dist/javascript/templating/placeholder-replacement.js.map +1 -0
  55. package/dist/javascript/templating/rewrite.d.ts +84 -0
  56. package/dist/javascript/templating/rewrite.d.ts.map +1 -0
  57. package/dist/javascript/templating/rewrite.js +208 -0
  58. package/dist/javascript/templating/rewrite.js.map +1 -0
  59. package/dist/javascript/templating/template.d.ts +230 -0
  60. package/dist/javascript/templating/template.d.ts.map +1 -0
  61. package/dist/javascript/templating/template.js +367 -0
  62. package/dist/javascript/templating/template.js.map +1 -0
  63. package/dist/javascript/templating/types.d.ts +610 -0
  64. package/dist/javascript/templating/types.d.ts.map +1 -0
  65. package/dist/javascript/templating/types.js +3 -0
  66. package/dist/javascript/templating/types.js.map +1 -0
  67. package/dist/javascript/templating/utils.d.ts +135 -0
  68. package/dist/javascript/templating/utils.d.ts.map +1 -0
  69. package/dist/javascript/templating/utils.js +251 -0
  70. package/dist/javascript/templating/utils.js.map +1 -0
  71. package/dist/javascript/type-mapping.d.ts.map +1 -1
  72. package/dist/javascript/type-mapping.js +21 -11
  73. package/dist/javascript/type-mapping.js.map +1 -1
  74. package/dist/json/rpc.js +2 -2
  75. package/dist/json/rpc.js.map +1 -1
  76. package/dist/recipe/order-imports.js.map +1 -1
  77. package/dist/test/rewrite-test.d.ts.map +1 -1
  78. package/dist/test/rewrite-test.js +10 -6
  79. package/dist/test/rewrite-test.js.map +1 -1
  80. package/dist/version.txt +1 -1
  81. package/dist/visitor.d.ts +4 -4
  82. package/dist/visitor.d.ts.map +1 -1
  83. package/dist/visitor.js +8 -3
  84. package/dist/visitor.js.map +1 -1
  85. package/package.json +5 -2
  86. package/src/java/tree.ts +10 -3
  87. package/src/java/type-visitor.ts +1 -1
  88. package/src/java/visitor.ts +11 -5
  89. package/src/javascript/assertions.ts +9 -3
  90. package/src/javascript/comparator.ts +1095 -3373
  91. package/src/javascript/format.ts +72 -33
  92. package/src/javascript/index.ts +2 -1
  93. package/src/javascript/parser.ts +67 -45
  94. package/src/javascript/templating/capture.ts +595 -0
  95. package/src/javascript/templating/comparator.ts +1383 -0
  96. package/src/javascript/templating/engine.ts +750 -0
  97. package/src/javascript/templating/index.ts +67 -0
  98. package/src/javascript/templating/pattern.ts +1101 -0
  99. package/src/javascript/templating/placeholder-replacement.ts +475 -0
  100. package/src/javascript/templating/rewrite.ts +229 -0
  101. package/src/javascript/templating/template.ts +414 -0
  102. package/src/javascript/templating/types.ts +674 -0
  103. package/src/javascript/templating/utils.ts +298 -0
  104. package/src/javascript/type-mapping.ts +20 -11
  105. package/src/json/rpc.ts +2 -2
  106. package/src/recipe/order-imports.ts +1 -1
  107. package/src/test/rewrite-test.ts +12 -7
  108. package/src/visitor.ts +14 -6
  109. package/dist/javascript/templating.d.ts +0 -265
  110. package/dist/javascript/templating.d.ts.map +0 -1
  111. package/dist/javascript/templating.js +0 -1027
  112. package/dist/javascript/templating.js.map +0 -1
  113. package/src/javascript/templating.ts +0 -1226
@@ -0,0 +1,475 @@
1
+ /*
2
+ * Copyright 2025 the original author or authors.
3
+ * <p>
4
+ * Licensed under the Moderne Source Available License (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ * <p>
8
+ * https://docs.moderne.io/licensing/moderne-source-available-license
9
+ * <p>
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import {Cursor, isTree} from '../..';
17
+ import {J} from '../../java';
18
+ import {JS} from '..';
19
+ import {JavaScriptVisitor} from '../visitor';
20
+ import {produce} from 'immer';
21
+ import {PlaceholderUtils} from './utils';
22
+ import {CaptureImpl, TemplateParamImpl, CaptureValue, CAPTURE_NAME_SYMBOL} from './capture';
23
+ import {Parameter} from './types';
24
+
25
+ /**
26
+ * Visitor that replaces placeholder nodes with actual parameter values.
27
+ */
28
+ export class PlaceholderReplacementVisitor extends JavaScriptVisitor<any> {
29
+ constructor(
30
+ private readonly substitutions: Map<string, Parameter>,
31
+ private readonly values: Pick<Map<string, J | J[]>, 'get'> = new Map(),
32
+ private readonly wrappersMap: Pick<Map<string, J.RightPadded<J> | J.RightPadded<J>[]>, 'get'> = new Map()
33
+ ) {
34
+ super();
35
+ }
36
+
37
+ async visit<R extends J>(tree: J, p: any, parent?: Cursor): Promise<R | undefined> {
38
+ // Check if this node is a placeholder
39
+ // BUT: Don't handle `JS.BindingElement` here - let `visitBindingElement` preserve `propertyName`
40
+ if (tree.kind !== JS.Kind.BindingElement && this.isPlaceholder(tree)) {
41
+ const replacement = this.replacePlaceholder(tree);
42
+ if (replacement !== tree) {
43
+ return replacement as R;
44
+ }
45
+ }
46
+
47
+ // Continue with normal traversal
48
+ return super.visit(tree, p, parent);
49
+ }
50
+
51
+ /**
52
+ * Override visitBindingElement to preserve propertyName from template when replacing.
53
+ * For example, in `{ ref: ${ref} }`, we want to preserve `ref:` when replacing ${ref}.
54
+ */
55
+ override async visitBindingElement(bindingElement: JS.BindingElement, p: any): Promise<J | undefined> {
56
+ // Visit the name to potentially replace placeholders
57
+ const visitedName = await this.visit(bindingElement.name, p);
58
+
59
+ // If the name changed (placeholder was replaced), preserve the BindingElement structure
60
+ // including the propertyName from the template
61
+ if (visitedName !== bindingElement.name) {
62
+ return produce(bindingElement, draft => {
63
+ draft.name = visitedName as any;
64
+ // propertyName is already set from the template and will be preserved by produce
65
+ });
66
+ }
67
+
68
+ return bindingElement;
69
+ }
70
+
71
+ /**
72
+ * Override visitContainer to handle variadic expansion for containers.
73
+ * This handles J.Container instances anywhere in the AST (method arguments, etc.).
74
+ */
75
+ override async visitContainer<T extends J>(container: J.Container<T>, p: any): Promise<J.Container<T>> {
76
+ // Check if any elements are placeholders (possibly variadic)
77
+ const hasPlaceholder = container.elements.some(elem => this.isPlaceholder(elem.element));
78
+
79
+ if (!hasPlaceholder) {
80
+ return super.visitContainer(container, p);
81
+ }
82
+
83
+ // Expand variadic placeholders in the container's elements
84
+ const newElements = await this.expandVariadicElements(container.elements, undefined, p);
85
+
86
+ return produce(container, draft => {
87
+ draft.elements = newElements as any;
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Override visitRightPadded to handle single placeholder replacements.
93
+ * The base implementation will visit the element, which triggers our visit() override
94
+ * for placeholder detection and replacement.
95
+ */
96
+ override async visitRightPadded<T extends J | boolean>(right: J.RightPadded<T>, p: any): Promise<J.RightPadded<T> | undefined> {
97
+ return super.visitRightPadded(right, p);
98
+ }
99
+
100
+ /**
101
+ * Override visitBlock to handle variadic expansion in block statements.
102
+ * Block.statements is J.RightPadded<Statement>[] (not a Container), so we need
103
+ * array-level access for variadic expansion.
104
+ */
105
+ override async visitBlock(block: J.Block, p: any): Promise<J | undefined> {
106
+ const hasPlaceholder = block.statements.some(stmt => {
107
+ const stmtElement = stmt.element;
108
+ // Check if it's an ExpressionStatement containing a placeholder
109
+ if (stmtElement.kind === JS.Kind.ExpressionStatement) {
110
+ const exprStmt = stmtElement as JS.ExpressionStatement;
111
+ return this.isPlaceholder(exprStmt.expression);
112
+ }
113
+ return this.isPlaceholder(stmtElement);
114
+ });
115
+
116
+ if (!hasPlaceholder) {
117
+ return super.visitBlock(block, p);
118
+ }
119
+
120
+ // Unwrap function to extract placeholder from ExpressionStatement
121
+ const unwrapStatement = (element: J): J => {
122
+ if (element.kind === JS.Kind.ExpressionStatement) {
123
+ return (element as JS.ExpressionStatement).expression;
124
+ }
125
+ return element;
126
+ };
127
+
128
+ const newStatements = await this.expandVariadicElements(block.statements, unwrapStatement, p);
129
+
130
+ return produce(block, draft => {
131
+ draft.statements = newStatements;
132
+ });
133
+ }
134
+
135
+ /**
136
+ * Override visitJsCompilationUnit to handle variadic expansion in top-level statements.
137
+ * CompilationUnit.statements is J.RightPadded<Statement>[] (not a Container), so we need
138
+ * array-level access for variadic expansion.
139
+ */
140
+ override async visitJsCompilationUnit(compilationUnit: JS.CompilationUnit, p: any): Promise<J | undefined> {
141
+ const hasPlaceholder = compilationUnit.statements.some(stmt => this.isPlaceholder(stmt.element));
142
+
143
+ if (!hasPlaceholder) {
144
+ return super.visitJsCompilationUnit(compilationUnit, p);
145
+ }
146
+
147
+ const newStatements = await this.expandVariadicElements(compilationUnit.statements, undefined, p);
148
+
149
+ return produce(compilationUnit, draft => {
150
+ draft.statements = newStatements;
151
+ });
152
+ }
153
+
154
+ /**
155
+ * Merges prefixes by preserving comments from the source element
156
+ * while using whitespace from the template placeholder.
157
+ *
158
+ * @param sourcePrefix The prefix from the captured element (may contain comments)
159
+ * @param templatePrefix The prefix from the template placeholder (defines whitespace)
160
+ * @returns A merged prefix with source comments and template whitespace
161
+ */
162
+ private mergePrefix(sourcePrefix: J.Space, templatePrefix: J.Space): J.Space {
163
+ // If source has no comments, just use template prefix
164
+ if (sourcePrefix.comments.length === 0) {
165
+ return templatePrefix;
166
+ }
167
+
168
+ // Preserve comments from source, use whitespace from template
169
+ return {
170
+ kind: J.Kind.Space,
171
+ comments: sourcePrefix.comments,
172
+ whitespace: templatePrefix.whitespace
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Expands variadic placeholders in a list of elements.
178
+ *
179
+ * @param elements The list of wrapped elements to process
180
+ * @param unwrapElement Optional function to unwrap the placeholder node from its container (e.g., ExpressionStatement)
181
+ * @param p Context parameter for visitor
182
+ * @returns Promise of new list with placeholders expanded
183
+ */
184
+ private async expandVariadicElements(
185
+ elements: J.RightPadded<J>[],
186
+ unwrapElement: (element: J) => J = (e) => e,
187
+ p: any
188
+ ): Promise<J.RightPadded<J>[]> {
189
+ const newElements: J.RightPadded<J>[] = [];
190
+
191
+ for (const wrapped of elements) {
192
+ const element = wrapped.element;
193
+ const placeholderNode = unwrapElement(element);
194
+
195
+ // Check if this element contains a placeholder
196
+ if (this.isPlaceholder(placeholderNode)) {
197
+ const placeholderText = this.getPlaceholderText(placeholderNode);
198
+ if (placeholderText) {
199
+ const param = this.substitutions.get(placeholderText);
200
+ if (param) {
201
+ let arrayToExpand: J[] | J.RightPadded<J>[] | undefined = undefined;
202
+
203
+ // Check if it's a J.Container
204
+ const isContainer = param.value && typeof param.value === 'object' &&
205
+ param.value.kind === J.Kind.Container;
206
+ if (isContainer) {
207
+ // Extract elements from J.Container
208
+ arrayToExpand = param.value.elements as J.RightPadded<J>[];
209
+ }
210
+ // Check if it's a direct Tree[] array
211
+ else if (Array.isArray(param.value)) {
212
+ arrayToExpand = param.value as J[];
213
+ }
214
+ // Check if it's a CaptureValue
215
+ else if (param.value instanceof CaptureValue) {
216
+ const resolved = param.value.resolve(this.values);
217
+ if (Array.isArray(resolved)) {
218
+ arrayToExpand = resolved;
219
+ }
220
+ }
221
+ // Check if it's a direct variadic capture
222
+ else {
223
+ const isCapture = param.value instanceof CaptureImpl ||
224
+ (param.value && typeof param.value === 'object' && param.value[CAPTURE_NAME_SYMBOL]);
225
+ if (isCapture) {
226
+ const name = param.value[CAPTURE_NAME_SYMBOL] || param.value.name;
227
+ const capture = Array.from(this.substitutions.values())
228
+ .map(p => p.value)
229
+ .find(v => v instanceof CaptureImpl && v.getName() === name) as CaptureImpl | undefined;
230
+
231
+ if (capture?.isVariadic()) {
232
+ // Prefer wrappers if available (to preserve markers like Semicolon)
233
+ // Otherwise fall back to elements
234
+ const wrappersArray = this.wrappersMap.get(name);
235
+ if (Array.isArray(wrappersArray)) {
236
+ arrayToExpand = wrappersArray;
237
+ } else {
238
+ const matchedArray = this.values.get(name);
239
+ if (Array.isArray(matchedArray)) {
240
+ arrayToExpand = matchedArray;
241
+ }
242
+ }
243
+ }
244
+ }
245
+ }
246
+
247
+ // Expand the array if we found one
248
+ if (arrayToExpand !== undefined) {
249
+ if (arrayToExpand.length > 0) {
250
+ for (let i = 0; i < arrayToExpand.length; i++) {
251
+ const item = arrayToExpand[i];
252
+
253
+ // Check if item is a JRightPadded wrapper or just an element
254
+ // JRightPadded wrappers have 'element', 'after', and 'markers' properties
255
+ // Also ensure the element field is not null
256
+ const isWrapper = item && typeof item === 'object' && 'element' in item && 'after' in item && item.element != null;
257
+
258
+ if (isWrapper) {
259
+ // Item is a JRightPadded wrapper - use it directly to preserve markers
260
+ newElements.push(produce(item, draft => {
261
+ if (i === 0 && draft.element) {
262
+ // Merge the placeholder's prefix with the first item's prefix
263
+ // Modify prefix directly without nested produce to avoid immer issues
264
+ draft.element.prefix = this.mergePrefix(draft.element.prefix, element.prefix);
265
+ }
266
+ // Keep all other wrapper properties (including markers with Semicolon)
267
+ }));
268
+ } else if (item) {
269
+ // Item is just an element (not a wrapper) - wrap it (backward compatibility)
270
+ const elem = item as J;
271
+ newElements.push(produce(wrapped, draft => {
272
+ draft.element = produce(elem, itemDraft => {
273
+ if (i === 0) {
274
+ itemDraft.prefix = this.mergePrefix(elem.prefix, element.prefix);
275
+ }
276
+ // For i > 0, prefix is already correct, no changes needed
277
+ });
278
+ }));
279
+ }
280
+ }
281
+ continue; // Skip adding the placeholder itself
282
+ } else {
283
+ // Empty array - don't add any elements
284
+ continue;
285
+ }
286
+ }
287
+ }
288
+ }
289
+ }
290
+
291
+ // Not a placeholder (or expansion failed) - process normally
292
+ const replacedElement = await this.visit(element, p);
293
+ if (replacedElement) {
294
+ // Check if the replacement came from a capture with a wrapper (to preserve markers)
295
+ const placeholderNode = unwrapElement(element);
296
+ const placeholderText = this.getPlaceholderText(placeholderNode);
297
+ let wrapperToUse = wrapped;
298
+
299
+ if (placeholderText && this.isPlaceholder(placeholderNode)) {
300
+ const param = this.substitutions.get(placeholderText);
301
+ if (param) {
302
+ const isCapture = param.value instanceof CaptureImpl ||
303
+ (param.value && typeof param.value === 'object' && param.value[CAPTURE_NAME_SYMBOL]);
304
+ if (isCapture) {
305
+ const name = param.value[CAPTURE_NAME_SYMBOL] || param.value.name;
306
+ const wrapper = this.wrappersMap.get(name);
307
+ // Use captured wrapper if available and not an array (non-variadic)
308
+ if (wrapper && !Array.isArray(wrapper)) {
309
+ wrapperToUse = wrapper as J.RightPadded<J>;
310
+ }
311
+ }
312
+ }
313
+ }
314
+
315
+ newElements.push(produce(wrapperToUse, draft => {
316
+ draft.element = replacedElement;
317
+ }));
318
+ }
319
+ }
320
+
321
+ return newElements;
322
+ }
323
+
324
+ /**
325
+ * Checks if a node is a placeholder.
326
+ *
327
+ * @param node The node to check
328
+ * @returns True if the node is a placeholder
329
+ */
330
+ private isPlaceholder(node: J): boolean {
331
+ if (node.kind === J.Kind.Identifier) {
332
+ const identifier = node as J.Identifier;
333
+ return identifier.simpleName.startsWith(PlaceholderUtils.PLACEHOLDER_PREFIX);
334
+ } else if (node.kind === J.Kind.Literal) {
335
+ const literal = node as J.Literal;
336
+ return literal.valueSource?.startsWith(PlaceholderUtils.PLACEHOLDER_PREFIX) || false;
337
+ } else if (node.kind === JS.Kind.BindingElement) {
338
+ // Check if the BindingElement's name is a placeholder
339
+ const bindingElement = node as JS.BindingElement;
340
+ return this.isPlaceholder(bindingElement.name);
341
+ }
342
+ return false;
343
+ }
344
+
345
+ /**
346
+ * Replaces a placeholder node with the actual parameter value.
347
+ *
348
+ * @param placeholder The placeholder node
349
+ * @returns The replacement node or the original if not a placeholder
350
+ */
351
+ private replacePlaceholder(placeholder: J): J {
352
+ const placeholderText = this.getPlaceholderText(placeholder);
353
+
354
+ if (!placeholderText || !placeholderText.startsWith(PlaceholderUtils.PLACEHOLDER_PREFIX)) {
355
+ return placeholder;
356
+ }
357
+
358
+ // Find the corresponding parameter
359
+ const param = this.substitutions.get(placeholderText);
360
+ if (!param || param.value === undefined) {
361
+ return placeholder;
362
+ }
363
+
364
+ // Check if the parameter value is a CaptureValue
365
+ const isCaptureValue = param.value instanceof CaptureValue;
366
+
367
+ if (isCaptureValue) {
368
+ // Resolve the capture value to get the actual property value
369
+ const propertyValue = param.value.resolve(this.values);
370
+
371
+ if (propertyValue !== undefined) {
372
+ // If the property value is already a J node, use it
373
+ if (isTree(propertyValue)) {
374
+ const propValueAsJ = propertyValue as J;
375
+ return produce(propValueAsJ, draft => {
376
+ draft.markers = placeholder.markers;
377
+ draft.prefix = this.mergePrefix(propValueAsJ.prefix, placeholder.prefix);
378
+ });
379
+ }
380
+ // If it's a primitive value and placeholder is an identifier, update the simpleName
381
+ if (typeof propertyValue === 'string' && placeholder.kind === J.Kind.Identifier) {
382
+ return produce(placeholder as J.Identifier, draft => {
383
+ draft.simpleName = propertyValue;
384
+ });
385
+ }
386
+ // If it's a primitive value and placeholder is a literal, update the value
387
+ if (typeof propertyValue === 'string' && placeholder.kind === J.Kind.Literal) {
388
+ return produce(placeholder as J.Literal, draft => {
389
+ draft.value = propertyValue;
390
+ draft.valueSource = `"${propertyValue}"`;
391
+ });
392
+ }
393
+ }
394
+
395
+ // If no match found or unhandled type, return placeholder unchanged
396
+ return placeholder;
397
+ }
398
+
399
+ // Check if the parameter value is a Capture (could be a Proxy) or TemplateParam
400
+ const isCapture = param.value instanceof CaptureImpl ||
401
+ (param.value && typeof param.value === 'object' && param.value[CAPTURE_NAME_SYMBOL]);
402
+ const isTemplateParam = param.value instanceof TemplateParamImpl;
403
+
404
+ if (isCapture || isTemplateParam) {
405
+ // Simple capture/template param (no property path for template params)
406
+ const name = isTemplateParam ? param.value.name :
407
+ (param.value[CAPTURE_NAME_SYMBOL] || param.value.name);
408
+ const matchedNode = this.values.get(name);
409
+ if (matchedNode && !Array.isArray(matchedNode)) {
410
+ return produce(matchedNode, draft => {
411
+ draft.markers = placeholder.markers;
412
+ draft.prefix = this.mergePrefix(matchedNode.prefix, placeholder.prefix);
413
+ });
414
+ }
415
+
416
+ // If no match found, return placeholder unchanged
417
+ return placeholder;
418
+ }
419
+
420
+ // Check if the parameter value is a J.RightPadded wrapper
421
+ const isRightPadded = param.value && typeof param.value === 'object' &&
422
+ param.value.kind === J.Kind.RightPadded && isTree(param.value.element);
423
+
424
+ if (isRightPadded) {
425
+ // Extract the element from the J.RightPadded wrapper
426
+ const element = param.value.element as J;
427
+ return produce(element, draft => {
428
+ draft.markers = placeholder.markers;
429
+ draft.prefix = this.mergePrefix(element.prefix, placeholder.prefix);
430
+ });
431
+ }
432
+
433
+ // Check if the parameter value is a J.Container
434
+ const isContainer = param.value && typeof param.value === 'object' &&
435
+ param.value.kind === J.Kind.Container;
436
+
437
+ if (isContainer) {
438
+ // J.Container should be handled by expandVariadicElements
439
+ // For now, return placeholder - the expansion will happen at a higher level
440
+ // This should not happen in normal usage, as containers are typically used in argument positions
441
+ return placeholder;
442
+ }
443
+
444
+ // If the parameter value is an AST node, use it directly
445
+ if (isTree(param.value)) {
446
+ // Return the AST node, preserving comments from the source
447
+ return produce(param.value as J, draft => {
448
+ draft.markers = placeholder.markers;
449
+ draft.prefix = this.mergePrefix(param.value.prefix, placeholder.prefix);
450
+ });
451
+ }
452
+
453
+ return placeholder;
454
+ }
455
+
456
+ /**
457
+ * Gets the placeholder text from a node.
458
+ *
459
+ * @param node The node to get placeholder text from
460
+ * @returns The placeholder text or null
461
+ */
462
+ private getPlaceholderText(node: J): string | null {
463
+ if (node.kind === J.Kind.Identifier) {
464
+ return (node as J.Identifier).simpleName;
465
+ } else if (node.kind === J.Kind.Literal) {
466
+ return (node as J.Literal).valueSource || null;
467
+ } else if (node.kind === JS.Kind.BindingElement) {
468
+ // Extract placeholder text from the BindingElement's name
469
+ const bindingElement = node as JS.BindingElement;
470
+ return this.getPlaceholderText(bindingElement.name);
471
+ }
472
+ return null;
473
+ }
474
+
475
+ }