From f18d9d3ab3893c9dc9769e2af2ca6aa7f04dd6d8 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 8 Feb 2024 10:21:48 +0100 Subject: [PATCH] Add webpack5-compiler automigration --- .../fixes/webpack5-compiler-setup.ts | 105 ++++++++++++++++++ .../src/automigrate/helpers/mainConfigFile.ts | 4 +- code/lib/cli/src/helpers.ts | 25 +++++ 3 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts new file mode 100644 index 000000000000..75aa19d3466a --- /dev/null +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts @@ -0,0 +1,105 @@ +import execa from 'execa'; +import prompts from 'prompts'; +import type { SupportedFrameworks } from '@storybook/types'; +import { frameworkPackages } from '@storybook/core-common'; +import type { Fix } from '../types'; +import { getFrameworkPackageName } from '../helpers/mainConfigFile'; +import { frameworkToDefaultBuilder } from '../../helpers'; +import { CoreBuilder } from '../../project_types'; + +type Options = { + shouldAddSWC: boolean; + shouldRemoveSWCFlag: boolean; + isNextJs: boolean; +}; + +export const webpack5Migration: Fix = { + id: 'webpack5-compiler-setup', + + async check({ mainConfig, packageManager }) { + const frameworkPackageName = getFrameworkPackageName(mainConfig); + const frameworkName = Object.entries(frameworkPackages).find( + ([name]) => name === frameworkPackageName + )?.[1]; + + const builder = frameworkName + ? // TODO: Consider using also custom builders + frameworkToDefaultBuilder[frameworkName] + : await (async () => { + const webpackVersion = await packageManager.getPackageVersion('webpack'); + return !!webpackVersion ? CoreBuilder.Webpack5 : CoreBuilder.Vite; + })(); + + if (builder !== CoreBuilder.Webpack5) { + return null; + } + + const excludedFrameworks: SupportedFrameworks[] = ['angular', 'ember', 'nextjs']; + + const isExcludedFramework = frameworkName ? excludedFrameworks.includes(frameworkName) : false; + + if (isExcludedFramework) { + return null; + } + + const usesSWC = mainConfig.framework?.options?.builder?.useSWC; + + if (!isExcludedFramework) { + return { + shouldAddSWC: usesSWC === true, + shouldRemoveSWCFlag: 'useSWC' in mainConfig.framework?.options?.builder, + isNextJs: packageManager.type === 'next.js', + }; + } + + return {}; + }, + + async prompt({ shouldAddSWC, shouldRemoveSWCFlag, isNextJs }) { + let message = 'We need to update your Storybook configuration for Webpack 5.\n'; + if (shouldRemoveSWCFlag) { + message += 'The "frameworks.options.builder.useSWC" flag will be removed.\n'; + } + + if (shouldAddSWC !== undefined) { + const addonName = shouldAddSWC + ? '@storybook/addon-webpack5-compiler-swc' + : '@storybook/addon-webpack5-compiler-babel'; + message += `The "${addonName}" will be added to your project.\n`; + + message += + 'After the migration, you can switch compilers if needed by manually adding the alternative addon.'; + } + + if (isNextJs) { + message += 'Next.js-specific migrations will be applied.'; + } + + return message; + }, + + async run({ result, mainConfigPath, dryRun }) { + const { shouldAddSWC, shouldRemoveSWCFlag, isNextJs } = result; + + if (shouldRemoveSWCFlag && !dryRun) { + // Logic for remove `frameworks.options.builder.useSWC` from mainConfig + } + + if (shouldAddSWC !== undefined && !dryRun) { + const compiler = shouldAddSWC ? 'swc' : 'babel'; + const addonName = `@storybook/addon-webpack5-compiler-${compiler}`; + const command = `npx storybook@next add ${addonName}`; + + try { + await execa.command(command, { stdio: 'inherit' }); + } catch (error) { + console.error(`Failed to install ${addonName}`, error); + throw new Error(`Failed to install ${addonName}`); + } + } + + if (isNextJs && !dryRun) { + // Next.js-specific migration logic + } + }, +}; diff --git a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts index c721dae39a31..c89efcbc845f 100644 --- a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts +++ b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts @@ -18,8 +18,8 @@ const logger = console; /** * Given a Storybook configuration object, retrieves the package name or file path of the framework. - * @param mainConfig - The main Storybook configuration object to lookup. - * @returns - The package name of the framework. If not found, returns null. + * @param mainConfig The main Storybook configuration object to lookup. + * @returns The package name of the framework. If not found, returns null. */ export const getFrameworkPackageName = (mainConfig?: StorybookConfigRaw) => { const packageNameOrPath = diff --git a/code/lib/cli/src/helpers.ts b/code/lib/cli/src/helpers.ts index 167340059828..5659216412a7 100644 --- a/code/lib/cli/src/helpers.ts +++ b/code/lib/cli/src/helpers.ts @@ -15,6 +15,7 @@ import type { } from '@storybook/core-common'; import type { SupportedFrameworks } from '@storybook/types'; import type { SupportedRenderers } from './project_types'; +import { CoreBuilder } from './project_types'; import { SupportedLanguage } from './project_types'; import { versions as storybookMonorepoPackages } from '@storybook/core-common'; @@ -168,6 +169,30 @@ const frameworkToRenderer: Record< 'web-components': 'web-components', }; +export const frameworkToDefaultBuilder: Record = { + angular: CoreBuilder.Webpack5, + ember: CoreBuilder.Webpack5, + 'html-vite': CoreBuilder.Vite, + 'html-webpack5': CoreBuilder.Webpack5, + nextjs: CoreBuilder.Webpack5, + 'preact-vite': CoreBuilder.Vite, + 'preact-webpack5': CoreBuilder.Webpack5, + qwik: CoreBuilder.Vite, + 'react-vite': CoreBuilder.Vite, + 'react-webpack5': CoreBuilder.Webpack5, + 'server-webpack5': CoreBuilder.Webpack5, + solid: CoreBuilder.Vite, + 'svelte-vite': CoreBuilder.Vite, + 'svelte-webpack5': CoreBuilder.Webpack5, + sveltekit: CoreBuilder.Vite, + 'vue-vite': CoreBuilder.Vite, + 'vue-webpack5': CoreBuilder.Webpack5, + 'vue3-vite': CoreBuilder.Vite, + 'vue3-webpack5': CoreBuilder.Webpack5, + 'web-components-vite': CoreBuilder.Vite, + 'web-components-webpack5': CoreBuilder.Webpack5, +}; + export async function copyTemplateFiles({ packageManager, renderer,