@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.
- package/dist/java/rpc.d.ts +2 -2
- package/dist/java/rpc.d.ts.map +1 -1
- package/dist/java/rpc.js +10 -4
- package/dist/java/rpc.js.map +1 -1
- package/dist/java/type.d.ts +1 -1
- package/dist/java/type.d.ts.map +1 -1
- package/dist/java/type.js +3 -3
- package/dist/java/type.js.map +1 -1
- package/dist/javascript/assertions.d.ts +1 -1
- package/dist/javascript/assertions.d.ts.map +1 -1
- package/dist/javascript/assertions.js +35 -65
- package/dist/javascript/assertions.js.map +1 -1
- package/dist/javascript/comparator.d.ts +2 -2
- package/dist/javascript/comparator.d.ts.map +1 -1
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/dependency-workspace.d.ts +44 -0
- package/dist/javascript/dependency-workspace.d.ts.map +1 -0
- package/dist/javascript/dependency-workspace.js +335 -0
- package/dist/javascript/dependency-workspace.js.map +1 -0
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +5 -2
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/preconditions.js +2 -2
- package/dist/javascript/preconditions.js.map +1 -1
- package/dist/javascript/templating.d.ts +110 -5
- package/dist/javascript/templating.d.ts.map +1 -1
- package/dist/javascript/templating.js +412 -38
- package/dist/javascript/templating.js.map +1 -1
- package/dist/javascript/type-mapping.js +2 -2
- package/dist/javascript/type-mapping.js.map +1 -1
- package/dist/rpc/queue.d.ts +1 -0
- package/dist/rpc/queue.d.ts.map +1 -1
- package/dist/rpc/queue.js +11 -1
- package/dist/rpc/queue.js.map +1 -1
- package/dist/rpc/request/install-recipes.d.ts.map +1 -1
- package/dist/rpc/request/install-recipes.js +116 -21
- package/dist/rpc/request/install-recipes.js.map +1 -1
- package/dist/rpc/server.d.ts.map +1 -1
- package/dist/rpc/server.js +5 -0
- package/dist/rpc/server.js.map +1 -1
- package/dist/test/rewrite-test.d.ts +1 -1
- package/dist/test/rewrite-test.d.ts.map +1 -1
- package/dist/test/rewrite-test.js +27 -5
- package/dist/test/rewrite-test.js.map +1 -1
- package/dist/version.txt +1 -1
- package/package.json +1 -1
- package/src/java/rpc.ts +4 -4
- package/src/java/type.ts +3 -3
- package/src/javascript/assertions.ts +14 -21
- package/src/javascript/comparator.ts +2 -2
- package/src/javascript/dependency-workspace.ts +317 -0
- package/src/javascript/parser.ts +6 -3
- package/src/javascript/preconditions.ts +2 -2
- package/src/javascript/templating.ts +535 -44
- package/src/javascript/type-mapping.ts +2 -2
- package/src/rpc/queue.ts +11 -1
- package/src/rpc/request/install-recipes.ts +127 -24
- package/src/rpc/server.ts +5 -0
- 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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
package/src/test/rewrite-test.ts
CHANGED
|
@@ -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
|
-
|
|
70
|
-
|
|
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>);
|