Skip to content

Commit 6926b37

Browse files
clydinalan-agius4
authored andcommitted
fix(@angular/cli): clean node modules directory prior to updating
Prior to performing the initial updated package installation during the `ng update` process, the workspace node modules directory will be removed. This cleaning increases the guarantees that the package manager will hoist packages into the correct locations and avoid peer dependency inconsistencies.
1 parent 09bd28f commit 6926b37

File tree

3 files changed

+68
-4
lines changed

3 files changed

+68
-4
lines changed

packages/angular/cli/commands/update-impl.ts

+21-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { Command } from '../models/command';
1717
import { Arguments } from '../models/interface';
1818
import { SchematicEngineHost } from '../models/schematic-engine-host';
1919
import { colors } from '../utilities/color';
20-
import { runTempPackageBin } from '../utilities/install-package';
20+
import { installAllPackages, runTempPackageBin } from '../utilities/install-package';
2121
import { writeErrorToLogFile } from '../utilities/log-file';
2222
import { ensureCompatibleNpm, getPackageManager } from '../utilities/package-manager';
2323
import {
@@ -655,6 +655,26 @@ export class UpdateCommand extends Command<UpdateCommandSchema> {
655655
packages: packagesToUpdate,
656656
});
657657

658+
if (success) {
659+
try {
660+
// Remove existing node modules directory to provide a stronger guarantee that packages
661+
// will be hoisted into the correct locations.
662+
await fs.promises.rmdir(path.join(this.context.root, 'node_modules'), {
663+
recursive: true,
664+
maxRetries: 3,
665+
});
666+
} catch {}
667+
668+
const result = await installAllPackages(
669+
this.packageManager,
670+
options.force ? ['--force'] : [],
671+
this.context.root,
672+
);
673+
if (result !== 0) {
674+
return result;
675+
}
676+
}
677+
658678
if (success && options.createCommits) {
659679
const committed = this.commit(
660680
`Angular CLI update for packages - ${packagesToUpdate.join(', ')}`,

packages/angular/cli/src/commands/update/schematic/index.ts

-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
import { logging, tags } from '@angular-devkit/core';
1010
import { Rule, SchematicContext, SchematicsException, Tree } from '@angular-devkit/schematics';
11-
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
1211
import * as npa from 'npm-package-arg';
1312
import * as semver from 'semver';
1413
import { getNpmPackageJson } from './npm';
@@ -310,9 +309,7 @@ function _performUpdate(
310309
const newContent = JSON.stringify(packageJson, null, 2);
311310
if (packageJsonContent.toString() != newContent || migrateOnly) {
312311
if (!migrateOnly) {
313-
// If something changed, also hook up the task.
314312
tree.overwrite('/package.json', JSON.stringify(packageJson, null, 2));
315-
context.addTask(new NodePackageInstallTask());
316313
}
317314

318315
const externalMigrations: {}[] = [];

packages/angular/cli/utilities/install-package.ts

+47
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,55 @@ interface PackageManagerOptions {
1919
silent: string;
2020
saveDev: string;
2121
install: string;
22+
installAll?: string;
2223
prefix: string;
2324
noLockfile: string;
2425
}
2526

27+
export async function installAllPackages(
28+
packageManager: PackageManager = PackageManager.Npm,
29+
extraArgs: string[] = [],
30+
cwd = process.cwd(),
31+
): Promise<1 | 0> {
32+
const packageManagerArgs = getPackageManagerArguments(packageManager);
33+
34+
const installArgs: string[] = [];
35+
if (packageManagerArgs.installAll) {
36+
installArgs.push(packageManagerArgs.installAll);
37+
}
38+
installArgs.push(packageManagerArgs.silent);
39+
40+
const spinner = new Spinner();
41+
spinner.start('Installing packages...');
42+
43+
const bufferedOutput: { stream: NodeJS.WriteStream; data: Buffer }[] = [];
44+
45+
return new Promise((resolve, reject) => {
46+
const childProcess = spawn(packageManager, [...installArgs, ...extraArgs], {
47+
stdio: 'pipe',
48+
shell: true,
49+
cwd,
50+
}).on('close', (code: number) => {
51+
if (code === 0) {
52+
spinner.succeed('Packages successfully installed.');
53+
resolve(0);
54+
} else {
55+
spinner.stop();
56+
bufferedOutput.forEach(({ stream, data }) => stream.write(data));
57+
spinner.fail('Package install failed, see above.');
58+
reject(1);
59+
}
60+
});
61+
62+
childProcess.stdout?.on('data', (data: Buffer) =>
63+
bufferedOutput.push({ stream: process.stdout, data: data }),
64+
);
65+
childProcess.stderr?.on('data', (data: Buffer) =>
66+
bufferedOutput.push({ stream: process.stderr, data: data }),
67+
);
68+
});
69+
}
70+
2671
export async function installPackage(
2772
packageName: string,
2873
packageManager: PackageManager = PackageManager.Npm,
@@ -193,6 +238,7 @@ function getPackageManagerArguments(packageManager: PackageManager): PackageMana
193238
silent: '--silent',
194239
saveDev: '--save-dev',
195240
install: 'add',
241+
installAll: 'install',
196242
prefix: '--prefix',
197243
noLockfile: '--no-lockfile',
198244
};
@@ -201,6 +247,7 @@ function getPackageManagerArguments(packageManager: PackageManager): PackageMana
201247
silent: '--quiet',
202248
saveDev: '--save-dev',
203249
install: 'install',
250+
installAll: 'install',
204251
prefix: '--prefix',
205252
noLockfile: '--no-package-lock',
206253
};

0 commit comments

Comments
 (0)