@openrewrite/rewrite 8.63.0 → 8.63.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/java/index.d.ts +1 -0
  2. package/dist/java/index.d.ts.map +1 -1
  3. package/dist/java/index.js +1 -0
  4. package/dist/java/index.js.map +1 -1
  5. package/dist/java/rpc.d.ts +2 -0
  6. package/dist/java/rpc.d.ts.map +1 -1
  7. package/dist/java/rpc.js +749 -410
  8. package/dist/java/rpc.js.map +1 -1
  9. package/dist/java/tree.d.ts +1 -1
  10. package/dist/java/tree.d.ts.map +1 -1
  11. package/dist/java/type-visitor.d.ts +48 -0
  12. package/dist/java/type-visitor.d.ts.map +1 -0
  13. package/dist/java/type-visitor.js +260 -0
  14. package/dist/java/type-visitor.js.map +1 -0
  15. package/dist/java/type.d.ts +2 -0
  16. package/dist/java/type.d.ts.map +1 -1
  17. package/dist/java/type.js +0 -317
  18. package/dist/java/type.js.map +1 -1
  19. package/dist/java/visitor.d.ts.map +1 -1
  20. package/dist/java/visitor.js +579 -363
  21. package/dist/java/visitor.js.map +1 -1
  22. package/dist/javascript/preconditions.d.ts +1 -1
  23. package/dist/javascript/preconditions.d.ts.map +1 -1
  24. package/dist/javascript/preconditions.js +7 -6
  25. package/dist/javascript/preconditions.js.map +1 -1
  26. package/dist/javascript/rpc.js +430 -350
  27. package/dist/javascript/rpc.js.map +1 -1
  28. package/dist/javascript/tree.d.ts +1 -1
  29. package/dist/javascript/tree.d.ts.map +1 -1
  30. package/dist/javascript/type-mapping.d.ts.map +1 -1
  31. package/dist/javascript/type-mapping.js +7 -0
  32. package/dist/javascript/type-mapping.js.map +1 -1
  33. package/dist/javascript/visitor.d.ts.map +1 -1
  34. package/dist/javascript/visitor.js +504 -309
  35. package/dist/javascript/visitor.js.map +1 -1
  36. package/dist/json/visitor.d.ts.map +1 -1
  37. package/dist/json/visitor.js +46 -21
  38. package/dist/json/visitor.js.map +1 -1
  39. package/dist/rpc/queue.d.ts +7 -4
  40. package/dist/rpc/queue.d.ts.map +1 -1
  41. package/dist/rpc/queue.js +22 -32
  42. package/dist/rpc/queue.js.map +1 -1
  43. package/dist/rpc/request/generate.d.ts.map +1 -1
  44. package/dist/rpc/request/generate.js +2 -3
  45. package/dist/rpc/request/generate.js.map +1 -1
  46. package/dist/rpc/request/get-languages.d.ts.map +1 -1
  47. package/dist/rpc/request/get-languages.js +4 -3
  48. package/dist/rpc/request/get-languages.js.map +1 -1
  49. package/dist/rpc/request/get-object.d.ts +1 -1
  50. package/dist/rpc/request/get-object.d.ts.map +1 -1
  51. package/dist/rpc/request/get-object.js +8 -7
  52. package/dist/rpc/request/get-object.js.map +1 -1
  53. package/dist/rpc/request/get-recipes.d.ts.map +1 -1
  54. package/dist/rpc/request/get-recipes.js +2 -2
  55. package/dist/rpc/request/get-recipes.js.map +1 -1
  56. package/dist/rpc/request/index.d.ts +1 -0
  57. package/dist/rpc/request/index.d.ts.map +1 -1
  58. package/dist/rpc/request/index.js +1 -0
  59. package/dist/rpc/request/index.js.map +1 -1
  60. package/dist/rpc/request/install-recipes.d.ts.map +1 -1
  61. package/dist/rpc/request/install-recipes.js +30 -21
  62. package/dist/rpc/request/install-recipes.js.map +1 -1
  63. package/dist/rpc/request/metrics.d.ts +10 -10
  64. package/dist/rpc/request/metrics.d.ts.map +1 -1
  65. package/dist/rpc/request/metrics.js +38 -31
  66. package/dist/rpc/request/metrics.js.map +1 -1
  67. package/dist/rpc/request/parse.d.ts.map +1 -1
  68. package/dist/rpc/request/parse.js +10 -12
  69. package/dist/rpc/request/parse.js.map +1 -1
  70. package/dist/rpc/request/prepare-recipe.d.ts.map +1 -1
  71. package/dist/rpc/request/prepare-recipe.js +4 -4
  72. package/dist/rpc/request/prepare-recipe.js.map +1 -1
  73. package/dist/rpc/request/print.d.ts +1 -1
  74. package/dist/rpc/request/print.d.ts.map +1 -1
  75. package/dist/rpc/request/print.js +10 -6
  76. package/dist/rpc/request/print.js.map +1 -1
  77. package/dist/rpc/request/trace-get-object.d.ts +5 -0
  78. package/dist/rpc/request/trace-get-object.d.ts.map +1 -0
  79. package/dist/rpc/request/trace-get-object.js +3 -0
  80. package/dist/rpc/request/trace-get-object.js.map +1 -0
  81. package/dist/rpc/request/visit.d.ts.map +1 -1
  82. package/dist/rpc/request/visit.js +2 -3
  83. package/dist/rpc/request/visit.js.map +1 -1
  84. package/dist/rpc/rewrite-rpc.d.ts +2 -3
  85. package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
  86. package/dist/rpc/rewrite-rpc.js +14 -5
  87. package/dist/rpc/rewrite-rpc.js.map +1 -1
  88. package/dist/rpc/server.d.ts.map +1 -1
  89. package/dist/rpc/server.js +15 -44
  90. package/dist/rpc/server.js.map +1 -1
  91. package/dist/rpc/trace.d.ts +1 -1
  92. package/dist/rpc/trace.d.ts.map +1 -1
  93. package/dist/rpc/trace.js +3 -3
  94. package/dist/rpc/trace.js.map +1 -1
  95. package/dist/util.d.ts +6 -0
  96. package/dist/util.d.ts.map +1 -1
  97. package/dist/util.js +14 -0
  98. package/dist/util.js.map +1 -1
  99. package/dist/version.txt +1 -1
  100. package/package.json +1 -1
  101. package/src/java/index.ts +1 -0
  102. package/src/java/rpc.ts +726 -537
  103. package/src/java/tree.ts +1 -1
  104. package/src/java/type-visitor.ts +241 -0
  105. package/src/java/type.ts +13 -277
  106. package/src/java/visitor.ts +581 -378
  107. package/src/javascript/preconditions.ts +7 -6
  108. package/src/javascript/rpc.ts +431 -360
  109. package/src/javascript/tree.ts +1 -1
  110. package/src/javascript/type-mapping.ts +7 -0
  111. package/src/javascript/visitor.ts +505 -310
  112. package/src/json/visitor.ts +47 -22
  113. package/src/rpc/queue.ts +20 -17
  114. package/src/rpc/request/generate.ts +31 -25
  115. package/src/rpc/request/get-languages.ts +18 -10
  116. package/src/rpc/request/get-object.ts +42 -34
  117. package/src/rpc/request/get-recipes.ts +15 -8
  118. package/src/rpc/request/index.ts +1 -0
  119. package/src/rpc/request/install-recipes.ts +96 -79
  120. package/src/rpc/request/metrics.ts +54 -48
  121. package/src/rpc/request/parse.ts +31 -25
  122. package/src/rpc/request/prepare-recipe.ts +31 -23
  123. package/src/rpc/request/print.ts +28 -14
  124. package/src/rpc/request/trace-get-object.ts +4 -0
  125. package/src/rpc/request/visit.ts +22 -16
  126. package/src/rpc/rewrite-rpc.ts +23 -10
  127. package/src/rpc/server.ts +17 -52
  128. package/src/rpc/trace.ts +3 -3
  129. package/src/util.ts +14 -0
  130. package/dist/rpc/chrome-profiler.d.ts +0 -25
  131. package/dist/rpc/chrome-profiler.d.ts.map +0 -1
  132. package/dist/rpc/chrome-profiler.js +0 -405
  133. package/dist/rpc/chrome-profiler.js.map +0 -1
  134. package/src/rpc/chrome-profiler.ts +0 -373
