@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
@@ -78,69 +78,75 @@ export function initializeMetricsCsv(metricsCsv?: string, logger?: rpc.Logger):
78
78
  }
79
79
  }
80
80
 
81
- /**
82
- * Internal function to wrap a handler with metrics recording.
83
- */
84
- async function wrapWithMetrics<R>(
85
- handler: () => Promise<R>,
86
- target: { target: string },
87
- request: string,
88
- metricsCsv?: string
89
- ): Promise<R> {
90
- if (!metricsCsv) {
91
- // No metrics recording requested, just execute the handler
92
- return handler();
93
- }
94
-
95
- const startTime = Date.now();
96
-
97
- try {
98
- const result = await handler();
99
- recordMetrics(metricsCsv, target.target, request, startTime);
100
- return result;
101
- } catch (error) {
102
- recordMetrics(metricsCsv, target.target, request, startTime);
103
- throw error;
104
- }
105
- }
106
-
107
81
  /**
108
82
  * Wraps an RPC request handler to record performance metrics to a CSV file.
109
83
  *
110
- * @param request The request type name (e.g., "Visit", "GetObject")
111
- * @param target A mutable object containing the target identifier for metrics
84
+ * @param requestType The request type name (e.g., "Visit", "GetObject")
112
85
  * @param metricsCsv Optional path to the CSV file for recording metrics
113
- * @returns A function that wraps a request handler with metrics recording
86
+ * @param handlerFactory A function that creates the handler with a mutable context for setting the target
87
+ * @returns A request handler compatible with rpc.MessageConnection#onRequest
114
88
  */
