@openrewrite/rewrite 8.67.0-20251104-160327 → 8.67.0-20251105-075447

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.
@@ -1,6 +1,6 @@
1
1
  export type { VariadicOptions, CaptureOptions, Capture, Any, TemplateParam, PatternOptions, TemplateParameter, TemplateOptions, RewriteRule, RewriteConfig } from './types';
2
2
  export { and, or, not, capture, any, param, _ } from './capture';
3
3
  export { Pattern, PatternBuilder, MatchResult, pattern } from './pattern';
4
- export { rewrite } from './rewrite';
4
+ export { rewrite, fromRecipe } from './rewrite';
5
5
  export { Template, TemplateBuilder, template } from './template';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/javascript/templating/index.ts"],"names":[],"mappings":"AAiBA,YAAY,EACR,eAAe,EACf,cAAc,EACd,OAAO,EACP,GAAG,EACH,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,aAAa,EAChB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACH,GAAG,EACH,EAAE,EACF,GAAG,EACH,OAAO,EACP,GAAG,EACH,KAAK,EACL,CAAC,EACJ,MAAM,WAAW,CAAC;AAGnB,OAAO,EACH,OAAO,EACP,cAAc,EACd,WAAW,EACX,OAAO,EACV,MAAM,WAAW,CAAC;AAGnB,OAAO,EACH,OAAO,EACV,MAAM,WAAW,CAAC;AAGnB,OAAO,EACH,QAAQ,EACR,eAAe,EACf,QAAQ,EACX,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/javascript/templating/index.ts"],"names":[],"mappings":"AAiBA,YAAY,EACR,eAAe,EACf,cAAc,EACd,OAAO,EACP,GAAG,EACH,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,aAAa,EAChB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACH,GAAG,EACH,EAAE,EACF,GAAG,EACH,OAAO,EACP,GAAG,EACH,KAAK,EACL,CAAC,EACJ,MAAM,WAAW,CAAC;AAGnB,OAAO,EACH,OAAO,EACP,cAAc,EACd,WAAW,EACX,OAAO,EACV,MAAM,WAAW,CAAC;AAGnB,OAAO,EACH,OAAO,EACP,UAAU,EACb,MAAM,WAAW,CAAC;AAGnB,OAAO,EACH,QAAQ,EACR,eAAe,EACf,QAAQ,EACX,MAAM,YAAY,CAAC"}
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.template = exports.TemplateBuilder = exports.Template = exports.rewrite = exports.pattern = exports.MatchResult = exports.PatternBuilder = exports.Pattern = exports._ = exports.param = exports.any = exports.capture = exports.not = exports.or = exports.and = void 0;
18
+ exports.template = exports.TemplateBuilder = exports.Template = exports.fromRecipe = exports.rewrite = exports.pattern = exports.MatchResult = exports.PatternBuilder = exports.Pattern = exports._ = exports.param = exports.any = exports.capture = exports.not = exports.or = exports.and = void 0;
19
19
  // Export capture functionality
20
20
  var capture_1 = require("./capture");
21
21
  Object.defineProperty(exports, "and", { enumerable: true, get: function () { return capture_1.and; } });
