Merge pull request #18769 from storybookjs/feat/automigrate-sb-scripts

CLI: add "storybook scripts 7.0" automigrate command
This commit is contained in:
Yann Braga 2022-08-09 17:23:43 +02:00 committed by GitHub
commit da08392ae7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 358 additions and 0 deletions

View File

@ -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';
@ -18,4 +19,5 @@ export const fixes: Fix[] = [
eslintPlugin,
builderVite,
npm7,
sbScripts,
];

View File

@ -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();
});
});
});
});
});

View File

@ -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();
}
},
};