115
89
  export function withMetrics<P, R>(
116
- request: string,
117
- target: { target: string },
118
- metricsCsv?: string
119
- ): (handler: (request: P) => Promise<R>) => (request: P) => Promise<R> {
120
- return (handler: (requestParam: P) => Promise<R>) => {
121
- return async (requestParam: P): Promise<R> => {
122
- return wrapWithMetrics(() => handler(requestParam), target, request, metricsCsv);
123
- };
90
+ requestType: string,
91
+ metricsCsv: string | undefined,
92
+ handlerFactory: (context: {target: string}) => (request: P) => Promise<R>
93
+ ): (request: P) => Promise<R> {
94
+ return async (request: P): Promise<R> => {
95
+ const context = {target: ''};
96
+ const handler = handlerFactory(context);
97
+
98
+ if (!metricsCsv) {
99
+ // No metrics recording requested, just execute the handler
100
+ return await handler(request);
101
+ }
102
+
103
+ const startTime = Date.now();
104
+
105
+ try {
106
+ const result = await handler(request);
107
+ recordMetrics(metricsCsv, context.target, requestType, startTime);
108
+ return result;
109
+ } catch (error) {
110
+ // On error, still try to record metrics with the target (maybe empty)
111
+ recordMetrics(metricsCsv, context.target, requestType, startTime);
112
+ throw error;
113
+ }
124
114
  };
125
115
  }
126
116
 
127
117
  /**
128
118
  * Wraps an RPC request handler without parameters (RequestType0) to record performance metrics.
129
119
  *
130
- * @param request The request type name (e.g., "GetLanguages", "GetRecipes")
131
- * @param target A mutable object containing the target identifier for metrics
120
+ * @param requestType The request type name (e.g., "GetLanguages", "GetRecipes")
132
121
  * @param metricsCsv Optional path to the CSV file for recording metrics
133
- * @returns A function that wraps a request handler with metrics recording
122
+ * @param handlerFactory A function that creates the handler with a mutable context for setting the target
123
+ * @returns A request handler compatible with rpc.MessageConnection#onRequest
134
124
  */
135
125
  export function withMetrics0<R>(
136
- request: string,
137
- target: { target: string },
138
- metricsCsv?: string
139
- ): (handler: () => Promise<R>) => (token: any) => Promise<R> {
140
- return (handler: () => Promise<R>) => {
141
- return async (_: any): Promise<R> => {
142
- return wrapWithMetrics(handler, target, request, metricsCsv);
143
- };
126
+ requestType: string,
127
+ metricsCsv: string | undefined,
128
+ handlerFactory: (context: {target: string}) => () => Promise<R>
129
+ ): (token: any) => Promise<R> {
130
+ return async (_: any): Promise<R> => {
131
+ const context = {target: ''};
132
+ const handler = handlerFactory(context);
133
+
134
+ if (!metricsCsv) {
135
+ // No metrics recording requested, just execute the handler
136
+ return await handler();
137
+ }
138
+
139
+ const startTime = Date.now();
140
+
141
+ try {
142
+ const result = await handler();
143
+ recordMetrics(metricsCsv, context.target, requestType, startTime);
144
+ return result;
145
+ } catch (error) {
146
+ // On error, still try to record metrics with the target (may be empty)
147
+ recordMetrics(metricsCsv, context.target, requestType, startTime);
148
+ throw error;
149
+ }
144
150
  };
145
151
  }
146
152
 
@@ -29,33 +29,39 @@ export class Parse {
29
29
  static handle(connection: rpc.MessageConnection,
30
30
  localObjects: Map<string, ((input: string) => any) | any>,
31
31
  metricsCsv?: string): void {
32
- const target = { target: '' };
33
- connection.onRequest(new rpc.RequestType<Parse, UUID[], Error>("Parse"), withMetrics<Parse, UUID[]>("Parse", target, metricsCsv)(async (request) => {
34
- // Set target to comma-separated list of file paths
35
- target.target = request.inputs.map(input =>
36
- typeof input === 'string' ? input : input.sourcePath
37
- ).join(',');
38
- let parser = Parsers.createParser("javascript", {
39
- ctx: new ExecutionContext(),
40
- relativeTo: request.relativeTo
41
- });
32
+ connection.onRequest(
33
+ new rpc.RequestType<Parse, UUID[], Error>("Parse"),
34
+ withMetrics<Parse, UUID[]>(
35
+ "Parse",
36
+ metricsCsv,
37
+ (context) => async (request) => {
38
+ // Set target to comma-separated list of file paths
39
+ context.target = request.inputs.map(input =>
40
+ typeof input === 'string' ? input : input.sourcePath
41
+ ).join(',');
42
42
 
43
- if (!parser) {
44
- return [];
45
- }
46
- const generator = parser.parse(...request.inputs);
47
- const result: UUID[] = [];
43
+ const parser = Parsers.createParser("javascript", {
44
+ ctx: new ExecutionContext(),
45
+ relativeTo: request.relativeTo
46
+ })!;
48
47
 
49
- for (let i = 0; i < request.inputs.length; i++) {
50
- const id = randomId();
51
- localObjects.set(id, async (id: string) => {
52
- let sourceFile: SourceFile = (await generator.next()).value;
53
- return produce(sourceFile, (draft) => {draft.id = id;});
54
- });
55
- result.push(id);
56
- }
48
+ const generator = parser.parse(...request.inputs);
49
+ const resultIds: UUID[] = [];
57
50
 
58
- return result;
59
- }));
51
+ for (let i = 0; i < request.inputs.length; i++) {
52
+ const id = randomId();
53
+ localObjects.set(id, async (id: string) => {
54
+ const sourceFile: SourceFile = (await generator.next()).value;
55
+ return produce(sourceFile, (draft) => {
56
+ draft.id = id;
57
+ });
58
+ });
59
+ resultIds.push(id);
60
+ }
61
+
62
+ return resultIds;
63
+ }
64
+ )
65
+ );
60
66
  }
61
67
  }
@@ -32,33 +32,41 @@ export class PrepareRecipe {
32
32
  preparedRecipes: Map<String, Recipe>,
33
33
  metricsCsv?: string) {
34
34
  const snowflake = SnowflakeId();
35
- const target = { target: '' };
36
- connection.onRequest(new rpc.RequestType<PrepareRecipe, PrepareRecipeResponse, Error>("PrepareRecipe"), withMetrics<PrepareRecipe, PrepareRecipeResponse>("PrepareRecipe", target, metricsCsv)(async (request) => {
37
- target.target = request.id;
38
- const id = snowflake.generate();
39
- const recipeCtor = registry.all.get(request.id);
40
- if (!recipeCtor) {
41
- throw new Error(`Could not find recipe with id ${request.id}`);
42
- }
43
- let recipe = new recipeCtor(request.options);
35
+ connection.onRequest(
36
+ new rpc.RequestType<PrepareRecipe, PrepareRecipeResponse, Error>("PrepareRecipe"),
37
+ withMetrics<PrepareRecipe, PrepareRecipeResponse>(
38
+ "PrepareRecipe",
39
+ metricsCsv,
40
+ (context) => async (request) => {
41
+ context.target = request.id;
42
+ const id = snowflake.generate();
43
+ const recipeCtor = registry.all.get(request.id);
44
+ if (!recipeCtor) {
45
+ throw new Error(`Could not find recipe with id ${request.id}`);
46
+ }
47
+ let recipe = new recipeCtor(request.options);
44
48
 
45
- const editPreconditions: Precondition[] = [];
46
- recipe = await this.optimizePreconditions(recipe, "edit", editPreconditions);
49
+ const editPreconditions: Precondition[] = [];
50
+ recipe = await this.optimizePreconditions(recipe, "edit", editPreconditions);
47
51
 
48
- const scanPreconditions: Precondition[] = [];
49
- recipe = await this.optimizePreconditions(recipe, "scan", scanPreconditions);
52
+ const scanPreconditions: Precondition[] = [];
53
+ recipe = await this.optimizePreconditions(recipe, "scan", scanPreconditions);
50
54
 
51
- preparedRecipes.set(id, recipe);
55
+ preparedRecipes.set(id, recipe);
52
56
 
53
- return {
54
- id: id,
55
- descriptor: await recipe.descriptor(),
56
- editVisitor: `edit:${id}`,
57
- editPreconditions: editPreconditions,
58
- scanVisitor: recipe instanceof ScanningRecipe ? `scan:${id}` : undefined,
59
- scanPreconditions: scanPreconditions
60
- }
61
- }));
57
+ const result = {
58
+ id: id,
59
+ descriptor: await recipe.descriptor(),
60
+ editVisitor: `edit:${id}`,
61
+ editPreconditions: editPreconditions,
62
+ scanVisitor: recipe instanceof ScanningRecipe ? `scan:${id}` : undefined,
63
+ scanPreconditions: scanPreconditions
64
+ };
65
+
66
+ return result;
67
+ }
68
+ )
69
+ );
62
70
  }
63
71
 
64
72
  /**
@@ -17,7 +17,7 @@ import * as rpc from "vscode-jsonrpc/node";
17
17
  import {isSourceFile, Tree} from "../../tree";
18
18
  import {MarkerPrinter as PrintMarkerPrinter, printer, PrintOutputCapture} from "../../print";
19
19
  import {UUID} from "../../uuid";
20
- import {withMetrics, extractSourcePath} from "./metrics";
20
+ import {extractSourcePath, withMetrics} from "./metrics";
21
21
 
22
22
  export const enum MarkerPrinter {
23
23
  DEFAULT = "DEFAULT",
@@ -26,22 +26,36 @@ export const enum MarkerPrinter {
26
26
  }
27
27
 
28
28
  export class Print {
29
- constructor(private readonly treeId: UUID, private readonly sourceFileType: string, readonly markerPrinter: MarkerPrinter = MarkerPrinter.DEFAULT) {
29
+ constructor(private readonly treeId: UUID,
30
+ private readonly sourceFileType: string,
31
+ readonly markerPrinter: MarkerPrinter = MarkerPrinter.DEFAULT) {
30
32
  }
31
33
 
32
34
  static handle(connection: rpc.MessageConnection,
33
- getObject: (id: string, sourceFileType?: string) => any,
35
+ getObject: (id: string, sourceFileType: string) => any,
36
+ logger?: rpc.Logger,
34
37
  metricsCsv?: string): void {
35
- const target = { target: '' };
36
- connection.onRequest(new rpc.RequestType<Print, string, Error>("Print"), withMetrics<Print, string>("Print", target, metricsCsv)(async request => {
37
- const tree: Tree = await getObject(request.treeId.toString(), request.sourceFileType);
38
- target.target = extractSourcePath(tree);
39
- const out = new PrintOutputCapture(PrintMarkerPrinter[request.markerPrinter]);
40
- if (isSourceFile(tree)) {
41
- return await printer(tree).print(tree, out);
42
- } else {
43
- return await printer(request.sourceFileType).print(tree, out);
44
- }
45
- }));
38
+ connection.onRequest(
39
+ new rpc.RequestType<Print, string, Error>("Print"),
40
+ withMetrics<Print, string>(
41
+ "Print",
42
+ metricsCsv,
43
+ (context) => async request => {
44
+ const tree: Tree = await getObject(request.treeId.toString(), request.sourceFileType);
45
+ context.target = extractSourcePath(tree);
46
+ if (logger) {
47
+ logger.log("Printing " + (isSourceFile(tree) ? tree.sourcePath : `tree of type ${tree.kind} in ${context.target}`));
48
+ }
49
+ const out = new PrintOutputCapture(PrintMarkerPrinter[request.markerPrinter]);
50
+ let result: string;
51
+ if (isSourceFile(tree)) {
52
+ result = await printer(tree).print(tree, out);
53
+ } else {
54
+ result = await printer(request.sourceFileType).print(tree, out);
55
+ }
56
+ return result;
57
+ }
58
+ )
59
+ );
46
60
  }
47
61
  }
@@ -0,0 +1,4 @@
1
+ export interface TraceGetObject {
2
+ receive: boolean
3
+ send: boolean
4
+ }
@@ -40,24 +40,30 @@ export class Visit {
40
40
  getObject: (id: string, sourceFileType?: string) => any,
41
41
  getCursor: (cursorIds: string[] | undefined, sourceFileType?: string) => Promise<Cursor>,
42
42
  metricsCsv?: string): void {
43
- const target = { target: '' };
44
- connection.onRequest(new rpc.RequestType<Visit, VisitResponse, Error>("Visit"), withMetrics<Visit, VisitResponse>("Visit", target, metricsCsv)(async (request) => {
45
- const p = await getObject(request.p, undefined);
46
- const before: Tree = await getObject(request.treeId, request.sourceFileType);
47
- const cursor = await getCursor(request.cursor, request.sourceFileType);
48
- target.target = extractSourcePath(before, cursor);
49
- localObjects.set(before.id.toString(), before);
43
+ connection.onRequest(
44
+ new rpc.RequestType<Visit, VisitResponse, Error>("Visit"),
45
+ withMetrics<Visit, VisitResponse>(
46
+ "Visit",
47
+ metricsCsv,
48
+ (context) => async (request) => {
49
+ const p = await getObject(request.p, undefined);
50
+ const before: Tree = await getObject(request.treeId, request.sourceFileType);
51
+ const cursor = await getCursor(request.cursor, request.sourceFileType);
52
+ context.target = extractSourcePath(before, cursor);
53
+ localObjects.set(before.id.toString(), before);
50
54
 
51
- const visitor = await Visit.instantiateVisitor(request, preparedRecipes, recipeCursors, p);
52
- const after = await visitor.visit(before, p, cursor);
53
- if (!after) {
54
- localObjects.delete(before.id.toString());
55
- } else if (after !== before) {
56
- localObjects.set(after.id.toString(), after);
57
- }
55
+ const visitor = await Visit.instantiateVisitor(request, preparedRecipes, recipeCursors, p);
56
+ const after = await visitor.visit(before, p, cursor);
57
+ if (!after) {
58
+ localObjects.delete(before.id.toString());
59
+ } else if (after !== before) {
60
+ localObjects.set(after.id.toString(), after);
61
+ }
58
62
 
59
- return {modified: before !== after};
60
- }));
63
+ return {modified: before !== after};
64
+ }
65
+ )
66
+ );
61
67
  }
62
68
 
63
69
  private static async instantiateVisitor(request: Visit,
@@ -19,13 +19,15 @@ 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, GenerateResponse,
22
+ Generate,
23
+ GenerateResponse,
23
24
  GetObject,
24
25
  GetRecipes,
25
26
  Parse,
26
27
  PrepareRecipe,
27
28
  PrepareRecipeResponse,
28
29
  Print,
30
+ TraceGetObject,
29
31
  Visit,
30
32
  VisitResponse
31
33
  } from "./request";
@@ -36,7 +38,6 @@ import {ExecutionContext} from "../execution";
36
38
  import {InstallRecipes, InstallRecipesResponse} from "./request/install-recipes";
37
39
  import {ParserInput} from "../parser";
38
40
  import {ReferenceMap} from "../reference";
39
- import {Writable} from "node:stream";
40
41
  import {GetLanguages} from "./request/get-languages";
41
42
 
42
43
  export class RewriteRpc {
@@ -53,6 +54,8 @@ export class RewriteRpc {
53
54
  readonly localRefs: ReferenceMap = new ReferenceMap();
54
55
 
55
56
  private remoteLanguages?: string[];
57
+ private readonly logger?: rpc.Logger;
58
+ private traceGetObject: TraceGetObject = {receive: false, send: false};
56
59
 
57
60
  constructor(readonly connection: MessageConnection = rpc.createMessageConnection(
58
61
  new rpc.StreamMessageReader(process.stdin),
@@ -63,12 +66,11 @@ export class RewriteRpc {
63
66
  registry?: RecipeRegistry,
64
67
  logger?: rpc.Logger,
65
68
  metricsCsv?: string,
66
- traceGetObjectOutput?: boolean,
67
- traceGetObjectInput?: Writable,
68
69
  recipeInstallDir?: string
69
70
  }) {
70
71
  // Initialize metrics CSV file if configured
71
72
  initializeMetricsCsv(options.metricsCsv, options.logger);
73
+ this.logger = options.logger;
72
74
 
73
75
  const preparedRecipes: Map<String, Recipe> = new Map();
74
76
  const recipeCursors: WeakMap<Recipe, Cursor> = new WeakMap()
@@ -76,20 +78,30 @@ export class RewriteRpc {
76
78
  // Need this indirection, otherwise `this` will be undefined when executed in the handlers.
77
79
  const getObject = (id: string, sourceFileType?: string) => this.getObject(id, sourceFileType);
78
80
  const getCursor = (cursorIds: string[] | undefined, sourceFileType?: string) => this.getCursor(cursorIds, sourceFileType);
81
+ const traceGetObject = () => this.traceGetObject.send;
79
82
 
80
83
  const registry = options.registry || new RecipeRegistry();
81
84
 
82
85
  Visit.handle(this.connection, this.localObjects, preparedRecipes, recipeCursors, getObject, getCursor, options.metricsCsv);
83
86
  Generate.handle(this.connection, this.localObjects, preparedRecipes, recipeCursors, getObject, options.metricsCsv);
84
87
  GetObject.handle(this.connection, this.remoteObjects, this.localObjects,
85
- this.localRefs, options?.batchSize || 200, !!options?.traceGetObjectOutput, options.metricsCsv);
88
+ this.localRefs, options?.batchSize || 200, traceGetObject, options.metricsCsv);
86
89
  GetRecipes.handle(this.connection, registry, options.metricsCsv);
87
90
  GetLanguages.handle(this.connection, options.metricsCsv);
88
91
  PrepareRecipe.handle(this.connection, registry, preparedRecipes, options.metricsCsv);
89
92
  Parse.handle(this.connection, this.localObjects, options.metricsCsv);
90
- Print.handle(this.connection, getObject, options.metricsCsv);
93
+ Print.handle(this.connection, getObject, options.logger, options.metricsCsv);
91
94
  InstallRecipes.handle(this.connection, options.recipeInstallDir ?? ".rewrite", registry, options.logger, options.metricsCsv);
92
95
 
96
+ this.connection.onRequest(
97
+ new rpc.RequestType<TraceGetObject, boolean, Error>("TraceGetObject"),
98
+ async (request) => {
99
+ this.traceGetObject = request;
100
+ return true;
101
+ }
102
+ )
103
+
104
+ RewriteRpc.set(this);
93
105
  this.connection.listen();
94
106
  }
95
107
 
@@ -112,14 +124,15 @@ export class RewriteRpc {
112
124
  const q = new RpcReceiveQueue(this.remoteRefs, sourceFileType, () => {
113
125
  return this.connection.sendRequest(
114
126
  new rpc.RequestType<GetObject, RpcObjectData[], Error>("GetObject"),
115
- new GetObject(id, sourceFileType)
127
+ new GetObject(id, sourceFileType),
116
128
  );
117
- }, this.options.traceGetObjectInput);
129
+ }, this.logger, this.traceGetObject.receive);
118
130
 
119
131
  const remoteObject = await q.receive<P>(localObject);
120
132
 
121
133
  const eof = (await q.take());
122
134
  if (eof.state !== RpcObjectState.END_OF_OBJECT) {
135
+ RpcObjectData.logTrace(eof, this.traceGetObject.receive, this.logger);
123
136
  throw new Error(`Expected END_OF_OBJECT but got: ${eof.state}`);
124
137
  }
125
138
 
@@ -159,10 +172,10 @@ export class RewriteRpc {
159
172
  throw new Error("Cursor is required for non-SourceFile trees");
160
173
  }
161
174
  this.localObjects.set(tree.id.toString(), tree);
175
+ const sourceFile = isSourceFile(tree) ? tree : cursor!.firstEnclosing(t => isSourceFile(t))!;
162
176
  return await this.connection.sendRequest(
163
177
  new rpc.RequestType<Print, string, Error>("Print"),
164
- new Print(tree.id, isSourceFile(tree) ? tree.kind :
165
- cursor!.firstEnclosing(t => isSourceFile(t))!.kind)
178
+ new Print(tree.id, sourceFile.kind)
166
179
  );
167
180
  }
168
181
 
package/src/rpc/server.ts CHANGED
@@ -15,12 +15,10 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import * as rpc from "vscode-jsonrpc/node";
18
- import {Trace, Tracer} from "vscode-jsonrpc/node";
19
18
  import {RewriteRpc} from "./rewrite-rpc";
20
19
  import * as fs from "fs";
21
20
  import {Command} from 'commander';
22
21
  import {dir} from 'tmp-promise';
23
- import {ChromeProfiler} from './chrome-profiler';
24
22
 
25
23
  // Include all languages you want this server to support.
26
24
  import "../text";
@@ -34,10 +32,8 @@ require('v8').setFlagsFromString('--stack-size=8000');
34
32
  interface ProgramOptions {
35
33
  logFile?: string;
36
34
  metricsCsv?: string;
37
- verbose?: boolean;
35
+ traceRpcMessages?: boolean;
38
36
  batchSize?: number;
39
- traceGetObjectOutput?: boolean;
40
- traceGetObjectInput?: boolean;
41
37
  recipeInstallDir?: string;
42
38
  profile?: boolean;
43
39
  }
@@ -48,23 +44,13 @@ async function main() {
48
44
  .option('--port <number>', 'port number')
49
45
  .option('--log-file <log_path>', 'log file path')
50
46
  .option('--metrics-csv <metrics_csv_path>', 'metrics CSV output path')
51
- .option('-v, --verbose', 'enable verbose output')
47
+ .option('--trace-rpc-messages', 'trace RPC messages at the protocol level')
52
48
  .option('--batch-size [size]', 'sets the batch size (default is 200)', s => parseInt(s, 10), 200)
53
- .option('--trace-get-object-output', 'enable `GetObject` output tracing')
54
- .option('--trace-get-object-input', 'enable `GetObject` input tracing')
55
49
  .option('--recipe-install-dir <install_dir>', 'Recipe installation directory (default is a temporary directory)')
56
- .option('--profile', 'enable profiling')
57
50
  .parse();
58
51
 
59
52
  const options = program.opts() as ProgramOptions;
60
53
 
61
- // Chrome profiling
62
- let profiler: ChromeProfiler | undefined;
63
- if (options.profile) {
64
- profiler = new ChromeProfiler();
65
- profiler.start().catch(console.error);
66
- }
67
-
68
54
  let recipeInstallDir: string;
69
55
  if (!options.recipeInstallDir) {
70
56
  let recipeCleanup: () => Promise<void>;
@@ -77,9 +63,6 @@ async function main() {
77
63
 
78
64
  // Register cleanup on exit
79
65
  process.on('SIGINT', async () => {
80
- if (profiler) {
81
- await profiler.stop();
82
- }
83
66
  if (recipeCleanup) {
84
67
  await recipeCleanup();
85
68
  }
@@ -87,9 +70,6 @@ async function main() {
87
70
  });
88
71
 
89
72
  process.on('SIGTERM', async () => {
90
- if (profiler) {
91
- await profiler.stop();
92
- }
93
73
  if (recipeCleanup) {
94
74
  await recipeCleanup();
95
75
  }
@@ -99,31 +79,18 @@ async function main() {
99
79
  recipeInstallDir = await setupRecipeDir();
100
80
  } else {
101
81
  recipeInstallDir = options.recipeInstallDir;
102
-
103
- // Register cleanup for profiler when no recipe cleanup is needed
104
- if (profiler) {
105
- process.on('SIGINT', async () => {
106
- await profiler.stop();
107
- process.exit(0);
108
- });
109
-
110
- process.on('SIGTERM', async () => {
111
- await profiler.stop();
112
- process.exit(0);
113
- });
114
- }
115
82
  }
116
83
 
117
84
  const log = options.logFile ? fs.createWriteStream(options.logFile, {flags: 'a'}) : undefined;
118
85
  const logger: rpc.Logger = {
119
- error: (msg: string) => log && log.write(`[js-rewrite-rpc] [error] ${msg}\n`),
120
- warn: (msg: string) => log && log.write(`[js-rewrite-rpc] [warn] ${msg}\n`),
121
- info: (msg: string) => log && options.verbose && log.write(`[js-rewrite-rpc] [info] ${msg}\n`),
122
- log: (msg: string) => log && options.verbose && log.write(`[js-rewrite-rpc] [log] ${msg}\n`)
86
+ error: (msg: string) => log && log.write(`[js error] ${msg}\n`),
87
+ warn: (msg: string) => log && log.write(`[js warn] ${msg}\n`),
88
+ info: (msg: string) => log && log.write(`[js info] ${msg}\n`),
89
+ // The RPC Tracer configured below itself writes to this "log" level for every message it sends or receives,
90
+ // because the Tracer type has a log method on it that matches this signature.
91
+ log: (msg: string) => log && options.traceRpcMessages && log.write(`[js trace] ${msg}\n`)
123
92
  };
124
93
 
125
- logger.log(`starting`);
126
-
127
94
  // Create the connection with the custom logger
128
95
  const connection = rpc.createMessageConnection(
129
96
  new rpc.StreamMessageReader(process.stdin),
@@ -131,13 +98,13 @@ async function main() {
131
98
  logger
132
99
  );
133
100
 
134
- if (options.verbose) {
101
+ if (options.traceRpcMessages) {
135
102
  await connection.trace(rpc.Trace.Verbose, logger).catch((err: Error) => {
136
103
  // Handle any unexpected errors during trace configuration
137
104
  logger.error(`Failed to set trace: ${err}`);
138
105
  });
139
106
  } else {
140
- await connection.trace(Trace.Off, {} as Tracer);
107
+ await connection.trace(rpc.Trace.Off, {} as rpc.Tracer);
141
108
  }
142
109
 
143
110
  connection.onError(err => {
@@ -152,20 +119,18 @@ async function main() {
152
119
  logger.info(`connection disposed`);
153
120
  });
154
121
 
155
- RewriteRpc.set(new RewriteRpc(connection, {
156
- batchSize: options.batchSize,
157
- logger: logger,
158
- metricsCsv: options.metricsCsv,
159
- traceGetObjectInput: options.traceGetObjectInput ? log : undefined,
160
- traceGetObjectOutput: options.traceGetObjectOutput,
161
- recipeInstallDir: recipeInstallDir
162
- }));
163
-
164
122
  // log uncaught exceptions
165
123
  process.on('uncaughtException', (error) => {
166
124
  logger.error('Fatal error:' + error.message);
167
125
  process.exit(8);
168
126
  });
127
+
128
+ new RewriteRpc(connection, {
129
+ batchSize: options.batchSize,
130
+ logger: logger,
131
+ metricsCsv: options.metricsCsv,
132
+ recipeInstallDir: recipeInstallDir
133
+ });
169
134
  }
170
135
 
171
136
  main().catch(console.error);
package/src/rpc/trace.ts CHANGED
@@ -18,8 +18,8 @@ import {AsyncLocalStorage} from "node:async_hooks";
18
18
 
19
19
  const stackStorage = new AsyncLocalStorage<string>();
20
20
 
21
- export function saveTrace<T>(condition: any | undefined, action: () => Promise<T>): Promise<T> {
22
- if (condition) {
21
+ export function saveTrace<T>(enabled: boolean, action: () => Promise<T>): Promise<T> {
22
+ if (enabled) {
23
23
  const entryStack = new Error().stack ?? '';
24
24
  return stackStorage.run(entryStack, action);
25
25
  }
@@ -42,7 +42,7 @@ export function trace(type: "Receiver" | "Sender"): string | undefined {
42
42
  const codeLine = readFileSync(file, 'utf-8')
43
43
  .split('\n')[parseInt(line, 10) - 1]
44
44
  .trim();
45
- return `${className}:${line} => ${codeLine}`;
45
+ return `${className}:${line}:depth(${stack.length}) => ${codeLine}`;
46
46
  } catch {
47
47
  // ignore if reading the source file fails
48
48
  }