Skip to content

Commit 3f57956

Browse files
clydinvikerman
authored andcommitted
fix(@angular/cli): verify package specifier when adding a package
Fixes #16029
1 parent e7bf105 commit 3f57956

File tree

2 files changed

+40
-3
lines changed

2 files changed

+40
-3
lines changed

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

+20-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { colors } from '../utilities/color';
1818
import { getPackageManager } from '../utilities/package-manager';
1919
import {
2020
NgAddSaveDepedency,
21+
PackageIdentifier,
2122
PackageManifest,
2223
fetchPackageManifest,
2324
fetchPackageMetadata,
@@ -50,10 +51,26 @@ export class AddCommand extends SchematicCommand<AddCommandSchema> {
5051
}
5152

5253
if (packageIdentifier.registry && this.isPackageInstalled(packageIdentifier.name)) {
53-
// Already installed so just run schematic
54-
this.logger.info('Skipping installation: Package already installed');
54+
let validVersion = false;
55+
const installedVersion = await this.findProjectVersion(packageIdentifier.name);
56+
if (installedVersion) {
57+
if (packageIdentifier.type === 'range') {
58+
validVersion = satisfies(installedVersion, packageIdentifier.fetchSpec);
59+
} else if (packageIdentifier.type === 'version') {
60+
const v1 = valid(packageIdentifier.fetchSpec);
61+
const v2 = valid(installedVersion);
62+
validVersion = v1 !== null && v1 === v2;
63+
} else if (!packageIdentifier.rawSpec) {
64+
validVersion = true;
65+
}
66+
}
5567

56-
return this.executeSchematic(packageIdentifier.name, options['--']);
68+
if (validVersion) {
69+
// Already installed so just run schematic
70+
this.logger.info('Skipping installation: Package already installed');
71+
72+
return this.executeSchematic(packageIdentifier.name, options['--']);
73+
}
5774
}
5875

5976
const packageManager = await getPackageManager(this.workspace.root);

tests/legacy-cli/e2e/tests/commands/add/add-material.ts

+20
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,24 @@ export default async function () {
88

99
await ng('add', '@angular/material');
1010
await expectFileToMatch('package.json', /@angular\/material/);
11+
12+
const output1 = await ng('add', '@angular/material');
13+
if (!output1.stdout.includes('Skipping installation: Package already installed')) {
14+
throw new Error('Installation was not skipped');
15+
}
16+
17+
const output2 = await ng('add', '@angular/material@latest');
18+
if (output2.stdout.includes('Skipping installation: Package already installed')) {
19+
throw new Error('Installation should not have been skipped');
20+
}
21+
22+
const output3 = await ng('add', '@angular/material@8.0.0');
23+
if (output3.stdout.includes('Skipping installation: Package already installed')) {
24+
throw new Error('Installation should not have been skipped');
25+
}
26+
27+
const output4 = await ng('add', '@angular/material@8');
28+
if (!output4.stdout.includes('Skipping installation: Package already installed')) {
29+
throw new Error('Installation was not skipped');
30+
}
1131
}

0 commit comments

Comments
 (0)