Skip to content

Commit 64d41c0

Browse files
Automigration: Refactor automigration command handling and add tests
- Introduced `getAutomigrateCommand` function to standardize the command generation for automigrating addons. - Updated `postinstall.ts` files for `addonA11y` and `vitest` to utilize the new command function. - Added unit tests for `getAutomigrateCommand` to ensure correct command generation. - Updated various fix definitions to include a generic key type for better type safety.
1 parent f65c44c commit 64d41c0

22 files changed

+109
-29
lines changed

code/addons/a11y/src/postinstall.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { getAutomigrateCommand } from 'storybook/internal/cli';
2+
13
// eslint-disable-next-line depend/ban-dependencies
24
import { execa } from 'execa';
35

@@ -13,5 +15,9 @@ const $ = execa({
1315
export default async function postinstall(options: PostinstallOptions) {
1416
await $({
1517
stdio: 'inherit',
16-
})`storybook automigrate addonA11yAddonTest ${options.yes ? '--yes' : ''}`;
18+
})`${getAutomigrateCommand('addonA11yAddonTest', {
19+
yes: options.yes,
20+
configDir: options.configDir,
21+
packageManager: options.packageManager,
22+
})}`;
1723
}

code/addons/vitest/src/postinstall.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as fs from 'node:fs/promises';
33
import { writeFile } from 'node:fs/promises';
44

