@openrewrite/rewrite 8.62.0 → 8.62.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 (97) hide show
  1. package/dist/java/rpc.d.ts +3 -3
  2. package/dist/java/rpc.d.ts.map +1 -1
  3. package/dist/java/rpc.js +6 -6
  4. package/dist/java/rpc.js.map +1 -1
  5. package/dist/java/tree.d.ts +31 -33
  6. package/dist/java/tree.d.ts.map +1 -1
  7. package/dist/java/tree.js +125 -7
  8. package/dist/java/tree.js.map +1 -1
  9. package/dist/java/type.d.ts +70 -57
  10. package/dist/java/type.d.ts.map +1 -1
  11. package/dist/java/type.js +454 -25
  12. package/dist/java/type.js.map +1 -1
  13. package/dist/java/visitor.d.ts +4 -4
  14. package/dist/java/visitor.d.ts.map +1 -1
  15. package/dist/java/visitor.js +34 -28
  16. package/dist/java/visitor.js.map +1 -1
  17. package/dist/javascript/assertions.d.ts +3 -0
  18. package/dist/javascript/assertions.d.ts.map +1 -1
  19. package/dist/javascript/assertions.js +78 -1
  20. package/dist/javascript/assertions.js.map +1 -1
  21. package/dist/javascript/format.js +4 -4
  22. package/dist/javascript/format.js.map +1 -1
  23. package/dist/javascript/parser.d.ts +1 -1
  24. package/dist/javascript/parser.d.ts.map +1 -1
  25. package/dist/javascript/parser.js +38 -11
  26. package/dist/javascript/parser.js.map +1 -1
  27. package/dist/javascript/rpc.js +1 -4
  28. package/dist/javascript/rpc.js.map +1 -1
  29. package/dist/javascript/tree.d.ts +33 -35
  30. package/dist/javascript/tree.d.ts.map +1 -1
  31. package/dist/javascript/tree.js +70 -17
  32. package/dist/javascript/tree.js.map +1 -1
  33. package/dist/javascript/type-mapping.d.ts +44 -8
  34. package/dist/javascript/type-mapping.d.ts.map +1 -1
  35. package/dist/javascript/type-mapping.js +571 -101
  36. package/dist/javascript/type-mapping.js.map +1 -1
  37. package/dist/javascript/visitor.d.ts +2 -2
  38. package/dist/javascript/visitor.d.ts.map +1 -1
  39. package/dist/javascript/visitor.js.map +1 -1
  40. package/dist/json/tree.d.ts +0 -2
  41. package/dist/json/tree.d.ts.map +1 -1
  42. package/dist/json/tree.js +12 -2
  43. package/dist/json/tree.js.map +1 -1
  44. package/dist/reference.js +1 -1
  45. package/dist/reference.js.map +1 -1
  46. package/dist/rpc/chrome-profiler.d.ts +25 -0
  47. package/dist/rpc/chrome-profiler.d.ts.map +1 -0
  48. package/dist/rpc/chrome-profiler.js +405 -0
  49. package/dist/rpc/chrome-profiler.js.map +1 -0
  50. package/dist/rpc/queue.d.ts +0 -3
  51. package/dist/rpc/queue.d.ts.map +1 -1
  52. package/dist/rpc/queue.js +6 -6
  53. package/dist/rpc/queue.js.map +1 -1
  54. package/dist/rpc/request/parse.d.ts.map +1 -1
  55. package/dist/rpc/request/parse.js.map +1 -1
  56. package/dist/rpc/rewrite-rpc.d.ts +1 -0
  57. package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
  58. package/dist/rpc/rewrite-rpc.js +1 -1
  59. package/dist/rpc/rewrite-rpc.js.map +1 -1
  60. package/dist/rpc/server.d.ts.map +1 -1
  61. package/dist/rpc/server.js +26 -1
  62. package/dist/rpc/server.js.map +1 -1
  63. package/dist/test/rewrite-test.d.ts +1 -1
  64. package/dist/test/rewrite-test.d.ts.map +1 -1
  65. package/dist/test/rewrite-test.js +18 -2
  66. package/dist/test/rewrite-test.js.map +1 -1
  67. package/dist/text/tree.d.ts +0 -2
  68. package/dist/text/tree.d.ts.map +1 -1
  69. package/dist/text/tree.js +4 -17
  70. package/dist/text/tree.js.map +1 -1
  71. package/dist/tree.d.ts +1 -0
  72. package/dist/tree.d.ts.map +1 -1
  73. package/dist/tree.js +14 -0
  74. package/dist/tree.js.map +1 -1
  75. package/dist/version.txt +1 -1
  76. package/package.json +3 -3
  77. package/src/java/rpc.ts +18 -15
  78. package/src/java/tree.ts +68 -38
  79. package/src/java/type.ts +475 -74
  80. package/src/java/visitor.ts +45 -39
  81. package/src/javascript/assertions.ts +52 -1
  82. package/src/javascript/format.ts +4 -4
  83. package/src/javascript/parser.ts +51 -18
  84. package/src/javascript/rpc.ts +8 -10
  85. package/src/javascript/tree.ts +34 -35
  86. package/src/javascript/type-mapping.ts +582 -47
  87. package/src/javascript/visitor.ts +5 -5
  88. package/src/json/tree.ts +1 -2
  89. package/src/reference.ts +1 -1
  90. package/src/rpc/chrome-profiler.ts +373 -0
  91. package/src/rpc/queue.ts +8 -12
  92. package/src/rpc/request/parse.ts +1 -1
  93. package/src/rpc/rewrite-rpc.ts +3 -2
  94. package/src/rpc/server.ts +30 -1
  95. package/src/test/rewrite-test.ts +19 -3
  96. package/src/text/tree.ts +0 -1
  97. package/src/tree.ts +15 -0
