@openrewrite/rewrite 8.63.2 → 8.63.4

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 (59) hide show
  1. package/dist/java/rpc.d.ts +2 -2
  2. package/dist/java/rpc.d.ts.map +1 -1
  3. package/dist/java/rpc.js +10 -4
  4. package/dist/java/rpc.js.map +1 -1
  5. package/dist/java/type.d.ts +1 -1
  6. package/dist/java/type.d.ts.map +1 -1
  7. package/dist/java/type.js +3 -3
  8. package/dist/java/type.js.map +1 -1
  9. package/dist/javascript/assertions.d.ts +1 -1
  10. package/dist/javascript/assertions.d.ts.map +1 -1
  11. package/dist/javascript/assertions.js +35 -65
  12. package/dist/javascript/assertions.js.map +1 -1
  13. package/dist/javascript/comparator.d.ts +2 -2
  14. package/dist/javascript/comparator.d.ts.map +1 -1
  15. package/dist/javascript/comparator.js.map +1 -1
  16. package/dist/javascript/dependency-workspace.d.ts +44 -0
  17. package/dist/javascript/dependency-workspace.d.ts.map +1 -0
  18. package/dist/javascript/dependency-workspace.js +335 -0
  19. package/dist/javascript/dependency-workspace.js.map +1 -0
  20. package/dist/javascript/parser.d.ts.map +1 -1
  21. package/dist/javascript/parser.js +5 -2
  22. package/dist/javascript/parser.js.map +1 -1
  23. package/dist/javascript/preconditions.js +2 -2
  24. package/dist/javascript/preconditions.js.map +1 -1
  25. package/dist/javascript/templating.d.ts +110 -5
  26. package/dist/javascript/templating.d.ts.map +1 -1
  27. package/dist/javascript/templating.js +412 -38
  28. package/dist/javascript/templating.js.map +1 -1
  29. package/dist/javascript/type-mapping.js +2 -2
  30. package/dist/javascript/type-mapping.js.map +1 -1
  31. package/dist/rpc/queue.d.ts +1 -0
  32. package/dist/rpc/queue.d.ts.map +1 -1
  33. package/dist/rpc/queue.js +11 -1
  34. package/dist/rpc/queue.js.map +1 -1
  35. package/dist/rpc/request/install-recipes.d.ts.map +1 -1
  36. package/dist/rpc/request/install-recipes.js +116 -21
  37. package/dist/rpc/request/install-recipes.js.map +1 -1
  38. package/dist/rpc/server.d.ts.map +1 -1
  39. package/dist/rpc/server.js +5 -0
  40. package/dist/rpc/server.js.map +1 -1
  41. package/dist/test/rewrite-test.d.ts +1 -1
  42. package/dist/test/rewrite-test.d.ts.map +1 -1
  43. package/dist/test/rewrite-test.js +27 -5
  44. package/dist/test/rewrite-test.js.map +1 -1
  45. package/dist/version.txt +1 -1
  46. package/package.json +1 -1
  47. package/src/java/rpc.ts +4 -4
  48. package/src/java/type.ts +3 -3
  49. package/src/javascript/assertions.ts +14 -21
  50. package/src/javascript/comparator.ts +2 -2
  51. package/src/javascript/dependency-workspace.ts +317 -0
  52. package/src/javascript/parser.ts +6 -3
  53. package/src/javascript/preconditions.ts +2 -2
  54. package/src/javascript/templating.ts +535 -44
  55. package/src/javascript/type-mapping.ts +2 -2
  56. package/src/rpc/queue.ts +11 -1
  57. package/src/rpc/request/install-recipes.ts +127 -24
  58. package/src/rpc/server.ts +5 -0
  59. package/src/test/rewrite-test.ts +11 -3
