-
Notifications
You must be signed in to change notification settings - Fork 683
Expand file tree
/
Copy pathRushPnpmCommandLineParser.ts
More file actions
668 lines (596 loc) · 25.5 KB
/
RushPnpmCommandLineParser.ts
File metadata and controls
668 lines (596 loc) · 25.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import * as path from 'node:path';
import {
AlreadyReportedError,
EnvironmentMap,
FileConstants,
FileSystem,
JsonFile,
type JsonObject,
Objects
} from '@rushstack/node-core-library';
import {
Colorize,
ConsoleTerminalProvider,
type ITerminal,
type ITerminalProvider,
Terminal,
PrintUtilities
} from '@rushstack/terminal';
import { RushConfiguration } from '../api/RushConfiguration';
import { NodeJsCompatibility } from '../logic/NodeJsCompatibility';
import { RushConstants } from '../logic/RushConstants';
import { RushGlobalFolder } from '../api/RushGlobalFolder';
import { PurgeManager } from '../logic/PurgeManager';
import type { IBuiltInPluginConfiguration } from '../pluginFramework/PluginLoader/BuiltInPluginLoader';
import type { BaseInstallManager } from '../logic/base/BaseInstallManager';
import type { IInstallManagerOptions } from '../logic/base/BaseInstallManagerTypes';
import { Utilities } from '../utilities/Utilities';
import type { Subspace } from '../api/Subspace';
import type { PnpmOptionsConfiguration } from '../logic/pnpm/PnpmOptionsConfiguration';
import { PnpmWorkspaceFile } from '../logic/pnpm/PnpmWorkspaceFile';
import { EnvironmentVariableNames } from '../api/EnvironmentConfiguration';
import { initializeDotEnv } from '../logic/dotenv';
const RUSH_SKIP_CHECKS_PARAMETER: string = '--rush-skip-checks';
/**
* Options for RushPnpmCommandLineParser
*/
export interface IRushPnpmCommandLineParserOptions {
alreadyReportedNodeTooNewError?: boolean;
builtInPluginConfigurations?: IBuiltInPluginConfiguration[];
terminalProvider?: ITerminalProvider;
}
function _reportErrorAndSetExitCode(error: Error, terminal: ITerminal, debugEnabled: boolean): never {
if (!(error instanceof AlreadyReportedError)) {
const prefix: string = 'ERROR: ';
terminal.writeErrorLine('\n' + PrintUtilities.wrapWords(prefix + error.message));
}
if (debugEnabled) {
// If catchSyncErrors() called this, then show a call stack similar to what Node.js
// would show for an uncaught error
terminal.writeErrorLine('\n' + error.stack);
}
process.exit(process.exitCode ?? 1);
}
export class RushPnpmCommandLineParser {
private readonly _terminal: ITerminal;
private readonly _rushConfiguration: RushConfiguration;
private readonly _pnpmArgs: string[];
private _commandName: string | undefined;
private readonly _debugEnabled: boolean;
private _subspace: Subspace;
private constructor(
options: IRushPnpmCommandLineParserOptions,
terminal: ITerminal,
debugEnabled: boolean
) {
this._debugEnabled = debugEnabled;
this._terminal = terminal;
// Are we in a Rush repo?
const rushJsonFilePath: string | undefined = RushConfiguration.tryFindRushJsonLocation({
// showVerbose is false because the logging message may break JSON output
showVerbose: false
});
initializeDotEnv(terminal, rushJsonFilePath);
const rushConfiguration: RushConfiguration | undefined = rushJsonFilePath
? RushConfiguration.loadFromConfigurationFile(rushJsonFilePath)
: undefined;
NodeJsCompatibility.warnAboutCompatibilityIssues({
isRushLib: true,
alreadyReportedNodeTooNewError: !!options.alreadyReportedNodeTooNewError,
rushConfiguration
});
if (!rushConfiguration) {
throw new Error(
'The "rush-pnpm" command must be executed in a folder that is under a Rush workspace folder'
);
}
this._rushConfiguration = rushConfiguration;
if (rushConfiguration.packageManager !== 'pnpm') {
throw new Error(
`The "rush-pnpm" command requires your ${RushConstants.rushJsonFilename} to be configured to use the PNPM package manager`
);
}
if (!rushConfiguration.pnpmOptions.useWorkspaces) {
const pnpmConfigFilename: string =
rushConfiguration.pnpmOptions.jsonFilename || RushConstants.rushJsonFilename;
throw new Error(
`The "rush-pnpm" command requires the "useWorkspaces" setting to be enabled in ${pnpmConfigFilename}`
);
}
let pnpmArgs: string[] = [];
let subspaceName: string = 'default';
if (process.argv.indexOf('--subspace') >= 0) {
if (process.argv[2] !== '--subspace') {
throw new Error(
'If you want to specify a subspace, you should place "--subspace <subspace_name>" immediately after the "rush-pnpm" command'
);
}
subspaceName = process.argv[3];
// 0 = node.exe
// 1 = rush-pnpm
// 2 = --subspace
// 3 = <subspace_name>
pnpmArgs = process.argv.slice(4);
} else {
// 0 = node.exe
// 1 = rush-pnpm
pnpmArgs = process.argv.slice(2);
}
this._pnpmArgs = pnpmArgs;
const subspace: Subspace = rushConfiguration.getSubspace(subspaceName);
this._subspace = subspace;
const workspaceFolder: string = subspace.getSubspaceTempFolderPath();
const workspaceFilePath: string = path.join(workspaceFolder, 'pnpm-workspace.yaml');
if (!FileSystem.exists(workspaceFilePath)) {
this._terminal.writeErrorLine('Error: The PNPM workspace file has not been generated:');
this._terminal.writeErrorLine(` ${workspaceFilePath}\n`);
this._terminal.writeLine(Colorize.cyan(`Do you need to run "rush install" or "rush update"?`));
throw new AlreadyReportedError();
}
if (!FileSystem.exists(rushConfiguration.packageManagerToolFilename)) {
this._terminal.writeErrorLine('Error: The PNPM local binary has not been installed yet.');
this._terminal.writeLine('\n' + Colorize.cyan(`Do you need to run "rush install" or "rush update"?`));
throw new AlreadyReportedError();
}
}
public static async initializeAsync(
options: IRushPnpmCommandLineParserOptions
): Promise<RushPnpmCommandLineParser> {
const debugEnabled: boolean = process.argv.indexOf('--debug') >= 0;
const verboseEnabled: boolean = process.argv.indexOf('--verbose') >= 0;
const localTerminalProvider: ITerminalProvider =
options.terminalProvider ??
new ConsoleTerminalProvider({
debugEnabled,
verboseEnabled
});
const terminal: ITerminal = new Terminal(localTerminalProvider);
try {
const rushPnpmCommandLineParser: RushPnpmCommandLineParser = new RushPnpmCommandLineParser(
options,
terminal,
debugEnabled
);
await rushPnpmCommandLineParser._validatePnpmUsageAsync(rushPnpmCommandLineParser._pnpmArgs);
return rushPnpmCommandLineParser;
} catch (error) {
_reportErrorAndSetExitCode(error as Error, terminal, debugEnabled);
}
}
public async executeAsync(): Promise<void> {
// Node.js can sometimes accidentally terminate with a zero exit code (e.g. for an uncaught
// promise exception), so we start with the assumption that the exit code is 1
// and set it to 0 only on success.
process.exitCode = 1;
await this._executeAsync();
if (process.exitCode === 0) {
await this._postExecuteAsync();
}
}
private async _validatePnpmUsageAsync(pnpmArgs: string[]): Promise<void> {
if (pnpmArgs[0] === RUSH_SKIP_CHECKS_PARAMETER) {
pnpmArgs.shift();
// Ignore other checks
return;
}
if (pnpmArgs.length === 0) {
return;
}
const firstArg: string = pnpmArgs[0];
// Detect common safe invocations
if (pnpmArgs.includes('-h') || pnpmArgs.includes('--help') || pnpmArgs.includes('-?')) {
return;
}
if (pnpmArgs.length === 1) {
if (firstArg === '-v' || firstArg === '--version') {
return;
}
}
const BYPASS_NOTICE: string = `To bypass this check, add "${RUSH_SKIP_CHECKS_PARAMETER}" as the very first command line option.`;
if (!/^[a-z]+([a-z0-9\-])*$/.test(firstArg)) {
// We can't parse this CLI syntax
this._terminal.writeErrorLine(
`Warning: The "rush-pnpm" wrapper expects a command verb before "${firstArg}"\n`
);
this._terminal.writeLine(Colorize.cyan(BYPASS_NOTICE));
throw new AlreadyReportedError();
} else {
const commandName: string = firstArg;
// Also accept SKIP_RUSH_CHECKS_PARAMETER immediately after the command verb
if (pnpmArgs[1] === RUSH_SKIP_CHECKS_PARAMETER) {
pnpmArgs.splice(1, 1);
return;
}
if (pnpmArgs.indexOf(RUSH_SKIP_CHECKS_PARAMETER) >= 0) {
// We do not attempt to parse PNPM's complete CLI syntax, so we cannot be sure how to interpret
// strings that appear outside of the specific patterns that this parser recognizes
this._terminal.writeErrorLine(
PrintUtilities.wrapWords(
`Error: The "${RUSH_SKIP_CHECKS_PARAMETER}" option must be the first parameter for the "rush-pnpm" command.`
)
);
throw new AlreadyReportedError();
}
this._commandName = commandName;
// Warn about commands known not to work
/* eslint-disable no-fallthrough */
switch (commandName) {
// Blocked
case 'import': {
this._terminal.writeErrorLine(
PrintUtilities.wrapWords(
`Error: The "pnpm ${commandName}" command is known to be incompatible with Rush's environment.`
) + '\n'
);
this._terminal.writeLine(Colorize.cyan(BYPASS_NOTICE));
throw new AlreadyReportedError();
}
// Show warning for install commands
case 'add':
case 'install':
/* synonym */
case 'i':
case 'install-test':
/* synonym */
case 'it': {
this._terminal.writeErrorLine(
PrintUtilities.wrapWords(
`Error: The "pnpm ${commandName}" command is incompatible with Rush's environment.` +
` Use the "rush install" or "rush update" commands instead.`
) + '\n'
);
this._terminal.writeLine(Colorize.cyan(BYPASS_NOTICE));
throw new AlreadyReportedError();
}
// Show warning
case 'link':
/* synonym */
case 'ln':
case 'remove':
/* synonym */
case 'rm':
case 'unlink':
case 'update':
/* synonym */
case 'up': {
this._terminal.writeWarningLine(
PrintUtilities.wrapWords(
`Warning: The "pnpm ${commandName}" command makes changes that may invalidate Rush's workspace state.`
) + '\n'
);
this._terminal.writeWarningLine(
`==> Consider running "rush install" or "rush update" afterwards.\n`
);
break;
}
// Know safe after validation
case 'patch': {
const semver: typeof import('semver') = await import('semver');
/**
* If you were to accidentally attempt to use rush-pnpm patch with a pnpmVersion < 7.4.0, pnpm patch may fallback to the system patch command.
* For instance, /usr/bin/patch which may just hangs forever
* So, erroring out the command if the pnpm version is < 7.4.0
*/
if (semver.lt(this._rushConfiguration.packageManagerToolVersion, '7.4.0')) {
this._terminal.writeErrorLine(
PrintUtilities.wrapWords(
`Error: The "pnpm patch" command is added after pnpm@7.4.0.` +
` Please update "pnpmVersion" >= 7.4.0 in ${RushConstants.rushJsonFilename} file and run "rush update" to use this command.`
) + '\n'
);
throw new AlreadyReportedError();
}
break;
}
case 'patch-commit': {
const pnpmOptionsJsonFilename: string = path.join(
this._rushConfiguration.commonRushConfigFolder,
RushConstants.pnpmConfigFilename
);
if (this._rushConfiguration.rushConfigurationJson.pnpmOptions) {
this._terminal.writeErrorLine(
PrintUtilities.wrapWords(
`Error: The "pnpm patch-commit" command is incompatible with specifying "pnpmOptions" in ${RushConstants.rushJsonFilename} file.` +
` Please move the content of "pnpmOptions" in ${RushConstants.rushJsonFilename} file to ${pnpmOptionsJsonFilename}`
) + '\n'
);
throw new AlreadyReportedError();
}
break;
}
case 'patch-remove': {
const semver: typeof import('semver') = await import('semver');
/**
* The "patch-remove" command was introduced in pnpm version 8.5.0
*/
if (semver.lt(this._rushConfiguration.packageManagerToolVersion, '8.5.0')) {
this._terminal.writeErrorLine(
PrintUtilities.wrapWords(
`Error: The "pnpm patch-remove" command is added after pnpm@8.5.0.` +
` Please update "pnpmVersion" >= 8.5.0 in ${RushConstants.rushJsonFilename} file and run "rush update" to use this command.`
) + '\n'
);
throw new AlreadyReportedError();
}
break;
}
case 'approve-builds': {
const semver: typeof import('semver') = await import('semver');
/**
* The "approve-builds" command was introduced in pnpm version 10.1.0
* to approve packages for running build scripts when onlyBuiltDependencies is used
*/
if (semver.lt(this._rushConfiguration.packageManagerToolVersion, '10.1.0')) {
this._terminal.writeErrorLine(
PrintUtilities.wrapWords(
`Error: The "pnpm approve-builds" command is added after pnpm@10.1.0.` +
` Please update "pnpmVersion" >= 10.1.0 in ${RushConstants.rushJsonFilename} file and run "rush update" to use this command.`
) + '\n'
);
throw new AlreadyReportedError();
}
const pnpmOptionsJsonFilename: string = path.join(
this._rushConfiguration.commonRushConfigFolder,
RushConstants.pnpmConfigFilename
);
if (this._rushConfiguration.rushConfigurationJson.pnpmOptions) {
this._terminal.writeErrorLine(
PrintUtilities.wrapWords(
`Error: The "pnpm approve-builds" command is incompatible with specifying "pnpmOptions" in ${RushConstants.rushJsonFilename} file.` +
` Please move the content of "pnpmOptions" in ${RushConstants.rushJsonFilename} file to ${pnpmOptionsJsonFilename}`
) + '\n'
);
throw new AlreadyReportedError();
}
break;
}
// Known safe
case 'audit':
case 'exec':
case 'list':
/* synonym */
case 'ls':
case 'outdated':
case 'pack':
case 'prune':
case 'publish':
case 'rebuild':
/* synonym */
case 'rb':
case 'root':
case 'run':
case 'start':
case 'store':
case 'test':
/* synonym */
case 't':
case 'why': {
break;
}
// Unknown
default: {
this._terminal.writeErrorLine(
PrintUtilities.wrapWords(
`Error: The "pnpm ${commandName}" command has not been tested with Rush's environment. It may be incompatible.`
) + '\n'
);
this._terminal.writeLine(Colorize.cyan(BYPASS_NOTICE));
}
}
/* eslint-enable no-fallthrough */
}
}
private async _executeAsync(): Promise<void> {
const rushConfiguration: RushConfiguration = this._rushConfiguration;
const workspaceFolder: string = this._subspace.getSubspaceTempFolderPath();
const pnpmEnvironmentMap: EnvironmentMap = new EnvironmentMap(process.env);
pnpmEnvironmentMap.set('NPM_CONFIG_WORKSPACE_DIR', workspaceFolder);
if (rushConfiguration.pnpmOptions.pnpmStorePath) {
pnpmEnvironmentMap.set('NPM_CONFIG_STORE_DIR', rushConfiguration.pnpmOptions.pnpmStorePath);
pnpmEnvironmentMap.set('NPM_CONFIG_CACHE_DIR', rushConfiguration.pnpmOptions.pnpmStorePath);
pnpmEnvironmentMap.set('NPM_CONFIG_STATE_DIR', rushConfiguration.pnpmOptions.pnpmStorePath);
}
if (rushConfiguration.pnpmOptions.environmentVariables) {
for (const [envKey, { value: envValue, override }] of Object.entries(
rushConfiguration.pnpmOptions.environmentVariables
)) {
if (override) {
pnpmEnvironmentMap.set(envKey, envValue);
} else {
if (undefined === pnpmEnvironmentMap.get(envKey)) {
pnpmEnvironmentMap.set(envKey, envValue);
}
}
}
}
let onStdoutStreamChunk: ((chunk: string) => string | void) | undefined;
switch (this._commandName) {
case 'patch': {
// Replace `pnpm patch-commit` with `rush-pnpm patch-commit` when running
// `pnpm patch` to avoid the `pnpm patch` command being suggested in the output
onStdoutStreamChunk = (stdoutChunk: string) => {
return stdoutChunk.replace(
/pnpm patch-commit/g,
`rush-pnpm --subspace ${this._subspace.subspaceName} patch-commit`
);
};
break;
}
}
try {
const { exitCode } = await Utilities.executeCommandAsync({
command: rushConfiguration.packageManagerToolFilename,
args: this._pnpmArgs,
workingDirectory: process.cwd(),
environment: pnpmEnvironmentMap.toObject(),
keepEnvironment: true,
onStdoutStreamChunk,
captureExitCodeAndSignal: true
});
if (typeof exitCode === 'number') {
process.exitCode = exitCode;
} else {
// If the exit code is not a number, the process was terminated by a signal
process.exitCode = 1;
}
} catch (e) {
this._terminal.writeDebugLine(`Error: ${e}`);
}
}
private async _postExecuteAsync(): Promise<void> {
const commandName: string | undefined = this._commandName;
if (!commandName) {
return;
}
const subspaceTempFolder: string = this._subspace.getSubspaceTempFolderPath();
switch (commandName) {
case 'patch-remove':
case 'patch-commit': {
// why need to throw error when pnpm-config.json not exists?
// 1. pnpm-config.json is required for `rush-pnpm patch-commit`. Rush writes the patched dependency to the pnpm-config.json when finishes.
// 2. we can not fallback to use Monorepo config folder (common/config/rush) due to that this command is intended to apply to input subspace only.
// It will produce unexpected behavior if we use the fallback.
if (this._subspace.getPnpmOptions() === undefined) {
const subspaceConfigFolder: string = this._subspace.getSubspaceConfigFolderPath();
this._terminal.writeErrorLine(
`The "rush-pnpm patch-commit" command cannot proceed without a pnpm-config.json file.` +
` Create one in this folder: ${subspaceConfigFolder}`
);
break;
}
// Example: "C:\MyRepo\common\temp\package.json"
const commonPackageJsonFilename: string = `${subspaceTempFolder}/${FileConstants.PackageJson}`;
const commonPackageJson: JsonObject = JsonFile.load(commonPackageJsonFilename);
const newGlobalPatchedDependencies: Record<string, string> | undefined =
commonPackageJson?.pnpm?.patchedDependencies;
const pnpmOptions: PnpmOptionsConfiguration | undefined = this._subspace.getPnpmOptions();
const currentGlobalPatchedDependencies: Record<string, string> | undefined =
pnpmOptions?.globalPatchedDependencies;
if (!Objects.areDeepEqual(currentGlobalPatchedDependencies, newGlobalPatchedDependencies)) {
const commonTempPnpmPatchesFolder: string = `${subspaceTempFolder}/${RushConstants.pnpmPatchesFolderName}`;
const rushPnpmPatchesFolder: string = this._subspace.getSubspacePnpmPatchesFolderPath();
// Copy (or delete) common\temp\subspace\patches\ --> common\config\pnpm-patches\ OR common\config\rush\pnpm-patches\
if (FileSystem.exists(commonTempPnpmPatchesFolder)) {
FileSystem.ensureEmptyFolder(rushPnpmPatchesFolder);
// eslint-disable-next-line no-console
console.log(`Copying ${commonTempPnpmPatchesFolder}`);
// eslint-disable-next-line no-console
console.log(` --> ${rushPnpmPatchesFolder}`);
FileSystem.copyFiles({
sourcePath: commonTempPnpmPatchesFolder,
destinationPath: rushPnpmPatchesFolder
});
} else {
if (FileSystem.exists(rushPnpmPatchesFolder)) {
// eslint-disable-next-line no-console
console.log(`Deleting ${rushPnpmPatchesFolder}`);
FileSystem.deleteFolder(rushPnpmPatchesFolder);
}
}
// Update patchedDependencies to pnpm configuration file
pnpmOptions?.updateGlobalPatchedDependencies(newGlobalPatchedDependencies);
// Rerun installation to update
await this._doRushUpdateAsync();
this._terminal.writeWarningLine(
`Rush refreshed the ${RushConstants.pnpmConfigFilename}, shrinkwrap file and patch files under the ` +
`"${commonTempPnpmPatchesFolder}" folder.\n` +
' Please commit this change to Git.'
);
}
break;
}
case 'approve-builds': {
if (this._subspace.getPnpmOptions() === undefined) {
const subspaceConfigFolder: string = this._subspace.getSubspaceConfigFolderPath();
this._terminal.writeErrorLine(
`The "rush-pnpm approve-builds" command cannot proceed without a pnpm-config.json file.` +
` Create one in this folder: ${subspaceConfigFolder}`
);
break;
}
// Example: "C:\MyRepo\common\temp\package.json"
const commonPackageJsonFilename: string = `${subspaceTempFolder}/${FileConstants.PackageJson}`;
const commonPackageJson: JsonObject = await JsonFile.loadAsync(commonPackageJsonFilename);
const newGlobalOnlyBuiltDependencies: string[] | undefined =
commonPackageJson?.pnpm?.onlyBuiltDependencies;
const pnpmOptions: PnpmOptionsConfiguration | undefined = this._subspace.getPnpmOptions();
const currentGlobalOnlyBuiltDependencies: string[] | undefined =
pnpmOptions?.globalOnlyBuiltDependencies;
if (!Objects.areDeepEqual(currentGlobalOnlyBuiltDependencies, newGlobalOnlyBuiltDependencies)) {
// Update onlyBuiltDependencies to pnpm configuration file
await pnpmOptions?.updateGlobalOnlyBuiltDependenciesAsync(newGlobalOnlyBuiltDependencies);
// Rerun installation to update
await this._doRushUpdateAsync();
this._terminal.writeWarningLine(
`Rush refreshed the ${RushConstants.pnpmConfigFilename} and shrinkwrap file.\n` +
' Please commit this change to Git.'
);
}
break;
}
case 'up':
case 'update': {
const pnpmOptions: PnpmOptionsConfiguration | undefined = this._subspace.getPnpmOptions();
if (pnpmOptions === undefined) {
break;
}
const workspaceYamlFilename: string = path.join(subspaceTempFolder, 'pnpm-workspace.yaml');
const newCatalogs: Record<string, Record<string, string>> | undefined =
await PnpmWorkspaceFile.loadCatalogsFromFileAsync(workspaceYamlFilename);
const currentCatalogs: Record<string, Record<string, string>> | undefined =
pnpmOptions.globalCatalogs;
if (!Objects.areDeepEqual(currentCatalogs, newCatalogs)) {
await pnpmOptions.updateGlobalCatalogsAsync(newCatalogs);
this._terminal.writeWarningLine(
`Rush refreshed the ${RushConstants.pnpmConfigFilename} with updated catalog definitions.\n` +
` Run "rush update --recheck" to update the lockfile, then commit these changes to Git.`
);
}
break;
}
}
}
private async _doRushUpdateAsync(): Promise<void> {
this._terminal.writeLine();
this._terminal.writeLine(Colorize.green('Running "rush update"'));
this._terminal.writeLine();
const rushGlobalFolder: RushGlobalFolder = new RushGlobalFolder();
const purgeManager: PurgeManager = new PurgeManager(this._rushConfiguration, rushGlobalFolder);
const installManagerOptions: IInstallManagerOptions = {
debug: this._debugEnabled,
allowShrinkwrapUpdates: true,
bypassPolicy: false,
noLink: false,
fullUpgrade: false,
recheckShrinkwrap: true,
networkConcurrency: undefined,
offline: false,
collectLogFile: false,
variant: process.env[EnvironmentVariableNames.RUSH_VARIANT], // For `rush-pnpm`, only use the env var
maxInstallAttempts: RushConstants.defaultMaxInstallAttempts,
pnpmFilterArgumentValues: [],
selectedProjects: new Set(this._rushConfiguration.projects),
checkOnly: false,
subspace: this._subspace,
terminal: this._terminal
};
const installManagerFactoryModule: typeof import('../logic/InstallManagerFactory') = await import(
/* webpackChunkName: 'InstallManagerFactory' */
'../logic/InstallManagerFactory'
);
const installManager: BaseInstallManager =
await installManagerFactoryModule.InstallManagerFactory.getInstallManagerAsync(
this._rushConfiguration,
rushGlobalFolder,
purgeManager,
installManagerOptions
);
try {
await installManager.doInstallAsync();
} finally {
await purgeManager.startDeleteAllAsync();
}
}
}