55
import { babelParse, generate, traverse } from 'storybook/internal/babel';
6+
import { getAutomigrateCommand } from 'storybook/internal/cli';
67
import {
78
JsPackageManagerFactory,
89
extractProperFrameworkName,
@@ -337,7 +338,11 @@ export default async function postInstall(options: PostinstallOptions) {
337338
logger.plain(`${step} Setting up ${addonA11yName} for @storybook/addon-vitest:`);
338339
await $({
339340
stdio: 'inherit',
340-
})`storybook automigrate addonA11yAddonTest ${options.yes ? '--yes' : ''}`;
341+
})`${getAutomigrateCommand('addonA11yAddonTest', {
342+
yes: options.yes,
343+
configDir: options.configDir,
344+
packageManager: options.packageManager,
345+
})}`;
341346
} catch (e) {
342347
printError(
343348
'🚨 Oh no!',

code/core/src/cli/automigrate.test.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
import { getAutomigrateCommand } from './automigrate';
4+
5+
describe('getAutomigrateCommand', () => {
6+
it('should return the correct command', () => {
7+
const command = getAutomigrateCommand('addon-a11y', {
8+
yes: true,
9+
configDir: 'config',
10+
packageManager: 'npm',
11+
});
12+
expect(command).toEqual([
13+
'storybook',
14+
'automigrate',
15+
'addon-a11y',
16+
'--yes',
17+
'--config-dir',
18+
'config',
19+
'--package-manager',
20+
'npm',
21+
]);
22+
});
23+
});

code/core/src/cli/automigrate.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type {
2+
AutofixOptionsFromCLI,
3+
FixesIDs,
4+
allFixes,
5+
} from '../../../lib/cli-storybook/src/automigrate/fixes';
6+
7+
/**
8+
* Get the command to automigrate an addon
9+
*
10+
* @example
11+
*
12+
* ```ts
13+
* const command = getAutomigrateCommand('addon-a11y', {
14+
* yes: true,
15+
* configDir: 'config',
16+
* packageManager: 'npm',
17+
* });
18+
* // ['storybook', 'automigrate', 'addon-a11y', '--yes', '--config-dir', 'config', '--package-manager', 'npm']
19+
* ```
20+
*/
21+
export const getAutomigrateCommand = (
22+
fixId: FixesIDs<typeof allFixes>,
23+
options: Pick<AutofixOptionsFromCLI, 'yes' | 'configDir' | 'packageManager'>
24+
) => {
25+
const command = ['storybook', 'automigrate', fixId];
26+
27+
if (options.yes) {
28+
command.push('--yes');
29+
}
30+
31+
if (options.configDir) {
32+
command.push('--config-dir', options.configDir);
33+
}
34+
35+
if (options.packageManager) {
36+
command.push('--package-manager', options.packageManager);
37+
}
38+
39+
return command;
40+
};

code/core/src/cli/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export * from './dirs';
55
export * from './project_types';
66
export * from './NpmOptions';
77
export * from './eslintPlugin';
8+
export * from './automigrate';

code/lib/cli-storybook/src/automigrate/fixes/addon-a11y-addon-test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ interface AddonA11yAddonTestOptions {
3939
* - `.storybook/preview.<ts|js>` to set up tags.
4040
* - If we can't transform the files automatically, we'll prompt the user to do it manually.
4141
*/
42-
export const addonA11yAddonTest: Fix<AddonA11yAddonTestOptions> = {
42+
export const addonA11yAddonTest: Fix<AddonA11yAddonTestOptions, 'addonA11yAddonTest'> = {
4343
id: 'addonA11yAddonTest',
4444
versionRange: ['<9.0.0', '^9.0.0-0 || ^9.0.0'],
4545

code/lib/cli-storybook/src/automigrate/fixes/addon-a11y-parameters.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ interface A11yOptions {
2121

2222
const logger = console;
2323

24-
export const addonA11yParameters: Fix<A11yOptions> = {
24+
export const addonA11yParameters: Fix<A11yOptions, 'addon-a11y-parameters'> = {
2525
id: 'addon-a11y-parameters',
2626
versionRange: ['<9.0.0', '^9.0.0-0 || ^9.0.0'],
2727

code/lib/cli-storybook/src/automigrate/fixes/addon-essentials-remove-docs.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const consolidatedAddons = {
3434
* - If user had docs enabled (default): Install @storybook/addon-docs and add to main.ts
3535
* - If user had docs disabled: Skip addon-docs installation
3636
*/
37-
export const addonEssentialsRemoveDocs: Fix<AddonDocsOptions> = {
37+
export const addonEssentialsRemoveDocs: Fix<AddonDocsOptions, 'addon-essentials-remove-docs'> = {
3838
id: 'addon-essentials-remove-docs',
3939
versionRange: ['<9.0.0', '^9.0.0-0 || ^9.0.0'],
4040

code/lib/cli-storybook/src/automigrate/fixes/addon-experimental-test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ interface AddonExperimentalTestOptions {
1919
* project files
2020
* - Update package.json dependencies if needed
2121
*/
22-
export const addonExperimentalTest: Fix<AddonExperimentalTestOptions> = {
22+
export const addonExperimentalTest: Fix<AddonExperimentalTestOptions, 'addon-experimental-test'> = {
2323
id: 'addon-experimental-test',
2424

2525
versionRange: ['<9.0.0', '^9.0.0-0 || ^9.0.0'],

code/lib/cli-storybook/src/automigrate/fixes/addon-mdx-gfm-remove.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ interface AddonMdxGfmOptions {
1414
*
1515
* - Remove @storybook/addon-mdx-gfm from main.ts and package.json
1616
*/
17-
export const addonMdxGfmRemove: Fix<AddonMdxGfmOptions> = {
17+
export const addonMdxGfmRemove: Fix<AddonMdxGfmOptions, 'addon-mdx-gfm-remove'> = {
1818
id: 'addon-mdx-gfm-remove',
1919
versionRange: ['<9.0.0', '^9.0.0-0 || ^9.0.0'],
2020

code/lib/cli-storybook/src/automigrate/fixes/addon-storysource-remove.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ interface AddonStorysourceOptions {
1515
*
1616
* - Remove @storybook/addon-storysource from main.ts and package.json
1717
*/
18-
export const addonStorysourceRemove: Fix<AddonStorysourceOptions> = {
18+
export const addonStorysourceRemove: Fix<AddonStorysourceOptions, 'addon-storysource-remove'> = {
1919
id: 'addon-storysource-remove',
2020
versionRange: ['<9.0.0', '^9.0.0-0 || ^9.0.0'],
2121

code/lib/cli-storybook/src/automigrate/fixes/consolidated-imports.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export const transformPackageJsonFiles = async (files: string[], dryRun: boolean
9797
return errors;
9898
};
9999

100-
export const consolidatedImports: Fix<ConsolidatedOptions> = {
100+
export const consolidatedImports: Fix<ConsolidatedOptions, 'consolidated-imports'> = {
101101
id: 'consolidated-imports',
102102
versionRange: ['<9.0.0', '^9.0.0-0 || ^9.0.0'],
103103
check: async () => {

code/lib/cli-storybook/src/automigrate/fixes/eslint-plugin.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ interface EslintPluginRunOptions {
2424
*
2525
* - Install it, and if possible configure it
2626
*/
27-
export const eslintPlugin: Fix<EslintPluginRunOptions> = {
27+
export const eslintPlugin: Fix<EslintPluginRunOptions, 'eslintPlugin'> = {
2828
id: 'eslintPlugin',
2929

3030
versionRange: ['*', '*'],

code/lib/cli-storybook/src/automigrate/fixes/index.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { wrapRequire } from './wrap-require';
1818

1919
export * from '../types';
2020

21-
export const allFixes: Fix[] = [
21+
export const allFixes = [
2222
eslintPlugin,
2323
wrapRequire,
2424
addonMdxGfmRemove,
@@ -34,10 +34,12 @@ export const allFixes: Fix[] = [
3434
addonEssentialsRemoveDocs,
3535
addonA11yParameters,
3636
removeDocsAutodocs,
37-
];
37+
] satisfies Fix[];
3838

39-
export const initFixes: Fix[] = [eslintPlugin];
39+
export type FixesIDs<F extends Fix[]> = F[number]['id'];
40+
41+
export const initFixes = [eslintPlugin] satisfies Fix[];
4042

4143
// These are specific fixes that only occur when triggered on command, and are hidden otherwise.
4244
// e.g. npx storybook automigrate csf-factories
43-
export const commandFixes: CommandFix[] = [csfFactories];
45+
export const commandFixes = [csfFactories] satisfies CommandFix[];

code/lib/cli-storybook/src/automigrate/fixes/initial-globals.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ interface Options {
1919
}
2020

2121
/** Rename preview.js globals to initialGlobals */
22-
export const initialGlobals: Fix<Options> = {
22+
export const initialGlobals: Fix<Options, 'initial-globals'> = {
2323
id: 'initial-globals',
2424
versionRange: ['<9.0.0', '^9.0.0-0 || ^9.0.0'],
2525
async check({ previewConfigPath }) {

code/lib/cli-storybook/src/automigrate/fixes/remove-addon-interactions.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { dedent } from 'ts-dedent';
66
import type { Fix } from '../types';
77

88
/** Remove @storybook/addon-interactions since it's now part of Storybook core. */
9-
export const removeAddonInteractions: Fix<{}> = {
9+
export const removeAddonInteractions: Fix<{}, 'removeAddonInteractions'> = {
1010
id: 'removeAddonInteractions',
1111
versionRange: ['<9.0.0', '^9.0.0-0 || ^9.0.0'],
1212

code/lib/cli-storybook/src/automigrate/fixes/remove-docs-autodocs.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ interface RemoveDocsAutodocsOptions {
2222
* Migration to remove the docs.autodocs field from main.ts config This field was deprecated in
2323
* Storybook 7-8 and removed in Storybook 9
2424
*/
25-
export const removeDocsAutodocs: Fix<RemoveDocsAutodocsOptions> = {
25+
export const removeDocsAutodocs: Fix<RemoveDocsAutodocsOptions, 'remove-docs-autodocs'> = {
2626
id: 'remove-docs-autodocs',
2727
versionRange: ['<9.0.0', '^9.0.0-0 || ^9.0.0'],
2828

code/lib/cli-storybook/src/automigrate/fixes/renderer-to-framework.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ const checkPackageJson = async (
138138
return { frameworks, renderers };
139139
};
140140

141-
export const rendererToFramework: Fix<MigrationResult> = {
141+
export const rendererToFramework: Fix<MigrationResult, 'renderer-to-framework'> = {
142142
id: 'renderer-to-framework',
143143
versionRange: ['<9.0.0', '^9.0.0-0'],
144144
promptType: 'auto',

code/lib/cli-storybook/src/automigrate/fixes/rnstorybook-config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const getDotStorybookReferences = async () => {
3535
}
3636
};
3737

38-
export const rnstorybookConfig: Fix<Options> = {
38+
export const rnstorybookConfig: Fix<Options, 'rnstorybook-config'> = {
3939
id: 'rnstorybook-config',
4040

4141
versionRange: ['<9.0.0', '^9.0.0-0 || ^9.0.0'],

code/lib/cli-storybook/src/automigrate/fixes/upgrade-storybook-related-dependencies.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ async function getLatestVersions(
4040
*
4141
* See: https://github.com/storybookjs/storybook/issues/25731#issuecomment-1977346398
4242
*/
43-
export const upgradeStorybookRelatedDependencies = {
43+
export const upgradeStorybookRelatedDependencies: Fix<
44+
Options,
45+
'upgradeStorybookRelatedDependencies'
46+
> = {
4447
id: 'upgradeStorybookRelatedDependencies',
4548
versionRange: ['*.*.*', '*.*.*'],
4649
promptType: 'auto',
@@ -149,4 +152,4 @@ export const upgradeStorybookRelatedDependencies = {
149152
}
150153
console.log();
151154
},
152-
} satisfies Fix<Options>;
155+
};

code/lib/cli-storybook/src/automigrate/fixes/wrap-require.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ interface WrapRequireRunOptions {
2121
isConfigTypescript: boolean;
2222
}
2323

24-
export const wrapRequire: Fix<WrapRequireRunOptions> = {
24+
export const wrapRequire: Fix<WrapRequireRunOptions, 'wrap-require'> = {
2525
id: 'wrap-require',
2626

2727
versionRange: ['*', '*'],

code/lib/cli-storybook/src/automigrate/types.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ export interface RunOptions<ResultType> {
3434
*/
3535
export type Prompt = 'auto' | 'manual' | 'notification' | 'command';
3636

37-
type BaseFix<ResultType = any> = {
38-
id: string;
37+
type BaseFix<ResultType, Key extends string> = {
38+
id: Key;
3939
/**
4040
* The from/to version range of Storybook that this fix applies to. The strings are semver ranges.
4141
* The versionRange will only be checked if the automigration is part of an upgrade. If the
@@ -52,20 +52,20 @@ type PromptType<ResultType = any, T = Prompt> =
5252
| T
5353
| ((result: ResultType) => Promise<Prompt> | Prompt);
5454

55-
export type Fix<ResultType = any> =
55+
export type Fix<ResultType, Key extends string> =
5656
| ({
5757
promptType?: PromptType<ResultType, 'auto'>;
5858
run: (options: RunOptions<ResultType>) => Promise<void>;
59-
} & BaseFix<ResultType>)
59+
} & BaseFix<ResultType, Key>)
6060
| ({
6161
promptType: PromptType<ResultType, 'manual' | 'notification'>;
6262
run?: never;
63-
} & BaseFix<ResultType>);
63+
} & BaseFix<ResultType, Key>);
6464

65-
export type CommandFix<ResultType = any> = {
65+
export type CommandFix<ResultType = any, Key extends string = string> = {
6666
promptType: PromptType<ResultType, 'command'>;
6767
run: (options: RunOptions<ResultType>) => Promise<void>;
68-
} & Omit<BaseFix<ResultType>, 'versionRange' | 'check' | 'prompt'>;
68+
} & Omit<BaseFix<ResultType, Key>, 'versionRange' | 'check' | 'prompt'>;
6969

7070
export type FixId = string;
7171

0 commit comments

Comments
 (0)