diff --git a/code/lib/cli/src/automigrate/fixes/index.ts b/code/lib/cli/src/automigrate/fixes/index.ts
index 924c558d331..1fd9ba4c55e 100644
--- a/code/lib/cli/src/automigrate/fixes/index.ts
+++ b/code/lib/cli/src/automigrate/fixes/index.ts
@@ -17,6 +17,7 @@ import { autodocsTrue } from './autodocs-true';
import { sveltekitFramework } from './sveltekit-framework';
import { addReact } from './add-react';
import { nodeJsRequirement } from './nodejs-requirement';
+import { missingBabelRc } from './missing-babelrc';
export * from '../types';
@@ -38,4 +39,5 @@ export const fixes: Fix[] = [
mdx1to2,
autodocsTrue,
addReact,
+ missingBabelRc,
];
diff --git a/code/lib/cli/src/automigrate/fixes/missing-babelrc.test.ts b/code/lib/cli/src/automigrate/fixes/missing-babelrc.test.ts
new file mode 100644
index 00000000000..9aa0d770e4c
--- /dev/null
+++ b/code/lib/cli/src/automigrate/fixes/missing-babelrc.test.ts
@@ -0,0 +1,91 @@
+/* eslint-disable no-underscore-dangle */
+/// ;
+
+import path from 'path';
+import type { JsPackageManager } from '../../js-package-manager';
+import { missingBabelRc } from './missing-babelrc';
+
+// eslint-disable-next-line global-require, jest/no-mocks-import
+jest.mock('fs-extra', () => require('../../../../../__mocks__/fs-extra'));
+
+const babelContent = JSON.stringify({
+ sourceType: 'unambiguous',
+ presets: [
+ [
+ '@babel/preset-env',
+ {
+ targets: {
+ chrome: 100,
+ },
+ },
+ ],
+ '@babel/preset-typescript',
+ '@babel/preset-react',
+ ],
+ plugins: [],
+});
+
+const check = async ({ packageJson = {}, main = {}, extraFiles }: any) => {
+ if (extraFiles) {
+ // eslint-disable-next-line global-require
+ require('fs-extra').__setMockFiles({
+ [path.join('.storybook', 'main.js')]: `module.exports = ${JSON.stringify(main)};`,
+ ...extraFiles,
+ });
+ }
+ const packageManager = {
+ retrievePackageJson: () => ({ dependencies: {}, devDependencies: {}, ...packageJson }),
+ } as JsPackageManager;
+ return missingBabelRc.check({ packageManager });
+};
+
+describe('missing-babelrc fix', () => {
+ it('skips when babelrc config is present', async () => {
+ const packageJson = {
+ devDependencies: {
+ '@storybook/react': '^7.0.0',
+ '@storybook/react-webpack5': '^7.0.0',
+ },
+ };
+
+ // different babel extensions
+ await expect(
+ check({ extraFiles: { '.babelrc': babelContent }, packageJson })
+ ).resolves.toBeNull();
+ await expect(
+ check({ extraFiles: { '.babelrc.json': babelContent }, packageJson })
+ ).resolves.toBeNull();
+ await expect(
+ check({ extraFiles: { 'babel.config.json': babelContent }, packageJson })
+ ).resolves.toBeNull();
+
+ // babel field in package.json
+ await expect(
+ check({ packageJson: { ...packageJson, babel: babelContent } })
+ ).resolves.toBeNull();
+ });
+
+ it('skips when using a framework that provides babel config', async () => {
+ const packageJson = {
+ devDependencies: {
+ '@storybook/react': '^7.0.0',
+ '@storybook/nextjs': '^7.0.0',
+ },
+ };
+
+ await expect(check({ packageJson })).resolves.toBeNull();
+ });
+
+ it('prompts when babelrc file is missing and framework does not provide babel config', async () => {
+ const packageJson = {
+ devDependencies: {
+ '@storybook/react': '^7.0.0',
+ '@storybook/react-webpack5': '^7.0.0',
+ },
+ };
+
+ await expect(check({ packageJson })).resolves.toBe({
+ needsBabelRc: true,
+ });
+ });
+});
diff --git a/code/lib/cli/src/automigrate/fixes/missing-babelrc.ts b/code/lib/cli/src/automigrate/fixes/missing-babelrc.ts
new file mode 100644
index 00000000000..10b64d67858
--- /dev/null
+++ b/code/lib/cli/src/automigrate/fixes/missing-babelrc.ts
@@ -0,0 +1,91 @@
+import chalk from 'chalk';
+import dedent from 'ts-dedent';
+import semver from 'semver';
+import { getStorybookInfo } from '@storybook/core-common';
+import { loadPartialConfigAsync } from '@babel/core';
+import { readConfig } from '@storybook/csf-tools';
+import type { Fix } from '../types';
+
+interface MissingBabelRcOptions {
+ needsBabelRc: boolean;
+}
+
+const logger = console;
+
+const frameworksThatNeedBabelConfig = [
+ '@storybook/angular',
+ '@storybook/react-webpack5',
+ '@storybook/vue-webpack5',
+ '@storybook/vue3-webpack5',
+ '@storybook/preact-webpack5',
+ '@storybook/html-webpack5',
+ '@storybook/react-vite',
+ '@storybook/vue-vite',
+ '@storybook/vue3-vite',
+ '@storybook/preact-vite',
+ '@storybook/html-vite',
+];
+
+export const missingBabelRc: Fix = {
+ id: 'missing-babelrc',
+ promptOnly: true,
+
+ async check({ packageManager }) {
+ const packageJson = packageManager.retrievePackageJson();
+ const { mainConfig, version: storybookVersion } = getStorybookInfo(packageJson);
+
+ const storybookCoerced = storybookVersion && semver.coerce(storybookVersion)?.version;
+ if (!storybookCoerced) {
+ throw new Error(dedent`
+ ❌ Unable to determine storybook version.
+ 🤔 Are you running automigrate from your project directory?
+ `);
+ }
+
+ if (!semver.gte(storybookCoerced, '7.0.0')) {
+ return null;
+ }
+
+ if (!mainConfig) {
+ logger.warn('Unable to find storybook main.js config, skipping');
+ return null;
+ }
+
+ const main = await readConfig(mainConfig);
+
+ const frameworkField = main.getFieldValue(['framework']);
+ const frameworkPackage =
+ typeof frameworkField === 'string' ? frameworkField : frameworkField?.name;
+
+ if (frameworksThatNeedBabelConfig.includes(frameworkPackage)) {
+ const config = await loadPartialConfigAsync();
+ if (!config.config && !packageJson.babel) {
+ return { needsBabelRc: true };
+ }
+ }
+
+ return null;
+ },
+ prompt() {
+ return dedent`
+ ${chalk.bold(
+ chalk.red('Attention')
+ )}: We could not automatically make this change. You'll need to do it manually.
+
+ Storybook now uses Babel mode v7 exclusively. In 6.x, Storybook provided its own babel settings out of the box. Now, Storybook's uses your project's babel settings (.babelrc, babel.config.js, etc.) instead.
+
+ In the new mode, Storybook expects you to provide a configuration file. If you want a configuration file that's equivalent to the 6.x default, you can run the following command in your project directory:
+
+ ${chalk.blue('npx sb@next babelrc')}
+
+ This will create a ${chalk.blue(
+ '.babelrc.json'
+ )} file with some basic configuration and add new package devDependencies accordingly.
+
+ Please see the migration guide for more information:
+ ${chalk.yellow(
+ 'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#babel-mode-v7-exclusively'
+ )}
+ `;
+ },
+};