@openrewrite/rewrite 8.67.0-20251119-160338 → 8.67.0-20251119-202228

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 (28) hide show
  1. package/dist/javascript/templating/capture.d.ts +3 -4
  2. package/dist/javascript/templating/capture.d.ts.map +1 -1
  3. package/dist/javascript/templating/capture.js +3 -3
  4. package/dist/javascript/templating/capture.js.map +1 -1
  5. package/dist/javascript/templating/comparator.d.ts +7 -1
  6. package/dist/javascript/templating/comparator.d.ts.map +1 -1
  7. package/dist/javascript/templating/comparator.js +45 -10
  8. package/dist/javascript/templating/comparator.js.map +1 -1
  9. package/dist/javascript/templating/pattern.d.ts +10 -11
  10. package/dist/javascript/templating/pattern.d.ts.map +1 -1
  11. package/dist/javascript/templating/pattern.js +18 -36
  12. package/dist/javascript/templating/pattern.js.map +1 -1
  13. package/dist/javascript/templating/rewrite.js +2 -2
  14. package/dist/javascript/templating/rewrite.js.map +1 -1
  15. package/dist/javascript/templating/template.d.ts +27 -13
  16. package/dist/javascript/templating/template.d.ts.map +1 -1
  17. package/dist/javascript/templating/template.js +31 -14
  18. package/dist/javascript/templating/template.js.map +1 -1
  19. package/dist/javascript/templating/types.d.ts +111 -15
  20. package/dist/javascript/templating/types.d.ts.map +1 -1
  21. package/dist/version.txt +1 -1
  22. package/package.json +1 -1
  23. package/src/javascript/templating/capture.ts +7 -7
  24. package/src/javascript/templating/comparator.ts +50 -11
  25. package/src/javascript/templating/pattern.ts +32 -24
  26. package/src/javascript/templating/rewrite.ts +2 -2
  27. package/src/javascript/templating/template.ts +36 -18
  28. package/src/javascript/templating/types.ts +127 -16
@@ -16,10 +16,45 @@ export interface VariadicOptions {
16
16
  */
17
17
  max?: number;
18
18
  }
19
+ /**
20
+ * Read-only access to captures matched so far during pattern matching.
21
+ * Provides a consistent interface with MatchResult for looking up captured values.
22
+ */
23
+ export interface CaptureMap {
24
+ /**
25
+ * Gets the value of a capture by Capture object or name.
26
+ * Returns undefined if the capture hasn't been matched yet.
27
+ */
28
+ get<T>(capture: Capture<T>): T | undefined;
29
+ get(capture: string): any;
30
+ /**
31
+ * Checks if a capture has been matched.
32
+ */
33
+ has(capture: Capture | string): boolean;
34
+ }
35
+ /**
36
+ * Context passed to capture constraint functions.
37
+ * Provides access to the cursor for AST navigation and previously matched captures.
38
+ */
39
+ export interface CaptureConstraintContext {
40
+ /**
41
+ * The cursor pointing to the node being matched.
42
+ * Allows navigating the AST (parent, root, etc.).
43
+ */
44
+ cursor: Cursor;
45
+ /**
46
+ * Read-only view of values captured so far in the matching process.
47
+ * Allows constraints to depend on previous captures.
48
+ * Returns undefined for captures that haven't been processed yet.
49
+ */
50
+ captures: CaptureMap;
51
+ }
19
52
  /**
20
53
  * Constraint function for captures.
21
- * The cursor parameter is always provided with a defined value, but functions can
22
- * choose to accept it or not (TypeScript allows functions with fewer parameters).
54
+ *
55
+ * Receives the node being validated and a context providing access to:
56
+ * - cursor: For navigating the AST
57
+ * - captures: For accessing previously matched captures
23
58
  *
24
59
  * For non-variadic captures: use ConstraintFunction<T> where T is the node type
25
60
  * For variadic captures: use ConstraintFunction<T[]> where T[] is the array type
@@ -27,7 +62,7 @@ export interface VariadicOptions {
27
62
  * When used with variadic captures, the cursor points to the nearest common parent
28
63
  * of the captured elements.
29
64
  */
30
- export type ConstraintFunction<T> = (node: T, cursor: Cursor) => boolean;
65
+ export type ConstraintFunction<T> = (node: T, context: CaptureConstraintContext) => boolean;
31
66
  /**
32
67
  * Options for the capture function.
33
68
  *
@@ -36,39 +71,45 @@ export type ConstraintFunction<T> = (node: T, cursor: Cursor) => boolean;
36
71
  * - For regular captures: constraint receives a single node of type T
37
72
  * - For variadic captures: constraint receives an array of nodes of type T[]
38
73
  *
39
- * The constraint function can optionally receive a cursor parameter to perform
40
- * context-aware validation during pattern matching.
74
+ * The context parameter provides access to the cursor and previously matched captures.
41
75
  */