@@ -18,7 +18,7 @@
18
18
  import {mapAsync} from "../util";
19
19
  import {SourceFile} from "../tree";
20
20
  import {ValidImmerRecipeReturnType} from "../visitor";
21
- import {Expression, J, JavaType, JavaVisitor, NameTree, Statement, TypedTree} from "../java";
21
+ import {Expression, J, Type, JavaVisitor, NameTree, Statement, TypedTree} from "../java";
22
22
  import {createDraft, Draft, finishDraft} from "immer";
23
23
  import {isJavaScript, JS, JSX} from "./tree";
24
24
  import ComputedPropertyName = JS.ComputedPropertyName;
@@ -45,7 +45,7 @@ export class JavaScriptVisitor<P> extends JavaVisitor<P> {
45
45
  }
46
46
 
47
47
  // noinspection JSUnusedLocalSymbols
48
- protected override async visitType(javaType: JavaType | undefined, p: P): Promise<JavaType | undefined> {
48
+ protected override async visitType(javaType: Type | undefined, p: P): Promise<Type | undefined> {
49
49
  return javaType;
50
50
  }
51
51
 
@@ -254,7 +254,7 @@ export class JavaScriptVisitor<P> extends JavaVisitor<P> {
254
254
  draft.function = await this.visitOptionalRightPadded(functionCall.function, p);
255
255
  draft.typeParameters = await this.visitOptionalContainer(functionCall.typeParameters, p);
256
256
  draft.arguments = await this.visitContainer(functionCall.arguments, p);
257
- draft.functionType = await this.visitType(functionCall.functionType, p) as JavaType.Method | undefined;
257
+ draft.functionType = await this.visitType(functionCall.functionType, p) as Type.Method | undefined;
258
258
  });
259
259
  }
260
260
 
@@ -785,7 +785,7 @@ export class JavaScriptVisitor<P> extends JavaVisitor<P> {
785
785
  draft.name = await this.visitDefined<ComputedPropertyName>(computedPropMethod.name, p);
786
786
  draft.parameters = await this.visitContainer(computedPropMethod.parameters, p);
787
787
  draft.body = computedPropMethod.body && await this.visitDefined<J.Block>(computedPropMethod.body, p);
788
- draft.methodType = computedPropMethod.methodType && (await this.visitType(computedPropMethod.methodType, p) as JavaType.Method);
788
+ draft.methodType = computedPropMethod.methodType && (await this.visitType(computedPropMethod.methodType, p) as Type.Method);
789
789
  });
