@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,674 @@
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, Tree} from '../..';
17
+ import {J, Type} from '../../java';
18
+ import type {Pattern} from "./pattern";
19
+ import type {Template} from "./template";
20
+ import type {CaptureValue, RawCode} from "./capture";
21
+
22
+ /**
23
+ * Options for variadic captures that match zero or more nodes in a sequence.
24
+ */
25
+ export interface VariadicOptions {
26
+ /**
27
+ * Minimum number of nodes that must be matched (default: 0).
28
+ */
29
+ min?: number;
30
+
31
+ /**
32
+ * Maximum number of nodes that can be matched (default: unlimited).
33
+ */
34
+ max?: number;
35
+ }
36
+
37
+ /**
38
+ * Constraint function for captures.
39
+ * The cursor parameter is always provided with a defined value, but functions can
40
+ * choose to accept it or not (TypeScript allows functions with fewer parameters).
41
+ *
42
+ * For non-variadic captures: use ConstraintFunction<T> where T is the node type
43
+ * For variadic captures: use ConstraintFunction<T[]> where T[] is the array type
44
+ *
45
+ * When used with variadic captures, the cursor points to the nearest common parent
46
+ * of the captured elements.
47
+ */
48
+ export type ConstraintFunction<T> = (node: T, cursor: Cursor) => boolean;
49
+
50
+ /**
51
+ * Options for the capture function.
52
+ *
53
+ * The constraint function receives different parameter types depending on whether
54
+ * the capture is variadic:
55
+ * - For regular captures: constraint receives a single node of type T
56
+ * - For variadic captures: constraint receives an array of nodes of type T[]
57
+ *
58
+ * The constraint function can optionally receive a cursor parameter to perform
59
+ * context-aware validation during pattern matching.
60
+ */
61
+ export interface CaptureOptions<T = any> {
62
+ name?: string;
63
+ variadic?: boolean | VariadicOptions;
64
+ /**
65
+ * Optional constraint function that validates whether a captured node should be accepted.
66
+ * The function always receives:
67
+ * - node: The captured node (or array of nodes for variadic captures)
68
+ * - cursor: A cursor at the captured node's position (always defined)
69
+ *
70
+ * Functions can choose to accept just the node parameter if they don't need the cursor.
71
+ *
72
+ * @param node The captured node to validate
73
+ * @param cursor Cursor at the captured node's position
74
+ * @returns true if the capture should be accepted, false otherwise
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * // Simple node validation (cursor parameter ignored)
79
+ * capture<J.Literal>('size', {
80
+ * constraint: (node) => typeof node.value === 'number' && node.value > 100
81
+ * })
82
+ *
83
+ * // Context-aware validation (using cursor)
84
+ * capture<J.MethodInvocation>('method', {
85
+ * constraint: (node, cursor) => {
86
+ * if (!node.name.simpleName.startsWith('get')) return false;
87
+ * const cls = cursor.firstEnclosing(isClassDeclaration);
88
+ * return cls?.name.simpleName === 'ApiController';
89
+ * }
90
+ * })
91
+ * ```
92
+ */
93
+ constraint?: ConstraintFunction<T>;
94
+ /**
95
+ * Type annotation for this capture. When provided, the template engine will generate
96
+ * a preamble declaring the capture identifier with this type annotation, allowing
97
+ * the TypeScript parser/compiler to produce a properly type-attributed AST.
98
+ *
99
+ * Can be specified as:
100
+ * - A string type annotation (e.g., "boolean", "string", "number")
101
+ * - A Type instance from the AST (the type will be inferred from the Type)
102
+ */
103
+ type?: string | Type;
104
+ }
105
+
106
+ /**
107
+ * Capture specification for pattern matching.
108
+ * Represents a placeholder in a template pattern that can capture a part of the AST.
109
+ *
110
+ * @template T The expected type of the captured AST node (for TypeScript autocomplete)
111
+ *
112
+ * @remarks
113
+ * **Important: Type Parameter is for IDE Support Only**
114
+ *
115
+ * The generic type parameter `<T>` provides IDE autocomplete and type checking in your code,
116
+ * but does NOT enforce any runtime constraints on what the capture will match.
117
+ *
118
+ * **Pattern Matching Behavior:**
119
+ * - A bare `pattern`${capture()}`` will structurally match ANY expression
120
+ * - Pattern structure determines matching: `pattern`foo(${capture()})`` only matches `foo()` calls with one arg
121
+ * - Use structural patterns to narrow matching scope before applying semantic validation
122
+ *
123
+ * **Variadic Captures:**
124
+ * Use `{ variadic: true }` to match zero or more nodes in a sequence:
125
+ * ```typescript
126
+ * const args = capture({ variadic: true });
127
+ * pattern`foo(${args})` // Matches: foo(), foo(a), foo(a, b, c)
128
+ * ```
129
+ */
130
+ export interface Capture<T = any> {
131
+ /**
132
+ * Gets the string name of this capture.
133
+ */
134
+ getName(): string;
135
+
136
+ /**
137
+ * Returns true if this is a variadic capture (matching zero or more nodes).
138
+ */
139
+ isVariadic(): boolean;
140
+
141
+ /**
142
+ * Returns the variadic options if this is a variadic capture, undefined otherwise.
143
+ */
144
+ getVariadicOptions(): VariadicOptions | undefined;
145
+
146
+ /**
147
+ * Gets the constraint function if this capture has one.
148
+ * For regular captures (T = Expression), constraint receives a single node.
149
+ * For variadic captures (T = Expression[]), constraint receives an array of nodes.
150
+ * The constraint function can optionally receive a cursor for context-aware validation.
151
+ */
152
+ getConstraint?(): ConstraintFunction<T> | undefined;
153
+ }
154
+
155
+ /**
156
+ * Non-capturing pattern match specification.
157
+ * Represents a placeholder in a pattern that matches AST nodes without binding them to a name.
158
+ *
159
+ * Use `any()` when you need to match structure without caring about the specific values.
160
+ * The key difference from `Capture` is that `Any` cannot be used in templates - the TypeScript
161
+ * type system prevents this at compile time.
162
+ *
163
+ * @template T The expected type of the matched AST node (for TypeScript autocomplete and constraints)
164
+ *
165
+ * @remarks
166
+ * **Why Any<T> is Separate from Capture<T>:**
167
+ *
168
+ * Using a separate type provides compile-time safety:
169
+ * - `pattern`foo(${any()})`` - ✅ OK in patterns
170
+ * - `template`bar(${any()})`` - ❌ TypeScript error (Any<T> not assignable to template parameters)
171
+ *
172
+ * This prevents logical errors where you try to use a non-capturing match in a template.
173
+ *
174
+ * **Semantic Parallel with TypeScript's `any`:**
175
+ *
176
+ * Just as TypeScript's `any` type means "be permissive about types here",
177
+ * pattern matching's `any()` means "be permissive about values here":
178
+ * - TypeScript `any`: Accept any type, don't check it
179
+ * - Pattern `any()`: Match any value, don't bind it
180
+ *
181
+ * @example
182
+ * // Match without capturing
183
+ * const pat = pattern`foo(${any()})`
184
+ *
185
+ * @example
186
+ * // Variadic any - match zero or more without capturing
187
+ * const first = any();
188
+ * const rest = any({ variadic: true });
189
+ * const pat = pattern`bar(${first}, ${rest})`
190
+ *
191
+ * @example
192
+ * // With constraints - validate but don't capture
193
+ * const numericArg = any<J.Literal>({
194
+ * constraint: (node) => typeof node.value === 'number'
195
+ * });
196
+ * const pat = pattern`process(${numericArg})`
197
+ */
198
+ export interface Any<T = any> {
199
+ /**
200
+ * Gets the internal identifier for this any pattern.
201
+ */
202
+ getName(): string;
203
+
204
+ /**
205
+ * Returns true if this is a variadic any (matching zero or more nodes).
206
+ */
207
+ isVariadic(): boolean;
208
+
209
+ /**
210
+ * Returns the variadic options if this is a variadic any, undefined otherwise.
211
+ */
212
+ getVariadicOptions(): VariadicOptions | undefined;
213
+
214
+ /**
215
+ * Gets the constraint function if this any pattern has one.
216
+ * For regular any (T = Expression), constraint receives a single node.
217
+ * For variadic any (T = Expression[]), constraint receives an array of nodes.
218
+ */
219
+ getConstraint?(): ConstraintFunction<T> | undefined;
220
+ }
221
+
222
+ /**
223
+ * Template parameter specification for template-only parameter substitution.
224
+ * Unlike Capture, TemplateParam does not support property access and is simpler.
225
+ *
226
+ * @template T The expected type of the parameter value (for TypeScript autocomplete only)
227
+ */
228
+ export interface TemplateParam<T = any> {
229
+ /**
230
+ * The name of the parameter, used to look up the value in the values map.
231
+ */
232
+ readonly name: string;
233
+
234
+ /**
235
+ * Gets the string name of this parameter.
236
+ */
237
+ getName(): string;
238
+ }
239
+
240
+ /**
241
+ * Configuration options for patterns.
242
+ */
243
+ export interface PatternOptions {
244
+ /**
245
+ * Declarations to provide type attribution context for the pattern.
246
+ * These can include import statements, type declarations, function declarations, or any
247
+ * other declarations needed for proper type information. They are prepended to the pattern
248
+ * when parsing to ensure proper type attribution.
249
+ *
250
+ * @example
251
+ * ```typescript
252
+ * pattern`forwardRef(${capture('comp')})`
253
+ * .configure({
254
+ * context: [
255
+ * `import { forwardRef } from 'react'`,
256
+ * `type MyType = { value: number }`
257
+ * ]
258
+ * })
259
+ * ```
260
+ */
261
+ context?: string[];
262
+
263
+ /**
264
+ * @deprecated Use `context` instead. This alias will be removed in a future version.
265
+ *
266
+ * Import statements to provide type attribution context.
267
+ * These are prepended to the pattern when parsing to ensure proper type information.
268
+ */
269
+ imports?: string[];
270
+
271
+ /**
272
+ * NPM dependencies required for import resolution and type attribution.
273
+ * Maps package names to version specifiers (e.g., { 'util': '^1.0.0' }).
274
+ * The template engine will create a package.json with these dependencies.
275
+ */
276
+ dependencies?: Record<string, string>;
277
+
278
+ /**
279
+ * When true, allows patterns without type annotations to match code with type annotations.
280
+ * This enables more flexible pattern matching during development or when full type attribution
281
+ * is not needed. When false, enforces strict type matching where both pattern and target must
282
+ * have matching type annotations.
283
+ *
284
+ * @default true (lenient matching enabled for backward compatibility)
285
+ */
286
+ lenientTypeMatching?: boolean;
287
+
288
+ /**
289
+ * Enable debug logging for this pattern.
290
+ * When enabled, all match attempts will log detailed information to stderr,
291
+ * including the AST path traversed, mismatches encountered, and captured values.
292
+ *
293
+ * Can be overridden at the match() call level.
294
+ * Global debug can be enabled via PATTERN_DEBUG=true environment variable.
295
+ *
296
+ * Precedence: match() call > pattern configure() > PATTERN_DEBUG env var
297
+ *
298
+ * @default undefined (inherits from environment or match() call)
299
+ *
300
+ * @example
301
+ * ```typescript
302
+ * // Pattern-level debug
303
+ * const pat = pattern({ debug: true })`console.log(${value})`;
304
+ *
305
+ * // Disable debug for a noisy pattern when global debug is on
306
+ * const noisyPat = pattern({ debug: false })`import ${x} from ${y}`;
307
+ * ```
308
+ */
309
+ debug?: boolean;
310
+ }
311
+
312
+ /**
313
+ * Options for individual match() calls.
314
+ */
315
+ export interface MatchOptions {
316
+ /**
317
+ * Enable debug logging for this specific match() call.
318
+ * Overrides pattern-level debug setting and global PATTERN_DEBUG env var.
319
+ *
320
+ * @example
321
+ * ```typescript
322
+ * // Debug just this call
323
+ * const match = await pattern.match(node, cursor, { debug: true });
324
+ *
325
+ * // Disable debug for this call even if pattern or global debug is on
326
+ * const match = await pattern.match(node, cursor, { debug: false });
327
+ * ```
328
+ */
329
+ debug?: boolean;
330
+ }
331
+
332
+ /**
333
+ * Valid parameter types for template literals.
334
+ * - Capture: For pattern matching and reuse
335
+ * - CaptureValue: Result of property access or array operations on captures (e.g., capture.prop, capture[0], capture.slice(1))
336
+ * - TemplateParam: For standalone template parameters
337
+ * - RawCode: For inserting literal code strings at construction time
338
+ * - Tree: AST nodes to be inserted directly
339
+ * - Tree[]: Arrays of AST nodes (from variadic capture operations like slice)
340
+ * - J.RightPadded<any>: Wrapper containing an element with markers (element will be extracted)
341
+ * - J.RightPadded<any>[]: Array of wrappers (elements will be expanded)
342
+ * - J.Container<any>: Container with elements (elements will be expanded)
343
+ *
344
+ * Note: Primitive values (string, number, boolean) are NOT supported in template literals.
345
+ * Use raw() for inserting code strings, or Template.builder() API for programmatic construction.
346
+ */
347
+ export type TemplateParameter = Capture | CaptureValue | TemplateParam | RawCode | Tree | Tree[] | J.RightPadded<any> | J.RightPadded<any>[] | J.Container<any>;
348
+
349
+ /**
350
+ * Parameter specification for template generation (internal).
351
+ * Represents a placeholder in a template that will be replaced with a parameter value.
352
+ * This is the internal wrapper used by the template engine.
353
+ *
354
+ * Note: The value is typed as `any` rather than `TemplateParameter` to allow flexible
355
+ * internal handling without excessive type guards. The public API (template function)
356
+ * constrains inputs to `TemplateParameter`, providing type safety at the API boundary.
357
+ */
358
+ export interface Parameter {
359
+ /**
360
+ * The value to substitute into the template.
361
+ */
362
+ value: any;
363
+ }
364
+
365
+ /**
366
+ * Configuration options for templates.
367
+ */
368
+ export interface TemplateOptions {
369
+ /**
370
+ * Declarations to provide type attribution context for the template.
371
+ * These can include import statements, type declarations, function declarations, or any
372
+ * other declarations needed for proper type information. They are prepended to the template
373
+ * when parsing to ensure proper type attribution.
374
+ *
375
+ * @example
376
+ * ```typescript
377
+ * template`console.log(${capture('value')})`
378
+ * .configure({
379
+ * context: [
380
+ * `type MyType = { value: number }`,
381
+ * `const console = { log: (x: any) => void 0 }`
382
+ * ]
383
+ * })
384
+ * ```
385
+ */
386
+ context?: string[];
387
+
388
+ /**
389
+ * @deprecated Use `context` instead. This alias will be removed in a future version.
390
+ *
391
+ * Import statements to provide type attribution context.
392
+ * These are prepended to the template when parsing to ensure proper type information.
393
+ */
394
+ imports?: string[];
395
+
396
+ /**
397
+ * NPM dependencies required for import resolution and type attribution.
398
+ * Maps package names to version specifiers (e.g., { 'util': '^1.0.0' }).
399
+ * The template engine will create a package.json with these dependencies.
400
+ */
401
+ dependencies?: Record<string, string>;
402
+ }
403
+
404
+ /**
405
+ * Represents a replacement rule that can match a pattern and apply a template.
406
+ */
407
+ export interface RewriteRule {
408
+ /**
409
+ * Attempts to apply this rewrite rule to the given AST node.
410
+ *
411
+ * @param cursor The cursor context at the current position in the AST
412
+ * @param node The AST node to try matching and transforming
413
+ * @returns The transformed node if a pattern matched, or `undefined` if no pattern matched.
414
+ * When using in a visitor, always use the `|| node` pattern to return the original
415
+ * node when there's no match: `return await rule.tryOn(this.cursor, node) || node;`
416
+ */
417
+ tryOn(cursor: Cursor, node: J): Promise<J | undefined>;
418
+
419
+ /**
420
+ * Chains this rule with another rule, creating a composite rule that applies both transformations sequentially.
421
+ *
422
+ * The resulting rule:
423
+ * 1. First applies this rule to the input node
424
+ * 2. If this rule matches and transforms the node, applies the next rule to the result
425
+ * 3. If the next rule returns undefined (no match), keeps the result from the first rule
426
+ * 4. If this rule returns undefined (no match), returns undefined without trying the next rule
427
+ *
428
+ * @param next The rule to apply after this rule
429
+ * @returns A new RewriteRule that applies both rules in sequence
430
+ *
431
+ * @example
432
+ * ```typescript
433
+ * const rule1 = rewrite(() => {
434
+ * const { a, b } = { a: capture(), b: capture() };
435
+ * return {
436
+ * before: pattern`${a} + ${b}`,
437
+ * after: template`${b} + ${a}`
438
+ * };
439
+ * });
440
+ *
441
+ * const rule2 = rewrite(() => ({
442
+ * before: pattern`${capture('x')} + 1`,
443
+ * after: template`${capture('x')}++`
444
+ * }));
445
+ *
446
+ * const combined = rule1.andThen(rule2);
447
+ * // Will first swap operands, then if result matches "x + 1", change to "x++"
448
+ * ```
449
+ */
450
+ andThen(next: RewriteRule): RewriteRule;
451
+
452
+ /**
453
+ * Creates a composite rule that tries this rule first, and if it doesn't match, tries an alternative rule.
454
+ *
455
+ * The resulting rule:
456
+ * 1. First applies this rule to the input node
457
+ * 2. If this rule matches and transforms the node, returns the result
458
+ * 3. If this rule returns undefined (no match), tries the alternative rule on the original node
459
+ *
460
+ * @param alternative The rule to try if this rule doesn't match
461
+ * @returns A new RewriteRule that tries both rules with fallback behavior
462
+ *
463
+ * @example
464
+ * ```typescript
465
+ * // Try specific pattern first, fall back to general pattern
466
+ * const specific = rewrite(() => ({
467
+ * before: pattern`foo(${capture('x')}, 0)`,
468
+ * after: template`bar(${capture('x')})`
469
+ * }));
470
+ *
471
+ * const general = rewrite(() => ({
472
+ * before: pattern`foo(${capture('x')}, ${capture('y')})`,
473
+ * after: template`baz(${capture('x')}, ${capture('y')})`
474
+ * }));
475
+ *
476
+ * const combined = specific.orElse(general);
477
+ * // Will try specific pattern first, if no match, try general pattern
478
+ * ```
479
+ */
480
+ orElse(alternative: RewriteRule): RewriteRule;
481
+ }
482
+
483
+ /**
484
+ * Configuration for a replacement rule.
485
+ */
486
+ export interface RewriteConfig {
487
+ before: Pattern | Pattern[];
488
+ after: Template | ((match: MatchResult) => Template);
489
+
490
+ /**
491
+ * Optional context predicate that must evaluate to true for the transformation to be applied.
492
+ * Evaluated after the pattern matches structurally but before applying the template.
493
+ * Provides access to both the matched node and the cursor for context inspection.
494
+ *
495
+ * @param node The matched AST node
496
+ * @param cursor The cursor at the matched node, providing access to ancestors and context
497
+ * @returns true if the transformation should be applied, false otherwise
498
+ *
499
+ * @example
500
+ * ```typescript
501
+ * rewrite(() => ({
502
+ * before: pattern`await ${_('promise')}`,
503
+ * after: template`await ${_('promise')}.catch(handleError)`,
504
+ * where: (node, cursor) => {
505
+ * // Only apply inside async functions
506
+ * const method = cursor.firstEnclosing((n: any): n is J.MethodDeclaration =>
507
+ * n.kind === J.Kind.MethodDeclaration
508
+ * );
509
+ * return method?.modifiers.some(m => m.type === 'async') || false;
510
+ * }
511
+ * }));
512
+ * ```
513
+ */
514
+ where?: (node: J, cursor: Cursor) => boolean | Promise<boolean>;
515
+
516
+ /**
517
+ * Optional context predicate that must evaluate to false for the transformation to be applied.
518
+ * Evaluated after the pattern matches structurally but before applying the template.
519
+ * Provides access to both the matched node and the cursor for context inspection.
520
+ *
521
+ * @param node The matched AST node
522
+ * @param cursor The cursor at the matched node, providing access to ancestors and context
523
+ * @returns true if the transformation should NOT be applied, false if it should proceed
524
+ *
525
+ * @example
526
+ * ```typescript
527
+ * rewrite(() => ({
528
+ * before: pattern`await ${_('promise')}`,
529
+ * after: template`await ${_('promise')}.catch(handleError)`,
530
+ * whereNot: (node, cursor) => {
531
+ * // Don't apply inside try-catch blocks
532
+ * return cursor.firstEnclosing((n: any): n is J.Try =>
533
+ * n.kind === J.Kind.Try
534
+ * ) !== undefined;
535
+ * }
536
+ * }));
537
+ * ```
538
+ */
539
+ whereNot?: (node: J, cursor: Cursor) => boolean | Promise<boolean>;
540
+ }
541
+
542
+ /**
543
+ * Options for debugging pattern matching.
544
+ * Used in Layer 1 (Core Instrumentation) to control debug output.
545
+ */
546
+ export interface DebugOptions {
547
+ /**
548
+ * Enable detailed logging during pattern matching.
549
+ */
550
+ enabled?: boolean;
551
+
552
+ /**
553
+ * Log structural comparison steps.
554
+ */
555
+ logComparison?: boolean;
556
+
557
+ /**
558
+ * Log constraint evaluation.
559
+ */
560
+ logConstraints?: boolean;
561
+ }
562
+
563
+ /**
564
+ * A single debug log entry collected during pattern matching.
565
+ * Part of Layer 1 (Core Instrumentation).
566
+ */
567
+ export interface DebugLogEntry {
568
+ /**
569
+ * Severity level of the log entry.
570
+ */
571
+ level: 'trace' | 'debug' | 'info' | 'warn';
572
+
573
+ /**
574
+ * The scope/category of the log entry.
575
+ */
576
+ scope: 'matching' | 'comparison' | 'constraint';
577
+
578
+ /**
579
+ * Path in the AST where this log entry was generated.
580
+ */
581
+ path: string[];
582
+
583
+ /**
584
+ * Human-readable message.
585
+ */
586
+ message: string;
587
+
588
+ /**
589
+ * Optional data associated with this log entry.
590
+ */
591
+ data?: any;
592
+ }
593
+
594
+ /**
595
+ * Detailed explanation of why a pattern failed to match.
596
+ * Built by Layer 1 (Core Instrumentation) and exposed by Layer 2 (API).
597
+ */
598
+ export interface MatchExplanation {
599
+ /**
600
+ * The reason for the match failure.
601
+ */
602
+ reason: 'structural-mismatch' | 'constraint-failed' | 'type-mismatch' | 'kind-mismatch' | 'value-mismatch' | 'array-length-mismatch';
603
+
604
+ /**
605
+ * Path in the AST where the failure occurred (e.g., ['select', 'name']).
606
+ */
607
+ path: string[];
608
+
609
+ /**
610
+ * Human-readable description of what was expected.
611
+ */
612
+ expected: string;
613
+
614
+ /**
615
+ * Human-readable description of what was actually found.
616
+ */
617
+ actual: string;
618
+
619
+ /**
620
+ * For constraint failures, details about which constraints failed.
621
+ */
622
+ constraintFailures?: Array<{
623
+ captureName: string;
624
+ actualValue: any;
625
+ error?: string;
626
+ }>;
627
+
628
+ /**
629
+ * Additional context about the failure.
630
+ */
631
+ details?: string;
632
+ }
633
+
634
+ /**
635
+ * Interface for accessing captured nodes from a successful pattern match.
636
+ * Part of the public API.
637
+ */
638
+ export interface MatchResult {
639
+ /**
640
+ * Get a captured node by name or by Capture object.
641
+ *
642
+ * @param capture The capture name (string) or Capture object
643
+ * @returns The captured node(s), or undefined if not found
644
+ */
645
+ get(capture: string): any;
646
+ get<T>(capture: Capture<T>): T | undefined;
647
+ }
648
+
649
+ /**
650
+ * Result of a pattern match attempt with debug information.
651
+ * Part of Layer 2 (Public API).
652
+ */
653
+ export interface MatchAttemptResult {
654
+ /**
655
+ * Whether the pattern matched successfully.
656
+ */
657
+ matched: boolean;
658
+
659
+ /**
660
+ * If matched, the match result with captured nodes. Undefined if not matched.
661
+ * Use `result.get('captureName')` or `result.get(captureObject)` to access captures.
662
+ */
663
+ result?: MatchResult;
664
+
665
+ /**
666
+ * If not matched, explanation of why. Undefined if matched.
667
+ */
668
+ explanation?: MatchExplanation;
669
+
670
+ /**
671
+ * Debug log entries collected during matching (if debug was enabled).
672
+ */
673
+ debugLog?: DebugLogEntry[];
674
+ }