@@ -13,7 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import {mapAsync} from "../util";
16
+ import {mapAsync, updateIfChanged} from "../util";
17
17
  import {produceAsync, TreeVisitor, ValidImmerRecipeReturnType} from "../visitor";
18
18
  import {SourceFile} from "../tree";
19
19
  import {isJson, Json} from "./tree";
@@ -25,49 +25,74 @@ export class JsonVisitor<P> extends TreeVisitor<Json, P> {
25
25
  }
26
26
 
27
27
  protected async visitArray(array: Json.Array, p: P): Promise<Json | undefined> {
28
- return this.produceJson<Json.Array>(array, p, async draft => {
29
- draft.values = await mapAsync(array.values, value => this.visitRightPadded(value, p));
30
- })
28
+ const updates: any = {
29
+ prefix: await this.visitSpace(array.prefix, p),
30
+ markers: await this.visitMarkers(array.markers, p),
31
+ values: await mapAsync(array.values, value => this.visitRightPadded(value, p))
32
+ };
33
+ return updateIfChanged(array, updates);
31
34
  }
32
35
 
33
36
  protected async visitDocument(document: Json.Document, p: P): Promise<Json | undefined> {
34
- return this.produceJson<Json.Document>(document, p, async draft => {
35
- draft.value = await this.visitDefined(document.value, p);
36
- draft.eof = await this.visitSpace(document.eof, p);
37
- })
37
+ const updates: any = {
38
+ prefix: await this.visitSpace(document.prefix, p),
39
+ markers: await this.visitMarkers(document.markers, p),
40
+ value: await this.visitDefined(document.value, p),
41
+ eof: await this.visitSpace(document.eof, p)
42
+ };
43
+ return updateIfChanged(document, updates);
38
44
  }
