mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-09 00:19:13 +08:00
improvements
This commit is contained in:
parent
880479789f
commit
54eccd103d
@ -2,13 +2,13 @@ import { dedent } from 'ts-dedent';
|
||||
import type { Fix } from '../types';
|
||||
import { underline } from 'chalk';
|
||||
import { getIncompatibleStorybookPackages } from '../../doctor/getIncompatibleStorybookPackages';
|
||||
import { valid, coerce } from 'semver';
|
||||
|
||||
interface Options {
|
||||
list: { packageName: string; version: string }[];
|
||||
upgradable: { packageName: string; version: string }[];
|
||||
problematicPackages: { packageName: string; version: string }[];
|
||||
}
|
||||
|
||||
type ExcludesFalse = <T>(x: T | undefined) => x is T;
|
||||
|
||||
/**
|
||||
* Is the user upgrading to the `latest` version of Storybook?
|
||||
* Let's try to pull along some of the storybook related dependencies to `latest` as well!
|
||||
@ -20,36 +20,62 @@ type ExcludesFalse = <T>(x: T | undefined) => x is T;
|
||||
*/
|
||||
export const upgradeStorybookRelatedDependencies = {
|
||||
id: 'upgradeStorybookRelatedDependencies',
|
||||
|
||||
versionRange: ['<8', '>=8'],
|
||||
versionRange: ['*.*.*', '*.*.*'],
|
||||
promptType: 'auto',
|
||||
promptDefaultValue: false,
|
||||
|
||||
async check({ packageManager, storybookVersion }) {
|
||||
const out = await getIncompatibleStorybookPackages({
|
||||
const packageJson = await packageManager.readPackageJson();
|
||||
const analyzed = await getIncompatibleStorybookPackages({
|
||||
currentStorybookVersion: storybookVersion,
|
||||
packageManager,
|
||||
skipErrors: true,
|
||||
});
|
||||
|
||||
const list = await Promise.all(
|
||||
out.map(async ({ packageName, hasIncompatibleDependencies }) => {
|
||||
if (!hasIncompatibleDependencies) {
|
||||
return;
|
||||
}
|
||||
const all = await packageManager.getAllDependencies();
|
||||
|
||||
const associated = Object.keys(all).filter((dep) => dep.includes('storybook'));
|
||||
const detected = analyzed
|
||||
.filter((m) => m.hasIncompatibleDependencies)
|
||||
.map((m) => m.packageName);
|
||||
|
||||
const list = await Promise.all(
|
||||
Array.from(new Set([...associated, ...detected])).map(async (packageName) => {
|
||||
return {
|
||||
packageName,
|
||||
version: await packageManager.latestVersion(packageName),
|
||||
version: await packageManager.latestVersion(packageName).catch((e) => null),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const filtered = list.filter(Boolean as any as ExcludesFalse);
|
||||
return { list: filtered };
|
||||
const data = list.reduce<Options>(
|
||||
(acc, k) => {
|
||||
const upgradable = !(
|
||||
!valid(k.version) ||
|
||||
k.version === coerce(packageJson?.dependencies?.[k.packageName])?.toString() ||
|
||||
k.version === coerce(packageJson?.devDependencies?.[k.packageName])?.toString() ||
|
||||
k.version === coerce(packageJson?.peerDependencies?.[k.packageName])?.toString()
|
||||
);
|
||||
|
||||
if (upgradable) {
|
||||
acc.upgradable.push(k);
|
||||
} else {
|
||||
acc.problematicPackages.push(k);
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ upgradable: [], problematicPackages: [] }
|
||||
);
|
||||
|
||||
if (data.upgradable.length > 0) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
promptType: 'auto-no',
|
||||
|
||||
prompt({ list }) {
|
||||
prompt({ upgradable: list }) {
|
||||
return dedent`
|
||||
You're upgrading to the latest version of Storybook. We recommend upgrading the following packages:
|
||||
${list.map(({ packageName, version }) => `${packageName}@${version}`).join(', ')}
|
||||
@ -66,40 +92,58 @@ export const upgradeStorybookRelatedDependencies = {
|
||||
`;
|
||||
},
|
||||
|
||||
async run({ result: { list }, packageManager, dryRun, mainConfigPath }) {
|
||||
async run({ result: { upgradable, problematicPackages }, packageManager, dryRun }) {
|
||||
if (dryRun) {
|
||||
console.log(dedent`
|
||||
would have upgrade the following:
|
||||
${list.map(({ packageName, version }) => `${packageName}@${version}`).join('\n')}
|
||||
We would have upgrade the following:
|
||||
${upgradable.map(({ packageName, version }) => `${packageName}@${version}`).join('\n')}
|
||||
`);
|
||||
return;
|
||||
}
|
||||
|
||||
const packageJson = await packageManager.readPackageJson();
|
||||
if (upgradable.length > 0) {
|
||||
const packageJson = await packageManager.readPackageJson();
|
||||
|
||||
// mutate the packageJson data
|
||||
list.forEach((item) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
upgradable.forEach((item) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { packageName, version } = item;
|
||||
const prefixed = `^${version}`;
|
||||
const { packageName, version } = item;
|
||||
const prefixed = `^${version}`;
|
||||
|
||||
if (packageJson.dependencies?.[packageName]) {
|
||||
packageJson.dependencies[packageName] = prefixed;
|
||||
}
|
||||
if (packageJson.devDependencies?.[packageName]) {
|
||||
packageJson.devDependencies[packageName] = prefixed;
|
||||
}
|
||||
if (packageJson.peerDependencies?.[packageName]) {
|
||||
packageJson.peerDependencies[packageName] = prefixed;
|
||||
}
|
||||
});
|
||||
if (packageJson.dependencies?.[packageName]) {
|
||||
packageJson.dependencies[packageName] = prefixed;
|
||||
}
|
||||
if (packageJson.devDependencies?.[packageName]) {
|
||||
packageJson.devDependencies[packageName] = prefixed;
|
||||
}
|
||||
if (packageJson.peerDependencies?.[packageName]) {
|
||||
packageJson.peerDependencies[packageName] = prefixed;
|
||||
}
|
||||
});
|
||||
|
||||
await packageManager.writePackageJson(packageJson);
|
||||
await packageManager.installDependencies();
|
||||
await packageManager.writePackageJson(packageJson);
|
||||
await packageManager.installDependencies();
|
||||
|
||||
await packageManager.getRunCommand('dedupe');
|
||||
await packageManager
|
||||
.executeCommand({ command: 'dedupe', args: [], stdio: 'ignore' })
|
||||
.catch((e) => {});
|
||||
|
||||
console.log(dedent`
|
||||
We upgraded ${upgradable.length} packages:
|
||||
${upgradable.map(({ packageName, version }) => `- ${packageName}@${version}`).join('\n')}
|
||||
`);
|
||||
}
|
||||
|
||||
if (problematicPackages.length) {
|
||||
console.log(dedent`
|
||||
The following packages, could not be upgraded, likely because there's no update available that's compatible with the latest version of Storybook:
|
||||
${problematicPackages.map(({ packageName }) => `- ${packageName}`).join('\n')}
|
||||
|
||||
We suggest your reach out to the authors of these packages to get them updated.
|
||||
But before reporting, please check if there is already an open issue or PR for this.
|
||||
`);
|
||||
}
|
||||
},
|
||||
} satisfies Fix<Options>;
|
||||
|
@ -30,6 +30,7 @@ import { getStorybookData } from './helpers/mainConfigFile';
|
||||
import { doctor } from '../doctor';
|
||||
|
||||
import { upgradeStorybookRelatedDependencies } from './fixes/upgrade-storybook-related-dependencies';
|
||||
import dedent from 'ts-dedent';
|
||||
|
||||
const logger = console;
|
||||
const LOG_FILE_NAME = 'migration-storybook.log';
|
||||
@ -58,8 +59,16 @@ const cleanup = () => {
|
||||
};
|
||||
|
||||
const logAvailableMigrations = () => {
|
||||
const availableFixes = allFixes.map((f) => chalk.yellow(f.id)).join(', ');
|
||||
logger.info(`\nThe following migrations are available: ${availableFixes}`);
|
||||
const availableFixes = allFixes
|
||||
.map((f) => chalk.yellow(f.id))
|
||||
.map((x) => `- ${x}`)
|
||||
.join('\n');
|
||||
|
||||
console.log();
|
||||
logger.info(dedent`
|
||||
The following migrations are available:
|
||||
${availableFixes}
|
||||
`);
|
||||
};
|
||||
|
||||
export const doAutomigrate = async (options: AutofixOptionsFromCLI) => {
|
||||
@ -86,7 +95,7 @@ export const doAutomigrate = async (options: AutofixOptionsFromCLI) => {
|
||||
throw new Error('Could not determine main config path');
|
||||
}
|
||||
|
||||
await automigrate({
|
||||
const outcome = await automigrate({
|
||||
...options,
|
||||
packageManager,
|
||||
storybookVersion,
|
||||
@ -96,7 +105,9 @@ export const doAutomigrate = async (options: AutofixOptionsFromCLI) => {
|
||||
isUpgrade: false,
|
||||
});
|
||||
|
||||
await doctor({ configDir, packageManager: options.packageManager });
|
||||
if (outcome) {
|
||||
await doctor({ configDir, packageManager: options.packageManager });
|
||||
}
|
||||
};
|
||||
|
||||
export const automigrate = async ({
|
||||
@ -127,7 +138,11 @@ export const automigrate = async ({
|
||||
inputFixes ||
|
||||
allFixes.filter((fix) => {
|
||||
// we only allow this automigration when the user explicitly asks for it, or they are upgrading to the latest version of storybook
|
||||
if (fix.id === upgradeStorybookRelatedDependencies.id && isUpgrade !== 'latest') {
|
||||
if (
|
||||
fix.id === upgradeStorybookRelatedDependencies.id &&
|
||||
isUpgrade !== 'latest' &&
|
||||
fixId !== upgradeStorybookRelatedDependencies.id
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -319,13 +334,13 @@ export async function runFixes({
|
||||
fixResults[f.id] = FixStatus.MANUAL_SKIPPED;
|
||||
break;
|
||||
}
|
||||
} else if (promptType === 'auto' || promptType === 'auto-no') {
|
||||
} else if (promptType === 'auto') {
|
||||
runAnswer = await prompts(
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'fix',
|
||||
message: `Do you want to run the '${chalk.cyan(f.id)}' migration on your project?`,
|
||||
initial: promptType === 'auto-no' ? false : true,
|
||||
initial: f.promptDefaultValue ?? true,
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
|
@ -22,11 +22,10 @@ export interface RunOptions<ResultType> {
|
||||
/**
|
||||
* promptType defines how the user will be prompted to apply an automigration fix
|
||||
* - auto: the fix will be applied automatically
|
||||
* - auto-no: the fix will be applied automatically, but only when the user opts-in
|
||||
* - manual: the user will be prompted to apply the fix
|
||||
* - notification: the user will be notified about some changes. A fix isn't required, though
|
||||
*/
|
||||
export type Prompt = 'auto' | 'auto-no' | 'manual' | 'notification';
|
||||
export type Prompt = 'auto' | 'manual' | 'notification';
|
||||
|
||||
type BaseFix<ResultType = any> = {
|
||||
id: string;
|
||||
@ -38,6 +37,7 @@ type BaseFix<ResultType = any> = {
|
||||
versionRange: [from: string, to: string];
|
||||
check: (options: CheckOptions) => Promise<ResultType | null>;
|
||||
prompt: (result: ResultType) => string;
|
||||
promptDefaultValue?: boolean;
|
||||
};
|
||||
|
||||
type PromptType<ResultType = any, T = Prompt> =
|
||||
@ -46,7 +46,7 @@ type PromptType<ResultType = any, T = Prompt> =
|
||||
|
||||
export type Fix<ResultType = any> = (
|
||||
| {
|
||||
promptType?: PromptType<ResultType, 'auto' | 'auto-no'>;
|
||||
promptType?: PromptType<ResultType, 'auto'>;
|
||||
run: (options: RunOptions<ResultType>) => Promise<void>;
|
||||
}
|
||||
| {
|
||||
|
Loading…
x
Reference in New Issue
Block a user