Skip to content

Commit

Permalink
Merge pull request #18769 from storybookjs/feat/automigrate-sb-scripts
Browse files Browse the repository at this point in the history
CLI: add "storybook scripts 7.0" automigrate command
  • Loading branch information
yannbf committed Aug 9, 2022
2 parents f38f4cd + 13f6810 commit da08392
Show file tree
Hide file tree
Showing 3 changed files with 358 additions and 0 deletions.
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();
}
},
};

0 comments on commit da08392

Please sign in to comment.