From 60e02abd8d32bc10d74e9a8b10723fee296ecea5 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Mon, 25 Jul 2022 10:21:09 +0200 Subject: [PATCH] chore(cli): add uninstall method to jspackagemanager --- .../js-package-manager/JsPackageManager.ts | 41 +++++++++++++++++++ .../src/js-package-manager/NPMProxy.test.ts | 29 +++++++++++++ .../cli/src/js-package-manager/NPMProxy.ts | 17 ++++++++ .../src/js-package-manager/Yarn1Proxy.test.ts | 16 ++++++++ .../cli/src/js-package-manager/Yarn1Proxy.ts | 6 +++ .../src/js-package-manager/Yarn2Proxy.test.ts | 17 +++++++- .../cli/src/js-package-manager/Yarn2Proxy.ts | 6 +++ 7 files changed, 131 insertions(+), 1 deletion(-) diff --git a/code/lib/cli/src/js-package-manager/JsPackageManager.ts b/code/lib/cli/src/js-package-manager/JsPackageManager.ts index a2ba393e495..efd00173c62 100644 --- a/code/lib/cli/src/js-package-manager/JsPackageManager.ts +++ b/code/lib/cli/src/js-package-manager/JsPackageManager.ts @@ -130,6 +130,45 @@ export abstract class JsPackageManager { } } + /** + * Remove dependencies from a project using `yarn remove` or `npm uninstall`. + * + * @param {Object} options contains `skipInstall`, `packageJson` and `installAsDevDependencies` which we use to determine how we install packages. + * @param {Array} dependencies contains a list of packages to remove. + * @example + * removeDependencies(options, [ + * `@storybook/react`, + * `@storybook/addon-actions`, + * ]); + */ + public removeDependencies( + options: { + skipInstall?: boolean; + packageJson?: PackageJson; + }, + dependencies: string[] + ): void { + const { skipInstall } = options; + + if (skipInstall) { + const { packageJson } = options; + + dependencies.forEach((dep) => { + delete packageJson[dep]; + }, {}); + + writePackageJson(packageJson); + } else { + try { + this.runRemoveDeps(dependencies); + } catch (e) { + logger.error('An error occurred while removing dependencies.'); + logger.log(e.message); + process.exit(1); + } + } + } + /** * Return an array of strings matching following format: `@` * @@ -268,6 +307,8 @@ export abstract class JsPackageManager { protected abstract runAddDeps(dependencies: string[], installAsDevDependencies: boolean): void; + protected abstract runRemoveDeps(dependencies: string[]): void; + /** * Get the latest or all versions of the input package available on npmjs.com * diff --git a/code/lib/cli/src/js-package-manager/NPMProxy.test.ts b/code/lib/cli/src/js-package-manager/NPMProxy.test.ts index 601f990b35b..dd4da9507d0 100644 --- a/code/lib/cli/src/js-package-manager/NPMProxy.test.ts +++ b/code/lib/cli/src/js-package-manager/NPMProxy.test.ts @@ -75,6 +75,35 @@ describe('NPM Proxy', () => { }); }); + describe('removeDependencies', () => { + describe('npm6', () => { + it('with devDep it should run `npm uninstall @storybook/addons`', () => { + const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('6.0.0'); + + npmProxy.removeDependencies({}, ['@storybook/addons']); + + expect(executeCommandSpy).toHaveBeenLastCalledWith( + 'npm', + ['uninstall', '@storybook/addons'], + expect.any(String) + ); + }); + }); + describe('npm7', () => { + it('with devDep it should run `npm uninstall @storybook/addons`', () => { + const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('7.0.0'); + + npmProxy.removeDependencies({}, ['@storybook/addons']); + + expect(executeCommandSpy).toHaveBeenLastCalledWith( + 'npm', + ['uninstall', '--legacy-peer-deps', '@storybook/addons'], + expect.any(String) + ); + }); + }); + }); + describe('latestVersion', () => { it('without constraint it returns the latest version', async () => { const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('"5.3.19"'); diff --git a/code/lib/cli/src/js-package-manager/NPMProxy.ts b/code/lib/cli/src/js-package-manager/NPMProxy.ts index 8f74cf57e44..5c9f76aaae5 100644 --- a/code/lib/cli/src/js-package-manager/NPMProxy.ts +++ b/code/lib/cli/src/js-package-manager/NPMProxy.ts @@ -6,6 +6,8 @@ export class NPMProxy extends JsPackageManager { installArgs: string[] | undefined; + uninstallArgs: string[] | undefined; + initPackageJson() { return this.executeCommand('npm', ['init', '-y']); } @@ -49,6 +51,15 @@ export class NPMProxy extends JsPackageManager { return this.installArgs; } + getUninstallArgs(): string[] { + if (!this.uninstallArgs) { + this.uninstallArgs = this.needsLegacyPeerDeps(this.getNpmVersion()) + ? ['uninstall', '--legacy-peer-deps'] + : ['uninstall']; + } + return this.uninstallArgs; + } + protected runInstall(): void { this.executeCommand('npm', this.getInstallArgs(), 'inherit'); } @@ -63,6 +74,12 @@ export class NPMProxy extends JsPackageManager { this.executeCommand('npm', [...this.getInstallArgs(), ...args], 'inherit'); } + protected runRemoveDeps(dependencies: string[]): void { + const args = [...dependencies]; + + this.executeCommand('npm', [...this.getUninstallArgs(), ...args], 'inherit'); + } + protected runGetVersions( packageName: string, fetchAllVersions: T diff --git a/code/lib/cli/src/js-package-manager/Yarn1Proxy.test.ts b/code/lib/cli/src/js-package-manager/Yarn1Proxy.test.ts index b89c8b96543..b705c903164 100644 --- a/code/lib/cli/src/js-package-manager/Yarn1Proxy.test.ts +++ b/code/lib/cli/src/js-package-manager/Yarn1Proxy.test.ts @@ -45,6 +45,22 @@ describe('Yarn 1 Proxy', () => { }); }); + describe('removeDependencies', () => { + it('should run `yarn remove --ignore-workspace-root-check @storybook/addons`', () => { + const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockReturnValue(''); + + yarn1Proxy.removeDependencies({}, ['@storybook/addons']); + + expect(executeCommandSpy).toHaveBeenCalledWith( + 'yarn', + ['remove', '--ignore-workspace-root-check', '@storybook/addons'], + expect.any(String) + ); + }); + + it.todo('with devDep it should update package json without running yarn remove'); + }); + describe('latestVersion', () => { it('without constraint it returns the latest version', async () => { const executeCommandSpy = jest diff --git a/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts b/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts index 345a96a2599..88368e3f0ea 100644 --- a/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts +++ b/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts @@ -29,6 +29,12 @@ export class Yarn1Proxy extends JsPackageManager { this.executeCommand('yarn', ['add', ...args], 'inherit'); } + protected runRemoveDeps(dependencies: string[]): void { + const args = ['--ignore-workspace-root-check', ...dependencies]; + + this.executeCommand('yarn', ['remove', ...args], 'inherit'); + } + protected runGetVersions( packageName: string, fetchAllVersions: T diff --git a/code/lib/cli/src/js-package-manager/Yarn2Proxy.test.ts b/code/lib/cli/src/js-package-manager/Yarn2Proxy.test.ts index 7cfa2652774..74d4e7cf01a 100644 --- a/code/lib/cli/src/js-package-manager/Yarn2Proxy.test.ts +++ b/code/lib/cli/src/js-package-manager/Yarn2Proxy.test.ts @@ -1,6 +1,6 @@ import { Yarn2Proxy } from './Yarn2Proxy'; -describe('Yarn 1 Proxy', () => { +describe('Yarn 2 Proxy', () => { let yarn2Proxy: Yarn2Proxy; beforeEach(() => { @@ -45,6 +45,21 @@ describe('Yarn 1 Proxy', () => { }); }); + describe('removeDependencies', () => { + it('it should run `yarn remove @storybook/addons`', () => { + const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockReturnValue(''); + + yarn2Proxy.removeDependencies({}, ['@storybook/addons']); + + expect(executeCommandSpy).toHaveBeenCalledWith( + 'yarn', + ['remove', '@storybook/addons'], + expect.any(String) + ); + }); + it.todo('with devDep it should update package json without running yarn remove'); + }); + describe('latestVersion', () => { it('without constraint it returns the latest version', async () => { const executeCommandSpy = jest diff --git a/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts b/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts index adae2865dbc..133c384e00b 100644 --- a/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts +++ b/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts @@ -29,6 +29,12 @@ export class Yarn2Proxy extends JsPackageManager { this.executeCommand('yarn', ['add', ...args], 'inherit'); } + protected runRemoveDeps(dependencies: string[]): void { + const args = [...dependencies]; + + this.executeCommand('yarn', ['remove', ...args], 'inherit'); + } + protected runGetVersions( packageName: string, fetchAllVersions: T