42
76
  export interface CaptureOptions<T = any> {
43
77
  name?: string;
44
78
  variadic?: boolean | VariadicOptions;
45
79
  /**
46
80
  * Optional constraint function that validates whether a captured node should be accepted.
47
- * The function always receives:
81
+ * The function receives:
48
82
  * - node: The captured node (or array of nodes for variadic captures)
49
- * - cursor: A cursor at the captured node's position (always defined)
50
- *
51
- * Functions can choose to accept just the node parameter if they don't need the cursor.
83
+ * - context: Provides access to cursor and previously matched captures
52
84
  *
53
85
  * @param node The captured node to validate
54
- * @param cursor Cursor at the captured node's position
86
+ * @param context Provides cursor for AST navigation and previously matched captures
55
87
  * @returns true if the capture should be accepted, false otherwise
56
88
  *
57
89
  * @example
58
90
  * ```typescript
59
- * // Simple node validation (cursor parameter ignored)
91
+ * // Simple node validation
60
92
  * capture<J.Literal>('size', {
61
93
  * constraint: (node) => typeof node.value === 'number' && node.value > 100
62
94
  * })
63
95
  *
64
- * // Context-aware validation (using cursor)
96
+ * // Context-aware validation using cursor
65
97
  * capture<J.MethodInvocation>('method', {
66
- * constraint: (node, cursor) => {
98
+ * constraint: (node, context) => {
67
99
  * if (!node.name.simpleName.startsWith('get')) return false;
68
- * const cls = cursor.firstEnclosing(isClassDeclaration);
100
+ * const cls = context.cursor.firstEnclosing(isClassDeclaration);
69
101
  * return cls?.name.simpleName === 'ApiController';
70
102
  * }
71
103
  * })
104
+ *
105
+ * // Validation depending on previous captures
106
+ * const min = capture('min');
107
+ * const max = capture('max', {
108
+ * constraint: (node, context) => {
109
+ * const minVal = context.captures.get(min);
110
+ * return minVal && node.value > minVal.value;
111
+ * }
112
+ * })
72
113
  * ```
73
114
  */
74
115
  constraint?: ConstraintFunction<T>;
@@ -77,9 +118,37 @@ export interface CaptureOptions<T = any> {
77
118
  * a preamble declaring the capture identifier with this type annotation, allowing
78
119
  * the TypeScript parser/compiler to produce a properly type-attributed AST.
79
120
  *
121
+ * **Why Use Type Attribution:**
122
+ * When matching against TypeScript code with type information, providing a type ensures
123
+ * the pattern's AST has matching type attribution, which can be important for:
124
+ * - Semantic matching based on types
125
+ * - Matching code that depends on type inference
126
+ * - Ensuring pattern parses with correct type context
127
+ *
80
128
  * Can be specified as:
81
- * - A string type annotation (e.g., "boolean", "string", "number")
129
+ * - A string type annotation (e.g., "boolean", "string", "number", "Promise<any>", "User[]")
82
130
  * - A Type instance from the AST (the type will be inferred from the Type)
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * // Match promise chains with proper type attribution
135
+ * const chain = capture({
136
+ * name: 'chain',
137
+ * type: 'Promise<any>', // TypeScript will attribute this as Promise type
138
+ * constraint: (call: J.MethodInvocation) => {
139
+ * // Validate promise chain structure
140
+ * return call.name.simpleName === 'then';
141
+ * }
142
+ * });
143
+ * pattern`${chain}.catch(err => console.log(err))`
144
+ *
145
+ * // Match arrays with type annotation
146
+ * const items = capture({
147
+ * name: 'items',
148
+ * type: 'number[]', // Array of numbers
149
+ * });
150
+ * pattern`${items}.map(x => x * 2)`
151
+ * ```
83
152
  */
84
153
  type?: string | Type;
85
154
  }
@@ -360,6 +429,33 @@ export interface TemplateOptions {
360
429
  */
361
430
  dependencies?: Record<string, string>;
362
431
  }
432
+ /**
433
+ * Options for template application.
434
+ */
435
+ export interface ApplyOptions {
436
+ /**
437
+ * Values for parameters in the template.
438
+ * Can be a Map, MatchResult, or plain object with capture names as keys.
439
+ *
440
+ * @example
441
+ * ```typescript
442
+ * // Using MatchResult from pattern matching
443
+ * const match = await pattern.match(node, cursor);
444
+ * await template.apply(node, cursor, { values: match });
445
+ *
446
+ * // Using a Map
447
+ * await template.apply(node, cursor, {
448
+ * values: new Map([['x', someNode]])
449
+ * });
450
+ *
451
+ * // Using a plain object
452
+ * await template.apply(node, cursor, {
453
+ * values: { x: someNode }
454
+ * });
455
+ * ```
456
+ */
457
+ values?: Map<Capture | string, J> | MatchResult | Record<string, J>;
458
+ }
363
459
  /**
364
460
  * Represents a replacement rule that can match a pattern and apply a template.
365
461
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/javascript/templating/types.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,MAAM,EAAE,IAAI,EAAC,MAAM,OAAO,CAAC;AACnC,OAAO,EAAC,CAAC,EAAE,IAAI,EAAC,MAAM,YAAY,CAAC;AACnC,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAC,YAAY,EAAE,OAAO,EAAC,MAAM,WAAW,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;AAEzE;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,GAAG;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,GAAG,eAAe,CAAC;IACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,UAAU,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;IACnC;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,OAAO,CAAC,CAAC,GAAG,GAAG;IAC5B;;OAEG;IACH,OAAO,IAAI,MAAM,CAAC;IAElB;;OAEG;IACH,UAAU,IAAI,OAAO,CAAC;IAEtB;;OAEG;IACH,kBAAkB,IAAI,eAAe,GAAG,SAAS,CAAC;IAElD;;;;;OAKG;IACH,aAAa,CAAC,IAAI,kBAAkB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CACvD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,WAAW,GAAG,CAAC,CAAC,GAAG,GAAG;IACxB;;OAEG;IACH,OAAO,IAAI,MAAM,CAAC;IAElB;;OAEG;IACH,UAAU,IAAI,OAAO,CAAC;IAEtB;;OAEG;IACH,kBAAkB,IAAI,eAAe,GAAG,SAAS,CAAC;IAElD;;;;OAIG;IACH,aAAa,CAAC,IAAI,kBAAkB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CACvD;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,GAAG;IAClC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,OAAO,IAAI,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEtC;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,YAAY,GAAG,aAAa,GAAG,OAAO,GAAG,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAEhK;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACtB;;OAEG;IACH,KAAK,EAAE,GAAG,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,CAAC;IAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,MAAM,CAAC,WAAW,EAAE,WAAW,GAAG,WAAW,CAAC;CACjD;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,OAAO,GAAG,OAAO,EAAE,CAAC;IAC5B,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,QAAQ,CAAC,CAAC;IAErD;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEhE;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtE;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IACzB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;OAEG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC1B;;OAEG;IACH,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAE3C;;OAEG;IACH,KAAK,EAAE,UAAU,GAAG,YAAY,GAAG,YAAY,CAAC;IAEhD;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,IAAI,CAAC,EAAE,GAAG,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC7B;;OAEG;IACH,MAAM,EAAE,qBAAqB,GAAG,mBAAmB,GAAG,eAAe,GAAG,eAAe,GAAG,gBAAgB,GAAG,uBAAuB,CAAC;IAErI;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,kBAAkB,CAAC,EAAE,KAAK,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,GAAG,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IAEH;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IACxB;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;IAC1B,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;CAC9C;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAC/B;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IAErB;;OAEG;IACH,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAE/B;;OAEG;IACH,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;CAC9B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/javascript/templating/types.ts"],"names":[],"mappings":"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"}
package/dist/version.txt CHANGED
@@ -1 +1 @@
1
- 8.67.0-20251119-160338
1
+ 8.67.0-20251119-202228
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openrewrite/rewrite",
3
- "version": "8.67.0-20251119-160338",
3
+ "version": "8.67.0-20251119-202228",
4
4
  "license": "Moderne Source Available License",
5
5
  "description": "OpenRewrite JavaScript.",
6
6
  "homepage": "https://github.com/openrewrite/rewrite",
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import {Cursor} from '../..';
17
17
  import {J, Type} from '../../java';
18
- import {Any, Capture, CaptureOptions, ConstraintFunction, TemplateParam, VariadicOptions} from './types';
18
+ import {Any, Capture, CaptureConstraintContext, CaptureOptions, ConstraintFunction, TemplateParam, VariadicOptions} from './types';
19
19
 
20
20
  /**
21
21
  * Combines multiple constraints with AND logic.
@@ -30,8 +30,8 @@ import {Any, Capture, CaptureOptions, ConstraintFunction, TemplateParam, Variadi
30
30
  * )
31
31
  * });
32
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));
33
+ export function and<T>(...constraints: ConstraintFunction<T>[]): ConstraintFunction<T> {
34
+ return (node: T, context: CaptureConstraintContext) => constraints.every(c => c(node, context));
35
35
  }
36
36
 
37
37
  /**
@@ -46,8 +46,8 @@ export function and<T>(...constraints: ((node: T, cursor?: Cursor) => boolean)[]
46
46
  * )
47
47
  * });
48
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));
49
+ export function or<T>(...constraints: ConstraintFunction<T>[]): ConstraintFunction<T> {
50
+ return (node: T, context: CaptureConstraintContext) => constraints.some(c => c(node, context));
51
51
  }
52
52
 
53
53
  /**
@@ -59,8 +59,8 @@ export function or<T>(...constraints: ((node: T, cursor?: Cursor) => boolean)[])
59
59
  * constraint: not((node) => typeof node.value === 'string')
60
60
  * });
61
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);
62
+ export function not<T>(constraint: ConstraintFunction<T>): ConstraintFunction<T> {
63
+ return (node: T, context: CaptureConstraintContext) => !constraint(node, context);
64
64
  }
65
65
 
66
66
  // Symbol to access the internal capture name without triggering Proxy
@@ -18,7 +18,8 @@ import {J} from '../../java';
18
18
  import {JS} from '../index';
19
19
  import {JavaScriptSemanticComparatorVisitor} from '../comparator';
20
20
  import {CaptureMarker, CaptureStorageValue, PlaceholderUtils} from './utils';
21
- import {DebugLogEntry, MatchExplanation} from './types';
21
+ import {Capture, CaptureConstraintContext, CaptureMap, DebugLogEntry, MatchExplanation} from './types';
22
+ import {CAPTURE_NAME_SYMBOL} from './capture';
22
23
 
23
24
  /**
24
25
  * Debug callbacks for pattern matching.
@@ -62,6 +63,28 @@ export interface MatcherCallbacks {
62
63
  debug?: DebugCallbacks;
63
64
  }
64
65
 
66
+ /**
67
+ * Implementation of CaptureMap that wraps the capture storage.
68
+ * Provides read-only access to previously matched captures.
69
+ */
70
+ class CaptureMapImpl implements CaptureMap {
71
+ constructor(private readonly storage: Map<string, CaptureStorageValue>) {}
72
+
73
+ get<T>(capture: Capture<T>): T | undefined;
74
+ get(capture: string): any;
75
+ get(capture: Capture | string): any {
76
+ // Use symbol to get internal name without triggering Proxy
77
+ const name = typeof capture === 'string' ? capture : ((capture as any)[CAPTURE_NAME_SYMBOL] || capture.getName());
78
+ return this.storage.get(name);
79
+ }
80
+
81
+ has(capture: Capture | string): boolean {
82
+ // Use symbol to get internal name without triggering Proxy
83
+ const name = typeof capture === 'string' ? capture : ((capture as any)[CAPTURE_NAME_SYMBOL] || capture.getName());
84
+ return this.storage.has(name);
85
+ }
86
+ }
87
+
65
88
  /**
66
89
  * A comparator for pattern matching that is lenient about optional properties.
67
90
  * Allows patterns without type annotations to match actual code with type annotations.
@@ -76,6 +99,19 @@ export class PatternMatchingComparator extends JavaScriptSemanticComparatorVisit
76
99
  super(lenientTypeMatching);
77
100
  }
78
101
 
102
+ /**
103
+ * Builds the constraint context with the cursor and current captures.
104
+ * @param cursor The cursor to include in the context
105
+ * @returns The constraint context for evaluating capture constraints
106
+ */
107
+ protected buildConstraintContext(cursor: Cursor): CaptureConstraintContext {
108
+ const state = this.matcher.saveState();
109
+ return {
110
+ cursor,
111
+ captures: new CaptureMapImpl(state.storage)
112
+ };
113
+ }
114
+
79
115
  override async visit<R extends J>(j: Tree, p: J, parent?: Cursor): Promise<R | undefined> {
80
116
  // Check if the pattern node is a capture - this handles unwrapped captures
81
117
  // (Wrapped captures in J.RightPadded are handled by visitRightPadded override)
@@ -91,12 +127,15 @@ export class PatternMatchingComparator extends JavaScriptSemanticComparatorVisit
91
127
  : new Cursor(p);
92
128
  this.targetCursor = cursorAtCapturedNode;
93
129
  try {
94
- // Evaluate constraint with cursor at the captured node (always defined)
130
+ // Evaluate constraint with context (cursor + previous captures)
95
131
  // Skip constraint for variadic captures - they're evaluated in matchSequence with the full array
96
- if (captureMarker.constraint && !captureMarker.variadicOptions && !captureMarker.constraint(p, cursorAtCapturedNode)) {
97
- const captureName = captureMarker.captureName || 'unnamed';
98
- const targetKind = (p as any).kind || 'unknown';
99
- return this.constraintFailed(captureName, targetKind) as R;
132
+ if (captureMarker.constraint && !captureMarker.variadicOptions) {
133
+ const context = this.buildConstraintContext(cursorAtCapturedNode);
134
+ if (!captureMarker.constraint(p, context)) {
135
+ const captureName = captureMarker.captureName || 'unnamed';
136
+ const targetKind = (p as any).kind || 'unknown';
137
+ return this.constraintFailed(captureName, targetKind) as R;
138
+ }
100
139
  }
101
140
 
102
141
  const success = this.matcher.handleCapture(captureMarker, p, undefined);
@@ -164,7 +203,7 @@ export class PatternMatchingComparator extends JavaScriptSemanticComparatorVisit
164
203
  try {
165
204
  // Evaluate constraint with cursor at the captured node (always defined)
166
205
  // Skip constraint for variadic captures - they're evaluated in matchSequence with the full array
167
- if (captureMarker.constraint && !captureMarker.variadicOptions && !captureMarker.constraint(targetElement as J, cursorAtCapturedNode)) {
206
+ if (captureMarker.constraint && !captureMarker.variadicOptions && !captureMarker.constraint(targetElement as J, this.buildConstraintContext(cursorAtCapturedNode))) {
168
207
  const captureName = captureMarker.captureName || 'unnamed';
169
208
  const targetKind = (targetElement as any).kind || 'unknown';
170
209
  return this.constraintFailed(captureName, targetKind);
@@ -604,7 +643,7 @@ export class PatternMatchingComparator extends JavaScriptSemanticComparatorVisit
604
643
  // The targetCursor points to the parent container (always defined in container matching)
605
644
  if (captureMarker.constraint) {
606
645
  const cursor = this.targetCursor || new Cursor(targetElements[0]);
607
- if (!captureMarker.constraint(capturedElements as any, cursor)) {
646
+ if (!captureMarker.constraint(capturedElements as any, this.buildConstraintContext(cursor))) {
608
647
  continue; // Try next consumption amount
609
648
  }
610
649
  }
@@ -868,7 +907,7 @@ export class DebugPatternMatchingComparator extends PatternMatchingComparator {
868
907
  try {
869
908
  if (captureMarker.constraint && !captureMarker.variadicOptions) {
870
909
  this.debug.log('debug', 'constraint', `Evaluating constraint for capture: ${captureMarker.captureName}`);
871
- const constraintResult = captureMarker.constraint(p, cursorAtCapturedNode);
910
+ const constraintResult = captureMarker.constraint(p, this.buildConstraintContext(cursorAtCapturedNode));
872
911
  if (!constraintResult) {
873
912
  this.debug.log('info', 'constraint', `Constraint failed for capture: ${captureMarker.captureName}`);
874
913
  this.debug.setExplanation('constraint-failed', `Capture ${captureMarker.captureName} with valid constraint`, `Constraint failed for ${(p as any).kind}`, `Constraint evaluation returned false`);
@@ -965,7 +1004,7 @@ export class DebugPatternMatchingComparator extends PatternMatchingComparator {
965
1004
  try {
966
1005
  if (captureMarker.constraint && !captureMarker.variadicOptions) {
967
1006
  this.debug.log('debug', 'constraint', `Evaluating constraint for wrapped capture: ${captureMarker.captureName}`);
968
- const constraintResult = captureMarker.constraint(targetElement as J, cursorAtCapturedNode);
1007
+ const constraintResult = captureMarker.constraint(targetElement as J, this.buildConstraintContext(cursorAtCapturedNode));
969
1008
  if (!constraintResult) {
970
1009
  this.debug.log('info', 'constraint', `Constraint failed for wrapped capture: ${captureMarker.captureName}`);
971
1010
  this.debug.setExplanation('constraint-failed', `Capture ${captureMarker.captureName} with valid constraint`, `Constraint failed for ${(targetElement as any).kind}`, `Constraint evaluation returned false`);
@@ -1316,7 +1355,7 @@ export class DebugPatternMatchingComparator extends PatternMatchingComparator {
1316
1355
  if (captureMarker.constraint) {
1317
1356
  this.debug.log('debug', 'constraint', `Evaluating variadic constraint for capture: ${captureMarker.captureName} (${capturedElements.length} elements)`);
1318
1357
  const cursor = this.targetCursor || new Cursor(targetElements[0]);
1319
- const constraintResult = captureMarker.constraint(capturedElements as any, cursor);
1358
+ const constraintResult = captureMarker.constraint(capturedElements as any, this.buildConstraintContext(cursor));
1320
1359
  if (!constraintResult) {
1321
1360
  this.debug.log('info', 'constraint', `Variadic constraint failed for capture: ${captureMarker.captureName}`);
1322
1361
  continue;
@@ -15,7 +15,17 @@
15
15
  */
16
16
  import {Cursor} from '../..';
17
17
  import {J} from '../../java';
18
- import {Any, Capture, DebugLogEntry, DebugOptions, MatchAttemptResult, MatchExplanation, MatchOptions, PatternOptions, MatchResult as IMatchResult} from './types';
18
+ import {
19
+ Any,
20
+ Capture,
21
+ DebugLogEntry,
22
+ DebugOptions,
23
+ MatchAttemptResult,
24
+ MatchExplanation,
25
+ MatchOptions,
26
+ MatchResult as IMatchResult,
27
+ PatternOptions
28
+ } from './types';
19
29
  import {CAPTURE_CAPTURING_SYMBOL, CAPTURE_NAME_SYMBOL, CaptureImpl, RAW_CODE_SYMBOL, RawCode} from './capture';
20
30
  import {DebugPatternMatchingComparator, MatcherCallbacks, MatcherState, PatternMatchingComparator} from './comparator';
21
31
  import {CaptureMarker, CaptureStorageValue, generateCacheKey, globalAstCache, WRAPPERS_MAP_SYMBOL} from './utils';
@@ -190,7 +200,7 @@ export class Pattern {
190
200
  * })
191
201
  */
192
202
  configure(options: PatternOptions): Pattern {
193
- this._options = {...this._options, ...options};
203
+ this._options = { ...this._options, ...options };
194
204
  // Invalidate cache when configuration changes
195
205
  this._cachedAstPattern = undefined;
196
206
  return this;
@@ -252,23 +262,22 @@ export class Pattern {
252
262
  /**
253
263
  * Creates a matcher for this pattern against a specific AST node.
254
264
  *
255
- * @param ast The AST node to match against
256
- * @param cursor Optional cursor at the node's position in a larger tree. Used for context-aware
257
- * capture constraints to navigate to parent nodes. If omitted, a cursor will be
258
- * created at the ast root, allowing constraints to navigate within the matched subtree.
265
+ * @param tree The AST node to match against
266
+ * @param cursor Cursor at the node's position in a larger tree. Used for context-aware
267
+ * capture constraints to navigate to parent nodes.
259
268
  * @param options Optional match options (e.g., debug flag)
260
269
  * @returns A MatchResult if the pattern matches, undefined otherwise
261
270
  *
262
271
  * @example
263
272
  * ```typescript
264
273
  * // Normal match
265
- * const match = await pattern.match(node);
274
+ * const match = await pattern.match(node, cursor);
266
275
  *
267
276
  * // Debug this specific call
268
277
  * const match = await pattern.match(node, cursor, { debug: true });
269
278
  * ```
270
279
  */
271
- async match(ast: J, cursor?: Cursor, options?: MatchOptions): Promise<MatchResult | undefined> {
280
+ async match(tree: J, cursor: Cursor, options?: MatchOptions): Promise<MatchResult | undefined> {
272
281
  // Three-level precedence: call > pattern > global
273
282
  const debugEnabled =
274
283
  options?.debug !== undefined
@@ -279,8 +288,8 @@ export class Pattern {
279
288
 
280
289
  if (debugEnabled) {
281
290
  // Use matchWithExplanation and log the result
282
- const result = await this.matchWithExplanation(ast, cursor);
283
- await this.logMatchResult(ast, cursor, result);
291
+ const result = await this.matchWithExplanation(tree, cursor);
292
+ await this.logMatchResult(tree, cursor, result);
284
293
 
285
294
  if (result.matched) {
286
295
  // result.result is the MatchResult class instance
@@ -291,7 +300,7 @@ export class Pattern {
291
300
  }
292
301
 
293
302
  // Fast path - no debug
294
- const matcher = new Matcher(this, ast, cursor);
303
+ const matcher = new Matcher(this, tree, cursor);
295
304
  const success = await matcher.matches();
296
305
  if (!success) {
297
306
  return undefined;
@@ -305,10 +314,10 @@ export class Pattern {
305
314
  * Formats and logs the match result to stderr.
306
315
  * @private
307
316
  */
308
- private async logMatchResult(ast: J, cursor: Cursor | undefined, result: MatchAttemptResult): Promise<void> {
317
+ private async logMatchResult(tree: J, cursor: Cursor | undefined, result: MatchAttemptResult): Promise<void> {
309
318
  const patternSource = this.getPatternSource();
310
319
  const patternId = `Pattern #${this.patternId}`;
311
- const nodeKind = (ast as any).kind || 'unknown';
320
+ const nodeKind = (tree as any).kind || 'unknown';
312
321
  // Format kind: extract short name (e.g., "org.openrewrite.java.tree.J$Binary" -> "J$Binary")
313
322
  const shortKind = typeof nodeKind === 'string'
314
323
  ? nodeKind.split('.').pop() || nodeKind
@@ -324,7 +333,7 @@ export class Pattern {
324
333
  let treeStr: string;
325
334
  try {
326
335
  const printer = TreePrinters.printer(JS.Kind.CompilationUnit);
327
- treeStr = await printer.print(ast);
336
+ treeStr = await printer.print(tree);
328
337
  } catch (e) {
329
338
  treeStr = '(tree printing unavailable)';
330
339
  }
@@ -508,15 +517,15 @@ export class Pattern {
508
517
  * - Explanation of failure (if not matched)
509
518
  * - Debug log entries showing the matching process
510
519
  *
511
- * @param ast The AST node to match against
512
- * @param cursor Optional cursor at the node's position in a larger tree
520
+ * @param tree The AST node to match against
521
+ * @param cursor Cursor at the node's position in a larger tree
513
522
  * @param debugOptions Optional debug options (defaults to all logging enabled)
514
523
  * @returns Detailed result with debug information
515
524
  *
516
525
  * @example
517
526
  * const x = capture('x');
518
527
  * const pat = pattern`console.log(${x})`;
519
- * const attempt = await pat.matchWithExplanation(node);
528
+ * const attempt = await pat.matchWithExplanation(node, cursor);
520
529
  * if (attempt.matched) {
521
530
  * console.log('Matched!');
522
531
  * console.log('Captured x:', attempt.result.get('x'));
@@ -526,8 +535,8 @@ export class Pattern {
526
535
  * }
527
536
  */
528
537
  async matchWithExplanation(
529
- ast: J,
530
- cursor?: Cursor,
538
+ tree: J,
539
+ cursor: Cursor,
531
540
  debugOptions?: DebugOptions
532
541
  ): Promise<MatchAttemptResult> {
533
542
  // Default to full debug logging if not specified
@@ -538,7 +547,7 @@ export class Pattern {
538
547
  ...debugOptions
539
548
  };
540
549
 
541
- const matcher = new Matcher(this, ast, cursor, options);
550
+ const matcher = new Matcher(this, tree, cursor, options);
542
551
  const success = await matcher.matches();
543
552
 
544
553
  if (success) {
@@ -671,17 +680,16 @@ class Matcher {
671
680
  *
672
681
  * @param pattern The pattern to match
673
682
  * @param ast The AST node to match against
674
- * @param cursor Optional cursor at the AST node's position
683
+ * @param cursor Cursor at the AST node's position
675
684
  * @param debugOptions Optional debug options for instrumentation
676
685
  */
677
686
  constructor(
678
687
  private readonly pattern: Pattern,
679
688
  private readonly ast: J,
680
- cursor?: Cursor,
689
+ cursor: Cursor,
681
690
  debugOptions?: DebugOptions
682
691
  ) {
683
- // If no cursor provided, create one at the ast root so constraints can navigate up
684
- this.cursor = cursor ?? new Cursor(ast, undefined);
692
+ this.cursor = cursor;
685
693
  this.debugOptions = debugOptions ?? {};
686
694
  }
687
695
 
@@ -57,10 +57,10 @@ class RewriteRuleImpl implements RewriteRule {
57
57
  if (typeof this.after === 'function') {
58
58
  // Call the function to get a template, then apply it
59
59
  const template = this.after(match);
60
- result = await template.apply(cursor, node, match);
60
+ result = await template.apply(node, cursor, { values: match });
61
61
  } else {
62
62
  // Use template.apply() as before
63
- result = await this.after.apply(cursor, node, match);
63
+ result = await this.after.apply(node, cursor, { values: match });
64
64
  }
65
65
 
66
66
  if (result) {
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import {Cursor, Tree} from '../..';
17
17
  import {J} from '../../java';
18
- import {Capture, Parameter, TemplateOptions, TemplateParameter} from './types';
18
+ import {ApplyOptions, Parameter, TemplateOptions, TemplateParameter} from './types';
19
19
  import {MatchResult} from './pattern';
20
20
  import {generateCacheKey, globalAstCache, WRAPPERS_MAP_SYMBOL} from './utils';
21
21
  import {CAPTURE_NAME_SYMBOL, RAW_CODE_SYMBOL} from './capture';
@@ -174,6 +174,18 @@ export class Template {
174
174
  private options: TemplateOptions = {};
175
175
  private _cachedTemplate?: J;
176
176
 
177
+ /**
178
+ * Creates a new template.
179
+ *
180
+ * @param templateParts The string parts of the template
181
+ * @param parameters The parameters between the string parts
182
+ */
183
+ constructor(
184
+ private readonly templateParts: TemplateStringsArray,
185
+ private readonly parameters: Parameter[]
186
+ ) {
187
+ }
188
+
177
189
  /**
178
190
  * Creates a new builder for constructing templates programmatically.
179
191
  *
@@ -191,18 +203,6 @@ export class Template {
191
203
  return new TemplateBuilder();
192
204
  }
193
205
 
194
- /**
195
- * Creates a new template.
196
- *
197
- * @param templateParts The string parts of the template
198
- * @param parameters The parameters between the string parts
199
- */
200
- constructor(
201
- private readonly templateParts: TemplateStringsArray,
202
- private readonly parameters: Parameter[]
203
- ) {
204
- }
205
-
206
206
  /**
207
207
  * Configures this template with additional options.
208
208
  *
@@ -217,7 +217,7 @@ export class Template {
217
217
  * })
218
218
  */
219
219
  configure(options: TemplateOptions): Template {
220
- this.options = { ...this.options, ...options };
220
+ this.options = {...this.options, ...options};
221
221
  // Invalidate cache when configuration changes
222
222
  this._cachedTemplate = undefined;
223
223
  return this;
@@ -236,7 +236,7 @@ export class Template {
236
236
  * @returns The cached or newly computed template tree
237
237
  * @internal
238
238
  */
239
- async getTemplateTree(): Promise<JS.CompilationUnit> {
239
+ private async getTemplateTree(): Promise<JS.CompilationUnit> {
240
240
  // Level 1: Instance cache (fastest path)
241
241
  if (this._cachedTemplate) {
242
242
  return this._cachedTemplate as JS.CompilationUnit;
@@ -286,12 +286,30 @@ export class Template {
286
286
  /**
287
287
  * Applies this template and returns the resulting tree.
288
288
  *
289
+ * @param tree Input tree to transform
289
290
  * @param cursor The cursor pointing to the current location in the AST
290
- * @param tree Input tree
291
- * @param values values for parameters in template
291
+ * @param options Optional configuration including values for parameters
292
292
  * @returns A Promise resolving to the generated AST node
293
+ *
294
+ * @example
295
+ * ```typescript
296
+ * // Simple application without values
297
+ * const result = await tmpl.apply(node, cursor);
298
+ *
299
+ * // With values from pattern match
300
+ * const match = await pat.match(node, cursor);
301
+ * const result = await tmpl.apply(node, cursor, { values: match });
302
+ *
303
+ * // With explicit values
304
+ * const result = await tmpl.apply(node, cursor, {
305
+ * values: { x: someNode, y: anotherNode }
306
+ * });
307
+ * ```
293
308
  */
294
- async apply(cursor: Cursor, tree: J, values?: Map<Capture | string, J> | Pick<Map<string, J>, 'get'> | Record<string, J>): Promise<J | undefined> {
309
+ async apply(tree: J, cursor: Cursor, options?: ApplyOptions): Promise<J | undefined> {
310
+ // Extract values from options
311
+ const values = options?.values;
312
+
295
313
  // Normalize the values map: convert any Capture keys to string keys
296
314
  let normalizedValues: Pick<Map<string, J>, 'get'> | undefined;
297
315
  let wrappersMap: Map<string, J.RightPadded<J> | J.RightPadded<J>[]> = new Map();