1
0
mirror of https://github.com/storybookjs/storybook.git synced 2025-04-05 08:01:20 +08:00
2021-10-06 18:57:40 +08:00

158 lines
4.7 KiB
TypeScript

import { sync as spawnSync } from 'cross-spawn';
import semver from '@storybook/semver';
import { logger } from '@storybook/node-logger';
import {
getPackageDetails,
JsPackageManagerFactory,
PackageJsonWithDepsAndDevDeps,
} from './js-package-manager';
import { commandLog } from './helpers';
import { automigrate } from './automigrate';
type Package = {
package: string;
version: string;
};
const versionRegex = /(@storybook\/[^@]+)@(\S+)/;
export const getStorybookVersion = (line: string) => {
if (line.startsWith('npm ')) return null;
const match = versionRegex.exec(line);
if (!match || !semver.clean(match[2])) return null;
return {
package: match[1],
version: match[2],
};
};
const excludeList = [
'@storybook/linter-config',
'@storybook/design-system',
'@storybook/ember-cli-storybook',
'@storybook/semver',
'@storybook/eslint-config-storybook',
'@storybook/bench',
'@storybook/addon-bench',
'@storybook/addon-console',
'@storybook/csf',
'@storybook/storybook-deployer',
];
export const isCorePackage = (pkg: string) =>
pkg.startsWith('@storybook/') &&
!pkg.startsWith('@storybook/preset-') &&
!excludeList.includes(pkg);
const deprecatedPackages = [
{
minVersion: '6.0.0-alpha.0',
url: 'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#60-deprecations',
deprecations: [
'@storybook/addon-notes',
'@storybook/addon-info',
'@storybook/addon-contexts',
'@storybook/addon-options',
'@storybook/addon-centered',
],
},
];
const formatPackage = (pkg: Package) => `${pkg.package}@${pkg.version}`;
const warnPackages = (pkgs: Package[]) =>
pkgs.forEach((pkg) => logger.warn(`- ${formatPackage(pkg)}`));
export const checkVersionConsistency = () => {
const lines = spawnSync('npm', ['ls'], { stdio: 'pipe' }).output.toString().split('\n');
const storybookPackages = lines
.map(getStorybookVersion)
.filter(Boolean)
.filter((pkg) => isCorePackage(pkg.package));
if (!storybookPackages.length) {
logger.warn('No storybook core packages found.');
logger.warn(`'npm ls | grep storybook' can show if multiple versions are installed.`);
}
storybookPackages.sort((a, b) => semver.rcompare(a.version, b.version));
const latestVersion = storybookPackages[0].version;
const outdated = storybookPackages.filter((pkg) => pkg.version !== latestVersion);
if (outdated.length > 0) {
logger.warn(
`Found ${outdated.length} outdated packages (relative to '${formatPackage(
storybookPackages[0]
)}')`
);
logger.warn('Please make sure your packages are updated to ensure a consistent experience.');
warnPackages(outdated);
}
deprecatedPackages.forEach(({ minVersion, url, deprecations }) => {
if (semver.gte(latestVersion, minVersion)) {
const deprecated = storybookPackages.filter((pkg) => deprecations.includes(pkg.package));
if (deprecated.length > 0) {
logger.warn(`Found ${deprecated.length} deprecated packages since ${minVersion}`);
logger.warn(`See ${url}`);
warnPackages(deprecated);
}
}
});
};
type ExtraFlags = Record<string, string[]>;
const EXTRA_FLAGS: ExtraFlags = {
'react-scripts@<5': ['--reject', '/preset-create-react-app/'],
};
export const addExtraFlags = (
extraFlags: ExtraFlags,
flags: string[],
{ dependencies, devDependencies }: PackageJsonWithDepsAndDevDeps
) => {
return Object.entries(extraFlags).reduce(
(acc, entry) => {
const [pattern, extra] = entry;
const [pkg, specifier] = getPackageDetails(pattern);
const pkgVersion = dependencies[pkg] || devDependencies[pkg];
if (pkgVersion && semver.satisfies(semver.coerce(pkgVersion), specifier)) {
return [...acc, ...extra];
}
return acc;
},
[...flags]
);
};
interface UpgradeOptions {
prerelease: boolean;
skipCheck: boolean;
useNpm: boolean;
dryRun: boolean;
yes: boolean;
}
export const upgrade = async ({ prerelease, skipCheck, useNpm, dryRun, yes }: UpgradeOptions) => {
const packageManager = JsPackageManagerFactory.getPackageManager(useNpm);
commandLog(`Checking for latest versions of '@storybook/*' packages`);
let flags = [];
if (!dryRun) flags.push('--upgrade');
flags.push('--target');
flags.push(prerelease ? 'greatest' : 'latest');
flags = addExtraFlags(EXTRA_FLAGS, flags, packageManager.retrievePackageJson());
const check = spawnSync('npx', ['npm-check-updates', '/storybook/', ...flags], {
stdio: 'pipe',
}).output.toString();
logger.info(check);
if (!dryRun) {
commandLog(`Installing upgrades`);
packageManager.installDependencies();
}
if (!skipCheck) {
checkVersionConsistency();
await automigrate({ dryRun, yes });
}
};