@@ -34,6 +34,7 @@ Object.defineProperty(exports, "pattern", { enumerable: true, get: function () {
34
34
  // Export rewrite functionality
35
35
  var rewrite_1 = require("./rewrite");
36
36
  Object.defineProperty(exports, "rewrite", { enumerable: true, get: function () { return rewrite_1.rewrite; } });
37
+ Object.defineProperty(exports, "fromRecipe", { enumerable: true, get: function () { return rewrite_1.fromRecipe; } });
37
38
  // Export template functionality
38
39
  var template_1 = require("./template");
39
40
  Object.defineProperty(exports, "Template", { enumerable: true, get: function () { return template_1.Template; } });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/javascript/templating/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAgBH,+BAA+B;AAC/B,qCAQmB;AAPf,8FAAA,GAAG,OAAA;AACH,6FAAA,EAAE,OAAA;AACF,8FAAA,GAAG,OAAA;AACH,kGAAA,OAAO,OAAA;AACP,8FAAA,GAAG,OAAA;AACH,gGAAA,KAAK,OAAA;AACL,4FAAA,CAAC,OAAA;AAGL,+BAA+B;AAC/B,qCAKmB;AAJf,kGAAA,OAAO,OAAA;AACP,yGAAA,cAAc,OAAA;AACd,sGAAA,WAAW,OAAA;AACX,kGAAA,OAAO,OAAA;AAGX,+BAA+B;AAC/B,qCAEmB;AADf,kGAAA,OAAO,OAAA;AAGX,gCAAgC;AAChC,uCAIoB;AAHhB,oGAAA,QAAQ,OAAA;AACR,2GAAA,eAAe,OAAA;AACf,oGAAA,QAAQ,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/javascript/templating/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAgBH,+BAA+B;AAC/B,qCAQmB;AAPf,8FAAA,GAAG,OAAA;AACH,6FAAA,EAAE,OAAA;AACF,8FAAA,GAAG,OAAA;AACH,kGAAA,OAAO,OAAA;AACP,8FAAA,GAAG,OAAA;AACH,gGAAA,KAAK,OAAA;AACL,4FAAA,CAAC,OAAA;AAGL,+BAA+B;AAC/B,qCAKmB;AAJf,kGAAA,OAAO,OAAA;AACP,yGAAA,cAAc,OAAA;AACd,sGAAA,WAAW,OAAA;AACX,kGAAA,OAAO,OAAA;AAGX,+BAA+B;AAC/B,qCAGmB;AAFf,kGAAA,OAAO,OAAA;AACP,qGAAA,UAAU,OAAA;AAGd,gCAAgC;AAChC,uCAIoB;AAHhB,oGAAA,QAAQ,OAAA;AACR,2GAAA,eAAe,OAAA;AACf,oGAAA,QAAQ,OAAA"}
@@ -130,7 +130,7 @@ export declare class Pattern {
130
130
  * const capturedArgs = match.get(args); // Returns J[] for variadic captures
131
131
  * }
132
132
  */
133
- export declare class MatchResult implements Pick<Map<string, J>, "get"> {
133
+ export declare class MatchResult {
134
134
  private readonly storage;
135
135
  constructor(storage?: Map<string, CaptureStorageValue>);
136
136
  get<T>(capture: Capture<T[]>): T[] | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"pattern.d.ts","sourceRoot":"","sources":["../../../src/javascript/templating/pattern.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAC,CAAC,EAAO,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAC,GAAG,EAAE,OAAO,EAAE,cAAc,EAAC,MAAM,SAAS,CAAC;AAGrD,OAAO,EAAgB,mBAAmB,EAAmC,mBAAmB,EAAC,MAAM,SAAS,CAAC;AAEjH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,QAAQ,CAA8B;IAE9C;;;;;OAKG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAcvB;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,GAAG,IAAI;IAajD;;;;OAIG;IACH,KAAK,IAAI,OAAO;CAiBnB;AAED;;GAEG;AACH,qBAAa,OAAO;aAoCI,aAAa,EAAE,oBAAoB;aACnC,QAAQ,EAAE,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE;IApCpD,OAAO,CAAC,QAAQ,CAAsB;IAEtC;;;OAGG;IACH,IAAI,OAAO,IAAI,QAAQ,CAAC,cAAc,CAAC,CAEtC;IAED;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,OAAO,IAAI,cAAc;IAIhC;;;;;OAKG;gBAEiB,aAAa,EAAE,oBAAoB,EACnC,QAAQ,EAAE,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE;IAIpD;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO;IAK3C;;;;;OAKG;IACG,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;CAUxD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,WAAY,YAAW,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC;IAEvD,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAa;IAK1E,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS;IAE9C,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS;IAE1C,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAYnC;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;IAkBvB;;;;OAIG;IACH,CAAC,mBAAmB,CAAC,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;CAa9E;AAoeD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,QAAQ,EAAE,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,EAAE,GAAG,OAAO,CAY5G"}
1
+ {"version":3,"file":"pattern.d.ts","sourceRoot":"","sources":["../../../src/javascript/templating/pattern.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAC,CAAC,EAAO,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAC,GAAG,EAAE,OAAO,EAAE,cAAc,EAAC,MAAM,SAAS,CAAC;AAGrD,OAAO,EAAgB,mBAAmB,EAAmC,mBAAmB,EAAC,MAAM,SAAS,CAAC;AAEjH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,QAAQ,CAA8B;IAE9C;;;;;OAKG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAcvB;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,GAAG,IAAI;IAajD;;;;OAIG;IACH,KAAK,IAAI,OAAO;CAiBnB;AAED;;GAEG;AACH,qBAAa,OAAO;aAoCI,aAAa,EAAE,oBAAoB;aACnC,QAAQ,EAAE,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE;IApCpD,OAAO,CAAC,QAAQ,CAAsB;IAEtC;;;OAGG;IACH,IAAI,OAAO,IAAI,QAAQ,CAAC,cAAc,CAAC,CAEtC;IAED;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,OAAO,IAAI,cAAc;IAIhC;;;;;OAKG;gBAEiB,aAAa,EAAE,oBAAoB,EACnC,QAAQ,EAAE,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE;IAIpD;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO;IAK3C;;;;;OAKG;IACG,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;CAUxD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,WAAW;IAEhB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAa;IAK1E,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS;IAE9C,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS;IAE1C,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAYnC;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;IAkBvB;;;;OAIG;IACH,CAAC,mBAAmB,CAAC,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;CAa9E;AAoeD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,QAAQ,EAAE,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,EAAE,GAAG,OAAO,CAY5G"}
@@ -1,3 +1,4 @@
1
+ import { ExecutionContext, Recipe } from '../..';
1
2
  import { RewriteRule, RewriteConfig } from './types';
2
3
  /**
3
4
  * Creates a replacement rule using a capture context and configuration.
@@ -36,4 +37,42 @@ import { RewriteRule, RewriteConfig } from './types';
36
37
  * }
37
38
  */
38
39
  export declare function rewrite(builderFn: () => RewriteConfig): RewriteRule;
40
+ /**
41
+ * Creates a RewriteRule from a Recipe by using its editor visitor.
42
+ *
43
+ * This allows recipes to be used in the same chaining pattern as other rewrite rules,
44
+ * enabling composition with `andThen()`.
45
+ *
46
+ * @param recipe The recipe whose editor will be used to transform nodes
47
+ * @param ctx The execution context to pass to the recipe's editor
48
+ * @returns A RewriteRule that applies the recipe's editor to nodes
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * class MyRecipe extends Recipe {
53
+ * name = "my.recipe";
54
+ * displayName = "My Recipe";
55
+ * description = "Transforms code.";
56
+ *
57
+ * async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
58
+ * return new MyVisitor();
59
+ * }
60
+ * }
61
+ *
62
+ * // In a visitor:
63
+ * override async visitBinary(binary: J.Binary, p: ExecutionContext): Promise<J | undefined> {
64
+ * const rule1 = rewrite(() => ({
65
+ * before: pattern`${capture('a')} + ${capture('b')}`,
66
+ * after: template`${capture('b')} + ${capture('a')}`
67
+ * }));
68
+ *
69
+ * const rule2 = fromRecipe(new MyRecipe(), p);
70
+ *
71
+ * // Chain the pattern-based rule with the recipe
72
+ * const combined = rule1.andThen(rule2);
73
+ * return await combined.tryOn(this.cursor, binary) || binary;
74
+ * }
75
+ * ```
76
+ */
77
+ export declare const fromRecipe: (recipe: Recipe, ctx: ExecutionContext) => RewriteRule;
39
78
  //# sourceMappingURL=rewrite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rewrite.d.ts","sourceRoot":"","sources":["../../../src/javascript/templating/rewrite.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAC,WAAW,EAAE,aAAa,EAAC,MAAM,SAAS,CAAC;AA8BnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,OAAO,CACnB,SAAS,EAAE,MAAM,aAAa,GAC/B,WAAW,CASb"}
1
+ {"version":3,"file":"rewrite.d.ts","sourceRoot":"","sources":["../../../src/javascript/templating/rewrite.ts"],"names":[],"mappings":"AAeA,OAAO,EAAS,gBAAgB,EAAE,MAAM,EAAC,MAAM,OAAO,CAAC;AAEvD,OAAO,EAAC,WAAW,EAAE,aAAa,EAAC,MAAM,SAAS,CAAC;AA2DnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,OAAO,CACnB,SAAS,EAAE,MAAM,aAAa,GAC/B,WAAW,CASb;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,UAAU,GAAI,QAAQ,MAAM,EAAE,KAAK,gBAAgB,KAAG,WAgBlE,CAAA"}
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.fromRecipe = void 0;
12
13
  exports.rewrite = rewrite;
13
14
  /**
14
15
  * Implementation of a replacement rule.
@@ -23,7 +24,15 @@ class RewriteRuleImpl {
23
24
  for (const pattern of this.before) {
24
25
  const match = yield pattern.match(node);
25
26
  if (match) {
26
- const result = yield this.after.apply(cursor, node, match);
27
+ let result;
28
+ if (typeof this.after === 'function') {
29
+ // Call the function directly with the match result
30
+ result = yield this.after(match);
31
+ }
32
+ else {
33
+ // Use template.apply() as before
34
+ result = yield this.after.apply(cursor, node, match);
35
+ }
27
36
  if (result) {
28
37
  return result;
29
38
  }
@@ -33,6 +42,26 @@ class RewriteRuleImpl {
33
42
  return undefined;
34
43
  });
35
44
  }
45
+ andThen(next) {
46
+ const first = this;
47
+ return new (class extends RewriteRuleImpl {
48
+ constructor() {
49
+ // Pass empty patterns and a function that will never be called
50
+ // since we override tryOn
51
+ super([], () => __awaiter(this, void 0, void 0, function* () { return undefined; }));
52
+ }
53
+ tryOn(cursor, node) {
54
+ return __awaiter(this, void 0, void 0, function* () {
55
+ const firstResult = yield first.tryOn(cursor, node);
56
+ if (firstResult !== undefined) {
57
+ const secondResult = yield next.tryOn(cursor, firstResult);
58
+ return secondResult !== null && secondResult !== void 0 ? secondResult : firstResult;
59
+ }
60
+ return undefined;
61
+ });
62
+ }
63
+ })();
64
+ }
36
65
  }
37
66
  /**
38
67
  * Creates a replacement rule using a capture context and configuration.
@@ -78,4 +107,59 @@ function rewrite(builderFn) {
78
107
  }
79
108
  return new RewriteRuleImpl(Array.isArray(config.before) ? config.before : [config.before], config.after);
80
109
  }
110
+ /**
111
+ * Creates a RewriteRule from a Recipe by using its editor visitor.
112
+ *
113
+ * This allows recipes to be used in the same chaining pattern as other rewrite rules,
114
+ * enabling composition with `andThen()`.
115
+ *
116
+ * @param recipe The recipe whose editor will be used to transform nodes
117
+ * @param ctx The execution context to pass to the recipe's editor
118
+ * @returns A RewriteRule that applies the recipe's editor to nodes
119
+ *
120
+ * @example
121
+ * ```typescript
122
+ * class MyRecipe extends Recipe {
123
+ * name = "my.recipe";
124
+ * displayName = "My Recipe";
125
+ * description = "Transforms code.";
126
+ *
127
+ * async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
128
+ * return new MyVisitor();
129
+ * }
130
+ * }
131
+ *
132
+ * // In a visitor:
133
+ * override async visitBinary(binary: J.Binary, p: ExecutionContext): Promise<J | undefined> {
134
+ * const rule1 = rewrite(() => ({
135
+ * before: pattern`${capture('a')} + ${capture('b')}`,
136
+ * after: template`${capture('b')} + ${capture('a')}`
137
+ * }));
138
+ *
139
+ * const rule2 = fromRecipe(new MyRecipe(), p);
140
+ *
141
+ * // Chain the pattern-based rule with the recipe
142
+ * const combined = rule1.andThen(rule2);
143
+ * return await combined.tryOn(this.cursor, binary) || binary;
144
+ * }
145
+ * ```
146
+ */
147
+ const fromRecipe = (recipe, ctx) => {
148
+ return new (class extends RewriteRuleImpl {
149
+ constructor() {
150
+ // Pass empty patterns and a function that will never be called
151
+ // since we override tryOn
152
+ super([], () => __awaiter(this, void 0, void 0, function* () { return undefined; }));
153
+ }
154
+ tryOn(cursor, tree) {
155
+ return __awaiter(this, void 0, void 0, function* () {
156
+ const visitor = yield recipe.editor();
157
+ const result = yield visitor.visit(tree, ctx, cursor);
158
+ // Return undefined if the visitor didn't change the node
159
+ return result !== tree ? result : undefined;
160
+ });
161
+ }
162
+ })();
163
+ };
164
+ exports.fromRecipe = fromRecipe;
81
165
  //# sourceMappingURL=rewrite.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rewrite.js","sourceRoot":"","sources":["../../../src/javascript/templating/rewrite.ts"],"names":[],"mappings":";;;;;;;;;;;AAmFA,0BAWC;AAzED;;GAEG;AACH,MAAM,eAAe;IACjB,YACqB,MAAiB,EACjB,KAAe;QADf,WAAM,GAAN,MAAM,CAAW;QACjB,UAAK,GAAL,KAAK,CAAU;IAEpC,CAAC;IAEK,KAAK,CAAC,MAAc,EAAE,IAAO;;YAC/B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChC,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACxC,IAAI,KAAK,EAAE,CAAC;oBACR,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;oBAC3D,IAAI,MAAM,EAAE,CAAC;wBACT,OAAO,MAAM,CAAC;oBAClB,CAAC;gBACL,CAAC;YACL,CAAC;YAED,wCAAwC;YACxC,OAAO,SAAS,CAAC;QACrB,CAAC;KAAA;CACJ;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,SAAgB,OAAO,CACnB,SAA8B;IAE9B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,mDAAmD;IACnD,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;IAC/F,CAAC;IAED,OAAO,IAAI,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;AAC7G,CAAC"}
1
+ {"version":3,"file":"rewrite.js","sourceRoot":"","sources":["../../../src/javascript/templating/rewrite.ts"],"names":[],"mappings":";;;;;;;;;;;;AAgHA,0BAWC;AAtGD;;GAEG;AACH,MAAM,eAAe;IACjB,YACqB,MAAiB,EACjB,KAAsD;QADtD,WAAM,GAAN,MAAM,CAAW;QACjB,UAAK,GAAL,KAAK,CAAiD;IAE3E,CAAC;IAEK,KAAK,CAAC,MAAc,EAAE,IAAO;;YAC/B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChC,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACxC,IAAI,KAAK,EAAE,CAAC;oBACR,IAAI,MAAqB,CAAC;oBAE1B,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;wBACnC,mDAAmD;wBACnD,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;oBACrC,CAAC;yBAAM,CAAC;wBACJ,iCAAiC;wBACjC,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;oBACzD,CAAC;oBAED,IAAI,MAAM,EAAE,CAAC;wBACT,OAAO,MAAM,CAAC;oBAClB,CAAC;gBACL,CAAC;YACL,CAAC;YAED,wCAAwC;YACxC,OAAO,SAAS,CAAC;QACrB,CAAC;KAAA;IAED,OAAO,CAAC,IAAiB;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC;QACnB,OAAO,IAAI,CAAC,KAAM,SAAQ,eAAe;YACrC;gBACI,+DAA+D;gBAC/D,0BAA0B;gBAC1B,KAAK,CAAC,EAAE,EAAE,GAAS,EAAE,gDAAC,OAAA,SAAyB,CAAA,GAAA,CAAC,CAAC;YACrD,CAAC;YAEK,KAAK,CAAC,MAAc,EAAE,IAAO;;oBAC/B,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;oBACpD,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;wBAC5B,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;wBAC3D,OAAO,YAAY,aAAZ,YAAY,cAAZ,YAAY,GAAI,WAAW,CAAC;oBACvC,CAAC;oBACD,OAAO,SAAS,CAAC;gBACrB,CAAC;aAAA;SACJ,CAAC,EAAE,CAAC;IACT,CAAC;CACJ;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,SAAgB,OAAO,CACnB,SAA8B;IAE9B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,mDAAmD;IACnD,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;IAC/F,CAAC;IAED,OAAO,IAAI,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;AAC7G,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACI,MAAM,UAAU,GAAG,CAAC,MAAc,EAAE,GAAqB,EAAe,EAAE;IAC7E,OAAO,IAAI,CAAC,KAAM,SAAQ,eAAe;QACrC;YACI,+DAA+D;YAC/D,0BAA0B;YAC1B,KAAK,CAAC,EAAE,EAAE,GAAS,EAAE,gDAAC,OAAA,SAAyB,CAAA,GAAA,CAAC,CAAC;QACrD,CAAC;QAEK,KAAK,CAAC,MAAc,EAAE,IAAO;;gBAC/B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;gBACtC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAI,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;gBAEzD,yDAAyD;gBACzD,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;YAChD,CAAC;SAAA;KACJ,CAAC,EAAE,CAAC;AACT,CAAC,CAAA;AAhBY,QAAA,UAAU,cAgBtB"}
@@ -1,5 +1,7 @@
1
1
  import { Cursor, Tree } from '../..';
2
2
  import { J, Type } from '../../java';
3
+ import type { Pattern, MatchResult } from "./pattern";
4
+ import type { Template } from "./template";
3
5
  /**
4
6
  * Options for variadic captures that match zero or more nodes in a sequence.
5
7
  */
@@ -262,12 +264,41 @@ export interface RewriteRule {
262
264
  * node when there's no match: `return await rule.tryOn(this.cursor, node) || node;`
263
265
  */
264
266
  tryOn(cursor: Cursor, node: J): Promise<J | undefined>;
267
+ /**
268
+ * Chains this rule with another rule, creating a composite rule that applies both transformations sequentially.
269
+ *
270
+ * The resulting rule:
271
+ * 1. First applies this rule to the input node
272
+ * 2. If this rule matches and transforms the node, applies the next rule to the result
273
+ * 3. If the next rule returns undefined (no match), keeps the result from the first rule
274
+ * 4. If this rule returns undefined (no match), returns undefined without trying the next rule
275
+ *
276
+ * @param next The rule to apply after this rule
277
+ * @returns A new RewriteRule that applies both rules in sequence
278
+ *
279
+ * @example
280
+ * ```typescript
281
+ * const rule1 = rewrite(() => ({
282
+ * before: pattern`${capture('a')} + ${capture('b')}`,
283
+ * after: template`${capture('b')} + ${capture('a')}`
284
+ * }));
285
+ *
286
+ * const rule2 = rewrite(() => ({
287
+ * before: pattern`${capture('x')} + 1`,
288
+ * after: template`${capture('x')} + 2`
289
+ * }));
290
+ *
291
+ * const combined = rule1.andThen(rule2);
292
+ * // Will first swap operands, then if result matches "x + 1", change to "x + 2"
293
+ * ```
294
+ */
295
+ andThen(next: RewriteRule): RewriteRule;
265
296
  }
266
297
  /**
267
298
  * Configuration for a replacement rule.
268
299
  */
269
300
  export interface RewriteConfig {
270
- before: any;
271
- after: any;
301
+ before: Pattern | Pattern[];
302
+ after: Template | ((match: MatchResult) => Promise<J>);
272
303
  }
273
304
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/javascript/templating/types.ts"],"names":[],"mappings":"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"}
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-20251104-160327
1
+ 8.67.0-20251105-075447
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openrewrite/rewrite",
3
- "version": "8.67.0-20251104-160327",
3
+ "version": "8.67.0-20251105-075447",
4
4
  "license": "Moderne Source Available License",
5
5
  "description": "OpenRewrite JavaScript.",
6
6
  "homepage": "https://github.com/openrewrite/rewrite",
@@ -49,7 +49,8 @@ export {
49
49
 
50
50
  // Export rewrite functionality
51
51
  export {
52
- rewrite
52
+ rewrite,
53
+ fromRecipe
53
54
  } from './rewrite';
54
55
 
55
56
  // Export template functionality
@@ -218,7 +218,7 @@ export class Pattern {
218
218
  * const capturedArgs = match.get(args); // Returns J[] for variadic captures
219
219
  * }
220
220
  */
221
- export class MatchResult implements Pick<Map<string, J>, "get"> {
221
+ export class MatchResult {
222
222
  constructor(
223
223
  private readonly storage: Map<string, CaptureStorageValue> = new Map()
224
224
  ) {
@@ -13,10 +13,10 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import {Cursor} from '../..';
16
+ import {Cursor, ExecutionContext, Recipe} from '../..';
17
17
  import {J} from '../../java';
18
18
  import {RewriteRule, RewriteConfig} from './types';
19
- import {Pattern} from './pattern';
19
+ import {Pattern, MatchResult} from './pattern';
20
20
  import {Template} from './template';
21
21
 
22
22
  /**
@@ -25,7 +25,7 @@ import {Template} from './template';
25
25
  class RewriteRuleImpl implements RewriteRule {
26
26
  constructor(
27
27
  private readonly before: Pattern[],
28
- private readonly after: Template
28
+ private readonly after: Template | ((match: MatchResult) => Promise<J>)
29
29
  ) {
30
30
  }
31
31
 
@@ -33,7 +33,16 @@ class RewriteRuleImpl implements RewriteRule {
33
33
  for (const pattern of this.before) {
34
34
  const match = await pattern.match(node);
35
35
  if (match) {
36
- const result = await this.after.apply(cursor, node, match);
36
+ let result: J | undefined;
37
+
38
+ if (typeof this.after === 'function') {
39
+ // Call the function directly with the match result
40
+ result = await this.after(match);
41
+ } else {
42
+ // Use template.apply() as before
43
+ result = await this.after.apply(cursor, node, match);
44
+ }
45
+
37
46
  if (result) {
38
47
  return result;
39
48
  }
@@ -43,6 +52,26 @@ class RewriteRuleImpl implements RewriteRule {
43
52
  // Return undefined if no patterns match
44
53
  return undefined;
45
54
  }
55
+
56
+ andThen(next: RewriteRule): RewriteRule {
57
+ const first = this;
58
+ return new (class extends RewriteRuleImpl {
59
+ constructor() {
60
+ // Pass empty patterns and a function that will never be called
61
+ // since we override tryOn
62
+ super([], async () => undefined as unknown as J);
63
+ }
64
+
65
+ async tryOn(cursor: Cursor, node: J): Promise<J | undefined> {
66
+ const firstResult = await first.tryOn(cursor, node);
67
+ if (firstResult !== undefined) {
68
+ const secondResult = await next.tryOn(cursor, firstResult);
69
+ return secondResult ?? firstResult;
70
+ }
71
+ return undefined;
72
+ }
73
+ })();
74
+ }
46
75
  }
47
76
 
48
77
  /**
@@ -93,3 +122,58 @@ export function rewrite(
93
122
 
94
123
  return new RewriteRuleImpl(Array.isArray(config.before) ? config.before : [config.before], config.after);
95
124
  }
125
+
126
+ /**
127
+ * Creates a RewriteRule from a Recipe by using its editor visitor.
128
+ *
129
+ * This allows recipes to be used in the same chaining pattern as other rewrite rules,
130
+ * enabling composition with `andThen()`.
131
+ *
132
+ * @param recipe The recipe whose editor will be used to transform nodes
133
+ * @param ctx The execution context to pass to the recipe's editor
134
+ * @returns A RewriteRule that applies the recipe's editor to nodes
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * class MyRecipe extends Recipe {
139
+ * name = "my.recipe";
140
+ * displayName = "My Recipe";
141
+ * description = "Transforms code.";
142
+ *
143
+ * async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
144
+ * return new MyVisitor();
145
+ * }
146
+ * }
147
+ *
148
+ * // In a visitor:
149
+ * override async visitBinary(binary: J.Binary, p: ExecutionContext): Promise<J | undefined> {
150
+ * const rule1 = rewrite(() => ({
151
+ * before: pattern`${capture('a')} + ${capture('b')}`,
152
+ * after: template`${capture('b')} + ${capture('a')}`
153
+ * }));
154
+ *
155
+ * const rule2 = fromRecipe(new MyRecipe(), p);
156
+ *
157
+ * // Chain the pattern-based rule with the recipe
158
+ * const combined = rule1.andThen(rule2);
159
+ * return await combined.tryOn(this.cursor, binary) || binary;
160
+ * }
161
+ * ```
162
+ */
163
+ export const fromRecipe = (recipe: Recipe, ctx: ExecutionContext): RewriteRule => {
164
+ return new (class extends RewriteRuleImpl {
165
+ constructor() {
166
+ // Pass empty patterns and a function that will never be called
167
+ // since we override tryOn
168
+ super([], async () => undefined as unknown as J);
169
+ }
170
+
171
+ async tryOn(cursor: Cursor, tree: J): Promise<J | undefined> {
172
+ const visitor = await recipe.editor();
173
+ const result = await visitor.visit<J>(tree, ctx, cursor);
174
+
175
+ // Return undefined if the visitor didn't change the node
176
+ return result !== tree ? result : undefined;
177
+ }
178
+ })();
179
+ }
@@ -15,6 +15,8 @@
15
15
  */
16
16
  import {Cursor, Tree} from '../..';
17
17
  import {J, Type} from '../../java';
18
+ import type {Pattern, MatchResult} from "./pattern";
19
+ import type {Template} from "./template";
18
20
 
19
21
  /**
20
22
  * Options for variadic captures that match zero or more nodes in a sequence.
@@ -299,12 +301,42 @@ export interface RewriteRule {
299
301
  * node when there's no match: `return await rule.tryOn(this.cursor, node) || node;`
300
302
  */
301
303
  tryOn(cursor: Cursor, node: J): Promise<J | undefined>;
304
+
305
+ /**
306
+ * Chains this rule with another rule, creating a composite rule that applies both transformations sequentially.
307
+ *
308
+ * The resulting rule:
309
+ * 1. First applies this rule to the input node
310
+ * 2. If this rule matches and transforms the node, applies the next rule to the result
311
+ * 3. If the next rule returns undefined (no match), keeps the result from the first rule
312
+ * 4. If this rule returns undefined (no match), returns undefined without trying the next rule
313
+ *
314
+ * @param next The rule to apply after this rule
315
+ * @returns A new RewriteRule that applies both rules in sequence
316
+ *
317
+ * @example
318
+ * ```typescript
319
+ * const rule1 = rewrite(() => ({
320
+ * before: pattern`${capture('a')} + ${capture('b')}`,
321
+ * after: template`${capture('b')} + ${capture('a')}`
322
+ * }));
323
+ *
324
+ * const rule2 = rewrite(() => ({
325
+ * before: pattern`${capture('x')} + 1`,
326
+ * after: template`${capture('x')} + 2`
327
+ * }));
328
+ *
329
+ * const combined = rule1.andThen(rule2);
330
+ * // Will first swap operands, then if result matches "x + 1", change to "x + 2"
331
+ * ```
332
+ */
333
+ andThen(next: RewriteRule): RewriteRule;
302
334
  }
303
335
 
304
336
  /**
305
337
  * Configuration for a replacement rule.
306
338
  */
307
339
  export interface RewriteConfig {
308
- before: any; // Pattern | Pattern[], but we'll import Pattern in rewrite.ts
309
- after: any; // Template, but we'll import Template in rewrite.ts
340
+ before: Pattern | Pattern[],
341
+ after: Template | ((match: MatchResult) => Promise<J>)
310
342
  }