@openrewrite/rewrite 8.62.3 → 8.62.5

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 (170) hide show
  1. package/dist/execution.d.ts.map +1 -1
  2. package/dist/execution.js.map +1 -1
  3. package/dist/java/rpc.d.ts +1 -0
  4. package/dist/java/rpc.d.ts.map +1 -1
  5. package/dist/java/rpc.js +81 -0
  6. package/dist/java/rpc.js.map +1 -1
  7. package/dist/java/tree.d.ts +12 -6
  8. package/dist/java/tree.d.ts.map +1 -1
  9. package/dist/java/tree.js +12 -92
  10. package/dist/java/tree.js.map +1 -1
  11. package/dist/java/type.d.ts +2 -0
  12. package/dist/java/type.d.ts.map +1 -1
  13. package/dist/java/type.js +12 -0
  14. package/dist/java/type.js.map +1 -1
  15. package/dist/java/visitor.d.ts +12 -4
  16. package/dist/java/visitor.d.ts.map +1 -1
  17. package/dist/java/visitor.js +23 -1
  18. package/dist/java/visitor.js.map +1 -1
  19. package/dist/javascript/format.d.ts +2 -2
  20. package/dist/javascript/format.d.ts.map +1 -1
  21. package/dist/javascript/format.js.map +1 -1
  22. package/dist/javascript/index.d.ts +3 -0
  23. package/dist/javascript/index.d.ts.map +1 -1
  24. package/dist/javascript/index.js +3 -0
  25. package/dist/javascript/index.js.map +1 -1
  26. package/dist/javascript/method-matcher.d.ts +16 -0
  27. package/dist/javascript/method-matcher.d.ts.map +1 -0
  28. package/dist/javascript/method-matcher.js +222 -0
  29. package/dist/javascript/method-matcher.js.map +1 -0
  30. package/dist/javascript/parser.d.ts +1 -1
  31. package/dist/javascript/parser.d.ts.map +1 -1
  32. package/dist/javascript/parser.js +27 -5
  33. package/dist/javascript/parser.js.map +1 -1
  34. package/dist/javascript/preconditions.d.ts +6 -0
  35. package/dist/javascript/preconditions.d.ts.map +1 -0
  36. package/dist/javascript/preconditions.js +58 -0
  37. package/dist/javascript/preconditions.js.map +1 -0
  38. package/dist/javascript/print.d.ts +2 -2
  39. package/dist/javascript/print.d.ts.map +1 -1
  40. package/dist/javascript/print.js.map +1 -1
  41. package/dist/javascript/remove-import.d.ts +56 -0
  42. package/dist/javascript/remove-import.d.ts.map +1 -0
  43. package/dist/javascript/remove-import.js +715 -0
  44. package/dist/javascript/remove-import.js.map +1 -0
  45. package/dist/javascript/rpc.js +3 -18
  46. package/dist/javascript/rpc.js.map +1 -1
  47. package/dist/javascript/search/index.d.ts +3 -0
  48. package/dist/javascript/search/index.d.ts.map +1 -0
  49. package/dist/javascript/search/index.js +19 -0
  50. package/dist/javascript/search/index.js.map +1 -0
  51. package/dist/javascript/search/uses-method.d.ts +8 -0
  52. package/dist/javascript/search/uses-method.d.ts.map +1 -0
  53. package/dist/javascript/search/uses-method.js +35 -0
  54. package/dist/javascript/search/uses-method.js.map +1 -0
  55. package/dist/javascript/search/uses-type.d.ts +8 -0
  56. package/dist/javascript/search/uses-type.d.ts.map +1 -0
  57. package/dist/javascript/search/uses-type.js +71 -0
  58. package/dist/javascript/search/uses-type.js.map +1 -0
  59. package/dist/javascript/templating.d.ts +1 -1
  60. package/dist/javascript/templating.d.ts.map +1 -1
  61. package/dist/javascript/templating.js +1 -1
  62. package/dist/javascript/templating.js.map +1 -1
  63. package/dist/javascript/tree.d.ts +3 -3
  64. package/dist/javascript/tree.d.ts.map +1 -1
  65. package/dist/javascript/tree.js +28 -0
  66. package/dist/javascript/tree.js.map +1 -1
  67. package/dist/javascript/type-mapping.d.ts +7 -18
  68. package/dist/javascript/type-mapping.d.ts.map +1 -1
  69. package/dist/javascript/type-mapping.js +290 -203
  70. package/dist/javascript/type-mapping.js.map +1 -1
  71. package/dist/javascript/visitor.d.ts +1 -1
  72. package/dist/javascript/visitor.d.ts.map +1 -1
  73. package/dist/javascript/visitor.js +1 -1
  74. package/dist/javascript/visitor.js.map +1 -1
  75. package/dist/json/print.js.map +1 -1
  76. package/dist/json/rpc.js +46 -17
  77. package/dist/json/rpc.js.map +1 -1
  78. package/dist/json/visitor.d.ts +2 -2
  79. package/dist/json/visitor.d.ts.map +1 -1
  80. package/dist/json/visitor.js.map +1 -1
  81. package/dist/print.d.ts +1 -0
  82. package/dist/print.d.ts.map +1 -1
  83. package/dist/print.js +6 -0
  84. package/dist/print.js.map +1 -1
  85. package/dist/rpc/queue.d.ts +15 -6
  86. package/dist/rpc/queue.d.ts.map +1 -1
  87. package/dist/rpc/queue.js +37 -13
  88. package/dist/rpc/queue.js.map +1 -1
  89. package/dist/rpc/request/generate.d.ts +4 -0
  90. package/dist/rpc/request/generate.d.ts.map +1 -1
  91. package/dist/rpc/request/generate.js +9 -4
  92. package/dist/rpc/request/generate.js.map +1 -1
  93. package/dist/rpc/request/get-object.d.ts +2 -2
  94. package/dist/rpc/request/get-object.d.ts.map +1 -1
  95. package/dist/rpc/request/get-object.js +4 -12
  96. package/dist/rpc/request/get-object.js.map +1 -1
  97. package/dist/rpc/request/parse.d.ts.map +1 -1
  98. package/dist/rpc/request/parse.js.map +1 -1
  99. package/dist/rpc/request/print.d.ts +1 -1
  100. package/dist/rpc/request/print.d.ts.map +1 -1
  101. package/dist/rpc/request/print.js +1 -1
  102. package/dist/rpc/request/print.js.map +1 -1
  103. package/dist/rpc/request/visit.d.ts +3 -2
  104. package/dist/rpc/request/visit.d.ts.map +1 -1
  105. package/dist/rpc/request/visit.js +5 -4
  106. package/dist/rpc/request/visit.js.map +1 -1
  107. package/dist/rpc/rewrite-rpc.d.ts +4 -4
  108. package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
  109. package/dist/rpc/rewrite-rpc.js +16 -17
  110. package/dist/rpc/rewrite-rpc.js.map +1 -1
  111. package/dist/search/index.d.ts +2 -0
  112. package/dist/search/index.d.ts.map +1 -0
  113. package/dist/search/index.js +18 -0
  114. package/dist/search/index.js.map +1 -0
  115. package/dist/search/is-source-file.d.ts +8 -0
  116. package/dist/search/is-source-file.d.ts.map +1 -0
  117. package/dist/search/is-source-file.js +70 -0
  118. package/dist/search/is-source-file.js.map +1 -0
  119. package/dist/test/rewrite-test.d.ts.map +1 -1
  120. package/dist/test/rewrite-test.js +3 -0
  121. package/dist/test/rewrite-test.js.map +1 -1
  122. package/dist/text/rpc.js +37 -40
  123. package/dist/text/rpc.js.map +1 -1
  124. package/dist/util.d.ts +1 -0
  125. package/dist/util.d.ts.map +1 -1
  126. package/dist/util.js +13 -0
  127. package/dist/util.js.map +1 -1
  128. package/dist/version.txt +1 -1
  129. package/dist/visitor.d.ts +1 -1
  130. package/dist/visitor.d.ts.map +1 -1
  131. package/dist/visitor.js +3 -2
  132. package/dist/visitor.js.map +1 -1
  133. package/package.json +3 -1
  134. package/src/execution.ts +0 -2
  135. package/src/java/rpc.ts +68 -0
  136. package/src/java/tree.ts +20 -76
  137. package/src/java/type.ts +14 -0
  138. package/src/java/visitor.ts +32 -12
  139. package/src/javascript/format.ts +2 -2
  140. package/src/javascript/index.ts +4 -0
  141. package/src/javascript/method-matcher.ts +250 -0
  142. package/src/javascript/parser.ts +20 -6
  143. package/src/javascript/preconditions.ts +40 -0
  144. package/src/javascript/print.ts +3 -3
  145. package/src/javascript/remove-import.ts +780 -0
  146. package/src/javascript/rpc.ts +6 -19
  147. package/src/javascript/search/index.ts +2 -0
  148. package/src/javascript/search/uses-method.ts +21 -0
  149. package/src/javascript/search/uses-type.ts +27 -0
  150. package/src/javascript/templating.ts +4 -3
  151. package/src/javascript/tree.ts +47 -3
  152. package/src/javascript/type-mapping.ts +320 -214
  153. package/src/javascript/visitor.ts +126 -126
  154. package/src/json/print.ts +1 -1
  155. package/src/json/rpc.ts +40 -19
  156. package/src/json/visitor.ts +2 -2
  157. package/src/print.ts +9 -3
  158. package/src/rpc/queue.ts +36 -12
  159. package/src/rpc/request/generate.ts +18 -6
  160. package/src/rpc/request/get-object.ts +6 -13
  161. package/src/rpc/request/parse.ts +1 -1
  162. package/src/rpc/request/print.ts +2 -2
  163. package/src/rpc/request/visit.ts +6 -5
  164. package/src/rpc/rewrite-rpc.ts +22 -21
  165. package/src/search/index.ts +1 -0
  166. package/src/search/is-source-file.ts +26 -0
  167. package/src/test/rewrite-test.ts +5 -2
  168. package/src/text/rpc.ts +33 -37
  169. package/src/util.ts +19 -4
  170. package/src/visitor.ts +3 -3
