mirror of
https://github.com/storybookjs/storybook.git
synced 2025-03-17 05:02:23 +08:00
Merge pull request #20035 from storybookjs/fix/improve-sb-scripts-automigration
CLI: Improve sb-scripts automigration logic
This commit is contained in:
commit
8a076f6dc5
@ -12,16 +12,18 @@ 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',
|
||||
'build-storybook': {
|
||||
before: 'build-storybook',
|
||||
after: 'storybook build',
|
||||
},
|
||||
storybook: {
|
||||
before: 'start-storybook',
|
||||
after: 'storybook dev',
|
||||
},
|
||||
custom: {},
|
||||
});
|
||||
});
|
||||
|
||||
@ -29,37 +31,20 @@ describe('getStorybookScripts', () => {
|
||||
expect(
|
||||
getStorybookScripts({
|
||||
start: 'server start',
|
||||
'start-storybook': 'MOCKS=true start-storybook -p 9000',
|
||||
'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',
|
||||
'start-storybook': {
|
||||
before: 'MOCKS=true start-storybook -p 9000',
|
||||
after: 'MOCKS=true storybook dev -p 9000',
|
||||
},
|
||||
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-scripts fix', () => {
|
||||
describe('sb < 7.0', () => {
|
||||
describe('does nothing', () => {
|
||||
const packageJson = { dependencies: { '@storybook/react': '^6.2.0' } };
|
||||
@ -72,6 +57,7 @@ describe('sb scripts fix', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sb >= 7.0', () => {
|
||||
describe('with old scripts', () => {
|
||||
const packageJson = {
|
||||
@ -91,11 +77,14 @@ describe('sb scripts fix', () => {
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
storybookScripts: {
|
||||
official: {
|
||||
storybook: 'storybook dev -p 6006',
|
||||
'build-storybook': 'storybook build -o build/storybook',
|
||||
'build-storybook': {
|
||||
after: 'storybook build -o build/storybook',
|
||||
before: 'build-storybook -o build/storybook',
|
||||
},
|
||||
storybook: {
|
||||
after: 'storybook dev -p 6006',
|
||||
before: 'start-storybook -p 6006',
|
||||
},
|
||||
custom: {},
|
||||
},
|
||||
storybookVersion: '^7.0.0-alpha.0',
|
||||
})
|
||||
@ -104,22 +93,20 @@ describe('sb scripts fix', () => {
|
||||
});
|
||||
|
||||
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 () => {
|
||||
const packageJson = {
|
||||
dependencies: {
|
||||
'@storybook/react': '^7.0.0-alpha.0',
|
||||
},
|
||||
scripts: {
|
||||
'storybook:ci': 'yarn start-storybook --ci',
|
||||
'storybook:build': 'build-storybook -o build/storybook',
|
||||
'storybook:build-mocked': 'MOCKS=true yarn storybook:build',
|
||||
'test-storybook:ci':
|
||||
'concurrently -k -s first -n "SB,TEST" -c "magenta,blue" "CI=true build-storybook --quiet && npx http-server storybook-static --port 6006 --silent" "wait-on tcp:6006 && yarn test-storybook"',
|
||||
},
|
||||
};
|
||||
|
||||
await expect(
|
||||
checkSbScripts({
|
||||
packageJson,
|
||||
@ -127,94 +114,56 @@ describe('sb scripts fix', () => {
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
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"',
|
||||
'storybook:build': {
|
||||
after: 'storybook build -o build/storybook',
|
||||
before: 'build-storybook -o build/storybook',
|
||||
},
|
||||
'test-storybook:ci': {
|
||||
before:
|
||||
'concurrently -k -s first -n "SB,TEST" -c "magenta,blue" "CI=true build-storybook --quiet && npx http-server storybook-static --port 6006 --silent" "wait-on tcp:6006 && yarn test-storybook"',
|
||||
after:
|
||||
'concurrently -k -s first -n "SB,TEST" -c "magenta,blue" "CI=true storybook build --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', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
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(
|
||||
expect.objectContaining({
|
||||
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('already containing new scripts', () => {
|
||||
const packageJson = {
|
||||
dependencies: {
|
||||
'@storybook/react': '^7.0.0-alpha.0',
|
||||
storybook: '^7.0.0-alpha.0',
|
||||
},
|
||||
scripts: {
|
||||
storybook: 'storybook dev -p 6006',
|
||||
'build-storybook': 'storybook build -o build/storybook',
|
||||
},
|
||||
};
|
||||
it('should no-op', async () => {
|
||||
await expect(
|
||||
checkSbScripts({
|
||||
packageJson,
|
||||
})
|
||||
).resolves.toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with storybook lib installed', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
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', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
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();
|
||||
});
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -7,31 +7,54 @@ import { getStorybookVersionSpecifier } from '../../helpers';
|
||||
import type { PackageJsonWithDepsAndDevDeps } from '../../js-package-manager';
|
||||
|
||||
interface SbScriptsRunOptions {
|
||||
storybookScripts: {
|
||||
custom: Record<string, string>;
|
||||
official: Record<string, string>;
|
||||
};
|
||||
storybookScripts: Record<string, { before: string; after: string }>;
|
||||
storybookVersion: string;
|
||||
packageJson: PackageJsonWithDepsAndDevDeps;
|
||||
}
|
||||
|
||||
const logger = console;
|
||||
|
||||
export const getStorybookScripts = (scripts: Record<string, string>) => {
|
||||
const storybookScripts: SbScriptsRunOptions['storybookScripts'] = {
|
||||
custom: {},
|
||||
official: {},
|
||||
};
|
||||
/**
|
||||
* Slightly big function because JS regex doesn't have proper full-word boundary.
|
||||
* This goes through all the words in each script, and only return the scripts
|
||||
* that do contain the actual sb binary, and not something like "npm run start-storybook"
|
||||
* which could actually be a custom script even though the name matches the legacy binary name
|
||||
*/
|
||||
export const getStorybookScripts = (allScripts: Record<string, string>) => {
|
||||
return Object.keys(allScripts).reduce((acc, key) => {
|
||||
let isStorybookScript = false;
|
||||
const allWordsFromScript = allScripts[key].split(' ');
|
||||
const newScript = allWordsFromScript
|
||||
.map((currentWord, index) => {
|
||||
const previousWord = allWordsFromScript[index - 1];
|
||||
|
||||
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];
|
||||
// full word check, rather than regex which could be faulty
|
||||
const isSbBinary = currentWord === 'build-storybook' || currentWord === 'start-storybook';
|
||||
|
||||
// in case people have scripts like `yarn start-storybook`
|
||||
const isPrependedByPkgManager =
|
||||
previousWord && ['npx', 'run', 'yarn', 'pnpx'].some((cmd) => previousWord.includes(cmd));
|
||||
|
||||
if (isSbBinary && !isPrependedByPkgManager) {
|
||||
isStorybookScript = true;
|
||||
return currentWord
|
||||
.replace('start-storybook', 'storybook dev')
|
||||
.replace('build-storybook', 'storybook build');
|
||||
}
|
||||
|
||||
return currentWord;
|
||||
})
|
||||
.join(' ');
|
||||
|
||||
if (isStorybookScript) {
|
||||
acc[key] = {
|
||||
before: allScripts[key],
|
||||
after: newScript,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return storybookScripts;
|
||||
return acc;
|
||||
}, {} as Record<string, { before: string; after: string }>);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -66,47 +89,48 @@ export const sbScripts: Fix<SbScriptsRunOptions> = {
|
||||
|
||||
const storybookScripts = getStorybookScripts(scripts);
|
||||
|
||||
if (
|
||||
Object.keys(storybookScripts.official).length === 0 &&
|
||||
Object.keys(storybookScripts.custom).length === 0
|
||||
) {
|
||||
if (Object.keys(storybookScripts).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, '7.0.0')
|
||||
? { packageJson, storybookScripts, storybookVersion }
|
||||
: null;
|
||||
},
|
||||
|
||||
prompt({ storybookVersion }) {
|
||||
prompt({ storybookVersion, storybookScripts }) {
|
||||
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');
|
||||
const newScriptsMessage = Object.keys(storybookScripts).reduce((acc, scriptKey) => {
|
||||
acc.push(
|
||||
[
|
||||
chalk.bold(scriptKey),
|
||||
'from:',
|
||||
chalk.cyan(storybookScripts[scriptKey].before),
|
||||
'to:',
|
||||
chalk.cyan(storybookScripts[scriptKey].after),
|
||||
].join('\n')
|
||||
);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return [
|
||||
`We've detected you are using ${sbFormatted} with scripts from previous versions of Storybook.`,
|
||||
explanationMessage,
|
||||
`More info: ${chalk.yellow(
|
||||
return dedent`
|
||||
We've detected you are using ${sbFormatted} with scripts from previous versions of Storybook.
|
||||
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 adjust your scripts for you:
|
||||
|
||||
${newScriptsMessage.join('\n\n')}
|
||||
|
||||
In case this migration did not cover all of your scripts, or you'd like 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, packageJson }, packageManager, dryRun }) {
|
||||
@ -123,30 +147,15 @@ export const sbScripts: Fix<SbScriptsRunOptions> = {
|
||||
|
||||
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');
|
||||
if (!dryRun) {
|
||||
const newScripts = Object.keys(storybookScripts).reduce((acc, scriptKey) => {
|
||||
acc[scriptKey] = storybookScripts[scriptKey].after;
|
||||
return acc;
|
||||
}, {} as Record<string, string>);
|
||||
|
||||
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();
|
||||
packageManager.addScripts(newScripts);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user