Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI: Add pnpm support #19425

Merged
merged 4 commits into from
Oct 18, 2022
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
CLI: Add PNPM support (--use-pnpm)
IanVS committed Oct 10, 2022
commit 5fd8728ddcd0c8a277793da8da6812b867dfe433
8 changes: 6 additions & 2 deletions code/lib/cli/src/add.ts
Original file line number Diff line number Diff line change
@@ -65,8 +65,12 @@ const getVersionSpecifier = (addon: string) => {
* it will try to use the version specifier matching your current
* Storybook install version.
*/
export async function add(addon: string, options: { useNpm: boolean; skipPostinstall: boolean }) {
const packageManager = JsPackageManagerFactory.getPackageManager(options.useNpm);
export async function add(
addon: string,
options: { useNpm: boolean; usePnpm: boolean; skipPostinstall: boolean }
) {
const { useNpm, usePnpm } = options;
const packageManager = JsPackageManagerFactory.getPackageManager({ useNpm, usePnpm });
const packageJson = packageManager.retrievePackageJson();
const [addonName, versionSpecifier] = getVersionSpecifier(addon);

6 changes: 4 additions & 2 deletions code/lib/cli/src/automigrate/index.ts
Original file line number Diff line number Diff line change
@@ -12,10 +12,12 @@ interface FixOptions {
fixId?: string;
yes?: boolean;
dryRun?: boolean;
useNpm?: boolean;
usePnpm?: boolean;
}

export const automigrate = async ({ fixId, dryRun, yes }: FixOptions = {}) => {
const packageManager = JsPackageManagerFactory.getPackageManager();
export const automigrate = async ({ fixId, dryRun, yes, useNpm, usePnpm }: FixOptions = {}) => {
const packageManager = JsPackageManagerFactory.getPackageManager({ useNpm, usePnpm });
const filtered = fixId ? fixes.filter((f) => f.id === fixId) : fixes;

logger.info('🔎 checking possible migrations..');
10 changes: 8 additions & 2 deletions code/lib/cli/src/generate.ts
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import { sync as readUpSync } from 'read-pkg-up';

import { logger } from '@storybook/node-logger';

import { CommandOptions } from './generators/types';
import { initiate } from './initiate';
import { add } from './add';
import { migrate } from './migrate';
@@ -39,13 +40,14 @@ program
.option('-f --force', 'Force add Storybook')
.option('-s --skip-install', 'Skip installing deps')
.option('-N --use-npm', 'Use npm to install deps')
.option('--use-pnpm', 'Use pnpm to install deps')
.option('--use-pnp', 'Enable pnp mode')
.option('-p --parser <babel | babylon | flow | ts | tsx>', 'jscodeshift parser')
.option('-t --type <type>', 'Add Storybook for a specific project type')
.option('-y --yes', 'Answer yes to all prompts')
.option('-b --builder <builder>', 'Builder library')
.option('-b --builder <webpack5 | vite>', 'Builder library')
.option('-l --linkable', 'Prepare installation for link (contributor helper)')
.action((options) =>
.action((options: CommandOptions) =>
initiate(options, pkg).catch((err) => {
logger.error(err);
process.exit(1);
@@ -56,6 +58,7 @@ program
.command('add <addon>')
.description('Add an addon to your Storybook')
.option('-N --use-npm', 'Use NPM to build the Storybook server')
.option('--use-pnpm', 'Use PNPM to build the Storybook server')
.option('-s --skip-postinstall', 'Skip package specific postinstall config modifications')
.action((addonName, options) => add(addonName, options));

@@ -68,6 +71,7 @@ program
.command('upgrade')
.description('Upgrade your Storybook packages to the latest')
.option('-N --use-npm', 'Use NPM to build the Storybook server')
.option('--use-pnpm', 'Use PNPM to build the Storybook server')
.option('-y --yes', 'Skip prompting the user')
.option('-n --dry-run', 'Only check for upgrades, do not install')
.option('-p --prerelease', 'Upgrade to the pre-release packages')
@@ -177,6 +181,8 @@ program
.description('Check storybook for known problems or migrations and apply fixes')
.option('-y --yes', 'Skip prompting the user')
.option('-n --dry-run', 'Only check for fixes, do not actually run them')
.option('-N --use-npm', 'Use npm as package manager')
.option('--use-pnpm', 'Use pnpm as package manager')
.action((fixId, options) =>
automigrate({ fixId, ...options }).catch((e) => {
logger.error(e);
1 change: 1 addition & 0 deletions code/lib/cli/src/generators/types.ts
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ export type Generator = (

export type CommandOptions = {
useNpm?: boolean;
usePnpm?: boolean;
usePnp?: boolean;
type?: ProjectType;
force?: any;
5 changes: 3 additions & 2 deletions code/lib/cli/src/initiate.ts
Original file line number Diff line number Diff line change
@@ -251,7 +251,8 @@ const projectTypeInquirer = async (
};

export async function initiate(options: CommandOptions, pkg: Package): Promise<void> {
const packageManager = JsPackageManagerFactory.getPackageManager(options.useNpm);
const { useNpm, usePnpm } = options;
const packageManager = JsPackageManagerFactory.getPackageManager({ useNpm, usePnpm });
const welcomeMessage = 'storybook init - the simplest way to add a Storybook to your project.';
logger.log(chalk.inverse(`\n ${welcomeMessage} \n`));

@@ -307,7 +308,7 @@ export async function initiate(options: CommandOptions, pkg: Package): Promise<v
packageManager.installDependencies();
}

await automigrate({ yes: options.yes || process.env.CI === 'true' });
await automigrate({ yes: options.yes || process.env.CI === 'true', useNpm, usePnpm });

logger.log('\nTo run your Storybook, type:\n');
codeLog([packageManager.getRunStorybookCommand()]);
2 changes: 1 addition & 1 deletion code/lib/cli/src/js-package-manager/JsPackageManager.ts
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ interface JsPackageManagerOptions {
cwd?: string;
}
export abstract class JsPackageManager {
public abstract readonly type: 'npm' | 'yarn1' | 'yarn2';
public abstract readonly type: 'npm' | 'yarn1' | 'yarn2' | 'pnpm';

public abstract initPackageJson(): void;

259 changes: 190 additions & 69 deletions code/lib/cli/src/js-package-manager/JsPackageManagerFactory.test.ts
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import { sync as spawnSync } from 'cross-spawn';
import { sync as findUpSync } from 'find-up';
import { JsPackageManagerFactory } from './JsPackageManagerFactory';
import { NPMProxy } from './NPMProxy';
import { PNPMProxy } from './PNPMProxy';
import { Yarn1Proxy } from './Yarn1Proxy';
import { Yarn2Proxy } from './Yarn2Proxy';

@@ -16,121 +17,241 @@ describe('JsPackageManagerFactory', () => {
describe('getPackageManager', () => {
describe('return an NPM proxy', () => {
it('when `useNpm` option is used', () => {
expect(JsPackageManagerFactory.getPackageManager(true)).toBeInstanceOf(NPMProxy);
expect(JsPackageManagerFactory.getPackageManager({ useNpm: true })).toBeInstanceOf(
NPMProxy
);
});

it('when all package managers are ok, but only a `package-lock.json` file', () => {
spawnSyncMock.mockImplementation((command) => {
// Yarn is ok
if (command === 'yarn') {
return {
status: 0,
output: '1.22.4',
};
}
// NPM is ok
if (command === 'npm') {
return {
status: 0,
output: '6.5.12',
};
}
// PNPM is ok
if (command === 'pnpm') {
return {
status: 0,
output: '7.9.5',
};
}
// Unknown package manager is ko
return {
status: 1,
};
});

// There is only a package-lock.json
findUpSyncMock.mockImplementation((file) =>
file === 'package-lock.json' ? 'found' : undefined
);

expect(JsPackageManagerFactory.getPackageManager()).toBeInstanceOf(NPMProxy);
});
});

it('when NPM command is ok, Yarn is ok, there is no `yarn.lock` file', () => {
describe('return a PNPM proxy', () => {
it('when `usePnpm` option is used', () => {
expect(JsPackageManagerFactory.getPackageManager({ usePnpm: true })).toBeInstanceOf(
PNPMProxy
);
});

it('when all package managers are ok, but only a `pnpm-lock.yaml` file', () => {
spawnSyncMock.mockImplementation((command) => {
return command === 'yarn'
? {
// Yarn is ok
status: 0,
output: '1.22.4',
}
: {
// NPM is ok
status: 0,
output: '6.5.12',
};
// Yarn is ok
if (command === 'yarn') {
return {
status: 0,
output: '1.22.4',
};
}
// NPM is ok
if (command === 'npm') {
return {
status: 0,
output: '6.5.12',
};
}
// PNPM is ok
if (command === 'pnpm') {
return {
status: 0,
output: '7.9.5',
};
}
// Unknown package manager is ko
return {
status: 1,
};
});

// There is no yarn.lock
findUpSyncMock.mockImplementation((file) => (file === 'yarn.lock' ? undefined : ''));
// There is only a pnpm-lock.yaml
findUpSyncMock.mockImplementation((file) => {
if (file === 'pnpm-lock.yaml') {
return 'found';
}
return undefined;
});

expect(JsPackageManagerFactory.getPackageManager(false)).toBeInstanceOf(NPMProxy);
expect(JsPackageManagerFactory.getPackageManager()).toBeInstanceOf(PNPMProxy);
});
});

describe('return a Yarn 1 proxy', () => {
it('when Yarn command is ok, Yarn version is <2, NPM is ko', () => {
it('when Yarn command is ok, Yarn version is <2, NPM is ko, PNPM is ko', () => {
spawnSyncMock.mockImplementation((command) => {
return command === 'yarn'
? {
// Yarn is ok
status: 0,
output: '1.22.4',
}
: {
// NPM is ko
status: 1,
};
// Yarn is ok
if (command === 'yarn') {
return {
status: 0,
output: '1.22.4',
};
}
// NPM is ko
if (command === 'npm') {
return {
status: 1,
};
}
// PNPM is ko
if (command === 'pnpm') {
return {
status: 1,
};
}
// Unknown package manager is ko
return {
status: 1,
};
});

// there is no
// there is no lockfile
findUpSyncMock.mockReturnValue(undefined);

expect(JsPackageManagerFactory.getPackageManager(false)).toBeInstanceOf(Yarn1Proxy);
expect(JsPackageManagerFactory.getPackageManager()).toBeInstanceOf(Yarn1Proxy);
});

it('when Yarn command is ok, Yarn version is <2, NPM is ok, there is a `yarn.lock` file', () => {
it('when Yarn command is ok, Yarn version is <2, NPM and PNPM are ok, there is a `yarn.lock` file', () => {
spawnSyncMock.mockImplementation((command) => {
return command === 'yarn'
? {
// Yarn is ok
status: 0,
output: '1.22.4',
}
: {
// NPM is ok
status: 0,
output: '6.5.12',
};
// Yarn is ok
if (command === 'yarn') {
return {
status: 0,
output: '1.22.4',
};
}
// NPM is ok
if (command === 'npm') {
return {
status: 0,
output: '6.5.12',
};
}
// PNPM is ok
if (command === 'pnpm') {
return {
status: 0,
output: '7.9.5',
};
}
// Unknown package manager is ko
return {
status: 1,
};
});

// There is a yarn.lock
findUpSyncMock.mockImplementation((file) =>
file === 'yarn.lock' ? '/Users/johndoe/Documents/yarn.lock' : undefined
);

expect(JsPackageManagerFactory.getPackageManager(false)).toBeInstanceOf(Yarn1Proxy);
expect(JsPackageManagerFactory.getPackageManager()).toBeInstanceOf(Yarn1Proxy);
});
});

describe('return a Yarn 2 proxy', () => {
it('when Yarn command is ok, Yarn version is >=2, NPM is ko', () => {
it('when Yarn command is ok, Yarn version is >=2, NPM is ko, PNPM is ko', () => {
spawnSyncMock.mockImplementation((command) => {
return command === 'yarn'
? {
// Yarn is ok
status: 0,
output: '2.0.0-rc.33',
}
: {
// NPM is ko
status: 1,
};
// Yarn is ok
if (command === 'yarn') {
return {
status: 0,
output: '2.0.0-rc.33',
};
}
// NPM is ko
if (command === 'npm') {
return {
status: 1,
};
}
// PNPM is ko
if (command === 'pnpm') {
return {
status: 1,
};
}
// Unknown package manager is ko
return {
status: 1,
};
});

expect(JsPackageManagerFactory.getPackageManager(false)).toBeInstanceOf(Yarn2Proxy);
expect(JsPackageManagerFactory.getPackageManager()).toBeInstanceOf(Yarn2Proxy);
});

it('when Yarn command is ok, Yarn version is >=2, NPM is ok, there is a `yarn.lock` file', () => {
it('when Yarn command is ok, Yarn version is >=2, NPM and PNPM are ok, there is a `yarn.lock` file', () => {
spawnSyncMock.mockImplementation((command) => {
return command === 'yarn'
? {
// Yarn is ok
status: 0,
output: '2.0.0-rc.33',
}
: {
// NPM is ok
status: 0,
output: '6.5.12',
};
// Yarn is ok
if (command === 'yarn') {
return {
status: 0,
output: '2.0.0-rc.33',
};
}
// NPM is ok
if (command === 'npm') {
return {
status: 0,
output: '6.5.12',
};
}
// PNPM is ok
if (command === 'pnpm') {
return {
status: 0,
output: '7.9.5',
};
}
// Unknown package manager is ko
return {
status: 1,
};
});

// There is a yarn.lock
findUpSyncMock.mockImplementation((file) =>
file === 'yarn.lock' ? '/Users/johndoe/Documents/yarn.lock' : undefined
);

expect(JsPackageManagerFactory.getPackageManager(false)).toBeInstanceOf(Yarn2Proxy);
expect(JsPackageManagerFactory.getPackageManager()).toBeInstanceOf(Yarn2Proxy);
});
});

it('throws an error if Yarn is ko and NPM is ko', () => {
it('throws an error if Yarn, NPM, and PNPM are not found', () => {
spawnSyncMock.mockReturnValue({ status: 1 });
expect(() => JsPackageManagerFactory.getPackageManager(false)).toThrow();
expect(() => JsPackageManagerFactory.getPackageManager()).toThrow();
});
});
});
28 changes: 23 additions & 5 deletions code/lib/cli/src/js-package-manager/JsPackageManagerFactory.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,43 @@
import { sync as spawnSync } from 'cross-spawn';
import { sync as findUpSync } from 'find-up';
import { NPMProxy } from './NPMProxy';
import { PNPMProxy } from './PNPMProxy';
import { JsPackageManager } from './JsPackageManager';
import { Yarn2Proxy } from './Yarn2Proxy';
import { Yarn1Proxy } from './Yarn1Proxy';

export class JsPackageManagerFactory {
public static getPackageManager(forceNpmUsage = false, cwd?: string): JsPackageManager {
if (forceNpmUsage) {
public static getPackageManager(
{ usePnpm, useNpm }: { usePnpm?: boolean; useNpm?: boolean } = {},
cwd?: string
): JsPackageManager {
if (useNpm) {
return new NPMProxy({ cwd });
}
if (usePnpm) {
return new PNPMProxy({ cwd });
}

const yarnVersion = getYarnVersion(cwd);
const hasYarnLockFile = findUpSync('yarn.lock', { cwd });
const hasYarnLockFile = Boolean(findUpSync('yarn.lock', { cwd }));
const hasPNPMLockFile = Boolean(findUpSync('pnpm-lock.yaml', { cwd }));

const hasNPMCommand = hasNPM(cwd);
const hasPNPMCommand = hasPNPM(cwd);

if (yarnVersion && (hasYarnLockFile || !hasNPMCommand)) {
if (yarnVersion && (hasYarnLockFile || (!hasNPMCommand && !hasPNPMCommand))) {
return yarnVersion === 1 ? new Yarn1Proxy({ cwd }) : new Yarn2Proxy({ cwd });
}

if (hasPNPMCommand && hasPNPMLockFile) {
return new PNPMProxy({ cwd });
}

if (hasNPMCommand) {
return new NPMProxy({ cwd });
}

throw new Error('Unable to find a usable package manager within NPM, Yarn and Yarn 2');
throw new Error('Unable to find a usable package manager within NPM, PNPM, Yarn and Yarn 2');
}
}

@@ -33,6 +46,11 @@ function hasNPM(cwd?: string) {
return npmVersionCommand.status === 0;
}

function hasPNPM(cwd?: string) {
const pnpmVersionCommand = spawnSync('pnpm', ['--version'], { cwd, shell: true });
return pnpmVersionCommand.status === 0;
}

function getYarnVersion(cwd?: string): 1 | 2 | undefined {
const yarnVersionCommand = spawnSync('yarn', ['--version'], { cwd, shell: true });

204 changes: 204 additions & 0 deletions code/lib/cli/src/js-package-manager/PNPMProxy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import { PNPMProxy } from './PNPMProxy';

describe('NPM Proxy', () => {
let pnpmProxy: PNPMProxy;

beforeEach(() => {
pnpmProxy = new PNPMProxy();
});

it('type should be npm', () => {
expect(pnpmProxy.type).toEqual('pnpm');
});

describe('initPackageJson', () => {
it('should run `npm init -y`', () => {
const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('');

pnpmProxy.initPackageJson();

expect(executeCommandSpy).toHaveBeenCalledWith('pnpm', ['init', '-y']);
});
});

describe('setRegistryUrl', () => {
it('should run `npm config set registry https://foo.bar`', () => {
const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('');

pnpmProxy.setRegistryURL('https://foo.bar');

expect(executeCommandSpy).toHaveBeenCalledWith('npm', [
'config',
'set',
'registry',
'https://foo.bar',
]);
});
});

describe('installDependencies', () => {
it('should run `pnpm install`', () => {
const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('7.1.0');

pnpmProxy.installDependencies();

expect(executeCommandSpy).toHaveBeenLastCalledWith('pnpm', ['install'], expect.any(String));
});
});

describe('addDependencies', () => {
it('with devDep it should run `pnpm add -D @storybook/addons`', () => {
const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('6.0.0');

pnpmProxy.addDependencies({ installAsDevDependencies: true }, ['@storybook/addons']);

expect(executeCommandSpy).toHaveBeenLastCalledWith(
'pnpm',
['add', '-D', '@storybook/addons'],
expect.any(String)
);
});
});

describe('removeDependencies', () => {
it('with devDep it should run `npm uninstall @storybook/addons`', () => {
const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('6.0.0');

pnpmProxy.removeDependencies({}, ['@storybook/addons']);

expect(executeCommandSpy).toHaveBeenLastCalledWith(
'pnpm',
['remove', '@storybook/addons'],
expect.any(String)
);
});

describe('skipInstall', () => {
it('should only change package.json without running install', () => {
const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('7.0.0');
const writePackageSpy = jest
.spyOn(pnpmProxy, 'writePackageJson')
.mockImplementation(jest.fn);

pnpmProxy.removeDependencies(
{
skipInstall: true,
packageJson: {
devDependencies: {
'@storybook/manager-webpack5': 'x.x.x',
'@storybook/react': 'x.x.x',
},
},
},
['@storybook/manager-webpack5']
);

expect(writePackageSpy).toHaveBeenCalledWith({
devDependencies: {
'@storybook/react': 'x.x.x',
},
});
expect(executeCommandSpy).not.toHaveBeenCalled();
});
});
});

describe('latestVersion', () => {
it('without constraint it returns the latest version', async () => {
const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('"5.3.19"');

const version = await pnpmProxy.latestVersion('@storybook/addons');

expect(executeCommandSpy).toHaveBeenCalledWith('pnpm', [
'info',
'@storybook/addons',
'version',
'--json',
]);
expect(version).toEqual('5.3.19');
});

it('with constraint it returns the latest version satisfying the constraint', async () => {
const executeCommandSpy = jest
.spyOn(pnpmProxy, 'executeCommand')
.mockReturnValue('["4.25.3","5.3.19","6.0.0-beta.23"]');

const version = await pnpmProxy.latestVersion('@storybook/addons', '5.X');

expect(executeCommandSpy).toHaveBeenCalledWith('pnpm', [
'info',
'@storybook/addons',
'versions',
'--json',
]);
expect(version).toEqual('5.3.19');
});

it('throws an error if command output is not a valid JSON', async () => {
jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('NOT A JSON');

await expect(pnpmProxy.latestVersion('@storybook/addons')).rejects.toThrow();
});
});

describe('getVersion', () => {
it('with a Storybook package listed in versions.json it returns the version', async () => {
// eslint-disable-next-line global-require
const storybookAngularVersion = require('../versions').default['@storybook/angular'];
const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('"5.3.19"');

const version = await pnpmProxy.getVersion('@storybook/angular');

expect(executeCommandSpy).toHaveBeenCalledWith('pnpm', [
'info',
'@storybook/angular',
'version',
'--json',
]);
expect(version).toEqual(`^${storybookAngularVersion}`);
});

it('with a Storybook package not listed in versions.json it returns the latest version', async () => {
const packageVersion = '5.3.19';
const executeCommandSpy = jest
.spyOn(pnpmProxy, 'executeCommand')
.mockReturnValue(`"${packageVersion}"`);

const version = await pnpmProxy.getVersion('@storybook/react-native');

expect(executeCommandSpy).toHaveBeenCalledWith('pnpm', [
'info',
'@storybook/react-native',
'version',
'--json',
]);
expect(version).toEqual(`^${packageVersion}`);
});
});

describe('addPackageResolutions', () => {
it('adds resolutions to package.json and account for existing resolutions', () => {
const writePackageSpy = jest.spyOn(pnpmProxy, 'writePackageJson').mockImplementation(jest.fn);

jest.spyOn(pnpmProxy, 'retrievePackageJson').mockImplementation(
jest.fn(() => ({
overrides: {
bar: 'x.x.x',
},
}))
);

const versions = {
foo: 'x.x.x',
};
pnpmProxy.addPackageResolutions(versions);

expect(writePackageSpy).toHaveBeenCalledWith({
overrides: {
...versions,
bar: 'x.x.x',
},
});
});
});
});
77 changes: 77 additions & 0 deletions code/lib/cli/src/js-package-manager/PNPMProxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { JsPackageManager } from './JsPackageManager';
import type { PackageJson } from './PackageJson';

export class PNPMProxy extends JsPackageManager {
readonly type = 'pnpm';

installArgs: string[] | undefined;

uninstallArgs: string[] | undefined;

initPackageJson() {
return this.executeCommand('pnpm', ['init', '-y']);
}

getRunStorybookCommand(): string {
return 'pnpm run storybook';
}

getRunCommand(command: string): string {
return `pnpm run ${command}`;
}

getPnpmVersion(): string {
return this.executeCommand('pnpm', ['--version']);
}

protected getResolutions(packageJson: PackageJson, versions: Record<string, string>) {
return {
overrides: {
...packageJson.overrides,
...versions,
},
};
}

protected runInstall(): void {
this.executeCommand('pnpm', ['install'], 'inherit');
}

protected runAddDeps(dependencies: string[], installAsDevDependencies: boolean): void {
let args = [...dependencies];

if (installAsDevDependencies) {
args = ['-D', ...args];
}

this.executeCommand('pnpm', ['add', ...args], 'inherit');
}

protected runRemoveDeps(dependencies: string[]): void {
const args = [...dependencies];

this.executeCommand('pnpm', ['remove', ...args], 'inherit');
}

protected runGetVersions<T extends boolean>(
packageName: string,
fetchAllVersions: T
): Promise<T extends true ? string[] : string> {
const args = [fetchAllVersions ? 'versions' : 'version', '--json'];

const commandResult = this.executeCommand('pnpm', ['info', packageName, ...args]);

try {
const parsedOutput = JSON.parse(commandResult);

if (parsedOutput.error) {
// FIXME: improve error handling
throw new Error(parsedOutput.error.summary);
} else {
return parsedOutput;
}
} catch (e) {
throw new Error(`Unable to find versions of ${packageName} using pnpm`);
}
}
}
1 change: 1 addition & 0 deletions code/lib/cli/src/js-package-manager/Yarn2Proxy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { JsPackageManager } from './JsPackageManager';
import type { PackageJson } from './PackageJson';

// This encompasses both yarn 2 and yarn 3
export class Yarn2Proxy extends JsPackageManager {
readonly type = 'yarn2';

6 changes: 4 additions & 2 deletions code/lib/cli/src/upgrade.ts
Original file line number Diff line number Diff line change
@@ -141,6 +141,7 @@ interface UpgradeOptions {
prerelease: boolean;
skipCheck: boolean;
useNpm: boolean;
usePnpm: boolean;
dryRun: boolean;
yes: boolean;
disableTelemetry: boolean;
@@ -150,11 +151,12 @@ export const upgrade = async ({
prerelease,
skipCheck,
useNpm,
usePnpm,
dryRun,
yes,
...options
}: UpgradeOptions) => {
const packageManager = JsPackageManagerFactory.getPackageManager(useNpm);
const packageManager = JsPackageManagerFactory.getPackageManager({ useNpm, usePnpm });

commandLog(`Checking for latest versions of '@storybook/*' packages`);
if (!options.disableTelemetry) {
@@ -179,6 +181,6 @@ export const upgrade = async ({

if (!skipCheck) {
checkVersionConsistency();
await automigrate({ dryRun, yes });
await automigrate({ dryRun, yes, useNpm, usePnpm });
}
};
2 changes: 1 addition & 1 deletion scripts/next-repro-generators/generate-repros.ts
Original file line number Diff line number Diff line change
@@ -54,7 +54,7 @@ const addStorybook = async (baseDir: string, localRegistry: boolean) => {

await copy(beforeDir, tmpDir);

const packageManager = JsPackageManagerFactory.getPackageManager(false, tmpDir);
const packageManager = JsPackageManagerFactory.getPackageManager({}, tmpDir);
if (localRegistry) {
await withLocalRegistry(packageManager, async () => {
packageManager.addPackageResolutions(storybookVersions);
2 changes: 1 addition & 1 deletion scripts/tasks/sandbox-parts.ts
Original file line number Diff line number Diff line change
@@ -292,7 +292,7 @@ function addExtraDependencies({
const extraDeps = ['@storybook/jest', '@storybook/testing-library@0.0.14-next.0'];
if (debug) logger.log('🎁 Adding extra deps', extraDeps);
if (!dryRun) {
const packageManager = JsPackageManagerFactory.getPackageManager(false, cwd);
const packageManager = JsPackageManagerFactory.getPackageManager({}, cwd);
packageManager.addDependencies({ installAsDevDependencies: true }, extraDeps);
}
}