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];