39
45
 
40
46
  protected async visitEmpty(empty: Json.Empty, p: P): Promise<Json | undefined> {
41
- return this.produceJson(empty, p)
47
+ const updates: any = {
48
+ prefix: await this.visitSpace(empty.prefix, p),
49
+ markers: await this.visitMarkers(empty.markers, p)
50
+ };
51
+ return updateIfChanged(empty, updates);
42
52
  }
43
53
 
44
54
  protected async visitIdentifier(identifier: Json.Identifier, p: P): Promise<Json | undefined> {
45
- return this.produceJson(identifier, p)
55
+ const updates: any = {
56
+ prefix: await this.visitSpace(identifier.prefix, p),
57
+ markers: await this.visitMarkers(identifier.markers, p)
58
+ };
59
+ return updateIfChanged(identifier, updates);
46
60
  }
47
61
 
48
62
  protected async visitLiteral(literal: Json.Literal, p: P): Promise<Json | undefined> {
49
- return this.produceJson(literal, p)
63
+ const updates: any = {
64
+ prefix: await this.visitSpace(literal.prefix, p),
65
+ markers: await this.visitMarkers(literal.markers, p)
66
+ };
67
+ return updateIfChanged(literal, updates);
50
68
  }
51
69
 
52
70
  protected async visitMember(member: Json.Member, p: P): Promise<Json | undefined> {
53
- return this.produceJson<Json.Member>(member, p, async draft => {
54
- draft.key = (await this.visitRightPadded(member.key, p))!;
55
- draft.value = await this.visitDefined(member.value, p);
56
- });
71
+ const updates: any = {
72
+ prefix: await this.visitSpace(member.prefix, p),
73
+ markers: await this.visitMarkers(member.markers, p),
74
+ key: (await this.visitRightPadded(member.key, p))!,
75
+ value: await this.visitDefined(member.value, p)
76
+ };
77
+ return updateIfChanged(member, updates);
57
78
  }
58
79
 
59
80
  protected async visitObject(jsonObject: Json.Object, p: P): Promise<Json | undefined> {
60
- return this.produceJson<Json.Object>(jsonObject, p, async draft => {
61
- draft.members = await mapAsync(jsonObject.members, member => this.visitRightPadded(member, p));
62
- });
81
+ const updates: any = {
82
+ prefix: await this.visitSpace(jsonObject.prefix, p),
83
+ markers: await this.visitMarkers(jsonObject.markers, p),
84
+ members: await mapAsync(jsonObject.members, member => this.visitRightPadded(member, p))
85
+ };
86
+ return updateIfChanged(jsonObject, updates);
63
87
  }
64
88
 
65
89
  public async visitRightPadded<T extends Json>(right: Json.RightPadded<T>, p: P):
66
90
  Promise<Json.RightPadded<T> | undefined> {
67
- return produceAsync<Json.RightPadded<T>>(right, async draft => {
68
- draft.element = await this.visitDefined(right.element, p);
69
- draft.after = await this.visitSpace(right.after, p)
70
- });
91
+ const updates: any = {
92
+ element: await this.visitDefined(right.element, p),
93
+ after: await this.visitSpace(right.after, p)
94
+ };
95
+ return updateIfChanged(right, updates);
71
96
  }
72
97
 
