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 "storybook scripts 7.0" automigrate command #18769

Merged
merged 1 commit into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions code/lib/cli/src/automigrate/fixes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { mainjsFramework } from './mainjsFramework';
import { eslintPlugin } from './eslint-plugin';
import { builderVite } from './builder-vite';
import { npm7 } from './npm7';
import { sbScripts } from './sb-scripts';
import { Fix } from '../types';

export * from '../types';
Expand All @@ -18,4 +19,5 @@ export const fixes: Fix[] = [
eslintPlugin,
builderVite,
npm7,
sbScripts,
];
212 changes: 212 additions & 0 deletions code/lib/cli/src/automigrate/fixes/sb-scripts.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import { JsPackageManager } from '../../js-package-manager';
import { sbScripts, getStorybookScripts } from './sb-scripts';

const checkSbScripts = async ({ packageJson }) => {
const packageManager = {
retrievePackageJson: () => ({ dependencies: {}, devDependencies: {}, ...packageJson }),
} as JsPackageManager;
return sbScripts.check({ packageManager });
};

describe('getStorybookScripts', () => {
it('detects default storybook scripts', () => {
expect(
getStorybookScripts({
start: 'server start',
storybook: 'start-storybook',
'build-storybook': 'build-storybook',
})
).toEqual({
official: {
storybook: 'start-storybook',
'build-storybook': 'build-storybook',
},
custom: {},
});
});

it('skips non-storybook scripts', () => {
expect(
getStorybookScripts({
start: 'server start',
'storybook:start-ci': 'CI=true yarn start-storybook',
'storybook:build-ci': 'CI=true yarn build-storybook',
})
).toEqual({
custom: {
'storybook:start-ci': 'CI=true yarn start-storybook',
'storybook:build-ci': 'CI=true yarn build-storybook',
},
official: {},
});
});

it('works with custom storybook scripts', () => {
expect(
getStorybookScripts({
'sb:start': 'start-storybook',
'sb:mocked': 'MOCKS=true start-storybook',
'sb:build': 'build-storybook',
})
).toEqual({
custom: {
'sb:start': 'start-storybook',
'sb:mocked': 'MOCKS=true start-storybook',
'sb:build': 'build-storybook',
},
official: {},
});
});
});

describe('sb scripts fix', () => {
describe('sb < 7.0', () => {
describe('does nothing', () => {
const packageJson = { dependencies: { '@storybook/react': '^6.2.0' } };
it('should no-op', async () => {
await expect(
checkSbScripts({
packageJson,
})
).resolves.toBeFalsy();
});
});
});
describe('sb >= 7.0', () => {
describe('with old scripts', () => {
const packageJson = {
dependencies: {
'@storybook/react': '^7.0.0-alpha.0',
},
scripts: {
storybook: 'start-storybook -p 6006',
'build-storybook': 'build-storybook -o build/storybook',
},
};
it('should update scripts to new format', async () => {
await expect(
checkSbScripts({
packageJson,
})
).resolves.toEqual({
storybookScripts: {
official: {
storybook: 'storybook dev -p 6006',
'build-storybook': 'storybook build -o build/storybook',
},
custom: {},
},
storybookVersion: '^7.0.0-alpha.0',
});
});
});

describe('with old custom scripts', () => {
const packageJson = {
dependencies: {
'@storybook/react': '^7.0.0-alpha.0',
},
scripts: {
'sb:start': 'start-storybook -p 6006',
'sb:mocked': 'MOCKS=true sb:start',
'sb:start-ci': 'sb:start --ci',
'sb:build': 'build-storybook -o buid/storybook',
'sb:build-mocked': 'MOCKS=true sb:build',
'test-storybook:ci':
'concurrently -k -s first -n "SB,TEST" -c "magenta,blue" "yarn build-storybook --quiet && npx http-server storybook-static --port 6006 --silent" "wait-on tcp:6006 && yarn test-storybook"',
},
};

it('should update scripts to new format', async () => {
await expect(
checkSbScripts({
packageJson,
})
).resolves.toEqual({
storybookScripts: {
custom: {
'sb:start': 'start-storybook -p 6006',
'sb:build': 'build-storybook -o buid/storybook',
'test-storybook:ci':
'concurrently -k -s first -n "SB,TEST" -c "magenta,blue" "yarn build-storybook --quiet && npx http-server storybook-static --port 6006 --silent" "wait-on tcp:6006 && yarn test-storybook"',
},
official: {},
},
storybookVersion: '^7.0.0-alpha.0',
});
});

describe('with old official and custom scripts', () => {
const packageJson = {
dependencies: {
'@storybook/react': '^7.0.0-alpha.0',
},
scripts: {
storybook: 'start-storybook -p 6006',
'storybook:mocked': 'MOCKS=true storybook',
'storybook:ci': 'yarn storybook --ci',
'storybook:build': 'build-storybook -o buid/storybook',
'storybook:build-mocked': 'MOCKS=true yarn storybook:build',
'test-storybook:ci':
'concurrently -k -s first -n "SB,TEST" -c "magenta,blue" "yarn build-storybook --quiet && npx http-server storybook-static --port 6006 --silent" "wait-on tcp:6006 && yarn test-storybook"',
},
};
it('should update scripts to new format', async () => {
await expect(
checkSbScripts({
packageJson,
})
).resolves.toEqual({
storybookScripts: {
custom: {
'storybook:build': 'build-storybook -o buid/storybook',
'test-storybook:ci':
'concurrently -k -s first -n "SB,TEST" -c "magenta,blue" "yarn build-storybook --quiet && npx http-server storybook-static --port 6006 --silent" "wait-on tcp:6006 && yarn test-storybook"',
},
official: {
storybook: 'storybook dev -p 6006',
},
},
storybookVersion: '^7.0.0-alpha.0',
});
});
});

describe('with storybook lib installed', () => {
const packageJson = {
dependencies: {
'@storybook/react': '^7.0.0-alpha.0',
storybook: '^7.0.0-alpha.0',
},
};
it('should no-op', async () => {
await expect(
checkSbScripts({
packageJson,
})
).resolves.toBeFalsy();
});
});

describe('already containing new scripts', () => {
const packageJson = {
dependencies: {
'@storybook/react': '^7.0.0-alpha.0',
storybook: '^7.0.0-alpha.0',
},
scripts: {
storybook: 'npx sb dev -p 6006',
'build-storybook': 'npx sb build -o build/storybook',
},
};
it('should no-op', async () => {
await expect(
checkSbScripts({
packageJson,
})
).resolves.toBeFalsy();
});
});
});
});
});
144 changes: 144 additions & 0 deletions code/lib/cli/src/automigrate/fixes/sb-scripts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import chalk from 'chalk';
import { dedent } from 'ts-dedent';
import semver from '@storybook/semver';
import { getStorybookInfo } from '@storybook/core-common';
import { Fix } from '../types';

