8000 Merge pull request #3588 from sherlockfeng/add-rush-remove · mstomar698/rushstack@9d4cb72 · GitHub
[go: up one dir, main page]

8000
Skip to content

Commit 9d4cb72

Browse files
authored
Merge pull request microsoft#3588 from sherlockfeng/add-rush-remove
[rush-lib] Add rush remove
2 parents 2308280 + 16b54a1 commit 9d4cb72

18 files changed

+610
-115
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/rush",
5+
"comment": "Add a `rush remove` command that removes one or more dependencies from a project.",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@microsoft/rush"
10+
}

common/reviews/api/rush-lib.api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,8 @@ export class PackageJsonEditor {
654654
static load(filePath: string): PackageJsonEditor;
655655
// (undocumented)
656656
get name(): string;
657+
// (undocumented)
658+
removeDependency(packageName: string, dependencyType: DependencyType): void;
657659
get resolutionsList(): ReadonlyArray<PackageJsonDependency>;
658660
// (undocumented)
659661
saveIfModified(): boolean;

libraries/rush-lib/src/api/PackageJsonEditor.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,26 @@ export class PackageJsonEditor {
253253
this._modified = true;
254254
}
255255

256+
public removeDependency(packageName: string, dependencyType: DependencyType): void {
257+
switch (dependencyType) {
258+
case DependencyType.Regular:
259+
case DependencyType.Optional:
260+
case DependencyType.Peer:
261+
this._dependencies.delete(packageName);
262+
break;
263+
case DependencyType.Dev:
264+
this._devDependencies.delete(packageName);
265+
break;
266+
case DependencyType.YarnResolutions:
267+
this._resolutions.delete(packageName);
268+
break;
269+
default:
270+
throw new InternalError('Unsupported DependencyType');
271+
}
272+
273+
this._modified = true;
274+
}
275+
256276
public saveIfModified(): boolean {
257277
if (this._modified) {
258278
this._modified = false;

libraries/rush-lib/src/cli/RushCommandLineParser.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { LinkAction } from './actions/LinkAction';
3535
import { ListAction } from './actions/ListAction';
3636
import { PublishAction } from './actions/PublishAction';
3737
import { PurgeAction } from './actions/PurgeAction';
38+
import { RemoveAction } from './actions/RemoveAction';
3839
import { ScanAction } from './actions/ScanAction';
3940
import { UnlinkAction } from './actions/UnlinkAction';
4041
import { UpdateAction } from './actions/UpdateAction';
@@ -254,6 +255,7 @@ export class RushCommandLineParser extends CommandLineParser {
254255
this.addAction(new ListAction(this));
255256
this.addAction(new PublishAction(this));
256257
this.addAction(new PurgeAction(this));
258+
this.addAction(new RemoveAction(this));
257259
this.addAction(new ScanAction(this));
258260
this.addAction(new SetupAction(this));
259261
this.addAction(new UnlinkAction(this));

libraries/rush-lib/src/cli/actions/AddAction.ts

Lines changed: 18 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,21 @@
33

44
import * as os from 'os';
55
import * as semver from 'semver';
6-
import { CommandLineFlagParameter, CommandLineStringListParameter } from '@rushstack/ts-command-line';
6+
import { CommandLineFlagParameter } from '@rushstack/ts-command-line';
77

8-
import { RushConfigurationProject } from '../../api/RushConfigurationProject';
9-
import { BaseRushAction } from './BaseRushAction';
8+
import { BaseAddAndRemoveAction } from './BaseAddAndRemoveAction';
109
import { RushCommandLineParser } from '../RushCommandLineParser';
1110
import { DependencySpecifier } from '../../logic/DependencySpecifier';
11+
import { RushConfigurationProject } from '../../api/RushConfigurationProject';
1212

13+
import { SemVerStyle } from '../../logic/PackageJsonUpdater';
1314
import type * as PackageJsonUpdaterType from '../../logic/PackageJsonUpdater';
1415

15-
export class AddAction extends BaseRushAction {
16-
private _allFlag!: CommandLineFlagParameter;
16+
export class AddAction extends BaseAddAndRemoveAction {
1717
private _exactFlag!: CommandLineFlagParameter;
1818
private _caretFlag!: CommandLineFlagParameter;
1919
private _devDependencyFlag!: CommandLineFlagParameter;
2020
private _makeConsistentFlag!: CommandLineFlagParameter;
21-
private _skipUpdateFlag!: CommandLineFlagParameter;
22-
private _packageNameList!: CommandLineStringListParameter;
2321

2422
public constructor(parser: RushCommandLineParser) {
2523
const documentation: string[] = [
@@ -76,48 +74,25 @@ export class AddAction extends BaseRushAction {
7674
'If specified, other packages with this dependency will have their package.json' +
7775
' files updated to use the same version of the dependency.'
7876
});
79-
this._skipUpdateFlag = this.defineFlagParameter({
80-
parameterLongName: '--skip-update',
81-
parameterShortName: '-s',
82-
description:
83-
'If specified, the "rush update" command will not be run after updating the package.json files.'
84-
});
8577
this._allFlag = this.defineFlagParameter({
8678
parameterLongName: '--all',
8779
description: 'If specified, the dependency will be added to all projects.'
8880
});
81+
super.onDefineParameters();
8982
}
9083

91-
public async runAsync(): Promise<void> {
92-
let projects: RushConfigurationProject[];
93-
if (this._allFlag.value) {
94-
projects = this.rushConfiguration.projects;
95-
} else {
96-
const currentProject: RushConfigurationProject | undefined =
97-
this.rushConfiguration.tryGetProjectForPath(process.cwd());
98-
99-
if (!currentProject) {
100-
throw new Error(
101-
'The "rush add" command must be invoked under a project' +
102-
` folder that is registered in rush.json unless the ${this._allFlag.longName} is used.`
103-
);
104-
}
105-
106-
projects = [currentProject];
107-
}
84+
public getUpdateOptions(): PackageJsonUpdaterType.IPackageJsonUpdaterRushAddOptions {
85+
const projects: RushConfigurationProject[] = super.getProjects();
10886

10987
if (this._caretFlag.value && this._exactFlag.value) {
11088
throw new Error(
11189
`Only one of "${this._caretFlag.longName}" and "${this._exactFlag.longName}" should be specified`
11290
);
11391
}
11492

115-
const packageJsonUpdater: typeof PackageJsonUpdaterType = await import('../../logic/PackageJsonUpdater');
116-
117-
const specifiedPackageNameList: ReadonlyArray<string> = this._packageNameList.values!;
11893
const packagesToAdd: PackageJsonUpdaterType.IPackageForRushAdd[] = [];
11994

120-
for (const specifiedPackageName of specifiedPackageNameList) {
95+
for (const specifiedPackageName of this.specifiedPackageNameList) {
12196
/**
12297
* Name & Version
12398
*/
@@ -157,30 +132,25 @@ export class AddAction extends BaseRushAction {
157132
);
158133
}
159134

160-
rangeStyle = packageJsonUpdater.SemVerStyle.Passthrough;
135+
rangeStyle = SemVerStyle.Passthrough;
161136
} else {
162137
rangeStyle = this._caretFlag.value
163-
? packageJsonUpdater.SemVerStyle.Caret
138+
? SemVerStyle.Caret
164139
: this._exactFlag.value
165-
? packageJsonUpdater.SemVerStyle.Exact
166-
: packageJsonUpdater.SemVerStyle.Tilde;
140+
? SemVerStyle.Exact
141+
: SemVerStyle.Tilde;
167142
}
168143

169144
packagesToAdd.push({ packageName, version, rangeStyle });
170145
}
171-
172-
const updater: PackageJsonUpdaterType.PackageJsonUpdater = new packageJsonUpdater.PackageJsonUpdater(
173-
this.rushConfiguration,
174-
this.rushGlobalFolder
175-
);
176-
177-
await updater.doRushAddAsync({
146+
return {
178147
projects: projects,
179-
packagesToAdd,
148+
packagesToUpdate: packagesToAdd,
180149
devDependency: this._devDependencyFlag.value,
181150
updateOtherPackages: this._makeConsistentFlag.value,
182151
skipUpdate: this._skipUpdateFlag.value,
183-
debugInstall: this.parser.isDebug
184-
});
152+
debugInstall: this.parser.isDebug,
153+
actionName: this.actionName
154+
};
185155
}
186156
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2+
// See LICENSE in the project root for license information.
3+
4+
import { CommandLineFlagParameter, CommandLineStringListParameter } from '@rushstack/ts-command-line';
5+
6+
import { BaseRushAction } from './BaseRushAction';
7+
import { RushConfigurationProject } from '../../api/RushConfigurationProject';
8+
import type * as PackageJsonUpdaterType from '../../logic/PackageJsonUpdater';
9+
10+
export interface IBasePackageJsonUpdaterRushOptions {
11+
/**
12+
* The projects whose package.jsons should get updated
13+
*/
14+
projects: RushConfigurationProject[];
15+
/**
16+
* The dependencies to be added.
17+
*/
18+
packagesToHandle: PackageJsonUpdaterType.IPackageForRushUpdate[];
19+
/**
20+
* If specified, "rush update" will not be run after updating the package.json file(s).
21+
*/
22+
skipUpdate: boolean;
23+
/**
24+
* If specified, "rush update" will be run in debug mode.
25+
*/
26+
debugInstall: boolean;
27+
}
28+
29+
/**
30+
* This is the common base class for AddAction and RemoveAction.
31+
*/
32+
export abstract class BaseAddAndRemoveAction extends BaseRushAction {
33+
protected _allFlag!: CommandLineFlagParameter;
34+
protected _skipUpdateFlag!: CommandLineFlagParameter;
35+
protected _packageNameList!: CommandLineStringListParameter;
36+
37+
protected get specifiedPackageNameList(): readonly string[] {
38+
return this._packageNameList.values!;
39+
}
40+
41+
protected abstract getUpdateOptions(): PackageJsonUpdaterType.IPackageJsonUpdaterRushBaseUpdateOptions;
42+
43+
protected onDefineParameters(): void {
44+
this._skipUpdateFlag = this.defineFlagParameter({
45+
parameterLongName: '--skip-update',
46+
parameterShortName: '-s',
47+
description:
48+
'If specified, the "rush update" command will not be run after updating the package.json files.'
49+
});
50+
}
51+
52+
protected getProjects(): RushConfigurationProject[] {
53+
if (this._allFlag.value) {
54+
return this.rushConfiguration.projects;
55+
} else {
56+
const currentProject: RushConfigurationProject | undefined =
57+
this.rushConfiguration.tryGetProjectForPath(process.cwd());
58+
59+
if (!currentProject) {
60+
throw new Error(
61+
`The rush "${this.actionName}" command must be invoked under a project` +
62+
` folder that is registered in rush.json unless the ${this._allFlag.longName} is used.`
63+
);
64+
}
65+
66+
return [currentProject];
67+
}
68+
}
69+
70+
public async runAsync(): Promise<void> {
71+
const packageJsonUpdater: typeof PackageJsonUpdaterType = await import('../../logic/PackageJsonUpdater');
72+
const updater: PackageJsonUpdaterType.PackageJsonUpdater = new packageJsonUpdater.PackageJsonUpdater(
73+
this.rushConfiguration,
74+
this.rushGlobalFolder
75+
);
76+
77+
await updater.doRushUpdateAsync(this.getUpdateOptions());
78+
}
79+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2+
// See LICENSE in the project root for license information.
3+
4+
import * as os from 'os';
5+
import { ConsoleTerminalProvider, Terminal, ITerminal } from '@rushstack/node-core-library';
6+
7+
import { BaseAddAndRemoveAction } from './BaseAddAndRemoveAction';
8+
import { RushCommandLineParser } from '../RushCommandLineParser';
9+
import { RushConfigurationProject } from '../../api/RushConfigurationProject';
10+
11+
import type * as PackageJsonUpdaterType from '../../logic/PackageJsonUpdater';
12+
13+
export class RemoveAction extends BaseAddAndRemoveAction {
14+
private _terminalProvider: ConsoleTerminalProvider;
15+
private _terminal: ITerminal;
16+
17+
public constructor(parser: RushCommandLineParser) {
18+
const documentation: string[] = [
19+
'Removes specified package(s) from the dependencies of the current project (as determined by the current working directory)' +
20+
' and then runs "rush update".'
21+
];
22+
super({
23+
actionName: 'remove',
24+
summary: 'Removes one or more dependencies from the package.json and runs rush update.',
25+
documentation: documentation.join(os.EOL),
26+
safeForSimultaneousRushProcesses: false,
27+
parser
28+
});
29+
this._terminalProvider = new ConsoleTerminalProvider();
30+
this._terminal = new Terminal(this._terminalProvider);
31+
}
32+
33+
public onDefineParameters(): void {
34+
this._packageNameList = this.defineStringListParameter({
35+
parameterLongName: '--package',
36+
parameterShortName: '-p',
37+
required: true,
38+
argumentName: 'PACKAGE',
39+
description:
40+
'The name of the package which should be removed.' +
41+
' To remove multiple packages, run "rush remove --package foo --package bar".'
42+
});
43+
this._allFlag = this.defineFlagParameter({
44+
parameterLongName: '--all',
45+
description: 'If specified, the dependency will be removed from all projects that declare it.'
46+
});
47+
super.onDefineParameters();
48+
}
49+
50+
public getUpdateOptions(): PackageJsonUpdaterType.IPackageJsonUpdaterRushRemoveOptions {
51+
const projects: RushConfigurationProject[] = super.getProjects();
52+
53+
const packagesToRemove: PackageJsonUpdaterType.IPackageForRushRemove[] = [];
54+
55+
for (const specifiedPackageName of this.specifiedPackageNameList) {
56+
/**
57+
* Name
58+
*/
59+
const packageName: string = specifiedPackageName;
60+
61+
if (!this.rushConfiguration.packageNameParser.isValidName(packageName)) {
62+
throw new Error(`The package name "${packageName}" is not valid.`);
63+
}
64+
65+
for (const project of projects) {
66+
if (
67+
!project.packageJsonEditor.tryGetDependency(packageName) &&
68+
!project.packageJsonEditor.tryGetDevDependency(packageName)
69+
) {
70+
this._terminal.writeLine(
71+
`The project "${project.packageName}" do not have ${packageName} in package.json.`
72+
);
73+
}
74+
}
75+
76+
packagesToRemove.push({ packageName });
77+
}
78+
79+
return {
80+
projects: projects,
81+
packagesToUpdate: packagesToRemove,
82+
skipUpdate: this._skipUpdateFlag.value,
83+
debugInstall: this.parser.isDebug,
84+
actionName: this.actionName
85+
};
86+
}
87+
}

libraries/rush-lib/src/cli/actions/test/AddAction.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe(AddAction.name, () => {
1515

1616
beforeEach(() => {
1717
doRushAddMock = jest
18-
.spyOn(PackageJsonUpdater.prototype, 'doRushAddAsync')
18+
.spyOn(PackageJsonUpdater.prototype, 'doRushUpdateAsync')
1919
.mockImplementation(() => Promise.resolve());
2020
jest.spyOn(process, 'exit').mockImplementation();
2121
oldExitCode = process.exitCode;
@@ -50,7 +50,7 @@ describe(AddAction.name, () => {
5050
const doRushAddOptions: IPackageJsonUpdaterRushAddOptions = doRushAddMock.mock.calls[0][0];
5151
expect(doRushAddOptions.projects).toHaveLength(1);
5252
expect(doRushAddOptions.projects[0].packageName).toEqual('a');
53-
expect(doRushAddOptions.packagesToAdd).toMatchInlineSnapshot(`
53+
expect(doRushAddOptions.packagesToUpdate).toMatchInlineSnapshot(`
5454
Array [
5555
Object {
5656
"packageName": "assert",
@@ -85,7 +85,7 @@ describe(AddAction.name, () => {
8585
expect(doRushAddOptions.projects).toHaveLength(2);
8686
expect(doRushAddOptions.projects[0].packageName).toEqual('a');
8787
expect(doRushAddOptions.projects[1].packageName).toEqual('b');
88-
expect(doRushAddOptions.packagesToAdd).toMatchInlineSnapshot(`
88+
expect(doRushAddOptions.packagesToUpdate).toMatchInlineSnapshot(`
8989
Array [
9090
Object {
9191
"packageName": 37A6 "assert",

0 commit comments

Comments
 (0)
0