73
98
  public async visitSpace(space: Json.Space, p: P): Promise<Json.Space> {
package/src/rpc/queue.ts CHANGED
@@ -13,11 +13,11 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+ import * as rpc from "vscode-jsonrpc/node";
16
17
  import {emptyMarkers, Markers} from "../markers";
17
18
  import {saveTrace, trace} from "./trace";
18
19
  import {createDraft, finishDraft} from "immer";
19
20
  import {isRef, ReferenceMap} from "../reference";
20
- import {Writable} from "node:stream";
21
21
 
22
22
  /**
23
23
  * Interface representing an RPC codec that defines methods
@@ -261,7 +261,8 @@ export class RpcReceiveQueue {
261
261
  constructor(private readonly refs: Map<number, any>,
262
262
  private readonly sourceFileType: string | undefined,
263
263
  private readonly pull: () => Promise<RpcObjectData[]>,
264
- private readonly logFile?: Writable) {
264
+ private readonly logger: rpc.Logger | undefined,
265
+ private readonly trace: boolean) {
265
266
  }
266
267
 
267
268
  async take(): Promise<RpcObjectData> {
@@ -276,7 +277,7 @@ export class RpcReceiveQueue {
276
277
  markers = emptyMarkers;
277
278
  }
278
279
  return this.receive(markers, async m => {
279
- return saveTrace(this.logFile, async () => {
280
+ return saveTrace(this.trace, async () => {
280
281
  const draft = createDraft(markers!);
281
282
  draft.id = await this.receive(m.id);
282
283
  draft.markers = (await this.receiveList(m.markers))!;
@@ -289,9 +290,9 @@ export class RpcReceiveQueue {
289
290
  before: T | undefined,
290
291
  onChange?: (before: T) => T | Promise<T | undefined> | undefined
291
292
  ): Promise<T> {
292
- return saveTrace(this.logFile, async () => {
293
+ return saveTrace(this.trace, async () => {
293
294
  const message = await this.take();
294
- this.traceMessage(message);
295
+ RpcObjectData.logTrace(message, this.trace, this.logger);
295
296
  let ref: number | undefined;
296
297
  switch (message.state) {
297
298
  case RpcObjectState.NO_CHANGE:
@@ -353,9 +354,9 @@ export class RpcReceiveQueue {
353
354
  before: T[] | undefined,
354
355
  onChange?: (before: T) => T | Promise<T | undefined> | undefined
355
356
  ): Promise<T[] | undefined> {
356
- return saveTrace(this.logFile, async () => {
357
+ return saveTrace(this.trace, async () => {
357
358
  const message = await this.take();
358
- this.traceMessage(message);
359
+ RpcObjectData.logTrace(message, this.trace, this.logger);
359
360
  switch (message.state) {
360
361
  case RpcObjectState.NO_CHANGE:
361
362
  return before;
@@ -385,16 +386,6 @@ export class RpcReceiveQueue {
385
386
  });
386
387
  }
387
388
 
388
- private traceMessage(message: RpcObjectData) {
389
- if (this.logFile && message.trace) {
390
- const sendTrace = message.trace;
391
- delete message.trace;
392
- this.logFile.write(`${JSON.stringify(message)}\n`);
393
- this.logFile.write(` ${sendTrace}\n`);
394
- this.logFile.write(` ${trace("Receiver")}\n`);
395
- }
396
- }
397
-
398
389
  private newObj<T>(type: string): T {
399
390
  return {
400
391
  kind: type
@@ -413,6 +404,18 @@ export interface RpcObjectData {
413
404
  trace?: string
414
405
  }
415
406
 
407
+ export namespace RpcObjectData {
408
+ export function logTrace(message: RpcObjectData, enabled: boolean, logger: rpc.Logger | undefined): void {
409
+ if (enabled && logger && message.trace) {
410
+ const sendTrace = message.trace;
411
+ delete message.trace;
412
+ logger.info(`${JSON.stringify(message)}`);
413
+ logger.info(` ${sendTrace || 'No sender trace'}`);
414
+ logger.info(` ${trace("Receiver") || 'No receiver trace'}`);
415
+ }
416
+ }
417
+ }
418
+
416
419
  export enum RpcObjectState {
417
420
  NO_CHANGE = "NO_CHANGE",
418
421
  ADD = "ADD",
@@ -34,33 +34,39 @@ export class Generate {
34
34
  recipeCursors: WeakMap<Recipe, Cursor>,
35
35
  getObject: (id: string) => any,
36
36
  metricsCsv?: string): void {
37
- const target = { target: '' };
38
- connection.onRequest(new rpc.RequestType<Generate, GenerateResponse, Error>("Generate"), withMetrics<Generate, GenerateResponse>("Generate", target, metricsCsv)(async (request) => {
39
- target.target = request.id;
40
- const recipe = preparedRecipes.get(request.id);
41
- const response = {
42
- ids: [],
43
- sourceFileTypes: []
44
- } as GenerateResponse;
37
+ connection.onRequest(
38
+ new rpc.RequestType<Generate, GenerateResponse, Error>("Generate"),
39
+ withMetrics<Generate, GenerateResponse>(
40
+ "Generate",
41
+ metricsCsv,
42
+ (context) => async (request) => {
43
+ context.target = request.id;
44
+ const recipe = preparedRecipes.get(request.id);
45
+ const response = {
46
+ ids: [],
47
+ sourceFileTypes: []
48
+ } as GenerateResponse;
45
49
 
46
- if (recipe && recipe instanceof ScanningRecipe) {
47
- let cursor = recipeCursors.get(recipe);
48
- if (!cursor) {
49
- cursor = rootCursor();
50
- recipeCursors.set(recipe, cursor);
51
- }
52
- const ctx = getObject(request.p) as ExecutionContext;
53
- const acc = recipe.accumulator(cursor, ctx);
54
- const generated = await recipe.generate(acc, ctx)
50
+ if (recipe && recipe instanceof ScanningRecipe) {
51
+ let cursor = recipeCursors.get(recipe);
52
+ if (!cursor) {
53
+ cursor = rootCursor();
54
+ recipeCursors.set(recipe, cursor);
55
+ }
56
+ const ctx = getObject(request.p) as ExecutionContext;
57
+ const acc = recipe.accumulator(cursor, ctx);
58
+ const generated = await recipe.generate(acc, ctx)
55
59
 
56
- for (const g of generated) {
57
- localObjects.set(g.id.toString(), g);
58
- response.ids.push(g.id.toString());
59
- response.sourceFileTypes.push(g.kind);
60
- }
60
+ for (const g of generated) {
61
+ localObjects.set(g.id.toString(), g);
62
+ response.ids.push(g.id.toString());
63
+ response.sourceFileTypes.push(g.kind);
64
+ }
61
65
 
62
- }
63
- return response;
64
- }));
66
+ }
67
+ return response;
68
+ }
69
+ )
70
+ );
65
71
  }
66
72
  }
@@ -3,15 +3,23 @@ import {withMetrics0} from "./metrics";
3
3
 
4
4
  export class GetLanguages {
5
5
  static handle(connection: rpc.MessageConnection, metricsCsv?: string): void {
6
- const target = {target: ''};
7
- connection.onRequest(new rpc.RequestType0<string[], Error>("GetLanguages"), withMetrics0<string[]>("GetLanguages", target, metricsCsv)(async () => {
8
- // Include all languages you want this server to support receiving from a remote peer
9
- return [
10
- "org.openrewrite.text.PlainText",
11
- "org.openrewrite.json.tree.Json$Document",
12
- "org.openrewrite.java.tree.J$CompilationUnit",
13
- "org.openrewrite.javascript.tree.JS$CompilationUnit",
14
- ];
15
- }));
6
+ connection.onRequest(
7
+ new rpc.RequestType0<string[], Error>("GetLanguages"),
8
+ withMetrics0<string[]>(
9
+ "GetLanguages",
10
+ metricsCsv,
11
+ (context) => async () => {
12
+ // Include all languages you want this server to support receiving from a remote peer
13
+ const languages = [
14
+ "org.openrewrite.text.PlainText",
15
+ "org.openrewrite.json.tree.Json$Document",
16
+ "org.openrewrite.java.tree.J$CompilationUnit",
17
+ "org.openrewrite.javascript.tree.JS$CompilationUnit",
18
+ ];
19
+ context.target = '';
20
+ return languages;
21
+ }
22
+ )
23
+ );
16
24
  }
17
25
  }
@@ -16,7 +16,7 @@
16
16
  import * as rpc from "vscode-jsonrpc/node";
17
17
  import {RpcObjectData, RpcObjectState, RpcSendQueue} from "../queue";
18
18
  import {ReferenceMap} from "../../reference";
19
- import {withMetrics, extractSourcePath} from "./metrics";
19
+ import {extractSourcePath, withMetrics} from "./metrics";
20
20
 
21
21
  export class GetObject {
22
22
  constructor(private readonly id: string,
@@ -29,49 +29,57 @@ export class GetObject {
29
29
  localObjects: Map<string, any | ((input: string) => any)>,
30
30
  localRefs: ReferenceMap,
31
31
  batchSize: number,
32
- trace: boolean,
33
- metricsCsv?: string
32
+ trace: () => boolean,
33
+ metricsCsv?: string,
34
34
  ): void {
35
35
  const pendingData = new Map<string, RpcObjectData[]>();
36
- const target = { target: '' };
37
36
 
38
- connection.onRequest(new rpc.RequestType<GetObject, any, Error>("GetObject"), withMetrics<GetObject, any>("GetObject", target, metricsCsv)(async request => {
39
- let objId = request.id;
40
- if (!localObjects.has(objId)) {
41
- return [
42
- {state: RpcObjectState.DELETE},
43
- {state: RpcObjectState.END_OF_OBJECT}
44
- ];
45
- }
37
+ connection.onRequest(
38
+ new rpc.RequestType<GetObject, any, Error>("GetObject"),
39
+ withMetrics<GetObject, any>(
40
+ "GetObject",
41
+ metricsCsv,
42
+ (context) => async request => {
43
+ const objId = request.id;
44
+ if (!localObjects.has(objId)) {
45
+ context.target = '';
46
+ return [
47
+ {state: RpcObjectState.DELETE},
48
+ {state: RpcObjectState.END_OF_OBJECT}
49
+ ];
50
+ }
46
51
 
47
- let objectOrGenerator = localObjects.get(objId)!;
48
- if (typeof objectOrGenerator === 'function') {
49
- let obj = await objectOrGenerator(objId);
50
- localObjects.set(objId, obj);
51
- }
52
+ const objectOrGenerator = localObjects.get(objId)!;
53
+ if (typeof objectOrGenerator === 'function') {
54
+ const obj = await objectOrGenerator(objId);
55
+ localObjects.set(objId, obj);
56
+ }
52
57
 
53
- const obj = localObjects.get(objId);
54
- target.target = extractSourcePath(obj);
58
+ const obj = localObjects.get(objId);
59
+ context.target = extractSourcePath(obj);
55
60
 
56
- let allData = pendingData.get(objId);
57
- if (!allData) {
58
- const after = obj;
59
- const before = remoteObjects.get(objId);
61
+ let allData = pendingData.get(objId);
62
+ if (!allData) {
63
+ const after = obj;
64
+ const before = remoteObjects.get(objId);
60
65
 
61
- allData = await new RpcSendQueue(localRefs, request.sourceFileType, trace).generate(after, before);
62
- pendingData.set(objId, allData);
66
+ allData = await new RpcSendQueue(localRefs, request.sourceFileType, trace())
67
+ .generate(after, before);
68
+ pendingData.set(objId, allData);
63
69
 
64
- remoteObjects.set(objId, after);
65
- }
70
+ remoteObjects.set(objId, after);
71
+ }
66
72
 
67
- const batch = allData.splice(0, batchSize);
73
+ const batch = allData.splice(0, batchSize);
68
74
 
69
- // If we've sent all data, remove from pending
70
- if (allData.length === 0) {
71
- pendingData.delete(objId);
72
- }
75
+ // If we've sent all data, remove from pending
76
+ if (allData.length === 0) {
77
+ pendingData.delete(objId);
78
+ }
73
79
 
74
- return batch;
75
- }));
80
+ return batch;
81
+ }
82
+ )
83
+ );
76
84
  }
77
85
  }
@@ -19,13 +19,20 @@ import {withMetrics0} from "./metrics";
19
19
 
20
20
  export class GetRecipes {
21
21
  static handle(connection: rpc.MessageConnection, registry: RecipeRegistry, metricsCsv?: string): void {
22
- const target = { target: '' };
23
- connection.onRequest(new rpc.RequestType0<({ name: string } & RecipeDescriptor)[], Error>("GetRecipes"), withMetrics0<({ name: string } & RecipeDescriptor)[]>("GetRecipes", target, metricsCsv)(async () => {
24
- const recipes = [];
25
- for (const [_name, recipe] of registry.all.entries()) {
26
- recipes.push(await new recipe().descriptor());
27
- }
28
- return recipes;
29
- }));
22
+ connection.onRequest(
23
+ new rpc.RequestType0<({ name: string } & RecipeDescriptor)[], Error>("GetRecipes"),
24
+ withMetrics0<({ name: string } & RecipeDescriptor)[]>(
25
+ "GetRecipes",
26
+ metricsCsv,
27
+ (context) => async () => {
28
+ const recipes = [];
29
+ for (const [_name, recipe] of registry.all.entries()) {
30
+ recipes.push(await new recipe().descriptor());
31
+ }
32
+ context.target = '';
33
+ return recipes;
34
+ }
35
+ )
36
+ );
30
37
  }
31
38
  }
@@ -20,3 +20,4 @@ export * from "./parse";
20
20
  export * from "./prepare-recipe";
21
21
  export * from "./print";
22
22
  export * from "./visit";
23
+ export * from "./trace-get-object";
@@ -17,7 +17,7 @@ import * as rpc from "vscode-jsonrpc/node";
17
17
  import {RecipeRegistry} from "../../recipe";
18
18
  import * as path from "path";
19
19
  import * as fs from "fs";
20
- import {spawn, ChildProcess} from "child_process";
20
+ import {spawn} from "child_process";
21
21
  import {withMetrics} from "./metrics";
22
22
 
23
23
  export interface InstallRecipesResponse {
@@ -70,94 +70,111 @@ export class InstallRecipes {
70
70
 
71
71
  static handle(connection: rpc.MessageConnection, installDir: string, registry: RecipeRegistry,
72
72
  logger?: rpc.Logger, metricsCsv?: string): void {
73
- const target = { target: '' };
74
- connection.onRequest(new rpc.RequestType<InstallRecipes, InstallRecipesResponse, Error>("InstallRecipes"), withMetrics<InstallRecipes, InstallRecipesResponse>("InstallRecipes", target, metricsCsv)(async (request) => {
75
- target.target = typeof request.recipes === "object" ? request.recipes.packageName : request.recipes;
76
- const beforeInstall = registry.all.size;
77
- let resolvedPath;
78
- let recipesName = request.recipes;
79
-
80
- if (typeof request.recipes === "object") {
81
- const recipePackage = request.recipes;
82
- const absoluteInstallDir = path.isAbsolute(installDir) ? installDir : path.join(process.cwd(), installDir);
83
-
84
- // Ensure directory exists
85
- if (!fs.existsSync(absoluteInstallDir)) {
86
- fs.mkdirSync(absoluteInstallDir, {recursive: true});
87
- }
88
-
89
- // Check if package.json exists, if not create one
90
- const packageJsonPath = path.join(absoluteInstallDir, "package.json");
91
- if (!fs.existsSync(packageJsonPath)) {
92
- // Create a minimal package.json with a custom name
93
- const packageJson = {
94
- name: "openrewrite-recipes",
95
- version: "1.0.0",
96
- description: "OpenRewrite recipe marketplace",
97
- private: true
98
- };
99
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
100
- if (logger) {
101
- logger.info("Created package.json for recipe dependencies");
73
+ connection.onRequest(
74
+ new rpc.RequestType<InstallRecipes, InstallRecipesResponse, Error>("InstallRecipes"),
75
+ withMetrics<InstallRecipes, InstallRecipesResponse>(
76
+ "InstallRecipes",
77
+ metricsCsv,
78
+ (context) => async (request) => {
79
+ context.target = typeof request.recipes === "object" ? request.recipes.packageName : request.recipes;
80
+ const beforeInstall = registry.all.size;
81
+ let resolvedPath;
82
+ let recipesName = request.recipes;
83
+
84
+ if (typeof request.recipes === "object") {
85
+ const recipePackage = request.recipes;
86
+ const absoluteInstallDir = path.isAbsolute(installDir) ? installDir : path.join(process.cwd(), installDir);
87
+
88
+ // Ensure directory exists
89
+ if (!fs.existsSync(absoluteInstallDir)) {
90
+ fs.mkdirSync(absoluteInstallDir, {recursive: true});
91
+ }
92
+
93
+ // Check if package.json exists, if not create one
94
+ const packageJsonPath = path.join(absoluteInstallDir, "package.json");
95
+ if (!fs.existsSync(packageJsonPath)) {
96
+ // Create a minimal package.json with a custom name
97
+ const packageJson = {
98
+ name: "openrewrite-recipes",
99
+ version: "1.0.0",
100
+ description: "OpenRewrite recipe marketplace",
101
+ private: true
102
+ };
103
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
104
+ if (logger) {
105
+ logger.info("Created package.json for recipe dependencies");
106
+ }
107
+ }
108
+
109
+ // Rather than using npm on PATH, use `node_cli.js`.
110
+ // https://stackoverflow.com/questions/15957529/can-i-install-a-npm-package-from-javascript-running-in-node-js
111
+ const packageSpec = recipePackage.packageName + (recipePackage.version ? `@${recipePackage.version}` : "");
112
+ await spawnNpmCommand("npm", ["install", packageSpec, "--no-fund"], absoluteInstallDir, logger);
113
+ resolvedPath = require.resolve(path.join(absoluteInstallDir, "node_modules", recipePackage.packageName));
114
+ recipesName = request.recipes.packageName;
115
+ } else {
116
+ resolvedPath = request.recipes;
102
117
  }
103
- }
104
118
 
105
- // Rather than using npm on PATH, use `node_cli.js`.
106
- // https://stackoverflow.com/questions/15957529/can-i-install-a-npm-package-from-javascript-running-in-node-js
107
- const packageSpec = recipePackage.packageName + (recipePackage.version ? `@${recipePackage.version}` : "");
108
- await spawnNpmCommand("npm", ["install", packageSpec, "--no-fund"], absoluteInstallDir, logger);
109
- resolvedPath = require.resolve(path.join(absoluteInstallDir, "node_modules", recipePackage.packageName));
110
- recipesName = request.recipes.packageName;
111
- } else {
112
- resolvedPath = request.recipes;
113
- }
119
+ let recipeModule;
120
+ try {
121
+ setupSharedDependencies(resolvedPath);
122
+ recipeModule = require(resolvedPath);
123
+ } catch (e: any) {
124
+ throw new Error(`Failed to load recipe module from ${resolvedPath}: ${e.stack}`);
125
+ }
114
126
 
115
- let recipeModule;
116
- try {
117
- setupSharedDependencies(resolvedPath);
118
- recipeModule = require(resolvedPath);
119
- } catch (e: any) {
120
- throw new Error(`Failed to load recipe module from ${resolvedPath}: ${e.stack}`);
121
- }
127
+ if (typeof recipeModule.activate === "function") {
128
+ // noinspection JSVoidFunctionReturnValueUsed
129
+ const activatePromise = recipeModule.activate(registry);
130
+ // noinspection SuspiciousTypeOfGuard
131
+ if (activatePromise instanceof Promise) {
132
+ await activatePromise;
133
+ }
134
+ } else {
135
+ throw new Error(`${recipesName} does not export an 'activate' function`);
136
+ }
122
137
 
123
- if (typeof recipeModule.activate === "function") {
124
- // noinspection JSVoidFunctionReturnValueUsed
125
- const activatePromise = recipeModule.activate(registry);
126
- // noinspection SuspiciousTypeOfGuard
127
- if (activatePromise instanceof Promise) {
128
- await activatePromise;
138
+ return {recipesInstalled: registry.all.size - beforeInstall};
129
139
  }
130
- } else {
131
- throw new Error(`${recipesName} does not export an 'activate' function`);
132
- }
133
-
134
- return {recipesInstalled: registry.all.size - beforeInstall};
135
- }));
140
+ )
141
+ );
136
142
  }
137
143
  }
138
144
 
145
+ /**
146
+ * Ensures dynamically loaded modules share the same class instances as the host
147
+ * by mapping require.cache entries. This prevents instanceof failures when the
148
+ * same package is installed in multiple node_modules directories.
149
+ */
139
150
  function setupSharedDependencies(targetModulePath: string) {
140
- const sharedDeps = [
141
- '@openrewrite/rewrite',
142
- 'vscode-jsonrpc',
143
- ];
144
-
145
- sharedDeps.forEach(dep => {
146
- try {
147
- // Get your already-loaded version
148
- const yourDepPath = require.resolve(dep);
149
- const yourModule = require.cache[yourDepPath];
150
-
151
- if (yourModule) {
152
- // Find where the target would look for this dependency
153
- const targetDir = path.dirname(targetModulePath);
154
- const targetDepPath = require.resolve(dep, {paths: [targetDir]});
155
-
156
- // Make the target use your cached version
157
- require.cache[targetDepPath] = yourModule;
151
+ const sharedDeps = ['@openrewrite/rewrite', 'vscode-jsonrpc'];
152
+ const targetDir = path.dirname(targetModulePath);
153
+
154
+ sharedDeps.forEach(depName => {
155
+ const depPattern = path.sep + 'node_modules' + path.sep + depName.replace('/', path.sep);
156
+
157
+ for (const cachedPath of Object.keys(require.cache)) {
158
+ if (!cachedPath.includes(depPattern)) continue;
159
+
160
+ try {
161
+ // Extract subpath: /path/node_modules/@pkg/dist/tree.js -> dist/tree.js
162
+ const pkgIndex = cachedPath.indexOf(depPattern);
163
+ let subpath = cachedPath.substring(pkgIndex + depPattern.length)
164
+ .replace(/^[/\\]/, '') // Remove leading slash
165
+ .replace(/\.(js|ts)$/, '') // Remove extension
166
+ .replace(/^dist[/\\]/, '') // Remove dist/ prefix if present
167
+ .replace(/[/\\]index$/, ''); // Remove /index suffix
168
+
169
+ // Build require path: @pkg or @pkg/subpath
170
+ const requirePath = subpath ? `${depName}/${subpath}` : depName;
171
+
172
+ // Resolve from target's perspective and map cache
173
+ const targetDepPath = require.resolve(requirePath, {paths: [targetDir]});
174
+ require.cache[targetDepPath] = require.cache[cachedPath];
175
+ } catch (e) {
176
+ // Target can't resolve this path, skip
158
177
  }
159
- } catch (e) {
160
- // Module not found or not resolvable, skip
161
178
  }
162
179
  });
163
180
  }