import fse from 'fs-extra'; import { getStorybookBabelDependencies } from '@storybook/core-common'; import { NpmOptions } from '../NpmOptions'; import { StoryFormat, SupportedLanguage, SupportedFrameworks, Builder, CoreBuilder, } from '../project_types'; import { getBabelDependencies, copyComponents } from '../helpers'; import { configure } from './configure'; import { getPackageDetails, JsPackageManager } from '../js-package-manager'; import { generateStorybookBabelConfigInCWD } from '../babel-config'; export type GeneratorOptions = { language: SupportedLanguage; storyFormat: StoryFormat; builder: Builder; linkable: boolean; }; export interface FrameworkOptions { extraPackages?: string[]; extraAddons?: string[]; staticDir?: string; addScripts?: boolean; addComponents?: boolean; addBabel?: boolean; addESLint?: boolean; extraMain?: any; extensions?: string[]; commonJs?: boolean; } export type Generator = ( packageManager: JsPackageManager, npmOptions: NpmOptions, options: GeneratorOptions ) => Promise; const defaultOptions: FrameworkOptions = { extraPackages: [], extraAddons: [], staticDir: undefined, addScripts: true, addComponents: true, addBabel: true, addESLint: false, extraMain: undefined, extensions: undefined, commonJs: false, }; const builderDependencies = (builder: Builder) => { switch (builder) { case CoreBuilder.Webpack4: return []; case CoreBuilder.Webpack5: return ['@storybook/builder-webpack5', '@storybook/manager-webpack5']; default: return [builder]; } }; export async function baseGenerator( packageManager: JsPackageManager, npmOptions: NpmOptions, { language, builder }: GeneratorOptions, framework: SupportedFrameworks, options: FrameworkOptions = defaultOptions ) { const { extraAddons, extraPackages, staticDir, addScripts, addComponents, addBabel, addESLint, extraMain, extensions, } = { ...defaultOptions, ...options, }; // added to main.js // make sure to update `canUsePrebuiltManager` in dev-server.js and build-manager-config/main.js when this list changes const addons = ['@storybook/addon-links', '@storybook/addon-essentials']; // added to package.json const addonPackages = [...addons, '@storybook/addon-actions']; const yarn2Dependencies = packageManager.type === 'yarn2' ? ['@storybook/addon-docs', '@mdx-js/react'] : []; const files = await fse.readdir(process.cwd()); const isNewFolder = !files.some( (fname) => fname.startsWith('.babel') || fname.startsWith('babel') || fname === 'package.json' ); const packageJson = packageManager.retrievePackageJson(); const installedDependencies = new Set(Object.keys(packageJson.dependencies)); const packages = [ `@storybook/${framework}`, ...addonPackages, ...extraPackages, ...extraAddons, ...yarn2Dependencies, ...builderDependencies(builder), ] .filter(Boolean) .filter( (packageToInstall) => !installedDependencies.has(getPackageDetails(packageToInstall)[0]) ); const versionedPackages = await packageManager.getVersionedPackages(...packages); const mainOptions = builder !== CoreBuilder.Webpack4 ? { core: { builder, }, ...extraMain, } : extraMain; configure(framework, { addons: [...addons, ...extraAddons], extensions, commonJs: options.commonJs, ...mainOptions, }); if (addComponents) { copyComponents(framework, language); } const babelDependencies = addBabel ? await getBabelDependencies(packageManager, packageJson) : []; if (isNewFolder) { babelDependencies.push(...getStorybookBabelDependencies()); await generateStorybookBabelConfigInCWD(); } packageManager.addDependencies({ ...npmOptions, packageJson }, [ ...versionedPackages, ...babelDependencies, ]); if (addScripts) { packageManager.addStorybookCommandInScripts({ port: 6006, staticFolder: staticDir, }); } if (addESLint) { packageManager.addESLintConfig(); } }