@openrewrite/rewrite 8.70.3 → 8.70.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/javascript/add-import.d.ts +5 -0
- package/dist/javascript/add-import.d.ts.map +1 -1
- package/dist/javascript/add-import.js +22 -9
- package/dist/javascript/add-import.js.map +1 -1
- package/dist/javascript/assertions.d.ts.map +1 -1
- package/dist/javascript/assertions.js +45 -13
- package/dist/javascript/assertions.js.map +1 -1
- package/dist/javascript/dependency-workspace.d.ts +5 -0
- package/dist/javascript/dependency-workspace.d.ts.map +1 -1
- package/dist/javascript/dependency-workspace.js +47 -13
- package/dist/javascript/dependency-workspace.js.map +1 -1
- package/dist/javascript/package-json-parser.d.ts +24 -0
- package/dist/javascript/package-json-parser.d.ts.map +1 -1
- package/dist/javascript/package-json-parser.js +137 -31
- package/dist/javascript/package-json-parser.js.map +1 -1
- package/dist/javascript/package-manager.d.ts +45 -7
- package/dist/javascript/package-manager.d.ts.map +1 -1
- package/dist/javascript/package-manager.js +83 -45
- package/dist/javascript/package-manager.js.map +1 -1
- package/dist/javascript/recipes/add-dependency.d.ts +7 -3
- package/dist/javascript/recipes/add-dependency.d.ts.map +1 -1
- package/dist/javascript/recipes/add-dependency.js +71 -13
- package/dist/javascript/recipes/add-dependency.js.map +1 -1
- package/dist/javascript/recipes/upgrade-dependency-version.d.ts +7 -3
- package/dist/javascript/recipes/upgrade-dependency-version.d.ts.map +1 -1
- package/dist/javascript/recipes/upgrade-dependency-version.js +71 -13
- package/dist/javascript/recipes/upgrade-dependency-version.js.map +1 -1
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.d.ts +7 -3
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.d.ts.map +1 -1
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.js +71 -13
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.js.map +1 -1
- package/dist/path-utils.d.ts +45 -0
- package/dist/path-utils.d.ts.map +1 -0
- package/dist/path-utils.js +210 -0
- package/dist/path-utils.js.map +1 -0
- package/dist/version.txt +1 -1
- package/package.json +1 -1
- package/src/javascript/add-import.ts +28 -7
- package/src/javascript/assertions.ts +48 -6
- package/src/javascript/dependency-workspace.ts +66 -13
- package/src/javascript/package-json-parser.ts +169 -39
- package/src/javascript/package-manager.ts +120 -52
- package/src/javascript/recipes/add-dependency.ts +89 -17
- package/src/javascript/recipes/upgrade-dependency-version.ts +89 -17
- package/src/javascript/recipes/upgrade-transitive-dependency-version.ts +89 -17
- package/src/path-utils.ts +208 -0
|
@@ -29,6 +29,8 @@ import * as fsp from "fs/promises";
|
|
|
29
29
|
import * as path from "path";
|
|
30
30
|
import * as YAML from "yaml";
|
|
31
31
|
import {getLockFileDetectionConfig, runList} from "./package-manager";
|
|
32
|
+
import picomatch from "picomatch";
|
|
33
|
+
import {DEFAULT_DIR_EXCLUSIONS, walkDirs} from "../path-utils";
|
|
32
34
|
|
|
33
35
|
/**
|
|
34
36
|
* Bun.lock package entry metadata.
|
|
@@ -140,52 +142,42 @@ export class PackageJsonParser extends Parser {
|
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
async *parse(...inputs: ParserInput[]): AsyncGenerator<SourceFile> {
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
+
// Cache markers by directory to share NodeResolutionResult markers
|
|
146
|
+
// but maintain input order for output
|
|
147
|
+
const markersByDir = new Map<string, NodeResolutionResult | null>();
|
|
145
148
|
|
|
149
|
+
// Process each input in order, caching markers per directory
|
|
146
150
|
for (const input of inputs) {
|
|
147
151
|
const filePath = parserInputFile(input);
|
|
148
152
|
const dir = path.dirname(filePath);
|
|
149
153
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
inputsByDir.get(dir)!.push(input);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Process each directory's package.json files
|
|
157
|
-
for (const [dir, dirInputs] of inputsByDir) {
|
|
158
|
-
// Create a shared marker for this directory
|
|
159
|
-
let marker: NodeResolutionResult | null = null;
|
|
160
|
-
|
|
161
|
-
for (const input of dirInputs) {
|
|
162
|
-
// Parse as JSON first
|
|
163
|
-
const jsonGenerator = this.jsonParser.parse(input);
|
|
164
|
-
const jsonResult = await jsonGenerator.next();
|
|
165
|
-
|
|
166
|
-
if (jsonResult.done || !jsonResult.value) {
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
154
|
+
// Parse as JSON first
|
|
155
|
+
const jsonGenerator = this.jsonParser.parse(input);
|
|
156
|
+
const jsonResult = await jsonGenerator.next();
|
|
169
157
|
|
|
170
|
-
|
|
158
|
+
if (jsonResult.done || !jsonResult.value) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
171
161
|
|
|
172
|
-
|
|
173
|
-
if (!marker) {
|
|
174
|
-
marker = await this.createMarker(input, dir);
|
|
175
|
-
}
|
|
162
|
+
const jsonDoc = jsonResult.value as Json.Document;
|
|
176
163
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
164
|
+
// Create NodeResolutionResult marker if not already created for this directory
|
|
165
|
+
if (!markersByDir.has(dir)) {
|
|
166
|
+
markersByDir.set(dir, await this.createMarker(input, dir));
|
|
167
|
+
}
|
|
168
|
+
const marker = markersByDir.get(dir)!;
|
|
169
|
+
|
|
170
|
+
// Attach the marker to the JSON document
|
|
171
|
+
if (marker) {
|
|
172
|
+
yield {
|
|
173
|
+
...jsonDoc,
|
|
174
|
+
markers: {
|
|
175
|
+
...jsonDoc.markers,
|
|
176
|
+
markers: [...jsonDoc.markers.markers, marker]
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
} else {
|
|
180
|
+
yield jsonDoc;
|
|
189
181
|
}
|
|
190
182
|
}
|
|
191
183
|
}
|
|
@@ -222,11 +214,24 @@ export class PackageJsonParser extends Parser {
|
|
|
222
214
|
const projectDir = this.relativeTo || dir;
|
|
223
215
|
const npmrcConfigs = await readNpmrcConfigs(projectDir);
|
|
224
216
|
|
|
217
|
+
// Detect workspace member paths if this is a workspace root
|
|
218
|
+
let workspacePackagePaths: string[] | undefined;
|
|
219
|
+
if (packageJson.workspaces) {
|
|
220
|
+
const absoluteDir = this.relativeTo && !path.isAbsolute(dir)
|
|
221
|
+
? path.resolve(this.relativeTo, dir)
|
|
222
|
+
: dir;
|
|
223
|
+
workspacePackagePaths = await this.resolveWorkspacePackagePaths(
|
|
224
|
+
packageJson.workspaces,
|
|
225
|
+
absoluteDir,
|
|
226
|
+
this.relativeTo
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
225
230
|
return createNodeResolutionResultMarker(
|
|
226
231
|
relativePath,
|
|
227
232
|
packageJson,
|
|
228
233
|
lockContent,
|
|
229
|
-
|
|
234
|
+
workspacePackagePaths,
|
|
230
235
|
packageManager,
|
|
231
236
|
npmrcConfigs.length > 0 ? npmrcConfigs : undefined
|
|
232
237
|
);
|
|
@@ -236,6 +241,131 @@ export class PackageJsonParser extends Parser {
|
|
|
236
241
|
}
|
|
237
242
|
}
|
|
238
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Resolves workspace glob patterns to actual package.json paths.
|
|
246
|
+
*
|
|
247
|
+
* Workspaces can be specified as:
|
|
248
|
+
* - Array of globs: ["packages/*", "apps/*", "packages/**", "{apps,libs}/*"]
|
|
249
|
+
* - Object with packages array: { packages: ["packages/*"] }
|
|
250
|
+
* - Negation patterns: ["packages/*", "!packages/internal"]
|
|
251
|
+
*
|
|
252
|
+
* @param workspaces The workspaces field from package.json
|
|
253
|
+
* @param projectDir The absolute path to the project directory
|
|
254
|
+
* @param relativeTo Optional base path for creating relative paths
|
|
255
|
+
* @returns Array of relative paths to workspace member package.json files
|
|
256
|
+
*/
|
|
257
|
+
private async resolveWorkspacePackagePaths(
|
|
258
|
+
workspaces: string[] | { packages?: string[] },
|
|
259
|
+
projectDir: string,
|
|
260
|
+
relativeTo?: string
|
|
261
|
+
): Promise<string[] | undefined> {
|
|
262
|
+
// Normalize workspaces to array format
|
|
263
|
+
const patterns = Array.isArray(workspaces)
|
|
264
|
+
? workspaces
|
|
265
|
+
: workspaces.packages;
|
|
266
|
+
|
|
267
|
+
if (!patterns || patterns.length === 0) {
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Separate include and exclude patterns
|
|
272
|
+
const includePatterns = patterns.filter(p => !p.startsWith('!'));
|
|
273
|
+
const excludePatterns = patterns.filter(p => p.startsWith('!')).map(p => p.slice(1));
|
|
274
|
+
|
|
275
|
+
// Create picomatch matchers
|
|
276
|
+
const isIncluded = includePatterns.length > 0
|
|
277
|
+
? picomatch(includePatterns, { dot: false })
|
|
278
|
+
: () => false;
|
|
279
|
+
const isExcluded = excludePatterns.length > 0
|
|
280
|
+
? picomatch(excludePatterns, { dot: false })
|
|
281
|
+
: () => false;
|
|
282
|
+
|
|
283
|
+
// Collect all candidate directories by walking the project
|
|
284
|
+
const candidateDirs = await this.collectCandidateWorkspaceDirs(projectDir, includePatterns);
|
|
285
|
+
|
|
286
|
+
const memberPaths: string[] = [];
|
|
287
|
+
const basePath = relativeTo || projectDir;
|
|
288
|
+
|
|
289
|
+
for (const candidateDir of candidateDirs) {
|
|
290
|
+
// Get relative path from project root for pattern matching
|
|
291
|
+
const relativeDir = path.relative(projectDir, candidateDir);
|
|
292
|
+
|
|
293
|
+
// Check if directory matches include patterns and not exclude patterns
|
|
294
|
+
if (isIncluded(relativeDir) && !isExcluded(relativeDir)) {
|
|
295
|
+
const packageJsonPath = path.join(candidateDir, 'package.json');
|
|
296
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
297
|
+
const relativePath = path.relative(basePath, packageJsonPath);
|
|
298
|
+
memberPaths.push(relativePath);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return memberPaths.length > 0 ? memberPaths : undefined;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Collects candidate directories that might match workspace patterns.
|
|
308
|
+
* Uses the patterns to determine how deep to scan.
|
|
309
|
+
*/
|
|
310
|
+
private async collectCandidateWorkspaceDirs(
|
|
311
|
+
projectDir: string,
|
|
312
|
+
patterns: string[]
|
|
313
|
+
): Promise<string[]> {
|
|
314
|
+
const candidates: string[] = [];
|
|
315
|
+
|
|
316
|
+
// Determine the maximum depth we need to scan based on patterns
|
|
317
|
+
// "packages/*" -> depth 1 under packages/
|
|
318
|
+
// "packages/**" -> unlimited depth under packages/
|
|
319
|
+
// "{apps,libs}/*" -> depth 1 under apps/ and libs/
|
|
320
|
+
|
|
321
|
+
for (const pattern of patterns) {
|
|
322
|
+
// Extract base directories from pattern (before any wildcards)
|
|
323
|
+
const baseDirs = this.extractBaseDirs(pattern);
|
|
324
|
+
const hasRecursive = pattern.includes('**');
|
|
325
|
+
|
|
326
|
+
for (const baseDir of baseDirs) {
|
|
327
|
+
const absoluteBaseDir = path.join(projectDir, baseDir);
|
|
328
|
+
|
|
329
|
+
if (!fs.existsSync(absoluteBaseDir)) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Use walkDirs with appropriate depth limit
|
|
334
|
+
const dirs = await walkDirs(absoluteBaseDir, {
|
|
335
|
+
maxDepth: hasRecursive ? undefined : 0,
|
|
336
|
+
excludeDirs: DEFAULT_DIR_EXCLUSIONS
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
candidates.push(...dirs);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return candidates;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Extracts base directory paths from a glob pattern.
|
|
348
|
+
* Handles brace expansion like "{apps,libs}/*" -> ["apps", "libs"]
|
|
349
|
+
*/
|
|
350
|
+
private extractBaseDirs(pattern: string): string[] {
|
|
351
|
+
// Find the first wildcard character
|
|
352
|
+
const wildcardIndex = pattern.search(/[*?]/);
|
|
353
|
+
const beforeWildcard = wildcardIndex >= 0 ? pattern.slice(0, wildcardIndex) : pattern;
|
|
354
|
+
|
|
355
|
+
// Remove trailing slash if present
|
|
356
|
+
const basePath = beforeWildcard.replace(/\/$/, '');
|
|
357
|
+
|
|
358
|
+
// Handle brace expansion at the end of basePath: "dir/{a,b}" or "{a,b}"
|
|
359
|
+
const braceMatch = basePath.match(/^(.*?)(?:\{([^}]+)\})?$/);
|
|
360
|
+
if (braceMatch && braceMatch[2]) {
|
|
361
|
+
const prefix = braceMatch[1];
|
|
362
|
+
const options = braceMatch[2].split(',').map(s => s.trim());
|
|
363
|
+
return options.map(opt => prefix + opt);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return basePath ? [basePath] : ['.'];
|
|
367
|
+
}
|
|
368
|
+
|
|
239
369
|
/**
|
|
240
370
|
* Attempts to find and read a lock file by walking up the directory tree.
|
|
241
371
|
* Starts from the directory containing the package.json and walks up toward
|
|
@@ -19,8 +19,7 @@ import {
|
|
|
19
19
|
findNodeResolutionResult,
|
|
20
20
|
PackageJsonContent,
|
|
21
21
|
PackageLockContent,
|
|
22
|
-
PackageManager
|
|
23
|
-
readNpmrcConfigs
|
|
22
|
+
PackageManager
|
|
24
23
|
} from "./node-resolution-result";
|
|
25
24
|
import {replaceMarkerByKind} from "../markers";
|
|
26
25
|
import {Json, JsonParser, JsonVisitor} from "../json";
|
|
@@ -59,35 +58,37 @@ interface PackageManagerConfig {
|
|
|
59
58
|
const PACKAGE_MANAGER_CONFIGS: Record<PackageManager, PackageManagerConfig> = {
|
|
60
59
|
[PackageManager.Npm]: {
|
|
61
60
|
lockFile: 'package-lock.json',
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
// --ignore-scripts prevents prepublish/prepare scripts from running in temp directory
|
|
62
|
+
installLockOnlyCommand: ['npm', 'install', '--package-lock-only', '--ignore-scripts'],
|
|
63
|
+
installCommand: ['npm', 'install', '--ignore-scripts'],
|
|
64
64
|
listCommand: ['npm', 'list', '--json', '--all'],
|
|
65
65
|
},
|
|
66
66
|
[PackageManager.YarnClassic]: {
|
|
67
67
|
lockFile: 'yarn.lock',
|
|
68
|
-
// Yarn Classic doesn't have a lock-only mode
|
|
68
|
+
// Yarn Classic doesn't have a lock-only mode; --ignore-scripts prevents lifecycle scripts
|
|
69
69
|
installLockOnlyCommand: ['yarn', 'install', '--ignore-scripts'],
|
|
70
|
-
installCommand: ['yarn', 'install'],
|
|
70
|
+
installCommand: ['yarn', 'install', '--ignore-scripts'],
|
|
71
71
|
listCommand: ['yarn', 'list', '--json'],
|
|
72
72
|
},
|
|
73
73
|
[PackageManager.YarnBerry]: {
|
|
74
74
|
lockFile: 'yarn.lock',
|
|
75
|
-
//
|
|
75
|
+
// --mode skip-build skips post-install scripts in Yarn Berry
|
|
76
76
|
installLockOnlyCommand: ['yarn', 'install', '--mode', 'skip-build'],
|
|
77
|
-
installCommand: ['yarn', 'install'],
|
|
77
|
+
installCommand: ['yarn', 'install', '--mode', 'skip-build'],
|
|
78
78
|
listCommand: ['yarn', 'info', '--all', '--json'],
|
|
79
79
|
},
|
|
80
80
|
[PackageManager.Pnpm]: {
|
|
81
81
|
lockFile: 'pnpm-lock.yaml',
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
// --ignore-scripts prevents lifecycle scripts from running in temp directory
|
|
83
|
+
installLockOnlyCommand: ['pnpm', 'install', '--lockfile-only', '--ignore-scripts'],
|
|
84
|
+
installCommand: ['pnpm', 'install', '--ignore-scripts'],
|
|
84
85
|
listCommand: ['pnpm', 'list', '--json', '--depth=Infinity'],
|
|
85
86
|
},
|
|
86
87
|
[PackageManager.Bun]: {
|
|
87
88
|
lockFile: 'bun.lock',
|
|
88
|
-
// Bun doesn't have a lock-only mode
|
|
89
|
+
// Bun doesn't have a lock-only mode; --ignore-scripts prevents lifecycle scripts
|
|
89
90
|
installLockOnlyCommand: ['bun', 'install', '--ignore-scripts'],
|
|
90
|
-
installCommand: ['bun', 'install'],
|
|
91
|
+
installCommand: ['bun', 'install', '--ignore-scripts'],
|
|
91
92
|
},
|
|
92
93
|
};
|
|
93
94
|
|
|
@@ -447,8 +448,6 @@ export async function parseLockFileContent(
|
|
|
447
448
|
* Recipes extend this with additional fields specific to their needs.
|
|
448
449
|
*/
|
|
449
450
|
export interface BaseProjectUpdateInfo {
|
|
450
|
-
/** Absolute path to the project directory */
|
|
451
|
-
projectDir: string;
|
|
452
451
|
/** Relative path to package.json (from source root) */
|
|
453
452
|
packageJsonPath: string;
|
|
454
453
|
/** The package manager used by this project */
|
|
@@ -559,17 +558,15 @@ export async function updateNodeResolutionMarker<T extends BaseProjectUpdateInfo
|
|
|
559
558
|
}
|
|
560
559
|
}
|
|
561
560
|
|
|
562
|
-
//
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
// Create new marker
|
|
561
|
+
// Create new marker, preserving existing npmrc configs from the parser
|
|
562
|
+
// (recipes don't have filesystem access to re-read them)
|
|
566
563
|
const newMarker = createNodeResolutionResultMarker(
|
|
567
564
|
existingMarker.path,
|
|
568
565
|
packageJsonContent,
|
|
569
566
|
lockContent,
|
|
570
567
|
existingMarker.workspacePackagePaths,
|
|
571
568
|
existingMarker.packageManager,
|
|
572
|
-
|
|
569
|
+
existingMarker.npmrcConfigs
|
|
573
570
|
);
|
|
574
571
|
|
|
575
572
|
// Replace the marker in the document
|
|
@@ -591,52 +588,72 @@ export interface TempInstallOptions {
|
|
|
591
588
|
* Default: true (lock-only is faster and sufficient for most cases)
|
|
592
589
|
*/
|
|
593
590
|
lockOnly?: boolean;
|
|
591
|
+
/**
|
|
592
|
+
* Original lock file content to use. If provided, this content will be written
|
|
593
|
+
* to the temp directory instead of copying from projectDir.
|
|
594
|
+
* This allows recipes to work with in-memory SourceFiles without filesystem access.
|
|
595
|
+
*/
|
|
596
|
+
originalLockFileContent?: string;
|
|
597
|
+
/**
|
|
598
|
+
* Config file contents to use. Keys are filenames (e.g., '.npmrc'), values are content.
|
|
599
|
+
* If provided, these will be written to the temp directory instead of copying from projectDir.
|
|
600
|
+
*/
|
|
601
|
+
configFiles?: Record<string, string>;
|
|
594
602
|
}
|
|
595
603
|
|
|
596
604
|
/**
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
* This function:
|
|
600
|
-
* 1. Creates a temp directory
|
|
601
|
-
* 2. Writes the provided package.json content
|
|
602
|
-
* 3. Copies the existing lock file (if present)
|
|
603
|
-
* 4. Copies config files (.npmrc, .yarnrc, etc.)
|
|
604
|
-
* 5. Runs the package manager install
|
|
605
|
-
* 6. Returns the updated lock file content
|
|
606
|
-
* 7. Cleans up the temp directory
|
|
607
|
-
*
|
|
608
|
-
* @param projectDir The original project directory (for copying lock file and configs)
|
|
609
|
-
* @param pm The package manager to use
|
|
610
|
-
* @param modifiedPackageJson The modified package.json content to use
|
|
611
|
-
* @param options Optional settings for timeout and lock-only mode
|
|
612
|
-
* @returns Result containing success status and lock file content or error
|
|
605
|
+
* Options for running install in a temporary directory with workspace support.
|
|
613
606
|
*/
|
|
614
|
-
export
|
|
615
|
-
|
|
607
|
+
export interface WorkspaceTempInstallOptions extends TempInstallOptions {
|
|
608
|
+
/**
|
|
609
|
+
* Workspace package.json files. Keys are relative paths from the project root
|
|
610
|
+
* (e.g., "packages/foo/package.json"), values are the package.json content.
|
|
611
|
+
* The root package.json should have a "workspaces" field pointing to these packages.
|
|
612
|
+
*/
|
|
613
|
+
workspacePackages?: Record<string, string>;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Internal implementation for running package manager install in a temporary directory.
|
|
618
|
+
* Supports both simple projects and workspaces.
|
|
619
|
+
*/
|
|
620
|
+
async function runInstallInTempDirCore(
|
|
616
621
|
pm: PackageManager,
|
|
617
|
-
|
|
618
|
-
options:
|
|
622
|
+
rootPackageJson: string,
|
|
623
|
+
options: WorkspaceTempInstallOptions = {}
|
|
619
624
|
): Promise<TempInstallResult> {
|
|
620
|
-
const {
|
|
625
|
+
const {
|
|
626
|
+
timeout = 120000,
|
|
627
|
+
lockOnly = true,
|
|
628
|
+
originalLockFileContent,
|
|
629
|
+
configFiles: configFileContents,
|
|
630
|
+
workspacePackages
|
|
631
|
+
} = options;
|
|
621
632
|
const lockFileName = getLockFileName(pm);
|
|
622
633
|
const tempDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'openrewrite-pm-'));
|
|
623
634
|
|
|
624
635
|
try {
|
|
625
|
-
// Write
|
|
626
|
-
await fsp.writeFile(path.join(tempDir, 'package.json'),
|
|
636
|
+
// Write root package.json to temp directory
|
|
637
|
+
await fsp.writeFile(path.join(tempDir, 'package.json'), rootPackageJson);
|
|
638
|
+
|
|
639
|
+
// Write workspace package.json files (creating subdirectories as needed)
|
|
640
|
+
if (workspacePackages) {
|
|
641
|
+
for (const [relativePath, content] of Object.entries(workspacePackages)) {
|
|
642
|
+
const fullPath = path.join(tempDir, relativePath);
|
|
643
|
+
await fsp.mkdir(path.dirname(fullPath), {recursive: true});
|
|
644
|
+
await fsp.writeFile(fullPath, content);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
627
647
|
|
|
628
|
-
//
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
await fsp.copyFile(originalLockPath, path.join(tempDir, lockFileName));
|
|
648
|
+
// Write lock file if provided
|
|
649
|
+
if (originalLockFileContent !== undefined) {
|
|
650
|
+
await fsp.writeFile(path.join(tempDir, lockFileName), originalLockFileContent);
|
|
632
651
|
}
|
|
633
652
|
|
|
634
|
-
//
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
if (fs.existsSync(configPath)) {
|
|
639
|
-
await fsp.copyFile(configPath, path.join(tempDir, configFile));
|
|
653
|
+
// Write config files if provided
|
|
654
|
+
if (configFileContents) {
|
|
655
|
+
for (const [configFile, content] of Object.entries(configFileContents)) {
|
|
656
|
+
await fsp.writeFile(path.join(tempDir, configFile), content);
|
|
640
657
|
}
|
|
641
658
|
}
|
|
642
659
|
|
|
@@ -688,6 +705,57 @@ export async function runInstallInTempDir(
|
|
|
688
705
|
}
|
|
689
706
|
}
|
|
690
707
|
|
|
708
|
+
/**
|
|
709
|
+
* Runs package manager install in a temporary directory.
|
|
710
|
+
*
|
|
711
|
+
* This function:
|
|
712
|
+
* 1. Creates a temp directory
|
|
713
|
+
* 2. Writes the provided package.json content
|
|
714
|
+
* 3. Writes the lock file content (if provided)
|
|
715
|
+
* 4. Writes config files (if provided)
|
|
716
|
+
* 5. Runs the package manager install
|
|
717
|
+
* 6. Returns the updated lock file content
|
|
718
|
+
* 7. Cleans up the temp directory
|
|
719
|
+
*
|
|
720
|
+
* @param pm The package manager to use
|
|
721
|
+
* @param modifiedPackageJson The modified package.json content to use
|
|
722
|
+
* @param options Optional settings for timeout, lock-only mode, and file contents
|
|
723
|
+
* @returns Result containing success status and lock file content or error
|
|
724
|
+
*/
|
|
725
|
+
export async function runInstallInTempDir(
|
|
726
|
+
pm: PackageManager,
|
|
727
|
+
modifiedPackageJson: string,
|
|
728
|
+
options: TempInstallOptions = {}
|
|
729
|
+
): Promise<TempInstallResult> {
|
|
730
|
+
return runInstallInTempDirCore(pm, modifiedPackageJson, options);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Runs package manager install in a temporary directory with workspace support.
|
|
735
|
+
*
|
|
736
|
+
* This function:
|
|
737
|
+
* 1. Creates a temp directory
|
|
738
|
+
* 2. Writes the root package.json content
|
|
739
|
+
* 3. Writes workspace package.json files (creating subdirectories as needed)
|
|
740
|
+
* 4. Writes the lock file content (if provided)
|
|
741
|
+
* 5. Writes config files (if provided)
|
|
742
|
+
* 6. Runs the package manager install at the root
|
|
743
|
+
* 7. Returns the updated lock file content
|
|
744
|
+
* 8. Cleans up the temp directory
|
|
745
|
+
*
|
|
746
|
+
* @param pm The package manager to use
|
|
747
|
+
* @param rootPackageJson The root package.json content (should contain "workspaces" field)
|
|
748
|
+
* @param options Optional settings including workspace packages, timeout, lock-only mode, and file contents
|
|
749
|
+
* @returns Result containing success status and lock file content or error
|
|
750
|
+
*/
|
|
751
|
+
export async function runWorkspaceInstallInTempDir(
|
|
752
|
+
pm: PackageManager,
|
|
753
|
+
rootPackageJson: string,
|
|
754
|
+
options: WorkspaceTempInstallOptions = {}
|
|
755
|
+
): Promise<TempInstallResult> {
|
|
756
|
+
return runInstallInTempDirCore(pm, rootPackageJson, options);
|
|
757
|
+
}
|
|
758
|
+
|
|
691
759
|
/**
|
|
692
760
|
* Creates a lock file visitor that handles updating YAML lock files (pnpm-lock.yaml).
|
|
693
761
|
* This is a reusable component for dependency recipes.
|