diff --git a/MIGRATION.md b/MIGRATION.md index 6b8ea76261e9..6cfcc02433c1 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,6 +1,7 @@

Migration

- [From version 6.4.x to 6.5.0](#from-version-64x-to-650) + - [Vite builder renamed](#vite-builder-renamed) - [Docs framework refactor for React](#docs-framework-refactor-for-react) - [Opt-in MDX2 support](#opt-in-mdx2-support) - [CSF3 auto-title improvements](#csf3-auto-title-improvements) @@ -196,6 +197,16 @@ ## From version 6.4.x to 6.5.0 +### Vite builder renamed + +SB6.5 renames Storybook's [Vite builder](https://github.com/storybookjs/builder-vite) from `storybook-builder-vite` to `@storybook/builder-vite`. This move is part of a larger effort to improve Vite support in Storybook. + +Storybook's `automigrate` command can migrate for you. To manually migrate: + +1. Remove `storybook-builder-vite` from your `package.json` dependencies +2. Install `@storybook/builder-vite` +3. Update your `core.builder` setting in `.storybook/main.js` to `@storybook/builder-vite`. + ### Docs framework refactor for React SB6.5 moves framework specializations (e.g. ArgType inference, dynamic snippet rendering) out of `@storybook/addon-docs` and into the specific framework packages to which they apply (e.g. `@storybook/react`). diff --git a/lib/cli/src/automigrate/fixes/builder-vite.test.ts b/lib/cli/src/automigrate/fixes/builder-vite.test.ts new file mode 100644 index 000000000000..aa21e490b8af --- /dev/null +++ b/lib/cli/src/automigrate/fixes/builder-vite.test.ts @@ -0,0 +1,52 @@ +/* eslint-disable no-underscore-dangle */ +import path from 'path'; +import { JsPackageManager } from '../../js-package-manager'; +import { builderVite } from './builder-vite'; + +// eslint-disable-next-line global-require, jest/no-mocks-import +jest.mock('fs-extra', () => require('../../../../../__mocks__/fs-extra')); + +const checkBuilderVite = async ({ packageJson = {}, main }) => { + // eslint-disable-next-line global-require + require('fs-extra').__setMockFiles({ + [path.join('.storybook', 'main.js')]: `module.exports = ${JSON.stringify(main)};`, + }); + const packageManager = { + retrievePackageJson: () => ({ dependencies: {}, devDependencies: {}, ...packageJson }), + } as JsPackageManager; + return builderVite.check({ packageManager }); +}; + +describe('builder-vite fix', () => { + describe('storybook-builder-vite', () => { + it('using storybook-builder-vite', async () => { + const main = { core: { builder: 'storybook-builder-vite' } }; + await expect(checkBuilderVite({ main })).resolves.toMatchObject({ + builder: 'storybook-builder-vite', + }); + }); + it('using storybook-builder-vite with options', async () => { + const main = { core: { builder: { name: 'storybook-builder-vite', options: {} } } }; + await expect(checkBuilderVite({ main })).resolves.toMatchObject({ + builder: { + name: 'storybook-builder-vite', + options: {}, + }, + }); + }); + }); + describe('other builders', () => { + it('using @storybook/builder-vite', async () => { + const main = { core: { builder: { name: '@storybook/builder-vite', options: {} } } }; + await expect(checkBuilderVite({ main })).resolves.toBeFalsy(); + }); + it('using webpack5', async () => { + const main = { core: { builder: 'webpack5' } }; + await expect(checkBuilderVite({ main })).resolves.toBeFalsy(); + }); + it('no builder specified', async () => { + const main = {}; + await expect(checkBuilderVite({ main })).resolves.toBeFalsy(); + }); + }); +}); diff --git a/lib/cli/src/automigrate/fixes/builder-vite.ts b/lib/cli/src/automigrate/fixes/builder-vite.ts new file mode 100644 index 000000000000..edeb8b2a0e37 --- /dev/null +++ b/lib/cli/src/automigrate/fixes/builder-vite.ts @@ -0,0 +1,94 @@ +import chalk from 'chalk'; +import dedent from 'ts-dedent'; + +import { ConfigFile, readConfig, writeConfig } from '@storybook/csf-tools'; + +import { Fix } from '../types'; +import { getStorybookInfo } from '../helpers/getStorybookInfo'; +import { PackageJson, writePackageJson } from '../../js-package-manager'; + +const logger = console; + +interface BuilderViteOptions { + builder: any; + main: ConfigFile; + packageJson: PackageJson; +} + +/** + * Is the user using 'storybook-builder-vite' in their project? + * + * If so, prompt them to upgrade to '@storybook/builder-vite'. + * + * - Add '@storybook/builder-vite' as dev dependency + * - Remove 'storybook-builder-vite' dependency + * - Add core.builder = '@storybook/builder-vite' to main.js + */ +export const builderVite: Fix = { + id: 'builder-vite', + + async check({ packageManager }) { + const packageJson = packageManager.retrievePackageJson(); + const { mainConfig } = getStorybookInfo(packageJson); + if (!mainConfig) { + logger.warn('Unable to find storybook main.js config'); + return null; + } + const main = await readConfig(mainConfig); + const builder = main.getFieldValue(['core', 'builder']); + const builderName = typeof builder === 'string' ? builder : builder?.name; + + if (builderName !== 'storybook-builder-vite') { + logger.info(`Not using community vite builder, skipping`); + return null; + } + + return { builder, main, packageJson }; + }, + + prompt({ builder }) { + const builderFormatted = chalk.cyan(JSON.stringify(builder, null, 2)); + + return dedent` + We've detected you're using the community vite builder: ${builderFormatted} + + 'storybook-builder-vite' is deprecated and now located at ${chalk.cyan( + '@storybook/builder-vite' + )}. + + We can upgrade your project to use the new builder automatically. + + More info: ${chalk.yellow( + 'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#vite-builder-renamed' + )} + `; + }, + + async run({ result: { builder, main, packageJson }, packageManager, dryRun }) { + const { dependencies = {}, devDependencies = {} } = packageJson; + + logger.info(`Removing existing 'storybook-builder-vite' dependency`); + if (!dryRun) { + delete dependencies['storybook-builder-vite']; + delete devDependencies['storybook-builder-vite']; + writePackageJson(packageJson); + } + + logger.info(`Adding '@storybook/builder-vite' as dev dependency`); + if (!dryRun) { + packageManager.addDependencies({ installAsDevDependencies: true }, [ + '@storybook/builder-vite', + ]); + } + + logger.info(`Updating main.js to use vite builder`); + if (!dryRun) { + const updatedBuilder = + typeof builder === 'string' + ? '@storybook/builder-vite' + : { name: '@storybook/builder-vite', options: builder.options }; + main.setFieldValue(['core', 'builder'], updatedBuilder); + await writeConfig(main); + } + }, +}; diff --git a/lib/cli/src/automigrate/fixes/index.ts b/lib/cli/src/automigrate/fixes/index.ts index 2a30b3725dce..8af1741b392f 100644 --- a/lib/cli/src/automigrate/fixes/index.ts +++ b/lib/cli/src/automigrate/fixes/index.ts @@ -3,7 +3,8 @@ import { webpack5 } from './webpack5'; import { angular12 } from './angular12'; import { mainjsFramework } from './mainjsFramework'; import { eslintPlugin } from './eslint-plugin'; +import { builderVite } from './builder-vite'; import { Fix } from '../types'; export * from '../types'; -export const fixes: Fix[] = [cra5, webpack5, angular12, mainjsFramework, eslintPlugin]; +export const fixes: Fix[] = [cra5, webpack5, angular12, mainjsFramework, eslintPlugin, builderVite];