@@ -196,7 +196,7 @@ export class JavaScriptTypeMapping {
196
196
  private wrapperType(declaringType: (Type.FullyQualified & Type.Primitive) | Type.FullyQualified) {
197
197
  if (declaringType == Type.Primitive.String && this.stringWrapperType) {
198
198
  return this.getType(this.stringWrapperType) as Type.FullyQualified;
199
- } else if ((declaringType == Type.Primitive.Double || declaringType == Type.Primitive.Long) && this.numberWrapperType) {
199
+ } else if ((declaringType == Type.Primitive.Double || declaringType == Type.Primitive.BigInt) && this.numberWrapperType) {
200
200
  return this.getType(this.numberWrapperType) as Type.FullyQualified;
201
201
  } else if (declaringType == Type.Primitive.Boolean && this.booleanWrapperType) {
202
202
  return this.getType(this.booleanWrapperType) as Type.FullyQualified;
@@ -808,7 +808,7 @@ export class JavaScriptTypeMapping {
808
808
  type.flags === ts.TypeFlags.BigIntLiteral ||
809
809
  type.flags === ts.TypeFlags.BigIntLike
810
810
  ) {
811
- return Type.Primitive.Long;
811
+ return Type.Primitive.BigInt;
812
812
  } else if (
813
813
  (type.symbol !== undefined && type.symbol === this.regExpSymbol) ||
814
814
  this.checker.typeToString(type) === "RegExp"
package/src/rpc/queue.ts CHANGED
@@ -150,7 +150,8 @@ export class RpcSendQueue {
150
150
  return saveTrace(this.trace, async () => {
151
151
  if (before === after) {
152
152
  this.put({state: RpcObjectState.NO_CHANGE});
153
- } else if (before === undefined) {
153
+ } else if (before === undefined || (after !== undefined && this.typesAreDifferent(after, before))) {
154
+ // Treat as ADD when before is undefined OR types differ (it's a new object, not a change)
154
155
  await this.add(after, onChange);
155
156
  } else if (after === undefined) {
156
157
  this.put({state: RpcObjectState.DELETE});
@@ -182,6 +183,9 @@ export class RpcSendQueue {
182
183
  const aBefore = before ? before[beforePos] : undefined;
183
184
  if (aBefore === anAfter) {
184
185
  this.put({state: RpcObjectState.NO_CHANGE});
186
+ } else if (anAfter !== undefined && this.typesAreDifferent(anAfter, aBefore)) {
187
+ // Type changed - treat as ADD
188
+ await this.add(anAfter, onChangeRun);
185
189
  } else {
186
190
  this.put({state: RpcObjectState.CHANGE});
187
191
  await this.doChange(anAfter, aBefore, onChangeRun, RpcCodecs.forInstance(anAfter, this.sourceFileType));
@@ -248,6 +252,12 @@ export class RpcSendQueue {
248
252
  }
249
253
  }
250
254
 
255
+ private typesAreDifferent(after: any, before: any): boolean {
256
+ const afterKind = after !== undefined && after !== null && typeof after === "object" ? after["kind"] : undefined;
257
+ const beforeKind = before !== undefined && before !== null && typeof before === "object" ? before["kind"] : undefined;
258
+ return afterKind !== undefined && beforeKind !== undefined && afterKind !== beforeKind;
259
+ }
260
+
251
261
  private getValueType(after?: any): string | undefined {
252
262
  if (after !== undefined && after !== null && typeof after === "object" && "kind" in after) {
253
263
  return after["kind"];
@@ -118,7 +118,11 @@ export class InstallRecipes {
118
118
 
119
119
  let recipeModule;
120
120
  try {
121
- setupSharedDependencies(resolvedPath);
121
+ // Pre-load core modules that are used by recipes but loaded lazily
122
+ // This ensures they're in require.cache before setupSharedDependencies runs
123
+ preloadCoreModules(logger);
124
+
125
+ setupSharedDependencies(resolvedPath, logger);
122
126
  recipeModule = require(resolvedPath);
123
127
  } catch (e: any) {
124
128
  throw new Error(`Failed to load recipe module from ${resolvedPath}: ${e.stack}`);
@@ -142,38 +146,137 @@ export class InstallRecipes {
142
146
  }
143
147
  }
144
148
 
149
+ /**
150
+ * Pre-loads core modules that are typically loaded lazily by recipes.
151
+ * This ensures they're in require.cache before setupSharedDependencies runs,
152
+ * so they can be properly mapped to avoid instanceof failures.
153
+ */
154
+ function preloadCoreModules(logger?: rpc.Logger) {
155
+ const modulesToPreload = [
156
+ '../..',
157
+ '../../java',
158
+ '../../javascript',
159
+ '../../json',
160
+ '../../rpc',
161
+ '../../search',
162
+ '../../text',
163
+ ];
164
+
165
+ modulesToPreload.forEach(modulePath => {
166
+ try {
167
+ require(modulePath);
168
+ if (logger) {
169
+ logger.info(`[preloadCoreModules] Loaded ${modulePath}`);
170
+ }
171
+ } catch (e) {
172
+ if (logger) {
173
+ logger.warn(`[preloadCoreModules] Failed to load ${modulePath}: ${e}`);
174
+ }
175
+ }
176
+ });
177
+ }
178
+
145
179
  /**
146
180
  * Ensures dynamically loaded modules share the same class instances as the host
147
181
  * by mapping require.cache entries. This prevents instanceof failures when the
148
182
  * same package is installed in multiple node_modules directories.
149
183
  */
150
- function setupSharedDependencies(targetModulePath: string) {
184
+ function setupSharedDependencies(targetModulePath: string, logger?: rpc.Logger) {
151
185
  const sharedDeps = ['@openrewrite/rewrite', 'vscode-jsonrpc'];
152
186
  const targetDir = path.dirname(targetModulePath);
153
187
 
154
188
  sharedDeps.forEach(depName => {
155
- const depPattern = path.sep + 'node_modules' + path.sep + depName.replace('/', path.sep);
156
-
157
- for (const cachedPath of Object.keys(require.cache)) {
158
- if (!cachedPath.includes(depPattern)) continue;
159
-
160
- try {
161
- // Extract subpath: /path/node_modules/@pkg/dist/tree.js -> dist/tree.js
162
- const pkgIndex = cachedPath.indexOf(depPattern);
163
- let subpath = cachedPath.substring(pkgIndex + depPattern.length)
164
- .replace(/^[/\\]/, '') // Remove leading slash
165
- .replace(/\.(js|ts)$/, '') // Remove extension
166
- .replace(/^dist[/\\]/, '') // Remove dist/ prefix if present
167
- .replace(/[/\\]index$/, ''); // Remove /index suffix
168
-
169
- // Build require path: @pkg or @pkg/subpath
170
- const requirePath = subpath ? `${depName}/${subpath}` : depName;
171
-
172
- // Resolve from target's perspective and map cache
173
- const targetDepPath = require.resolve(requirePath, {paths: [targetDir]});
174
- require.cache[targetDepPath] = require.cache[cachedPath];
175
- } catch (e) {
176
- // Target can't resolve this path, skip
189
+ try {
190
+ // Step 1: Find where this package is currently loaded from (host)
191
+ const hostPackageEntry = require.resolve(depName);
192
+
193
+ // Step 2: Find the package root by looking for package.json
194
+ let hostPackageRoot = path.dirname(hostPackageEntry);
195
+ while (hostPackageRoot !== path.dirname(hostPackageRoot)) {
196
+ const packageJsonPath = path.join(hostPackageRoot, 'package.json');
197
+ if (fs.existsSync(packageJsonPath)) {
198
+ try {
199
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
200
+ if (pkg.name === depName) {
201
+ break; // Found the package root
202
+ }
203
+ } catch (e) {
204
+ // Not a valid package.json, continue
205
+ }
206
+ }
207
+ hostPackageRoot = path.dirname(hostPackageRoot);
208
+ }
209
+
210
+ if (logger) {
211
+ logger.info(`[setupSharedDependencies] Host package root for ${depName}: ${hostPackageRoot}`);
212
+ }
213
+
214
+ // Step 3: Find where the target's node_modules has this package
215
+ // We explicitly look in node_modules to avoid finding npm-linked global packages
216
+ let targetPackageRoot: string | undefined;
217
+
218
+ // Walk up from targetDir looking for node_modules containing this package
219
+ let searchDir = targetDir;
220
+ while (searchDir !== path.dirname(searchDir)) {
221
+ const nodeModulesPath = path.join(searchDir, 'node_modules', ...depName.split('/'));
222
+ if (fs.existsSync(nodeModulesPath)) {
223
+ const packageJsonPath = path.join(nodeModulesPath, 'package.json');
224
+ if (fs.existsSync(packageJsonPath)) {
225
+ try {
226
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
227
+ if (pkg.name === depName) {
228
+ targetPackageRoot = nodeModulesPath;
229
+ break;
230
+ }
231
+ } catch (e) {
232
+ // Not a valid package.json, continue
233
+ }
234
+ }
235
+ }
236
+ searchDir = path.dirname(searchDir);
237
+ }
238
+
239
+ if (!targetPackageRoot) {
240
+ if (logger) {
241
+ logger.warn(`[setupSharedDependencies] Could not find ${depName} in target's node_modules`);
242
+ }
243
+ return; // Can't map this package
244
+ }
245
+
246
+ if (logger) {
247
+ logger.info(`[setupSharedDependencies] Target package root for ${depName}: ${targetPackageRoot}`);
248
+ }
249
+
250
+ // If they're the same, no mapping needed
251
+ if (hostPackageRoot === targetPackageRoot) {
252
+ if (logger) {
253
+ logger.info(`[setupSharedDependencies] Same package root, no mapping needed for ${depName}`);
254
+ }
255
+ return;
256
+ }
257
+
258
+ // Step 4: Map all cached modules from host package to target package
259
+ const hostPrefix = hostPackageRoot + path.sep;
260
+
261
+ let mappedCount = 0;
262
+ for (const cachedPath of Object.keys(require.cache)) {
263
+ if (cachedPath.startsWith(hostPrefix)) {
264
+ // This module belongs to the host package
265
+ const relativePath = cachedPath.substring(hostPrefix.length);
266
+ const targetPath = path.join(targetPackageRoot, relativePath);
267
+
268
+ // Map the target path to use the host's cached module
269
+ require.cache[targetPath] = require.cache[cachedPath];
270
+ mappedCount++;
271
+ }
272
+ }
273
+
274
+ if (logger) {
275
+ logger.info(`[setupSharedDependencies] Mapped ${mappedCount} modules for ${depName}`);
276
+ }
277
+ } catch (e) {
278
+ if (logger) {
279
+ logger.error(`[setupSharedDependencies] Failed to setup ${depName}: ${e}`);
177
280
  }
178
281
  }
179
282
  });
package/src/rpc/server.ts CHANGED
@@ -19,6 +19,7 @@ import {RewriteRpc} from "./rewrite-rpc";
19
19
  import * as fs from "fs";
20
20
  import {Command} from 'commander';
21
21
  import {dir} from 'tmp-promise';
22
+ import {DependencyWorkspace} from "../javascript/dependency-workspace";
22
23
 
23
24
  // Include all languages you want this server to support.
24
25
  import "../text";
@@ -66,6 +67,8 @@ async function main() {
66
67
  if (recipeCleanup) {
67
68
  await recipeCleanup();
68
69
  }
70
+ // Clean up old dependency workspaces (older than 24 hours)
71
+ DependencyWorkspace.cleanupOldWorkspaces();
69
72
  process.exit(0);
70
73
  });
71
74
 
@@ -73,6 +76,8 @@ async function main() {
73
76
  if (recipeCleanup) {
74
77
  await recipeCleanup();
75
78
  }
79
+ // Clean up old dependency workspaces (older than 24 hours)
80
+ DependencyWorkspace.cleanupOldWorkspaces();
76
81
  process.exit(0);
77
82
  });
78
83
 
@@ -61,13 +61,21 @@ export class RecipeSpec {
61
61
  this.dataTableAssertions[name] = allRows;
62
62
  }
63
63
 
64
- async rewriteRun(...sourceSpecs: (SourceSpec<any> | Generator<SourceSpec<any>, void, unknown>)[]): Promise<void> {
64
+ async rewriteRun(...sourceSpecs: (SourceSpec<any> | Generator<SourceSpec<any>, void, unknown> | AsyncGenerator<SourceSpec<any>, void, unknown>)[]): Promise<void> {
65
65
  // Flatten generators into a list of sourceSpecs
66
66
  const flattenedSpecs: SourceSpec<any>[] = [];
67
67
  for (const specOrGenerator of sourceSpecs) {
68
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);
69
+ // Check if it's an async generator
70
+ if (typeof (specOrGenerator as any)[Symbol.asyncIterator] === 'function') {
71
+ for await (const spec of specOrGenerator as AsyncGenerator<SourceSpec<any>, void, unknown>) {
72
+ flattenedSpecs.push(spec);
73
+ }
74
+ } else {
75
+ // Sync generator
76
+ for (const spec of specOrGenerator as Generator<SourceSpec<any>, void, unknown>) {
77
+ flattenedSpecs.push(spec);
78
+ }
71
79
  }
72
80
  } else {
73
81
  flattenedSpecs.push(specOrGenerator as SourceSpec<any>);