790
790
  }
791
791
 
@@ -842,7 +842,7 @@ export class JavaScriptVisitor<P> extends JavaVisitor<P> {
842
842
  draft.propertyName = bindingElement.propertyName && await this.visitRightPadded(bindingElement.propertyName, p);
843
843
  draft.name = await this.visitDefined<TypedTree>(bindingElement.name, p);
844
844
  draft.initializer = bindingElement.initializer && await this.visitLeftPadded(bindingElement.initializer, p);
845
- draft.variableType = bindingElement.variableType && (await this.visitType(bindingElement.variableType, p) as JavaType.Variable);
845
+ draft.variableType = bindingElement.variableType && (await this.visitType(bindingElement.variableType, p) as Type.Variable);
846
846
  });
847
847
  }
848
848
 
package/src/json/tree.ts CHANGED
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import {Markers} from "../markers";
17
- import {SourceFile, Tree, TreeKind} from "../tree";
17
+ import {SourceFile, Tree} from "../tree";
18
18
 
19
19
 
20
20
  export interface Json extends Tree {
@@ -23,7 +23,6 @@ export interface Json extends Tree {
23
23
 
24
24
  export namespace Json {
25
25
  export const Kind = {
26
- ...TreeKind,
27
26
  Array: "org.openrewrite.json.tree.Json$Array",
28
27
  Document: "org.openrewrite.json.tree.Json$Document",
29
28
  Empty: "org.openrewrite.json.tree.Json$Empty",
package/src/reference.ts CHANGED
@@ -44,7 +44,7 @@ export function asRef<T extends {} | undefined>(obj: T): T extends undefined ? u
44
44
  }
45
45
 
46
46
  export function isRef(obj: any): obj is Reference {
47
- return obj !== undefined && obj[REFERENCE_KEY] === true;
47
+ return obj !== undefined && obj !== null && obj[REFERENCE_KEY] === true;
48
48
  }
49
49
 
50
50
  export class ReferenceMap {
@@ -0,0 +1,373 @@
1
+ /*
2
+ * Copyright 2025 the original author or authors.
3
+ * <p>
4
+ * Licensed under the Moderne Source Available License (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ * <p>
8
+ * https://docs.moderne.io/licensing/moderne-source-available-license
9
+ * <p>
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import * as fs from 'fs';
17
+ import * as path from 'path';
18
+ import * as v8 from 'v8';
19
+ import * as inspector from 'inspector';
20
+
21
+ export class ChromeProfiler {
22
+ private traceEvents: any[] = [];
23
+ private memoryInterval?: NodeJS.Timeout;
24
+ private saveInterval?: NodeJS.Timeout;
25
+ private session?: inspector.Session;
26
+ private readonly pid = process.pid;
27
+ private readonly tid = 1; // Main thread
28
+ private readonly tracePath: string;
29
+ private exitHandlersRegistered = false;
30
+ private startTime: number = 0;
31
+ private ttsCounter: number = 0;
32
+ private lastProfileTime: number = 0;
33
+ private profileNodes = new Map();
34
+
35
+ constructor(outputPath?: string) {
36
+ this.tracePath = outputPath || path.join(process.cwd(), 'chrome-trace.json');
37
+ }
38
+
39
+ async start() {
40
+ this.startTime = Date.now() * 1000; // Convert to microseconds
41
+ this.lastProfileTime = this.startTime;
42
+
43
+ // Add initial metadata events
44
+ this.addMetadataEvents();
45
+
46
+ // Start V8 Inspector session for CPU profiling
47
+ this.session = new inspector.Session();
48
+ this.session.connect();
49
+
50
+ // Enable and start CPU profiling
51
+ await this.enableCpuProfiling();
52
+
53
+ // Start collecting memory data
54
+ this.startMemoryTracking();
55
+
56
+ // Save trace periodically and collect CPU profile samples
57
+ this.saveInterval = setInterval(async () => {
58
+ await this.collectCpuProfile();
59
+ this.saveTrace();
60
+ }, 10000); // Save every 10 seconds
61
+
62
+ // Register exit handlers
63
+ if (!this.exitHandlersRegistered) {
64
+ this.registerExitHandlers();
65
+ this.exitHandlersRegistered = true;
66
+ }
67
+ }
68
+
69
+ private async enableCpuProfiling() {
70
+ if (!this.session) return;
71
+
72
+ // Enable profiler
73
+ await new Promise<void>((resolve, reject) => {
74
+ this.session!.post('Profiler.enable', (err) => {
75
+ if (err) reject(err);
76
+ else resolve();
77
+ });
78
+ });
79
+
80
+ // Start sampling
81
+ await new Promise<void>((resolve, reject) => {
82
+ this.session!.post('Profiler.start', {
83
+ // Sample at high frequency for detailed profiling
84
+ samplingInterval: 100
85
+ }, (err) => {
86
+ if (err) reject(err);
87
+ else resolve();
88
+ });
89
+ });
90
+
91
+ // Subscribe to console profile events
92
+ this.session.on('Profiler.consoleProfileStarted', (params: any) => {
93
+ this.traceEvents.push({
94
+ cat: 'disabled-by-default-devtools.timeline',
95
+ name: 'Profile',
96
+ ph: 'P',
97
+ id: params.id || '1',
98
+ pid: this.pid,
99
+ tid: this.tid,
100
+ ts: Date.now() * 1000
101
+ });
102
+ });
103
+ }
104
+
105
+ private addMetadataEvents() {
106
+ // Thread name metadata
107
+ this.traceEvents.push({
108
+ args: { name: 'CrRendererMain' },
109
+ cat: '__metadata',
110
+ name: 'thread_name',
111
+ ph: 'M',
112
+ pid: this.pid,
113
+ tid: this.tid,
114
+ ts: 0
115
+ });
116
+
117
+ // Process name metadata
118
+ this.traceEvents.push({
119
+ args: { name: 'Node.js' },
120
+ cat: '__metadata',
121
+ name: 'process_name',
122
+ ph: 'M',
123
+ pid: this.pid,
124
+ tid: 0,
125
+ ts: 0
126
+ });
127
+
128
+ // TracingStartedInBrowser event - required for Chrome DevTools
129
+ this.traceEvents.push({
130
+ args: {
131
+ data: {
132
+ frameTreeNodeId: 1,
133
+ frames: [{
134
+ frame: '0x1',
135
+ name: '',
136
+ processId: this.pid,
137
+ url: 'node://process'
138
+ }],
139
+ persistentIds: true
140
+ }
141
+ },
142
+ cat: 'disabled-by-default-devtools.timeline',
143
+ name: 'TracingStartedInBrowser',
144
+ ph: 'I',
145
+ pid: this.pid,
146
+ s: 't',
147
+ tid: this.tid,
148
+ ts: this.startTime,
149
+ tts: this.ttsCounter++
150
+ });
151
+ }
152
+
153
+ private startMemoryTracking() {
154
+ // Collect memory data every 50ms (matching Chrome's frequency)
155
+ this.memoryInterval = setInterval(() => {
156
+ const memStats = v8.getHeapStatistics();
157
+ const timestamp = Date.now() * 1000;
158
+
159
+ // UpdateCounters event with correct format
160
+ this.traceEvents.push({
161
+ args: {
162
+ data: {
163
+ jsHeapSizeUsed: memStats.used_heap_size,
164
+ jsEventListeners: 0,
165
+ documents: 1,
166
+ nodes: 0
167
+ }
168
+ },
169
+ cat: 'disabled-by-default-devtools.timeline',
170
+ name: 'UpdateCounters',
171
+ ph: 'I', // Instant event, not Counter
172
+ pid: this.pid,
173
+ s: 't', // Required for instant events
174
+ tid: this.tid,
175
+ ts: timestamp,
176
+ tts: this.ttsCounter++
177
+ });
178
+ }, 50); // Every 50ms
179
+ }
180
+
181
+ private saveTrace() {
182
+ const trace = {
183
+ metadata: {
184
+ 'command-line': process.argv.join(' '),
185
+ 'cpu-brand': 'Node.js V8',
186
+ 'dataOrigin': 'TraceEvents',
187
+ 'highres-ticks': true,
188
+ 'hostname': 'localhost',
189
+ 'num-cpus': require('os').cpus().length,
190
+ 'physical-memory': require('os').totalmem(),
191
+ 'platform': process.platform,
192
+ 'process-uptime': process.uptime(),
193
+ 'product-version': `Node.js ${process.version}`,
194
+ 'protocol-version': '1.0',
195
+ 'source': 'NodeProfiler',
196
+ 'startTime': new Date(this.startTime / 1000).toISOString(),
197
+ 'trace-config': '',
198
+ 'user-agent': `Node.js/${process.version}`,
199
+ 'v8-version': process.versions.v8
200
+ },
201
+ traceEvents: this.traceEvents
202
+ };
203
+
204
+ try {
205
+ fs.writeFileSync(this.tracePath, JSON.stringify(trace, null, 2));
206
+ } catch (e) {
207
+ // Ignore write errors
208
+ }
209
+ }
210
+
211
+ private async collectCpuProfile() {
212
+ if (!this.session) return;
213
+
214
+ try {
215
+ // Stop current profiling to get samples
216
+ const profile = await new Promise<any>((resolve, reject) => {
217
+ this.session!.post('Profiler.stop', (err, params) => {
218
+ if (err) reject(err);
219
+ else resolve(params.profile);
220
+ });
221
+ });
222
+
223
+ // Convert CPU profile samples to trace events
224
+ if (profile && profile.samples) {
225
+ this.addCpuProfileSamples(profile);
226
+ }
227
+
228
+ // Restart profiling for the next interval
229
+ await new Promise<void>((resolve, reject) => {
230
+ this.session!.post('Profiler.start', {
231
+ samplingInterval: 100
232
+ }, (err) => {
233
+ if (err) reject(err);
234
+ else resolve();
235
+ });
236
+ });
237
+ } catch (e) {
238
+ // Ignore errors and try to restart
239
+ try {
240
+ await new Promise<void>((resolve, reject) => {
241
+ this.session!.post('Profiler.start', {
242
+ samplingInterval: 100
243
+ }, (err) => {
244
+ if (err) reject(err);
245
+ else resolve();
246
+ });
247
+ });
248
+ } catch (e2) {
249
+ // Ignore restart errors
250
+ }
251
+ }
252
+ }
253
+
254
+ async stop() {
255
+ // Clear intervals
256
+ if (this.memoryInterval) {
257
+ clearInterval(this.memoryInterval);
258
+ this.memoryInterval = undefined;
259
+ }
260
+ if (this.saveInterval) {
261
+ clearInterval(this.saveInterval);
262
+ this.saveInterval = undefined;
263
+ }
264
+
265
+ // Collect final CPU profile
266
+ await this.collectCpuProfile();
267
+
268
+ // Disconnect session
269
+ if (this.session) {
270
+ this.session.disconnect();
271
+ this.session = undefined;
272
+ }
273
+
274
+ // Save final trace
275
+ this.saveTrace();
276
+ }
277
+
278
+ private addCpuProfileSamples(profile: any) {
279
+ if (!profile.samples || !profile.timeDeltas || !profile.nodes) return;
280
+
281
+ // Use the last profile time as starting point to maintain continuity
282
+ let currentTime = this.lastProfileTime;
283
+
284
+ // Update nodes map with new nodes
285
+ profile.nodes.forEach((node: any) => {
286
+ this.profileNodes.set(node.id, node);
287
+ });
288
+
289
+ // Convert samples to trace events with actual function names
290
+ profile.samples.forEach((nodeId: number, index: number) => {
291
+ const node = this.profileNodes.get(nodeId);
292
+ if (!node) return;
293
+
294
+ currentTime += (profile.timeDeltas[index] || 0);
295
+
296
+ const callFrame = node.callFrame;
297
+ if (callFrame) {
298
+ // Clean up function name for display
299
+ let functionName = callFrame.functionName || '(anonymous)';
300
+ if (functionName === '' || functionName === '(root)') {
301
+ functionName = '(program)';
302
+ }
303
+
304
+ // Extract filename from URL or use a meaningful default
305
+ let url = callFrame.url || '';
306
+ let fileName: string;
307
+
308
+ if (url) {
309
+ // Clean up the URL for display
310
+ if (url.startsWith('file://')) {
311
+ url = url.substring(7);
312
+ }
313
+ const parts = url.split('/');
314
+ fileName = parts[parts.length - 1] || url;
315
+
316
+ // Special handling for node internals
317
+ if (url.startsWith('node:')) {
318
+ fileName = url;
319
+ }
320
+ } else {
321
+ // No URL - try to provide context from function name
322
+ if (functionName === '(garbage collector)') {
323
+ fileName = 'v8::gc';
324
+ } else if (functionName === '(idle)') {
325
+ fileName = 'v8::idle';
326
+ } else if (functionName === '(program)') {
327
+ fileName = 'main';
328
+ } else if (functionName.includes('::')) {
329
+ // C++ internal function
330
+ fileName = 'native';
331
+ } else {
332
+ // JavaScript code without source mapping
333
+ fileName = 'javascript';
334
+ }
335
+ }
336
+
337
+ this.traceEvents.push({
338
+ args: {
339
+ data: {
340
+ columnNumber: callFrame.columnNumber || 0,
341
+ frame: `0x${nodeId.toString(16)}`,
342
+ functionName: functionName,
343
+ lineNumber: callFrame.lineNumber || 0,
344
+ scriptId: String(callFrame.scriptId || 0),
345
+ url: url || fileName // Use fileName as URL if no real URL
346
+ }
347
+ },
348
+ cat: 'devtools.timeline',
349
+ dur: Math.max(profile.timeDeltas[index] || 1, 1),
350
+ name: 'FunctionCall',
351
+ ph: 'X',
352
+ pid: this.pid,
353
+ tid: this.tid,
354
+ ts: Math.floor(currentTime),
355
+ tts: this.ttsCounter++
356
+ });
357
+ }
358
+ });
359
+
360
+ // Update lastProfileTime to maintain continuity for the next batch
361
+ this.lastProfileTime = currentTime;
362
+ }
363
+
364
+ private registerExitHandlers() {
365
+ const cleanup = async () => {
366
+ await this.stop();
367
+ };
368
+
369
+ process.once('beforeExit', cleanup);
370
+ process.once('SIGINT', cleanup);
371
+ process.once('SIGTERM', cleanup);
372
+ }
373
+ }
package/src/rpc/queue.ts CHANGED
@@ -13,10 +13,10 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import {emptyMarkers, Marker, Markers, MarkersKind} from "../markers";
16
+ import {emptyMarkers, Markers} from "../markers";
17
17
  import {saveTrace, trace} from "./trace";
18
18
  import {createDraft, finishDraft} from "immer";
19
- import {asRef, isRef, Reference, ReferenceMap} from "../reference";
19
+ import {isRef, ReferenceMap} from "../reference";
20
20
  import {Writable} from "node:stream";
21
21
 
22
22
  /**
@@ -106,16 +106,6 @@ export class RpcSendQueue {
106
106
  this.q.push(d);
107
107
  }
108
108
 
109
- sendMarkers<T extends { markers: Markers }>(parent: T, markersFn: (parent: T) => any): Promise<void> {
110
- return this.getAndSend(parent, t2 => asRef(markersFn(t2)), async (markersRef: Markers & Reference) => {
111
- await this.getAndSend(markersRef, m => m.id);
112
- await this.getAndSendList(markersRef,
113
- (m) => m.markers,
114
- (marker: Marker) => marker.id
115
- );
116
- });
117
- }
118
-
119
109
  getAndSend<T, U>(parent: T,
120
110
  value: (parent: T) => U | undefined,
121
111
  onChange?: (value: U) => Promise<any>): Promise<void> {
@@ -298,6 +288,12 @@ export class RpcReceiveQueue {
298
288
  before = message.valueType === undefined ?
299
289
  message.value :
300
290
  this.newObj(message.valueType);
291
+ if (ref !== undefined) {
292
+ // For an object like JavaType that we will mutate in place rather than using
293
+ // immutable updates because of its cyclic nature, the before instance will ultimately
294
+ // be the same as the after instance below.
295
+ this.refs.set(ref, before);
296
+ }
301
297
  }
302
298
  // Intentional fall-through...
303
299
  case RpcObjectState.CHANGE:
@@ -16,7 +16,7 @@
16
16
  import * as rpc from "vscode-jsonrpc/node";
17
17
  import {ExecutionContext} from "../../execution";
18
18
  import {UUID} from "node:crypto";
19
- import {ParserInput, Parsers} from "../../parser";
19
+ import {Parser, ParserInput, Parsers} from "../../parser";
20
20
  import {randomId} from "../../uuid";
21
21
  import {produce} from "immer";
22
22
  import {SourceFile} from "../../tree";
@@ -115,7 +115,7 @@ export class RewriteRpc {
115
115
  );
116
116
  }, this.options.traceGetObjectInput);
117
117
 
118
- const remoteObject = await q.receive<P>(this.localObjects.get(id));
118
+ const remoteObject = await q.receive<P>(localObject);
119
119
 
120
120
  const eof = (await q.take());
121
121
  if (eof.state !== RpcObjectState.END_OF_OBJECT) {
@@ -152,7 +152,8 @@ export class RewriteRpc {
152
152
  }
153
153
 
154
154
  async print(tree: SourceFile): Promise<string>;
155
- async print<T extends Tree>(tree: T, cursor?: Cursor): Promise<string> {
155
+ async print(tree: Tree, cursor: Cursor): Promise<string>;
156
+ async print(tree: Tree, cursor?: Cursor): Promise<string> {
156
157
  if (!cursor && !isSourceFile(tree)) {
157
158
  throw new Error("Cursor is required for non-SourceFile trees");
158
159
  }
package/src/rpc/server.ts CHANGED
@@ -20,6 +20,7 @@ import {RewriteRpc} from "./rewrite-rpc";
20
20
  import * as fs from "fs";
21
21
  import {Command} from 'commander';
22
22
  import {dir} from 'tmp-promise';
23
+ import {ChromeProfiler} from './chrome-profiler';
23
24
 
24
25
  // Include all languages you want this server to support.
25
26
  import "../text";
@@ -28,7 +29,7 @@ import "../java";
28
29
  import "../javascript";
29
30
 
30
31
  // Not possible to set the stack size when executing from npx for security reasons
31
- require('v8').setFlagsFromString('--stack-size=4000');
32
+ require('v8').setFlagsFromString('--stack-size=8000');
32
33
 
33
34
  interface ProgramOptions {
34
35
  logFile?: string;
@@ -37,6 +38,7 @@ interface ProgramOptions {
37
38
  traceGetObjectOutput?: boolean;
38
39
  traceGetObjectInput?: boolean;
39
40
  recipeInstallDir?: string;
41
+ profile?: boolean;
40
42
  }
41
43
 
42
44
  async function main() {
@@ -49,10 +51,18 @@ async function main() {
49
51
  .option('--trace-get-object-output', 'enable `GetObject` output tracing')
50
52
  .option('--trace-get-object-input', 'enable `GetObject` input tracing')
51
53
  .option('--recipe-install-dir <install_dir>', 'Recipe installation directory (default is a temporary directory)')
54
+ .option('--profile', 'enable profiling')
52
55
  .parse();
53
56
 
54
57
  const options = program.opts() as ProgramOptions;
55
58
 
59
+ // Chrome profiling
60
+ let profiler: ChromeProfiler | undefined;
61
+ if (options.profile) {
62
+ profiler = new ChromeProfiler();
63
+ profiler.start().catch(console.error);
64
+ }
65
+
56
66
  let recipeInstallDir: string;
57
67
  if (!options.recipeInstallDir) {
58
68
  let recipeCleanup: () => Promise<void>;
@@ -65,6 +75,9 @@ async function main() {
65
75
 
66
76
  // Register cleanup on exit
67
77
  process.on('SIGINT', async () => {
78
+ if (profiler) {
79
+ await profiler.stop();
80
+ }
68
81
  if (recipeCleanup) {
69
82
  await recipeCleanup();
70
83
  }
@@ -72,6 +85,9 @@ async function main() {
72
85
  });
73
86
 
74
87
  process.on('SIGTERM', async () => {
88
+ if (profiler) {
89
+ await profiler.stop();
90
+ }
75
91
  if (recipeCleanup) {
76
92
  await recipeCleanup();
77
93
  }
@@ -81,6 +97,19 @@ async function main() {
81
97
  recipeInstallDir = await setupRecipeDir();
82
98
  } else {
83
99
  recipeInstallDir = options.recipeInstallDir;
100
+
101
+ // Register cleanup for profiler when no recipe cleanup is needed
102
+ if (profiler) {
103
+ process.on('SIGINT', async () => {
104
+ await profiler.stop();
105
+ process.exit(0);
106
+ });
107
+
108
+ process.on('SIGTERM', async () => {
109
+ await profiler.stop();
110
+ process.exit(0);
111
+ });
112
+ }
84
113
  }
85
114
 
86
115
  const log = options.logFile ? fs.createWriteStream(options.logFile, {flags: 'a'}) : undefined;
@@ -61,8 +61,20 @@ export class RecipeSpec {
61
61
  this.dataTableAssertions[name] = allRows;
62
62
  }
63
63
 
64
- async rewriteRun(...sourceSpecs: SourceSpec<any>[]): Promise<void> {
65
- const specsByKind = sourceSpecs.reduce((groups, spec) => {
64
+ async rewriteRun(...sourceSpecs: (SourceSpec<any> | Generator<SourceSpec<any>, void, unknown>)[]): Promise<void> {
65
+ // Flatten generators into a list of sourceSpecs
66
+ const flattenedSpecs: SourceSpec<any>[] = [];
67
+ for (const specOrGenerator of sourceSpecs) {
68
+ if (specOrGenerator && typeof (specOrGenerator as any).next === 'function') {
69
+ for (const spec of specOrGenerator as Generator<SourceSpec<any>, void, unknown>) {
70
+ flattenedSpecs.push(spec);
71
+ }
72
+ } else {
73
+ flattenedSpecs.push(specOrGenerator as SourceSpec<any>);
74
+ }
75
+ }
76
+
77
+ const specsByKind = flattenedSpecs.reduce((groups, spec) => {
66
78
  const kind = spec.kind;
67
79
  if (!groups[kind]) {
68
80
  groups[kind] = [];
@@ -179,7 +191,11 @@ export class RecipeSpec {
179
191
  const b = spec.beforeRecipe ? spec.beforeRecipe(sourceFile) : sourceFile;
180
192
  if (b !== undefined) {
181
193
  if (b instanceof Promise) {
182
- return [spec, await b];
194
+ const mapped = await b;
195
+ if (mapped === undefined) {
196
+ throw new Error("Expected beforeRecipe to return a SourceFile, but got undefined. Did you forget a return statement?");
197
+ }
198
+ return [spec, mapped];
183
199
  }
184
200
  return [spec, b as SourceFile];
185
201
  }
package/src/text/tree.ts CHANGED
@@ -23,7 +23,6 @@ export interface PlainText extends SourceFile {
23
23
 
24
24
  export namespace PlainText {
25
25
  export const Kind = {
26
- ...TreeKind,
27
26
  PlainText: "org.openrewrite.text.PlainText",
28
27
  Snippet: "org.openrewrite.text.PlainText$Snippet"
29
28
  } as const
package/src/tree.ts CHANGED
@@ -77,6 +77,21 @@ export class Cursor {
77
77
  return path;
78
78
  }
79
79
 
80
+ parentTree(level: number = 1): Cursor | undefined {
81
+ let c: Cursor | undefined = this.parent;
82
+ let treeCount = 0;
83
+ while (c) {
84
+ if (isTree(c.value)) {
85
+ treeCount++;
86
+ if (treeCount === level) {
87
+ return c;
88
+ }
89
+ }
90
+ c = c.parent;
91
+ }
92
+ return undefined;
93
+ }
94
+
80
95
  firstEnclosing<T>(match: (value: any) => value is T): T | undefined {
81
96
  let c: Cursor | undefined = this;
82
97
  while (c) {