@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.
- package/dist/java/index.d.ts +1 -0
- package/dist/java/index.d.ts.map +1 -1
- package/dist/java/index.js +1 -0
- package/dist/java/index.js.map +1 -1
- package/dist/java/rpc.d.ts +2 -0
- package/dist/java/rpc.d.ts.map +1 -1
- package/dist/java/rpc.js +749 -410
- package/dist/java/rpc.js.map +1 -1
- package/dist/java/tree.d.ts +1 -1
- package/dist/java/tree.d.ts.map +1 -1
- package/dist/java/type-visitor.d.ts +48 -0
- package/dist/java/type-visitor.d.ts.map +1 -0
- package/dist/java/type-visitor.js +260 -0
- package/dist/java/type-visitor.js.map +1 -0
- package/dist/java/type.d.ts +2 -0
- package/dist/java/type.d.ts.map +1 -1
- package/dist/java/type.js +0 -317
- package/dist/java/type.js.map +1 -1
- package/dist/java/visitor.d.ts.map +1 -1
- package/dist/java/visitor.js +579 -363
- package/dist/java/visitor.js.map +1 -1
- package/dist/javascript/preconditions.d.ts +1 -1
- package/dist/javascript/preconditions.d.ts.map +1 -1
- package/dist/javascript/preconditions.js +7 -6
- package/dist/javascript/preconditions.js.map +1 -1
- package/dist/javascript/rpc.js +430 -350
- package/dist/javascript/rpc.js.map +1 -1
- package/dist/javascript/tree.d.ts +1 -1
- package/dist/javascript/tree.d.ts.map +1 -1
- package/dist/javascript/type-mapping.d.ts.map +1 -1
- package/dist/javascript/type-mapping.js +7 -0
- package/dist/javascript/type-mapping.js.map +1 -1
- package/dist/javascript/visitor.d.ts.map +1 -1
- package/dist/javascript/visitor.js +504 -309
- package/dist/javascript/visitor.js.map +1 -1
- package/dist/json/visitor.d.ts.map +1 -1
- package/dist/json/visitor.js +46 -21
- package/dist/json/visitor.js.map +1 -1
- package/dist/rpc/queue.d.ts +7 -4
- package/dist/rpc/queue.d.ts.map +1 -1
- package/dist/rpc/queue.js +22 -32
- package/dist/rpc/queue.js.map +1 -1
- package/dist/rpc/request/generate.d.ts.map +1 -1
- package/dist/rpc/request/generate.js +2 -3
- package/dist/rpc/request/generate.js.map +1 -1
- package/dist/rpc/request/get-languages.d.ts.map +1 -1
- package/dist/rpc/request/get-languages.js +4 -3
- package/dist/rpc/request/get-languages.js.map +1 -1
- package/dist/rpc/request/get-object.d.ts +1 -1
- package/dist/rpc/request/get-object.d.ts.map +1 -1
- package/dist/rpc/request/get-object.js +8 -7
- package/dist/rpc/request/get-object.js.map +1 -1
- package/dist/rpc/request/get-recipes.d.ts.map +1 -1
- package/dist/rpc/request/get-recipes.js +2 -2
- package/dist/rpc/request/get-recipes.js.map +1 -1
- package/dist/rpc/request/index.d.ts +1 -0
- package/dist/rpc/request/index.d.ts.map +1 -1
- package/dist/rpc/request/index.js +1 -0
- package/dist/rpc/request/index.js.map +1 -1
- package/dist/rpc/request/install-recipes.d.ts.map +1 -1
- package/dist/rpc/request/install-recipes.js +30 -21
- package/dist/rpc/request/install-recipes.js.map +1 -1
- package/dist/rpc/request/metrics.d.ts +10 -10
- package/dist/rpc/request/metrics.d.ts.map +1 -1
- package/dist/rpc/request/metrics.js +38 -31
- package/dist/rpc/request/metrics.js.map +1 -1
- package/dist/rpc/request/parse.d.ts.map +1 -1
- package/dist/rpc/request/parse.js +10 -12
- package/dist/rpc/request/parse.js.map +1 -1
- package/dist/rpc/request/prepare-recipe.d.ts.map +1 -1
- package/dist/rpc/request/prepare-recipe.js +4 -4
- package/dist/rpc/request/prepare-recipe.js.map +1 -1
- package/dist/rpc/request/print.d.ts +1 -1
- package/dist/rpc/request/print.d.ts.map +1 -1
- package/dist/rpc/request/print.js +10 -6
- package/dist/rpc/request/print.js.map +1 -1
- package/dist/rpc/request/trace-get-object.d.ts +5 -0
- package/dist/rpc/request/trace-get-object.d.ts.map +1 -0
- package/dist/rpc/request/trace-get-object.js +3 -0
- package/dist/rpc/request/trace-get-object.js.map +1 -0
- package/dist/rpc/request/visit.d.ts.map +1 -1
- package/dist/rpc/request/visit.js +2 -3
- package/dist/rpc/request/visit.js.map +1 -1
- package/dist/rpc/rewrite-rpc.d.ts +2 -3
- package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
- package/dist/rpc/rewrite-rpc.js +14 -5
- package/dist/rpc/rewrite-rpc.js.map +1 -1
- package/dist/rpc/server.d.ts.map +1 -1
- package/dist/rpc/server.js +15 -44
- package/dist/rpc/server.js.map +1 -1
- package/dist/rpc/trace.d.ts +1 -1
- package/dist/rpc/trace.d.ts.map +1 -1
- package/dist/rpc/trace.js +3 -3
- package/dist/rpc/trace.js.map +1 -1
- package/dist/util.d.ts +6 -0
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +14 -0
- package/dist/util.js.map +1 -1
- package/dist/version.txt +1 -1
- package/package.json +1 -1
- package/src/java/index.ts +1 -0
- package/src/java/rpc.ts +726 -537
- package/src/java/tree.ts +1 -1
- package/src/java/type-visitor.ts +241 -0
- package/src/java/type.ts +13 -277
- package/src/java/visitor.ts +581 -378
- package/src/javascript/preconditions.ts +7 -6
- package/src/javascript/rpc.ts +431 -360
- package/src/javascript/tree.ts +1 -1
- package/src/javascript/type-mapping.ts +7 -0
- package/src/javascript/visitor.ts +505 -310
- package/src/json/visitor.ts +47 -22
- package/src/rpc/queue.ts +20 -17
- package/src/rpc/request/generate.ts +31 -25
- package/src/rpc/request/get-languages.ts +18 -10
- package/src/rpc/request/get-object.ts +42 -34
- package/src/rpc/request/get-recipes.ts +15 -8
- package/src/rpc/request/index.ts +1 -0
- package/src/rpc/request/install-recipes.ts +96 -79
- package/src/rpc/request/metrics.ts +54 -48
- package/src/rpc/request/parse.ts +31 -25
- package/src/rpc/request/prepare-recipe.ts +31 -23
- package/src/rpc/request/print.ts +28 -14
- package/src/rpc/request/trace-get-object.ts +4 -0
- package/src/rpc/request/visit.ts +22 -16
- package/src/rpc/rewrite-rpc.ts +23 -10
- package/src/rpc/server.ts +17 -52
- package/src/rpc/trace.ts +3 -3
- package/src/util.ts +14 -0
- package/dist/rpc/chrome-profiler.d.ts +0 -25
- package/dist/rpc/chrome-profiler.d.ts.map +0 -1
- package/dist/rpc/chrome-profiler.js +0 -405
- package/dist/rpc/chrome-profiler.js.map +0 -1
- 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
|
|
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
|
-
* @
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
): (
|
|
120
|
-
return
|
|
121
|
-
|
|
122
|
-
|
|
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
|
|
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
|
-
* @
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
): (
|
|
140
|
-
return (
|
|
141
|
-
|
|
142
|
-
|
|
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
|
|
package/src/rpc/request/parse.ts
CHANGED
|
@@ -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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const result: UUID[] = [];
|
|
43
|
+
const parser = Parsers.createParser("javascript", {
|
|
44
|
+
ctx: new ExecutionContext(),
|
|
45
|
+
relativeTo: request.relativeTo
|
|
46
|
+
})!;
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
46
|
-
|
|
49
|
+
const editPreconditions: Precondition[] = [];
|
|
50
|
+
recipe = await this.optimizePreconditions(recipe, "edit", editPreconditions);
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
const scanPreconditions: Precondition[] = [];
|
|
53
|
+
recipe = await this.optimizePreconditions(recipe, "scan", scanPreconditions);
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
preparedRecipes.set(id, recipe);
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
/**
|
package/src/rpc/request/print.ts
CHANGED
|
@@ -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 {
|
|
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,
|
|
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
|
|
35
|
+
getObject: (id: string, sourceFileType: string) => any,
|
|
36
|
+
logger?: rpc.Logger,
|
|
34
37
|
metricsCsv?: string): void {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
}
|
package/src/rpc/request/visit.ts
CHANGED
|
@@ -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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
60
|
-
|
|
63
|
+
return {modified: before !== after};
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
);
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
private static async instantiateVisitor(request: Visit,
|
package/src/rpc/rewrite-rpc.ts
CHANGED
|
@@ -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,
|
|
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,
|
|
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.
|
|
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,
|
|
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
|
-
|
|
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('-
|
|
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
|
|
120
|
-
warn: (msg: string) => log && log.write(`[js
|
|
121
|
-
info: (msg: string) => log &&
|
|
122
|
-
|
|
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.
|
|
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>(
|
|
22
|
-
if (
|
|
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
|
}
|