@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,595 @@
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} from '../..';
17
+ import {J, Type} from '../../java';
18
+ import {Any, Capture, CaptureOptions, ConstraintFunction, TemplateParam, VariadicOptions} from './types';
19
+
20
+ /**
21
+ * Combines multiple constraints with AND logic.
22
+ * All constraints must return true for the combined constraint to pass.
23
+ *
24
+ * @example
25
+ * const largeEvenNumber = capture('n', {
26
+ * constraint: and(
27
+ * (node) => typeof node.value === 'number',
28
+ * (node) => node.value > 100,
29
+ * (node) => node.value % 2 === 0
30
+ * )
31
+ * });
32
+ */
33
+ export function and<T>(...constraints: ((node: T, cursor?: Cursor) => boolean)[]): (node: T, cursor?: Cursor) => boolean {
34
+ return (node: T, cursor?: Cursor) => constraints.every(c => c(node, cursor));
35
+ }
36
+
37
+ /**
38
+ * Combines multiple constraints with OR logic.
39
+ * At least one constraint must return true for the combined constraint to pass.
40
+ *
41
+ * @example
42
+ * const stringOrNumber = capture('value', {
43
+ * constraint: or(
44
+ * (node) => node.kind === J.Kind.Literal && typeof node.value === 'string',
45
+ * (node) => node.kind === J.Kind.Literal && typeof node.value === 'number'
46
+ * )
47
+ * });
48
+ */
49
+ export function or<T>(...constraints: ((node: T, cursor?: Cursor) => boolean)[]): (node: T, cursor?: Cursor) => boolean {
50
+ return (node: T, cursor?: Cursor) => constraints.some(c => c(node, cursor));
51
+ }
52
+
53
+ /**
54
+ * Negates a constraint.
55
+ * Returns true when the constraint returns false, and vice versa.
56
+ *
57
+ * @example
58
+ * const notString = capture('value', {
59
+ * constraint: not((node) => typeof node.value === 'string')
60
+ * });
61
+ */
62
+ export function not<T>(constraint: (node: T, cursor?: Cursor) => boolean): (node: T, cursor?: Cursor) => boolean {
63
+ return (node: T, cursor?: Cursor) => !constraint(node, cursor);
64
+ }
65
+
66
+ // Symbol to access the internal capture name without triggering Proxy
67
+ export const CAPTURE_NAME_SYMBOL = Symbol('captureName');
68
+ // Symbol to access variadic options without triggering Proxy
69
+ export const CAPTURE_VARIADIC_SYMBOL = Symbol('captureVariadic');
70
+ // Symbol to access constraint function without triggering Proxy
71
+ export const CAPTURE_CONSTRAINT_SYMBOL = Symbol('captureConstraint');
72
+ // Symbol to access capturing flag without triggering Proxy
73
+ export const CAPTURE_CAPTURING_SYMBOL = Symbol('captureCapturing');
74
+ // Symbol to access type information without triggering Proxy
75
+ export const CAPTURE_TYPE_SYMBOL = Symbol('captureType');
76
+ // Symbol to identify RawCode instances
77
+ export const RAW_CODE_SYMBOL = Symbol('rawCode');
78
+
79
+ export class CaptureImpl<T = any> implements Capture<T> {
80
+ public readonly name: string;
81
+ [CAPTURE_NAME_SYMBOL]: string;
82
+ [CAPTURE_VARIADIC_SYMBOL]: VariadicOptions | undefined;
83
+ [CAPTURE_CONSTRAINT_SYMBOL]: ConstraintFunction<T> | undefined;
84
+ [CAPTURE_CAPTURING_SYMBOL]: boolean;
85
+ [CAPTURE_TYPE_SYMBOL]: string | Type | undefined;
86
+
87
+ constructor(name: string, options?: CaptureOptions<T>, capturing: boolean = true) {
88
+ this.name = name;
89
+ this[CAPTURE_NAME_SYMBOL] = name;
90
+ this[CAPTURE_CAPTURING_SYMBOL] = capturing;
91
+
92
+ // Normalize variadic options
93
+ if (options?.variadic) {
94
+ if (typeof options.variadic === 'boolean') {
95
+ this[CAPTURE_VARIADIC_SYMBOL] = {};
96
+ } else {
97
+ this[CAPTURE_VARIADIC_SYMBOL] = {
98
+ min: options.variadic.min,
99
+ max: options.variadic.max
100
+ };
101
+ }
102
+ }
103
+
104
+ // Store constraint if provided
105
+ if (options?.constraint) {
106
+ this[CAPTURE_CONSTRAINT_SYMBOL] = options.constraint;
107
+ }
108
+
109
+ // Store type if provided
110
+ if (options?.type) {
111
+ this[CAPTURE_TYPE_SYMBOL] = options.type;
112
+ }
113
+ }
114
+
115
+ getName(): string {
116
+ return this[CAPTURE_NAME_SYMBOL];
117
+ }
118
+
119
+ isVariadic(): boolean {
120
+ return this[CAPTURE_VARIADIC_SYMBOL] !== undefined;
121
+ }
122
+
123
+ getVariadicOptions(): VariadicOptions | undefined {
124
+ return this[CAPTURE_VARIADIC_SYMBOL];
125
+ }
126
+
127
+ getConstraint(): ConstraintFunction<T> | undefined {
128
+ return this[CAPTURE_CONSTRAINT_SYMBOL];
129
+ }
130
+
131
+ isCapturing(): boolean {
132
+ return this[CAPTURE_CAPTURING_SYMBOL];
133
+ }
134
+
135
+ getType(): string | Type | undefined {
136
+ return this[CAPTURE_TYPE_SYMBOL];
137
+ }
138
+ }
139
+
140
+ export class TemplateParamImpl<T = any> implements TemplateParam<T> {
141
+ public readonly name: string;
142
+
143
+ constructor(name: string) {
144
+ this.name = name;
145
+ }
146
+
147
+ getName(): string {
148
+ return this.name;
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Represents a property access on a captured value.
154
+ * When you access a property on a Capture (e.g., method.name), you get a CaptureValue
155
+ * that knows how to resolve that property from the matched values.
156
+ */
157
+ export class CaptureValue {
158
+ constructor(
159
+ public readonly rootCapture: Capture,
160
+ public readonly propertyPath: string[],
161
+ public readonly arrayOperation?: { type: 'index' | 'slice' | 'length'; args?: number[] }
162
+ ) {}
163
+
164
+ /**
165
+ * Resolves this capture value by looking up the root capture in the values map
166
+ * and navigating through the property path.
167
+ */
168
+ resolve(values: Pick<Map<string, J | J[]>, 'get'>): any {
169
+ const rootName = this.rootCapture.getName();
170
+ let current: any = values.get(rootName);
171
+
172
+ // Handle array operations on variadic captures
173
+ if (this.arrayOperation && Array.isArray(current)) {
174
+ switch (this.arrayOperation.type) {
175
+ case 'index':
176
+ current = current[this.arrayOperation.args![0]];
177
+ break;
178
+ case 'slice':
179
+ current = current.slice(...this.arrayOperation.args!);
180
+ break;
181
+ case 'length':
182
+ current = current.length;
183
+ break;
184
+ }
185
+ }
186
+
187
+ // Navigate through property path
188
+ for (const prop of this.propertyPath) {
189
+ if (current === null || current === undefined || typeof current === 'number') {
190
+ return undefined;
191
+ }
192
+ current = current[prop];
193
+ }
194
+
195
+ return current;
196
+ }
197
+
198
+ /**
199
+ * Checks if this CaptureValue will resolve to an array that should be expanded.
200
+ */
201
+ isArrayExpansion(): boolean {
202
+ // Only slice operations and the root variadic capture return arrays
203
+ return this.arrayOperation?.type === 'slice' ||
204
+ (this.arrayOperation === undefined && this.propertyPath.length === 0 &&
205
+ (this.rootCapture as any).isVariadic?.());
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Creates a Proxy around a CaptureValue that allows further property access.
211
+ * This enables chaining like method.name.simpleName
212
+ */
213
+ function createCaptureValueProxy(
214
+ rootCapture: Capture,
215
+ propertyPath: string[],
216
+ arrayOperation?: { type: 'index' | 'slice' | 'length'; args?: number[] }
217
+ ): any {
218
+ const captureValue = new CaptureValue(rootCapture, propertyPath, arrayOperation);
219
+
220
+ return new Proxy(captureValue, {
221
+ get(target: CaptureValue, prop: string | symbol): any {
222
+ // Allow access to the CaptureValue instance itself and its methods
223
+ if (prop === 'resolve' || prop === 'rootCapture' || prop === 'propertyPath' ||
224
+ prop === 'arrayOperation' || prop === 'isArrayExpansion') {
225
+ const value = target[prop as keyof CaptureValue];
226
+ return typeof value === 'function' ? value.bind(target) : value;
227
+ }
228
+
229
+ // For string property access, extend the property path
230
+ if (typeof prop === 'string') {
231
+ return createCaptureValueProxy(target.rootCapture, [...target.propertyPath, prop], target.arrayOperation);
232
+ }
233
+
234
+ return undefined;
235
+ }
236
+ });
237
+ }
238
+
239
+ /**
240
+ * Creates a capture specification for use in template patterns.
241
+ *
242
+ * @template T The expected type of the captured AST node (for TypeScript autocomplete only)
243
+ * @returns A Capture object that supports property access for use in templates
244
+ *
245
+ * @remarks
246
+ * **Pattern Matching is Structural:**
247
+ *
248
+ * What a capture matches is determined by the pattern structure, not the type parameter:
249
+ * - `pattern`${capture('x')}`` matches ANY single expression (identifier, literal, call, binary, etc.)
250
+ * - `pattern`foo(${capture('x')})`` matches only expressions inside `foo()` calls
251
+ * - `pattern`${capture('x')} + ${capture('y')}`` matches only binary `+` expressions
252
+ *
253
+ * The TypeScript type parameter `<T>` provides IDE autocomplete but doesn't enforce runtime types.
254
+ *
255
+ * @example
256
+ * // Named inline captures
257
+ * const pat = pattern`${capture('left')} + ${capture('right')}`;
258
+ * // Matches: <any expression> + <any expression>
259
+ *
260
+ * // Unnamed captures
261
+ * const {left, right} = {left: capture(), right: capture()};
262
+ * const pattern = pattern`${left} + ${right}`;
263
+ *
264
+ * @example
265
+ * // Type parameter is for IDE autocomplete only
266
+ * const method = capture<J.MethodInvocation>('method');
267
+ * const pat = pattern`foo(${method})`;
268
+ * // Matches: foo(<any expression>) - not restricted to method invocations!
269
+ * // Type <J.MethodInvocation> only helps with autocomplete in your code
270
+ *
271
+ * @example
272
+ * // Structural pattern determines what matches
273
+ * const arg = capture('arg');
274
+ * const pat = pattern`process(${arg})`;
275
+ * // Matches: process(42), process("text"), process(x + y), etc.
276
+ * // The 'arg' capture will bind to whatever expression is passed to process()
277
+ *
278
+ * @example
279
+ * // Repeated patterns using the same capture
280
+ * const expr = capture('expr');
281
+ * const redundantOr = pattern`${expr} || ${expr}`;
282
+ * // Matches expressions where both sides are identical: x || x, foo() || foo()
283
+ *
284
+ * @example
285
+ * // Property access in templates
286
+ * const method = capture<J.MethodInvocation>('method');
287
+ * template`console.log(${method.name.simpleName})` // Accesses properties of captured node
288
+ */
289
+ /**
290
+ * Creates a Proxy wrapper for CaptureImpl that intercepts property accesses.
291
+ * Shared logic between capture() and any() to avoid duplication.
292
+ */
293
+ function createCaptureProxy<T>(impl: CaptureImpl<T>): any {
294
+ return new Proxy(impl as any, {
295
+ get(target: any, prop: string | symbol): any {
296
+ // Allow access to internal symbols
297
+ if (prop === CAPTURE_NAME_SYMBOL) {
298
+ return target[CAPTURE_NAME_SYMBOL];
299
+ }
300
+ if (prop === CAPTURE_VARIADIC_SYMBOL) {
301
+ return target[CAPTURE_VARIADIC_SYMBOL];
302
+ }
303
+ if (prop === CAPTURE_CONSTRAINT_SYMBOL) {
304
+ return target[CAPTURE_CONSTRAINT_SYMBOL];
305
+ }
306
+ if (prop === CAPTURE_CAPTURING_SYMBOL) {
307
+ return target[CAPTURE_CAPTURING_SYMBOL];
308
+ }
309
+ if (prop === CAPTURE_TYPE_SYMBOL) {
310
+ return target[CAPTURE_TYPE_SYMBOL];
311
+ }
312
+
313
+ // Support using Capture as object key via computed properties {[x]: value}
314
+ if (prop === Symbol.toPrimitive || prop === 'toString' || prop === 'valueOf') {
315
+ return () => target[CAPTURE_NAME_SYMBOL];
316
+ }
317
+
318
+ // Allow methods to be called directly on the target
319
+ if (prop === 'getName' || prop === 'isVariadic' || prop === 'getVariadicOptions' || prop === 'getConstraint' || prop === 'isCapturing' || prop === 'getType') {
320
+ return target[prop].bind(target);
321
+ }
322
+
323
+ // For variadic captures, support array-like operations
324
+ if (target.isVariadic() && typeof prop === 'string') {
325
+ // Numeric index access: args[0], args[1], etc.
326
+ const indexNum = Number(prop);
327
+ if (!isNaN(indexNum) && indexNum >= 0 && Number.isInteger(indexNum)) {
328
+ return createCaptureValueProxy(target, [], { type: 'index', args: [indexNum] });
329
+ }
330
+
331
+ // Array method: slice
332
+ if (prop === 'slice') {
333
+ return (...args: number[]) => {
334
+ return createCaptureValueProxy(target, [], { type: 'slice', args });
335
+ };
336
+ }
337
+
338
+ // Array property: length
339
+ if (prop === 'length') {
340
+ return createCaptureValueProxy(target, [], { type: 'length' });
341
+ }
342
+ }
343
+
344
+ // For string property access, create a CaptureValue with a property path
345
+ if (typeof prop === 'string') {
346
+ return createCaptureValueProxy(target, [prop]);
347
+ }
348
+
349
+ return undefined;
350
+ }
351
+ });
352
+ }
353
+
354
+ // Overload 1: Options object with constraint (no variadic)
355
+ export function capture<T = any>(
356
+ options: CaptureOptions<T> & { variadic?: never }
357
+ ): Capture<T> & T;
358
+
359
+ // Overload 2: Options object with variadic
360
+ export function capture<T = any>(
361
+ options: { name?: string; variadic: true | VariadicOptions; constraint?: ConstraintFunction<T[]>; min?: number; max?: number }
362
+ ): Capture<T[]> & T[];
363
+
364
+ // Overload 3: Just a string name (simple named capture)
365
+ export function capture<T = any>(name?: string): Capture<T> & T;
366
+
367
+ // Implementation
368
+ export function capture<T = any>(nameOrOptions?: string | CaptureOptions<T>): Capture<T> & T {
369
+ let name: string | undefined;
370
+ let options: CaptureOptions<T> | undefined;
371
+
372
+ if (typeof nameOrOptions === 'string') {
373
+ // Simple named capture: capture('name')
374
+ name = nameOrOptions;
375
+ options = undefined;
376
+ } else {
377
+ // Options-based API: capture({ name: 'name', ...options }) or capture()
378
+ options = nameOrOptions;
379
+ name = options?.name;
380
+ }
381
+
382
+ const captureName = name || `unnamed_${capture.nextUnnamedId++}`;
383
+ const impl = new CaptureImpl<T>(captureName, options);
384
+
385
+ return createCaptureProxy(impl);
386
+ }
387
+
388
+ // Static counter for generating unique IDs for unnamed captures
389
+ capture.nextUnnamedId = 1;
390
+
391
+ /**
392
+ * Creates a non-capturing pattern match for use in patterns.
393
+ *
394
+ * Use `any()` when you need to match AST structure without binding the matched value to a name.
395
+ * This is useful for validation patterns where you care about structure but not the specific values.
396
+ *
397
+ * **Key Differences from `capture()`:**
398
+ * - `any()` returns `Any<T>` type (not `Capture<T>`)
399
+ * - Cannot be used in templates (TypeScript compiler prevents this)
400
+ * - Does not bind matched values (more memory efficient for patterns)
401
+ * - Supports same features: constraints, variadic matching
402
+ *
403
+ * @template T The expected type of the matched AST node (for TypeScript autocomplete and constraints)
404
+ * @param options Optional configuration (variadic, constraint)
405
+ * @returns An Any object that matches patterns without capturing
406
+ *
407
+ * @example
408
+ * // Match any single argument without capturing
409
+ * const pat = pattern`foo(${any()})`;
410
+ *
411
+ * @example
412
+ * // Match with constraint validation
413
+ * const numericArg = any<J.Literal>({
414
+ * constraint: (node) => typeof node.value === 'number'
415
+ * });
416
+ * const pat = pattern`process(${numericArg})`;
417
+ *
418
+ * @example
419
+ * // Variadic any - match zero or more without capturing
420
+ * const rest = any({ variadic: true });
421
+ * const first = capture('first');
422
+ * const pat = pattern`foo(${first}, ${rest})`;
423
+ *
424
+ * @example
425
+ * // Mixed with captures - capture some, ignore others
426
+ * const important = capture('important');
427
+ * const pat = pattern`
428
+ * if (${any()}) {
429
+ * ${important}
430
+ * }
431
+ * `;
432
+ *
433
+ * @example
434
+ * // Variadic with constraints
435
+ * const numericArgs = any<J.Literal>({
436
+ * variadic: true,
437
+ * constraint: (nodes) => nodes.every(n => typeof n.value === 'number')
438
+ * });
439
+ * const pat = pattern`sum(${numericArgs})`;
440
+ */
441
+ // Overload 1: Regular any with constraint (most specific - no variadic property)
442
+ export function any<T = any>(
443
+ options: { constraint: ConstraintFunction<T> } & { variadic?: never }
444
+ ): Any<T> & T;
445
+
446
+ // Overload 2: Variadic any with constraint
447
+ export function any<T = any>(
448
+ options: { variadic: true | VariadicOptions; constraint?: ConstraintFunction<T[]>; min?: number; max?: number }
449
+ ): Any<T[]> & T[];
450
+
451
+ // Overload 3: Catch-all for simple any without special options
452
+ export function any<T = any>(
453
+ options?: CaptureOptions<T>
454
+ ): Any<T> & T;
455
+
456
+ // Implementation
457
+ export function any<T = any>(options?: CaptureOptions<T>): Any<T> & T {
458
+ const anonName = `anon_${any.nextAnonId++}`;
459
+ const impl = new CaptureImpl<T>(anonName, options, false); // capturing = false
460
+
461
+ return createCaptureProxy(impl);
462
+ }
463
+
464
+ // Static counter for generating unique IDs for anonymous (non-capturing) patterns
465
+ any.nextAnonId = 1;
466
+
467
+ /**
468
+ * Creates a parameter specification for use in standalone templates (not used with patterns).
469
+ *
470
+ * Use `param()` when creating templates that are not used with pattern matching.
471
+ * Use `capture()` when the template works with a pattern.
472
+ *
473
+ * @template T The expected type of the parameter value (for TypeScript autocomplete only)
474
+ * @param name Optional name for the parameter. If not provided, an auto-generated name is used.
475
+ * @returns A TemplateParam object (simpler than Capture, no property access support)
476
+ *
477
+ * @remarks
478
+ * **When to use `param()` vs `capture()`:**
479
+ *
480
+ * - Use `param()` in **standalone templates** (no pattern matching involved)
481
+ * - Use `capture()` in **patterns** and templates used with patterns
482
+ *
483
+ * **Key Differences:**
484
+ * - `TemplateParam` is simpler - no property access proxy overhead
485
+ * - `Capture` supports property access (e.g., `capture('x').name.simpleName`)
486
+ * - Both work in templates, but `param()` makes intent clearer for standalone use
487
+ *
488
+ * @example
489
+ * // ✅ GOOD: Use param() for standalone templates
490
+ * const value = param<J.Literal>('value');
491
+ * const tmpl = template`return ${value} * 2;`;
492
+ * await tmpl.apply(cursor, node, new Map([['value', someLiteral]]));
493
+ *
494
+ * @example
495
+ * // ✅ GOOD: Use capture() with patterns
496
+ * const value = capture('value');
497
+ * const pat = pattern`foo(${value})`;
498
+ * const tmpl = template`bar(${value})`; // capture() makes sense here
499
+ *
500
+ * @example
501
+ * // ⚠️ CONFUSING: Using capture() in standalone template
502
+ * const value = capture('value');
503
+ * template`return ${value} * 2;`; // "Capturing" what? There's no pattern!
504
+ *
505
+ * @example
506
+ * // ❌ WRONG: param() doesn't support property access
507
+ * const node = param<J.MethodInvocation>('invocation');
508
+ * template`console.log(${node.name})` // Error! Use capture() for property access
509
+ */
510
+ export function param<T = any>(name?: string): TemplateParam<T> {
511
+ const paramName = name || `unnamed_${capture.nextUnnamedId++}`;
512
+ return new TemplateParamImpl<T>(paramName);
513
+ }
514
+
515
+ /**
516
+ * Represents raw code that should be inserted verbatim into templates at construction time.
517
+ * This is useful for dynamic code generation where the code structure is determined at runtime.
518
+ */
519
+ export class RawCode {
520
+ [RAW_CODE_SYMBOL] = true;
521
+
522
+ constructor(public readonly code: string) {}
523
+ }
524
+
525
+ /**
526
+ * Creates a raw code specification for inserting literal code strings into templates.
527
+ *
528
+ * Use `raw()` when you need to insert code that is generated dynamically (e.g., from recipe options,
529
+ * computed field names, or programmatic string manipulation) directly into a template at construction time.
530
+ *
531
+ * The string is spliced into the template before parsing, so it becomes part of the template's AST.
532
+ * This is different from `param()` or `capture()` which are placeholders replaced during application.
533
+ *
534
+ * @param code The code string to insert verbatim into the template
535
+ * @returns A RawCode object that will be spliced into the template
536
+ *
537
+ * @remarks
538
+ * **When to use `raw()` vs `param()` vs `capture()`:**
539
+ *
540
+ * - Use `raw()` when you have a **code string** to insert at **template construction time**
541
+ * - Use `param()` when you have an **AST node** to substitute at **template application time**
542
+ * - Use `capture()` when working with **pattern matching** and need to reference matched values
543
+ *
544
+ * **Safety Considerations:**
545
+ * - No validation is performed on the code string
546
+ * - The code must be syntactically valid at the position where it's inserted
547
+ * - Recipe authors are trusted to provide valid code
548
+ *
549
+ * @example
550
+ * // Recipe option determines the log level
551
+ * class MyRecipe extends Recipe {
552
+ * @Option
553
+ * logLevel: string = "info";
554
+ *
555
+ * getVisitor() {
556
+ * // Template constructed with dynamic method name
557
+ * const replacement = template`logger.${raw(this.logLevel)}(${_('msg')})`;
558
+ * // Produces: logger.info(...) or logger.warn(...) etc.
559
+ * }
560
+ * }
561
+ *
562
+ * @example
563
+ * // Build object literal from collected field names
564
+ * const fields = ["userId", "timestamp", "status"];
565
+ * template`{ ${raw(fields.join(', '))} }`
566
+ * // Produces: { userId, timestamp, status }
567
+ *
568
+ * @example
569
+ * // Dynamic import path
570
+ * const modulePath = "./utils";
571
+ * template`import { helper } from ${raw(`'${modulePath}'`)}`
572
+ * // Produces: import { helper } from './utils'
573
+ *
574
+ * @example
575
+ * // Configurable operator
576
+ * const operator = ">=";
577
+ * template`${_('value')} ${raw(operator)} threshold`
578
+ * // Produces: value >= threshold
579
+ */
580
+ export function raw(code: string): RawCode {
581
+ return new RawCode(code);
582
+ }
583
+
584
+ /**
585
+ * Concise alias for `capture`. Works well for inline captures in patterns and templates.
586
+ *
587
+ * @param name Optional name for the capture. If not provided, an auto-generated name is used.
588
+ * @returns A Capture object
589
+ *
590
+ * @example
591
+ * // Inline captures with _ alias
592
+ * pattern`isDate(${_('dateArg')})`
593
+ * template`${_('dateArg')} instanceof Date`
594
+ */
595
+ export const _ = capture;