package/src/rpc/queue.ts CHANGED
@@ -46,37 +46,58 @@ export interface RpcCodec<T> {
46
46
  * A registry for managing RPC codecs based on object types.
47
47
  */
48
48
  export class RpcCodecs {
49
- private static codecs = new Map<string, RpcCodec<any>>();
49
+ private static nonTreeCodecs = new Map<string, RpcCodec<any>>();
50
+
51
+ /**
52
+ * The first key is on sourceFileType and the second on object type
53
+ */
54
+ private static treeCodecs = new Map<string, Map<string, RpcCodec<any>>>();
50
55
 
51
56
  /**
52
57
  * Registers an RPC codec for a given type.
53
58
  *
54
59
  * @param type - The string identifier of the object type.
55
60
  * @param codec - The codec implementation to be registered.
61
+ * @param sourceFileType The source file type of the source file containing (or will contain) this element.
56
62
  */
57
- static registerCodec(type: string, codec: RpcCodec<any>): void {
58
- this.codecs.set(type, codec);
63
+ static registerCodec(type: string, codec: RpcCodec<any>, sourceFileType?: string): void {
64
+ if (sourceFileType) {
65
+ let codecsForSourceFile = this.treeCodecs.get(sourceFileType);
66
+ if (!codecsForSourceFile) {
67
+ codecsForSourceFile = new Map<string, RpcCodec<any>>();
68
+ this.treeCodecs.set(sourceFileType, codecsForSourceFile);
69
+ }
70
+ codecsForSourceFile.set(type, codec);
71
+ } else {
72
+ this.nonTreeCodecs.set(type, codec);
73
+ }
59
74
  }
60
75
 
61
76
  /**
62
77
  * Retrieves the registered codec for a given type.
63
78
  *
64
79
  * @param type - The string identifier of the object type.
80
+ * @param sourceFileType The source file type of the source file containing (or will contain) this element.
65
81
  * @returns The corresponding `RpcCodec`, or `undefined` if not found.
66
82
  */
67
- static forType(type: string): RpcCodec<any> | undefined {
68
- return this.codecs.get(type);
83
+ static forType(type: string, sourceFileType?: string): RpcCodec<any> | undefined {
84
+ if (sourceFileType) {
85
+ const treeCodec = this.treeCodecs.get(sourceFileType)?.get(type);
86
+ return treeCodec || this.nonTreeCodecs.get(type);
87
+ }
88
+ return this.nonTreeCodecs.get(type);
69
89
  }
70
90
 
71
91
  /**
72
92
  * Determines the appropriate codec for an instance based on its `kind` property.
73
93
  *
74
94
  * @param before - The object instance to find a codec for.
95
+ * @param sourceFileType The source file type of the source file containing (or will contain) this element.
75
96
  * @returns The corresponding `RpcCodec`, or `undefined` if no matching codec is found.
76
97
  */
77
- static forInstance(before: any): RpcCodec<any> | undefined {
98
+ static forInstance(before: any, sourceFileType?: string): RpcCodec<any> | undefined {
78
99
  if (before !== undefined && before !== null && typeof before === "object" && "kind" in before) {
79
- return RpcCodecs.forType(before["kind"] as string);
100
+ return RpcCodecs.forType(before["kind"] as string, sourceFileType);
80
101
  }
81
102
  }
82
103
  }
@@ -86,7 +107,9 @@ export class RpcSendQueue {
86
107
 
87
108
  private before?: any;
88
109
 
89
- constructor(private readonly refs: ReferenceMap, private readonly trace: boolean) {
110
+ constructor(private readonly refs: ReferenceMap,
111
+ private readonly sourceFileType: string | undefined,
112
+ private readonly trace: boolean) {
90
113
  }
91
114
 
92
115
  async generate(after: any, before: any): Promise<RpcObjectData[]> {
@@ -132,7 +155,7 @@ export class RpcSendQueue {
132
155
  } else if (after === undefined) {
133
156
  this.put({state: RpcObjectState.DELETE});
134
157
  } else {
135
- let afterCodec = onChange ? undefined : RpcCodecs.forInstance(after);
158
+ let afterCodec = onChange ? undefined : RpcCodecs.forInstance(after, this.sourceFileType);
136
159
  this.put({state: RpcObjectState.CHANGE, value: onChange || afterCodec ? undefined : after});
137
160
  await this.doChange(after, before, onChange, afterCodec);
138
161
  }
@@ -161,7 +184,7 @@ export class RpcSendQueue {
161
184
  this.put({state: RpcObjectState.NO_CHANGE});
162
185
  } else {
163
186
  this.put({state: RpcObjectState.CHANGE});
164
- await this.doChange(anAfter, aBefore, onChangeRun, RpcCodecs.forInstance(anAfter));
187
+ await this.doChange(anAfter, aBefore, onChangeRun, RpcCodecs.forInstance(anAfter, this.sourceFileType));
165
188
  }
166
189
  }
167
190
  }
@@ -199,7 +222,7 @@ export class RpcSendQueue {
199
222
  }
200
223
  ref = this.refs.create(after);
201
224
  }
202
- let afterCodec = onChange ? undefined : RpcCodecs.forInstance(after);
225
+ let afterCodec = onChange ? undefined : RpcCodecs.forInstance(after, this.sourceFileType);
203
226
  this.put({
204
227
  state: RpcObjectState.ADD,
205
228
  valueType: this.getValueType(after),
@@ -236,6 +259,7 @@ export class RpcReceiveQueue {
236
259
  private batch: RpcObjectData[] = [];
237
260
 
238
261
  constructor(private readonly refs: Map<number, any>,
262
+ private readonly sourceFileType: string | undefined,
239
263
  private readonly pull: () => Promise<RpcObjectData[]>,
240
264
  private readonly logFile?: Writable) {
241
265
  }
@@ -301,7 +325,7 @@ export class RpcReceiveQueue {
301
325
  let codec;
302
326
  if (onChange) {
303
327
  after = await onChange(before!);
304
- } else if ((codec = RpcCodecs.forInstance(before))) {
328
+ } else if ((codec = RpcCodecs.forInstance(before, this.sourceFileType))) {
305
329
  after = await codec.rpcReceive(before, this);
306
330
  } else if (message.value !== undefined) {
307
331
  after = message.valueType ? {kind: message.valueType, ...message.value} : message.value;
@@ -17,7 +17,11 @@ import * as rpc from "vscode-jsonrpc/node";
17
17
  import {Recipe, ScanningRecipe} from "../../recipe";
18
18
  import {Cursor, rootCursor} from "../../tree";
19
19
  import {ExecutionContext} from "../../execution";
20
- import {UUID} from "node:crypto";
20
+
21
+ export interface GenerateResponse {
22
+ ids: string[]
23
+ sourceFileTypes: string[]
24
+ }
21
25
 
22
26
  export class Generate {
23
27
  constructor(private readonly id: string, private readonly p: string) {
@@ -28,8 +32,13 @@ export class Generate {
28
32
  preparedRecipes: Map<String, Recipe>,
29
33
  recipeCursors: WeakMap<Recipe, Cursor>,
30
34
  getObject: (id: string) => any): void {
31
- connection.onRequest(new rpc.RequestType<Generate, UUID[], Error>("Generate"), async (request) => {
35
+ connection.onRequest(new rpc.RequestType<Generate, GenerateResponse, Error>("Generate"), async (request) => {
32
36
  const recipe = preparedRecipes.get(request.id);
37
+ const response = {
38
+ ids: [],
39
+ sourceFileTypes: []
40
+ } as GenerateResponse;
41
+
33
42
  if (recipe && recipe instanceof ScanningRecipe) {
34
43
  let cursor = recipeCursors.get(recipe);
35
44
  if (!cursor) {
@@ -39,12 +48,15 @@ export class Generate {
39
48
  const ctx = getObject(request.p) as ExecutionContext;
40
49
  const acc = recipe.accumulator(cursor, ctx);
41
50
  const generated = await recipe.generate(acc, ctx)
42
- return generated.map(g => {
51
+
52
+ for (const g of generated) {
43
53
  localObjects.set(g.id.toString(), g);
44
- return g.id;
45
- })
54
+ response.ids.push(g.id.toString());
55
+ response.sourceFileTypes.push(g.kind);
56
+ }
57
+
46
58
  }
47
- return []
59
+ return response;
48
60
  });
49
61
  }
50
62
  }
@@ -18,7 +18,8 @@ import {RpcObjectData, RpcObjectState, RpcSendQueue} from "../queue";
18
18
  import {ReferenceMap} from "../../reference";
19
19
 
20
20
  export class GetObject {
21
- constructor(private readonly id: string, private readonly lastKnownId?: string) {
21
+ constructor(private readonly id: string,
22
+ private readonly sourceFileType?: string) {
22
23
  }
23
24
 
24
25
  static handle(
@@ -49,18 +50,9 @@ export class GetObject {
49
50
  let allData = pendingData.get(objId);
50
51
  if (!allData) {
51
52
  const after = localObjects.get(objId);
52
-
53
- // Determine what the remote has cached
54
- let before = undefined;
55
- if (request.lastKnownId) {
56
- before = remoteObjects.get(request.lastKnownId);
57
- if (before === undefined) {
58
- // Remote had something cached, but we've evicted it - must send full object
59
- remoteObjects.delete(request.lastKnownId);
60
- }
61
- }
53
+ const before = remoteObjects.get(objId);
62
54
 
63
- allData = await new RpcSendQueue(localRefs, trace).generate(after, before);
55
+ allData = await new RpcSendQueue(localRefs, request.sourceFileType, trace).generate(after, before);
64
56
  pendingData.set(objId, allData);
65
57
 
66
58
  remoteObjects.set(objId, after);
@@ -75,4 +67,5 @@ export class GetObject {
75
67
 
76
68
  return batch;
77
69
  });
78
- }}
70
+ }
71
+ }
@@ -16,7 +16,7 @@
16
16
  import * as rpc from "vscode-jsonrpc/node";
17
17
  import {ExecutionContext} from "../../execution";
18
18
  import {UUID} from "node:crypto";
19
- import {Parser, ParserInput, Parsers} from "../../parser";
19
+ import {ParserInput, Parsers} from "../../parser";
20
20
  import {randomId} from "../../uuid";
21
21
  import {produce} from "immer";
22
22
  import {SourceFile} from "../../tree";
@@ -29,9 +29,9 @@ export class Print {
29
29
  }
30
30
 
31
31
  static handle(connection: rpc.MessageConnection,
32
- getObject: (id: string) => any): void {
32
+ getObject: (id: string, sourceFileType?: string) => any): void {
33
33
  connection.onRequest(new rpc.RequestType<Print, string, Error>("Print"), async request => {
34
- const tree: Tree = await getObject(request.treeId.toString());
34
+ const tree: Tree = await getObject(request.treeId.toString(), request.sourceFileType);
35
35
  const out = new PrintOutputCapture(PrintMarkerPrinter[request.markerPrinter]);
36
36
  if (isSourceFile(tree)) {
37
37
  return await printer(tree).print(tree, out);
@@ -25,6 +25,7 @@ export interface VisitResponse {
25
25
 
26
26
  export class Visit {
27
27
  constructor(private readonly visitor: string,
28
+ private readonly sourceFileType: string,
28
29
  private readonly visitorOptions: Map<string, any> | undefined,
29
30
  private readonly treeId: string,
30
31
  private readonly p: string,
@@ -35,15 +36,15 @@ export class Visit {
35
36
  localObjects: Map<string, any>,
36
37
  preparedRecipes: Map<String, Recipe>,
37
38
  recipeCursors: WeakMap<Recipe, Cursor>,
38
- getObject: (id: string) => any,
39
- getCursor: (cursorIds: string[] | undefined) => Promise<Cursor>): void {
39
+ getObject: (id: string, sourceFileType?: string) => any,
40
+ getCursor: (cursorIds: string[] | undefined, sourceFileType?: string) => Promise<Cursor>): void {
40
41
  connection.onRequest(new rpc.RequestType<Visit, VisitResponse, Error>("Visit"), async (request) => {
41
- const p = await getObject(request.p);
42
- const before: Tree = await getObject(request.treeId);
42
+ const p = await getObject(request.p, undefined);
43
+ const before: Tree = await getObject(request.treeId, request.sourceFileType);
43
44
  localObjects.set(before.id.toString(), before);
44
45
 
45
46
  const visitor = await Visit.instantiateVisitor(request, preparedRecipes, recipeCursors, p);
46
- const after = await visitor.visit(before, p, await getCursor(request.cursor));
47
+ const after = await visitor.visit(before, p, await getCursor(request.cursor, request.sourceFileType));
47
48
  if (!after) {
48
49
  localObjects.delete(before.id.toString());
49
50
  } else if (after !== before) {
@@ -19,7 +19,7 @@ import {Cursor, isSourceFile, isTree, rootCursor, SourceFile, Tree} from "../tre
19
19
  import {Recipe, RecipeDescriptor, RecipeRegistry} from "../recipe";
20
20
  import {SnowflakeId} from "@akashrajpurohit/snowflake-id";
21
21
  import {
22
- Generate,
22
+ Generate, GenerateResponse,
23
23
  GetObject,
24
24
  GetRecipes,
25
25
  Parse,
@@ -69,8 +69,8 @@ export class RewriteRpc {
69
69
  const recipeCursors: WeakMap<Recipe, Cursor> = new WeakMap()
70
70
 
71
71
  // Need this indirection, otherwise `this` will be undefined when executed in the handlers.
72
- const getObject = (id: string) => this.getObject(id);
73
- const getCursor = (cursorIds: string[] | undefined) => this.getCursor(cursorIds);
72
+ const getObject = (id: string, sourceFileType?: string) => this.getObject(id, sourceFileType);
73
+ const getCursor = (cursorIds: string[] | undefined, sourceFileType?: string) => this.getCursor(cursorIds, sourceFileType);
74
74
 
75
75
  const registry = options.registry || new RecipeRegistry();
76
76
 
@@ -92,10 +92,7 @@ export class RewriteRpc {
92
92
  this._global = value;
93
93
  }
94
94
 
95
- static get(): RewriteRpc {
96
- if (!this._global) {
97
- throw new Error("RewriteRpc not initialized");
98
- }
95
+ static get(): RewriteRpc | undefined {
99
96
  return this._global;
100
97
  }
101
98
 
@@ -104,14 +101,13 @@ export class RewriteRpc {
104
101
  return this;
105
102
  }
106
103
 
107
- async getObject<P>(id: string): Promise<P> {
104
+ async getObject<P>(id: string, sourceFileType?: string): Promise<P> {
108
105
  const localObject = this.localObjects.get(id);
109
- const lastKnownId = localObject ? id : undefined;
110
106
 
111
- const q = new RpcReceiveQueue(this.remoteRefs, () => {
107
+ const q = new RpcReceiveQueue(this.remoteRefs, sourceFileType, () => {
112
108
  return this.connection.sendRequest(
113
109
  new rpc.RequestType<GetObject, RpcObjectData[], Error>("GetObject"),
114
- new GetObject(id, lastKnownId)
110
+ new GetObject(id, sourceFileType)
115
111
  );
116
112
  }, this.options.traceGetObjectInput);
117
113
 
@@ -128,11 +124,11 @@ export class RewriteRpc {
128
124
  return remoteObject;
129
125
  }
130
126
 
131
- async getCursor(cursorIds: string[] | undefined): Promise<Cursor> {
127
+ async getCursor(cursorIds: string[] | undefined, sourceFileType?: string): Promise<Cursor> {
132
128
  let cursor = rootCursor();
133
129
  if (cursorIds) {
134
130
  for (let i = cursorIds.length - 1; i >= 0; i--) {
135
- const cursorObject = await this.getObject(cursorIds[i]);
131
+ const cursorObject = await this.getObject(cursorIds[i], sourceFileType);
136
132
  this.remoteObjects.set(cursorIds[i], cursorObject);
137
133
  cursor = new Cursor(cursorObject, cursor);
138
134
  }
@@ -140,13 +136,13 @@ export class RewriteRpc {
140
136
  return cursor;
141
137
  }
142
138
 
143
- async parse(inputs: ParserInput[], relativeTo?: string): Promise<SourceFile[]> {
139
+ async parse(inputs: ParserInput[], sourceFileType: string, relativeTo?: string): Promise<SourceFile[]> {
144
140
  const parsed: SourceFile[] = [];
145
141
  for (const g of await this.connection.sendRequest(
146
142
  new rpc.RequestType<Parse, string[], Error>("Parse"),
147
143
  new Parse(inputs, relativeTo)
148
144
  )) {
149
- parsed.push(await this.getObject(g));
145
+ parsed.push(await this.getObject(g, sourceFileType));
150
146
  }
151
147
  return parsed;
152
148
  }
@@ -193,21 +189,26 @@ export class RewriteRpc {
193
189
  this.localObjects.set(tree.id.toString(), tree);
194
190
  const pId = this.localObject(p);
195
191
  const cursorIds = this.getCursorIds(cursor);
192
+
193
+ const sourceFileType = isSourceFile(tree) ? tree.kind :
194
+ cursor!.firstEnclosing(t => isSourceFile(t))!.kind;
195
+
196
196
  const response = await this.connection.sendRequest(
197
197
  new rpc.RequestType<Visit, VisitResponse, Error>("Visit"),
198
- new Visit(visitorName, undefined, tree.id.toString(), pId, cursorIds)
198
+ new Visit(visitorName, sourceFileType, undefined, tree.id.toString(), pId, cursorIds)
199
199
  );
200
- return response.modified ? this.getObject(tree.id.toString()) : tree;
200
+ return response.modified ? this.getObject(tree.id.toString(), sourceFileType) : tree;
201
201
  }
202
202
 
203
203
  async generate(remoteRecipeId: string, ctx: ExecutionContext): Promise<SourceFile[]> {
204
204
  const ctxId = this.localObject(ctx);
205
205
  const generated: SourceFile[] = [];
206
- for (const g of await this.connection.sendRequest(
207
- new rpc.RequestType<Generate, string[], Error>("Generate"),
206
+ const response = await this.connection.sendRequest(
207
+ new rpc.RequestType<Generate, GenerateResponse, Error>("Generate"),
208
208
  new Generate(remoteRecipeId, ctxId)
209
- )) {
210
- generated.push(await this.getObject(g));
209
+ );
210
+ for (let i = 0; i < response.ids.length; i++) {
211
+ generated.push(await this.getObject(response.ids[i], response.sourceFileTypes[i]));
211
212
  }
212
213
  return generated;
213
214
  }
@@ -0,0 +1 @@
1
+ export * from './is-source-file'
@@ -0,0 +1,26 @@
1
+ import {TreeVisitor} from "../visitor";
2
+ import {ExecutionContext} from "../execution";
3
+ import {isSourceFile} from "../tree";
4
+ import {foundSearchResult} from "../markers";
5
+ import * as picomatch from "picomatch";
6
+
7
+ export class IsSourceFile extends TreeVisitor<any, ExecutionContext> {
8
+ private readonly matcher: picomatch.Matcher;
9
+
10
+ constructor(filePattern: string) {
11
+ super();
12
+ // Create a picomatch matcher for the pattern
13
+ this.matcher = picomatch.default ? picomatch.default(filePattern) : (picomatch as any)(filePattern);
14
+ }
15
+
16
+ protected async preVisit(tree: any, _: ExecutionContext): Promise<any> {
17
+ this.stopAfterPreVisit();
18
+ if (isSourceFile(tree) && tree.sourcePath) {
19
+ const path = tree.sourcePath.replace(/\\/g, '/'); // Normalize to Unix separators
20
+ if (this.matcher(path)) {
21
+ return foundSearchResult(tree);
22
+ }
23
+ }
24
+ return tree;
25
+ }
26
+ }
@@ -22,7 +22,7 @@ import {SourceFile} from "../tree";
22
22
  import dedent from "dedent";
23
23
  import {Result, scheduleRun} from "../run";
24
24
  import {SnowflakeId} from "@akashrajpurohit/snowflake-id";
25
- import {mapAsync} from "../util";
25
+ import {mapAsync, trimIndent} from "../util";
26
26
  import {ParseErrorKind} from "../parse-error";
27
27
  import {MarkersKind, ParseExceptionResult} from "../markers";
28
28
  import {JavaScriptVisitor} from "../javascript";
@@ -158,6 +158,9 @@ export class RecipeSpec {
158
158
  }
159
159
 
160
160
  private async expectAfter(spec: SourceSpec<any>, after?: SourceFile) {
161
+ if (!after) {
162
+ throw new Error('Expected for recipe to have produced a change for file:\n' + trimIndent(spec.before))
163
+ }
161
164
  expect(after).toBeDefined();
162
165
  await new ValidateWhitespaceVisitor().visit(after!, this.executionContext);
163
166
  const actualAfter = await TreePrinters.print(after!);
@@ -212,7 +215,7 @@ export class RecipeSpec {
212
215
  }
213
216
 
214
217
  class ValidateWhitespaceVisitor extends JavaScriptVisitor<ExecutionContext> {
215
- protected override async visitSpace(space: J.Space, p: ExecutionContext): Promise<J.Space> {
218
+ public override async visitSpace(space: J.Space, p: ExecutionContext): Promise<J.Space> {
216
219
  const ret = super.visitSpace(space, p);
217
220
  expect(space.whitespace).toMatch(/^\s*$/);
218
221
  return ret;
package/src/text/rpc.ts CHANGED
@@ -13,10 +13,9 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import {RpcCodec, RpcCodecs, RpcReceiveQueue, RpcSendQueue} from "../rpc";
16
+ import {RpcCodecs, RpcReceiveQueue, RpcSendQueue} from "../rpc";
17
17
  import {PlainText} from "./tree";
18
18
  import {createDraft, Draft, finishDraft} from "immer";
19
- import {TreeKind} from "../tree";
20
19
 
21
20
  async function receiveSnippet(before: PlainText.Snippet, q: RpcReceiveQueue): Promise<PlainText.Snippet | undefined> {
22
21
  const draft: Draft<PlainText.Snippet> = createDraft(before);
@@ -26,40 +25,37 @@ async function receiveSnippet(before: PlainText.Snippet, q: RpcReceiveQueue): Pr
26
25
  return finishDraft(draft);
27
26
  }
28
27
 
29
- const textCodec: RpcCodec<PlainText> = {
30
- async rpcReceive(before: PlainText, q: RpcReceiveQueue): Promise<PlainText> {
31
- const draft: Draft<PlainText> = createDraft(before);
32
- draft.id = await q.receive(before.id);
33
- draft.markers = await q.receive(before.markers);
34
- draft.sourcePath = await q.receive(before.sourcePath);
35
- draft.charsetName = await q.receive(before.charsetName);
36
- draft.charsetBomMarked = await q.receive(before.charsetBomMarked);
37
- draft.checksum = await q.receive(before.checksum);
38
- draft.fileAttributes = await q.receive(before.fileAttributes);
39
- draft.text = await q.receive(before.text);
40
- draft.snippets = (await q.receiveList(before.snippets, snippet => receiveSnippet(snippet, q)))!;
41
- return finishDraft(draft);
42
- },
28
+ // Register codec for all Java AST node types
29
+ for (const kind of Object.values(PlainText.Kind)) {
30
+ RpcCodecs.registerCodec(kind as string, {
31
+ async rpcReceive(before: PlainText, q: RpcReceiveQueue): Promise<PlainText> {
32
+ const draft: Draft<PlainText> = createDraft(before);
33
+ draft.id = await q.receive(before.id);
34
+ draft.markers = await q.receive(before.markers);
35
+ draft.sourcePath = await q.receive(before.sourcePath);
36
+ draft.charsetName = await q.receive(before.charsetName);
37
+ draft.charsetBomMarked = await q.receive(before.charsetBomMarked);
38
+ draft.checksum = await q.receive(before.checksum);
39
+ draft.fileAttributes = await q.receive(before.fileAttributes);
40
+ draft.text = await q.receive(before.text);
41
+ draft.snippets = (await q.receiveList(before.snippets, snippet => receiveSnippet(snippet, q)))!;
42
+ return finishDraft(draft);
43
+ },
43
44
 
44
- async rpcSend(after: PlainText, q: RpcSendQueue): Promise<void> {
45
- await q.getAndSend(after, p => p.id);
46
- await q.getAndSend(after, p => p.markers);
47
- await q.getAndSend(after, p => p.sourcePath);
48
- await q.getAndSend(after, p => p.charsetName);
49
- await q.getAndSend(after, p => p.charsetBomMarked);
50
- await q.getAndSend(after, p => p.checksum);
51
- await q.getAndSend(after, p => p.fileAttributes);
52
- await q.getAndSend(after, p => p.text);
53
- await q.getAndSendList(after, a => a.snippets, s => s.id, async (snippet) => {
54
- await q.getAndSend(snippet, p => p.id);
55
- await q.getAndSend(snippet, p => p.markers);
56
- await q.getAndSend(snippet, p => p.text);
57
- });
58
- }
45
+ async rpcSend(after: PlainText, q: RpcSendQueue): Promise<void> {
46
+ await q.getAndSend(after, p => p.id);
47
+ await q.getAndSend(after, p => p.markers);
48
+ await q.getAndSend(after, p => p.sourcePath);
49
+ await q.getAndSend(after, p => p.charsetName);
50
+ await q.getAndSend(after, p => p.charsetBomMarked);
51
+ await q.getAndSend(after, p => p.checksum);
52
+ await q.getAndSend(after, p => p.fileAttributes);
53
+ await q.getAndSend(after, p => p.text);
54
+ await q.getAndSendList(after, a => a.snippets, s => s.id, async (snippet) => {
55
+ await q.getAndSend(snippet, p => p.id);
56
+ await q.getAndSend(snippet, p => p.markers);
57
+ await q.getAndSend(snippet, p => p.text);
58
+ });
59
+ }
60
+ }, PlainText.Kind.PlainText);
59
61
  }
60
-
61
- Object.values(PlainText.Kind).forEach(kind => {
62
- if (!Object.values(TreeKind).includes(kind as any)) {
63
- RpcCodecs.registerCodec(kind, textCodec);
64
- }
65
- });
package/src/util.ts CHANGED
@@ -15,15 +15,15 @@
15
15
  */
16
16
  export async function mapAsync<T, U>(arr: T[], fn: (t: T, i: number) => Promise<U | undefined>): Promise<U[]> {
17
17
  let results: U[] | undefined = undefined;
18
-
18
+
19
19
  for (let i = 0; i < arr.length; i++) {
20
20
  const result = await fn(arr[i], i);
21
-
21
+
22
22
  if (result !== arr[i]) {
23
23
  if (results === undefined) {
24
24
  results = arr.slice(0, i) as unknown[] as U[];
25
25
  }
26
-
26
+
27
27
  if (result !== undefined) {
28
28
  results.push(result);
29
29
  }
@@ -31,6 +31,21 @@ export async function mapAsync<T, U>(arr: T[], fn: (t: T, i: number) => Promise<
31
31
  results.push(result);
32
32
  }
33
33
  }
34
-
34
+
35
35
  return results === undefined ? arr as unknown[] as U[] : results;
36
36
  }
37
+
38
+ export function trimIndent(str: string | null | undefined): string {
39
+ if (!str) {
40
+ return "";
41
+ }
42
+ const lines = str.split("\n");
43
+ const nonEmpty = lines.filter(l => l.trim().length > 0);
44
+ const minIndent = Math.min(
45
+ ...nonEmpty.map(l => l.match(/^(\s*)/)![0].length)
46
+ );
47
+ return lines
48
+ .map(l => l.slice(minIndent))
49
+ .join("\n")
50
+ .trim();
51
+ }
package/src/visitor.ts CHANGED
@@ -40,7 +40,7 @@ const stopAfterPreVisit = Symbol("STOP_AFTER_PRE_VISIT")
40
40
  export abstract class TreeVisitor<T extends Tree, P> {
41
41
  protected cursor: Cursor = rootCursor();
42
42
  private visitCount: number = 0;
43
- private afterVisit?: TreeVisitor<any, P>[];
43
+ public afterVisit: TreeVisitor<any, P>[] = [];
44
44
 
45
45
  async visitDefined<R extends T>(tree: Tree, p: P, parent?: Cursor): Promise<R> {
46
46
  return (await this.visit<R>(tree, p, parent))!;
@@ -80,14 +80,14 @@ export abstract class TreeVisitor<T extends Tree, P> {
80
80
 
81
81
  if (topLevel) {
82
82
  if (this.afterVisit) {
83
- for (const v of this.afterVisit) {
83
+ while (this.afterVisit.length > 0) {
84
+ const v = this.afterVisit.shift()!;
84
85
  v.cursor = this.cursor;
85
86
  if (t !== undefined) {
86
87
  t = await v.visit(t, p);
87
88
  }
88
89
  }
89
90
  }
90
- this.afterVisit = undefined;
91
91
  this.visitCount = 0;
92
92
  }
93
93
  } catch (e) {