interface SbScriptsRunOptions {
storybookScripts: {
custom: Record<string, string>;
official: Record<string, string>;
};
storybookVersion: string;
}

const logger = console;

export const getStorybookScripts = (scripts: Record<string, string>) => {
const storybookScripts: SbScriptsRunOptions['storybookScripts'] = {
custom: {},
official: {},
};

Object.keys(scripts).forEach((key) => {
if (key === 'storybook' || key === 'build-storybook') {
storybookScripts.official[key] = scripts[key];
} else if (scripts[key].match(/start-storybook/) || scripts[key].match(/build-storybook/)) {
storybookScripts.custom[key] = scripts[key];
}
});

return storybookScripts;
};

/**
* Is the user using start-storybook
*
* If so:
* - Add storybook dependency
* - Change start-storybook and build-storybook scripts
*/
export const sbScripts: Fix<SbScriptsRunOptions> = {
id: 'sb-scripts',

async check({ packageManager }) {
const packageJson = packageManager.retrievePackageJson();
const { scripts = {}, devDependencies, dependencies } = packageJson;
const { version: storybookVersion } = getStorybookInfo(packageJson);

const allDeps = { ...dependencies, ...devDependencies };

const storybookCoerced = storybookVersion && semver.coerce(storybookVersion)?.version;
if (!storybookCoerced) {
logger.warn(dedent`
❌ Unable to determine storybook version, skipping ${chalk.cyan('sb-scripts')} fix.
🤔 Are you running automigrate from your project directory?
`);
return null;
}

if (allDeps.sb || allDeps.storybook) {
return null;
}

const storybookScripts = getStorybookScripts(scripts);

if (
Object.keys(storybookScripts.official).length === 0 &&
Object.keys(storybookScripts.custom).length === 0
) {
return null;
}

Object.keys(storybookScripts.official).forEach((key) => {
storybookScripts.official[key] = storybookScripts.official[key]
.replace('start-storybook', 'storybook dev')
.replace('build-storybook', 'storybook build');
});

return semver.gte(storybookCoerced, '6.0.0') ? { storybookScripts, storybookVersion } : null;
},

prompt({ storybookVersion }) {
const sbFormatted = chalk.cyan(`Storybook ${storybookVersion}`);

const explanationMessage = [
`Starting in Storybook 7, the ${chalk.yellow('start-storybook')} and ${chalk.yellow(
'build-storybook'
)} binaries have changed to ${chalk.magenta('storybook dev')} and ${chalk.magenta(
'storybook build'
)} respectively.`,
`In order to work with ${sbFormatted}, Storybook's ${chalk.magenta(
'storybook'
)} binary has to be installed and your storybook scripts have to be adjusted to use the binary. We can install the storybook binary and attempt to adjust your scripts for you.`,
].join('\n');

return [
`We've detected you are using ${sbFormatted} with scripts from previous versions of Storybook.`,
explanationMessage,
`More info: ${chalk.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#start-storybook--build-storybook-binaries-removed'
)}`,
]
.filter(Boolean)
.join('\n\n');
},

async run({ result: { storybookScripts }, packageManager, dryRun }) {
logger.log();
logger.info(`Adding 'storybook' as dev dependency`);
logger.log();

if (!dryRun) {
packageManager.addDependencies({ installAsDevDependencies: true }, ['storybook']);
}

logger.info(`Updating scripts in package.json`);
logger.log();
if (!dryRun && Object.keys(storybookScripts.official).length > 0) {
const message = [
`Migrating your scripts to:`,
chalk.yellow(JSON.stringify(storybookScripts.official, null, 2)),
].join('\n');

logger.log(message);
logger.log();

packageManager.addScripts(storybookScripts.official);
}

if (!dryRun && Object.keys(storybookScripts.custom).length > 0) {
const message = [
`We detected custom scripts that we can't automigrate:`,
chalk.yellow(JSON.stringify(storybookScripts.custom, null, 2)),
'\n',
`Please manually migrate the ones applicable and use the documentation below for reference: ${chalk.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#start-storybook--build-storybook-binaries-removed'
)}`,
].join('\n');

logger.log(message);
logger.log();
}
},
};