diff --git a/.babelrc.js b/.babelrc.js index 9710fe5c00c4..a7677d18032c 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -117,7 +117,7 @@ module.exports = { shippedProposals: true, useBuiltIns: 'usage', targets: { - node: '10', + node: '14', }, modules, corejs: '3', @@ -149,7 +149,7 @@ module.exports = { shippedProposals: true, useBuiltIns: 'usage', targets: { - node: '10', + node: '14', }, corejs: '3', modules: false, diff --git a/addons/docs/src/preset.ts b/addons/docs/src/preset.ts index 6e2090febae4..5b0962473783 100644 --- a/addons/docs/src/preset.ts +++ b/addons/docs/src/preset.ts @@ -69,6 +69,7 @@ export async function webpack( const sourceLoader = sourceLoaderOptions ? [ { + layer: 'storybook_source', test: /\.(stories|story)\.[tj]sx?$/, loader: require.resolve('@storybook/source-loader'), options: { ...sourceLoaderOptions, inspectLocalDependencies: true }, @@ -104,6 +105,7 @@ export async function webpack( rules: [ ...rules, { + layer: 'storybook_mdx', test: /(stories|story)\.mdx$/, use: [ { @@ -116,6 +118,7 @@ export async function webpack( ], }, { + layer: 'storybook_mdx', test: /\.mdx$/, exclude: /(stories|story)\.mdx$/, use: [ diff --git a/docs/snippets/common/storybook-builder-api-interface.ts.mdx b/docs/snippets/common/storybook-builder-api-interface.ts.mdx index 762ea1f146b7..67b56dd6c60a 100644 --- a/docs/snippets/common/storybook-builder-api-interface.ts.mdx +++ b/docs/snippets/common/storybook-builder-api-interface.ts.mdx @@ -16,7 +16,5 @@ export interface Builder { }) => Promise; bail: (e?: Error) => Promise; getConfig: (options: Options) => Promise; - corePresets?: string[]; - overridePresets?: string[]; } ``` \ No newline at end of file diff --git a/examples/html-kitchen-sink/package.json b/examples/html-kitchen-sink/package.json index 86022a72ad4e..7c4a81b8ac14 100644 --- a/examples/html-kitchen-sink/package.json +++ b/examples/html-kitchen-sink/package.json @@ -21,7 +21,7 @@ "@storybook/addon-highlight": "7.0.0-alpha.12", "@storybook/addon-jest": "7.0.0-alpha.12", "@storybook/addon-links": "7.0.0-alpha.12", - "@storybook/addon-postcss": "^2.0.0", + "@storybook/addon-postcss": "^3.0.0-alpha.0", "@storybook/addon-storyshots": "7.0.0-alpha.12", "@storybook/addon-storysource": "7.0.0-alpha.12", "@storybook/addon-viewport": "7.0.0-alpha.12", @@ -36,7 +36,6 @@ "format-json": "^1.0.3", "global": "^4.4.0", "postcss": "^8.2.4", - "postcss-color-rebeccapurple": "^6.0.0", "storybook": "7.0.0-alpha.12" }, "storybook": { diff --git a/examples/html-kitchen-sink/postcss.config.js b/examples/html-kitchen-sink/postcss.config.js index 253ca7d45395..3b1659581fcf 100644 --- a/examples/html-kitchen-sink/postcss.config.js +++ b/examples/html-kitchen-sink/postcss.config.js @@ -2,7 +2,5 @@ module.exports = { plugins: [ // eslint-disable-next-line global-require require('autoprefixer'), - // eslint-disable-next-line global-require - require('postcss-color-rebeccapurple'), ], }; diff --git a/lib/builder-manager/src/index.ts b/lib/builder-manager/src/index.ts index 8b3eba39b0dd..b842d27e32a3 100644 --- a/lib/builder-manager/src/index.ts +++ b/lib/builder-manager/src/index.ts @@ -11,6 +11,7 @@ import aliasPlugin from 'esbuild-plugin-alias'; import { renderHTML } from './utils/template'; import { definitions } from './utils/globals'; import { + BuilderBuildOptions, BuilderBuildResult, BuilderFunction, BuilderStartOptions, @@ -121,6 +122,8 @@ const starter: StarterFunction = async function* starterGeneratorFn({ } }); + logger.trace({ message: '=> Manager started', time: process.hrtime(startTime) }); + return { bail, stats: { @@ -211,7 +214,7 @@ export const start = async (options: BuilderStartOptions) => { return result.value; }; -export const build = async (options: BuilderStartOptions) => { +export const build = async (options: BuilderBuildOptions) => { asyncIterator = builder(options); let result; @@ -222,6 +225,3 @@ export const build = async (options: BuilderStartOptions) => { return result.value; }; - -export const corePresets: ManagerBuilder['corePresets'] = []; -export const overridePresets: ManagerBuilder['overridePresets'] = []; diff --git a/lib/builder-webpack5/preset.js b/lib/builder-webpack5/preset.js new file mode 100644 index 000000000000..f4f0e998846b --- /dev/null +++ b/lib/builder-webpack5/preset.js @@ -0,0 +1 @@ +module.exports = require('./dist/index'); diff --git a/lib/builder-webpack5/src/index.ts b/lib/builder-webpack5/src/index.ts index 2d33a372308c..63dcb611c453 100644 --- a/lib/builder-webpack5/src/index.ts +++ b/lib/builder-webpack5/src/index.ts @@ -5,6 +5,7 @@ import { logger } from '@storybook/node-logger'; import { useProgressReporting } from '@storybook/core-common'; import type { Builder, Options } from '@storybook/core-common'; import { checkWebpackVersion } from '@storybook/core-webpack'; +import { createWebpackConfig } from './utils/webpack'; export * from './types'; @@ -39,20 +40,11 @@ export const executor = { export const getConfig: WebpackBuilder['getConfig'] = async (options) => { const { presets } = options; - const typescriptOptions = await presets.apply('typescript', {}, options); - const babelOptions = await presets.apply('babel', {}, { ...options, typescriptOptions }); - const framework = await presets.apply('framework', {}, options); - - return presets.apply( - 'webpack', - {}, - { - ...options, - babelOptions, - typescriptOptions, - frameworkOptions: typeof framework === 'string' ? {} : framework?.options, - } - ) as any; + + const base = await createWebpackConfig(options); + const intermediate = await presets.apply('webpack', base); + + return presets.apply('webpackFinal', intermediate); }; let asyncIterator: ReturnType | ReturnType; @@ -261,6 +253,3 @@ export const build = async (options: BuilderStartOptions) => { return result.value; }; - -export const corePresets = [require.resolve('./presets/preview-preset.js')]; -export const overridePresets = [require.resolve('./presets/custom-webpack-preset.js')]; diff --git a/lib/builder-webpack5/src/presets/preview-preset.ts b/lib/builder-webpack5/src/preset.ts similarity index 84% rename from lib/builder-webpack5/src/presets/preview-preset.ts rename to lib/builder-webpack5/src/preset.ts index fdd31c435caf..ae84e8bc80f3 100644 --- a/lib/builder-webpack5/src/presets/preview-preset.ts +++ b/lib/builder-webpack5/src/preset.ts @@ -1,7 +1,3 @@ -import webpackConfig from '../preview/iframe-webpack.config'; - -export const webpack = async (_: unknown, options: any) => webpackConfig(options); - export const entries = async (_: unknown, options: any) => { let result: string[] = []; diff --git a/lib/builder-webpack5/src/presets/custom-webpack-preset.ts b/lib/builder-webpack5/src/presets/custom-webpack-preset.ts deleted file mode 100644 index 879dcc598107..000000000000 --- a/lib/builder-webpack5/src/presets/custom-webpack-preset.ts +++ /dev/null @@ -1,47 +0,0 @@ -import * as webpackReal from 'webpack'; -import { logger } from '@storybook/node-logger'; -import type { Options, CoreConfig } from '@storybook/core-common'; -import type { Configuration } from 'webpack'; -import deprecate from 'util-deprecate'; -import { dedent } from 'ts-dedent'; -import { loadCustomWebpackConfig } from '@storybook/core-webpack'; -import { createDefaultWebpackConfig } from '../preview/base-webpack.config'; - -export async function webpack(config: Configuration, options: Options) { - // @ts-ignore - const { configDir, configType, presets, webpackConfig } = options; - - const coreOptions = await presets.apply('core'); - - let defaultConfig = config; - if (!coreOptions?.disableWebpackDefaults) { - defaultConfig = await createDefaultWebpackConfig(config, options); - } - - const finalDefaultConfig = await presets.apply('webpackFinal', defaultConfig, options); - - // through standalone webpackConfig option - if (webpackConfig) { - return deprecate( - webpackConfig, - dedent` - You've provided a webpack config directly in CallOptions, this is not recommended. Please use presets instead. This feature will be removed in 7.0 - ` - )(finalDefaultConfig); - } - - // Check whether user has a custom webpack config file and - // return the (extended) base configuration if it's not available. - const customConfig = loadCustomWebpackConfig(configDir); - - if (typeof customConfig === 'function') { - logger.info('=> Loading custom Webpack config (full-control mode).'); - return customConfig({ config: finalDefaultConfig, mode: configType }); - } - - logger.info('=> Using default Webpack5 setup'); - return finalDefaultConfig; -} - -export const webpackInstance = async () => webpackReal; -export const webpackVersion = async () => '5'; diff --git a/lib/builder-webpack5/src/preview/babel-loader-preview.ts b/lib/builder-webpack5/src/preview/babel-loader-preview.ts deleted file mode 100644 index e73adc74b8bf..000000000000 --- a/lib/builder-webpack5/src/preview/babel-loader-preview.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { getProjectRoot } from '@storybook/core-common'; -import { TypescriptOptions } from '../types'; - -export const createBabelLoader = (options: any, typescriptOptions: TypescriptOptions) => { - return { - test: typescriptOptions.skipBabel ? /\.(mjs|jsx?)$/ : /\.(mjs|tsx?|jsx?)$/, - use: [ - { - loader: require.resolve('babel-loader'), - options, - }, - ], - include: [getProjectRoot()], - exclude: /node_modules/, - }; -}; diff --git a/lib/builder-webpack5/src/preview/base-webpack.config.ts b/lib/builder-webpack5/src/preview/base-webpack.config.ts deleted file mode 100644 index 26e871ca0c20..000000000000 --- a/lib/builder-webpack5/src/preview/base-webpack.config.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { logger } from '@storybook/node-logger'; -import type { Options, CoreConfig } from '@storybook/core-common'; -import type { Configuration } from 'webpack'; -import type { BuilderOptions } from '../types'; - -export async function createDefaultWebpackConfig( - storybookBaseConfig: Configuration, - options: Options -): Promise { - if ( - options.presetsList?.some((preset) => - /@storybook(\/|\\)preset-create-react-app/.test( - typeof preset === 'string' ? preset : preset.name - ) - ) - ) { - return storybookBaseConfig; - } - - const hasPostcssAddon = options.presetsList?.some((preset) => - /@storybook(\/|\\)addon-postcss/.test(typeof preset === 'string' ? preset : preset.name) - ); - - let cssLoaders = {}; - if (!hasPostcssAddon) { - logger.info(`=> Using implicit CSS loaders`); - cssLoaders = { - test: /\.css$/, - sideEffects: true, - use: [ - // TODO: Decide if we want to keep style-loader & css-loader in core - // Trying to apply style-loader or css-loader to files that already have been - // processed by them causes webpack to crash, so no one else can add similar - // loader configurations to the `.css` extension. - require.resolve('style-loader'), - { - loader: require.resolve('css-loader'), - options: { - importLoaders: 1, - }, - }, - ], - }; - } - - const isProd = storybookBaseConfig.mode !== 'development'; - - const coreOptions = await options.presets.apply('core'); - const builderOptions: BuilderOptions = - typeof coreOptions.builder === 'string' - ? {} - : coreOptions.builder?.options || ({} as BuilderOptions); - const cacheConfig = builderOptions.fsCache ? { cache: { type: 'filesystem' as const } } : {}; - const lazyCompilationConfig = - builderOptions.lazyCompilation && !isProd ? { lazyCompilation: { entries: false } } : {}; - - return { - ...storybookBaseConfig, - module: { - ...storybookBaseConfig.module, - rules: [ - ...(storybookBaseConfig.module?.rules || []), - cssLoaders, - { - test: /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/, - type: 'asset/resource', - generator: { - filename: isProd - ? 'static/media/[name].[contenthash:8][ext]' - : 'static/media/[path][name][ext]', - }, - }, - { - test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/, - type: 'asset', - parser: { - dataUrlCondition: { - maxSize: 10000, - }, - }, - generator: { - filename: isProd - ? 'static/media/[name].[contenthash:8][ext]' - : 'static/media/[path][name][ext]', - }, - }, - ], - }, - resolve: { - ...storybookBaseConfig.resolve, - fallback: { - ...storybookBaseConfig.resolve?.fallback, - crypto: false, - assert: false, - }, - }, - ...cacheConfig, - experiments: { ...storybookBaseConfig.experiments, ...lazyCompilationConfig }, - }; -} diff --git a/lib/builder-webpack5/src/preview/iframe-webpack.config.ts b/lib/builder-webpack5/src/preview/iframe-webpack.config.ts deleted file mode 100644 index 6305a306e4fb..000000000000 --- a/lib/builder-webpack5/src/preview/iframe-webpack.config.ts +++ /dev/null @@ -1,296 +0,0 @@ -import path from 'path'; -import { dedent } from 'ts-dedent'; -import { DefinePlugin, HotModuleReplacementPlugin, ProgressPlugin, ProvidePlugin } from 'webpack'; -import type { Configuration } from 'webpack'; -import HtmlWebpackPlugin from 'html-webpack-plugin'; -// @ts-ignore // -- this has typings for webpack4 in it, won't work -import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin'; -import TerserWebpackPlugin from 'terser-webpack-plugin'; -import VirtualModulePlugin from 'webpack-virtual-modules'; -import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; - -import type { Options, CoreConfig } from '@storybook/core-common'; -import { - stringifyProcessEnvs, - handlebars, - interpolate, - normalizeStories, - readTemplate, - loadPreviewOrConfigFile, -} from '@storybook/core-common'; -import { toRequireContextString, toImportFn } from '@storybook/core-webpack'; -import type { BuilderOptions, TypescriptOptions } from '../types'; -import { createBabelLoader } from './babel-loader-preview'; - -const storybookPaths: Record = { - global: path.dirname(require.resolve(`global/package.json`)), - ...[ - 'addons', - 'api', - 'store', - 'channels', - 'channel-postmessage', - 'channel-websocket', - 'components', - 'core-events', - 'router', - 'theming', - 'semver', - 'preview-web', - 'client-api', - 'client-logger', - ].reduce( - (acc, sbPackage) => ({ - ...acc, - [`@storybook/${sbPackage}`]: path.dirname( - require.resolve(`@storybook/${sbPackage}/package.json`) - ), - }), - {} - ), -}; - -export default async ( - options: Options & Record & { typescriptOptions: TypescriptOptions } -): Promise => { - const { - outputDir = path.join('.', 'public'), - quiet, - packageJson, - configType, - presets, - previewUrl, - babelOptions, - typescriptOptions, - features, - serverChannelUrl, - } = options; - - const framework = await presets.apply('framework', undefined); - if (!framework) { - throw new Error(dedent` - You must to specify a framework in '.storybook/main.js' config. - - https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#framework-field-mandatory - `); - } - const frameworkName = typeof framework === 'string' ? framework : framework.name; - const frameworkOptions = await presets.apply('frameworkOptions'); - - const isProd = configType === 'PRODUCTION'; - const envs = await presets.apply>('env'); - const logLevel = await presets.apply('logLevel', undefined); - - const headHtmlSnippet = await presets.apply('previewHead'); - const bodyHtmlSnippet = await presets.apply('previewBody'); - const template = await presets.apply('previewMainTemplate'); - const coreOptions = await presets.apply('core'); - const builderOptions: BuilderOptions = - typeof coreOptions.builder === 'string' ? {} : coreOptions.builder?.options || {}; - - const configs = [ - ...(await presets.apply('config', [], options)), - loadPreviewOrConfigFile(options), - ].filter(Boolean); - const entries = (await presets.apply('entries', [], options)) as string[]; - const workingDir = process.cwd(); - const stories = normalizeStories(await presets.apply('stories', [], options), { - configDir: options.configDir, - workingDir, - }); - - const virtualModuleMapping: Record = {}; - if (features?.storyStoreV7) { - const storiesFilename = 'storybook-stories.js'; - const storiesPath = path.resolve(path.join(workingDir, storiesFilename)); - - const needPipelinedImport = !!builderOptions.lazyCompilation && !isProd; - virtualModuleMapping[storiesPath] = toImportFn(stories, { needPipelinedImport }); - const configEntryPath = path.resolve(path.join(workingDir, 'storybook-config-entry.js')); - virtualModuleMapping[configEntryPath] = handlebars( - await readTemplate( - require.resolve( - '@storybook/builder-webpack5/templates/virtualModuleModernEntry.js.handlebars' - ) - ), - { - storiesFilename, - configs, - } - // We need to double escape `\` for webpack. We may have some in windows paths - ).replace(/\\/g, '\\\\'); - entries.push(configEntryPath); - } else { - const frameworkInitEntry = path.resolve( - path.join(workingDir, 'storybook-init-framework-entry.js') - ); - virtualModuleMapping[frameworkInitEntry] = `import '${frameworkName}';`; - entries.push(frameworkInitEntry); - - const entryTemplate = await readTemplate( - path.join(__dirname, 'virtualModuleEntry.template.js') - ); - - configs.forEach((configFilename: any) => { - const clientApi = storybookPaths['@storybook/client-api']; - const clientLogger = storybookPaths['@storybook/client-logger']; - - // NOTE: although this file is also from the `dist/cjs` directory, it is actually a ESM - // file, see https://github.com/storybookjs/storybook/pull/16727#issuecomment-986485173 - virtualModuleMapping[`${configFilename}-generated-config-entry.js`] = interpolate( - entryTemplate, - { - configFilename, - clientApi, - clientLogger, - } - ); - entries.push(`${configFilename}-generated-config-entry.js`); - }); - if (stories.length > 0) { - const storyTemplate = await readTemplate( - path.join(__dirname, 'virtualModuleStory.template.js') - ); - // NOTE: this file has a `.cjs` extension as it is a CJS file (from `dist/cjs`) and runs - // in the user's webpack mode, which may be strict about the use of require/import. - // See https://github.com/storybookjs/storybook/issues/14877 - const storiesFilename = path.resolve(path.join(workingDir, `generated-stories-entry.cjs`)); - virtualModuleMapping[storiesFilename] = interpolate(storyTemplate, { - frameworkName, - }) - // Make sure we also replace quotes for this one - .replace("'{{stories}}'", stories.map(toRequireContextString).join(',')); - entries.push(storiesFilename); - } - } - - const shouldCheckTs = typescriptOptions.check && !typescriptOptions.skipBabel; - const tsCheckOptions = typescriptOptions.checkOptions || {}; - - return { - name: 'preview', - mode: isProd ? 'production' : 'development', - bail: isProd, - devtool: 'cheap-module-source-map', - entry: entries, - output: { - path: path.resolve(process.cwd(), outputDir), - filename: isProd ? '[name].[contenthash:8].iframe.bundle.js' : '[name].iframe.bundle.js', - publicPath: '', - }, - stats: { - preset: 'none', - logging: 'error', - }, - watchOptions: { - ignored: /node_modules/, - }, - ignoreWarnings: [ - { - message: /export '\S+' was not found in 'global'/, - }, - ], - plugins: [ - Object.keys(virtualModuleMapping).length > 0 - ? new VirtualModulePlugin(virtualModuleMapping) - : (null as any), - new HtmlWebpackPlugin({ - filename: `iframe.html`, - // FIXME: `none` isn't a known option - chunksSortMode: 'none' as any, - alwaysWriteToDisk: true, - inject: false, - template, - templateParameters: { - version: packageJson.version, - globals: { - CONFIG_TYPE: configType, - LOGLEVEL: logLevel, - FRAMEWORK_OPTIONS: frameworkOptions, - CHANNEL_OPTIONS: coreOptions.channelOptions, - FEATURES: features, - PREVIEW_URL: previewUrl, - STORIES: stories.map((specifier) => ({ - ...specifier, - importPathMatcher: specifier.importPathMatcher.source, - })), - SERVER_CHANNEL_URL: serverChannelUrl, - }, - headHtmlSnippet, - bodyHtmlSnippet, - }, - minify: { - collapseWhitespace: true, - removeComments: true, - removeRedundantAttributes: true, - removeScriptTypeAttributes: false, - removeStyleLinkTypeAttributes: true, - useShortDoctype: true, - }, - }), - new DefinePlugin({ - ...stringifyProcessEnvs(envs), - NODE_ENV: JSON.stringify(process.env.NODE_ENV), - }), - new ProvidePlugin({ process: require.resolve('process/browser.js') }), - isProd ? null : new HotModuleReplacementPlugin(), - new CaseSensitivePathsPlugin(), - quiet ? null : new ProgressPlugin({}), - shouldCheckTs ? new ForkTsCheckerWebpackPlugin(tsCheckOptions) : null, - ].filter(Boolean), - module: { - rules: [ - { - test: /\.m?js$/, - type: 'javascript/auto', - }, - { - test: /\.m?js$/, - resolve: { - fullySpecified: false, - }, - }, - createBabelLoader(babelOptions, typescriptOptions), - { - test: /\.md$/, - type: 'asset/source', - }, - ], - }, - resolve: { - extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json', '.cjs'], - modules: ['node_modules'].concat(envs.NODE_PATH || []), - mainFields: ['browser', 'module', 'main'].filter(Boolean), - alias: storybookPaths, - fallback: { - path: require.resolve('path-browserify'), - assert: require.resolve('browser-assert'), - util: require.resolve('util'), - }, - }, - optimization: { - splitChunks: { - chunks: 'all', - }, - runtimeChunk: true, - sideEffects: true, - usedExports: isProd, - moduleIds: 'named', - minimizer: isProd - ? [ - new TerserWebpackPlugin({ - parallel: true, - terserOptions: { - sourceMap: true, - mangle: false, - keep_fnames: true, - }, - }), - ] - : [], - }, - performance: { - hints: isProd ? 'warning' : false, - }, - }; -}; diff --git a/lib/builder-webpack5/src/preview/virtualModuleStory.template.js b/lib/builder-webpack5/src/preview/virtualModuleStory.template.js deleted file mode 100644 index 3a180df5db28..000000000000 --- a/lib/builder-webpack5/src/preview/virtualModuleStory.template.js +++ /dev/null @@ -1,4 +0,0 @@ -/* eslint-disable import/no-unresolved */ -import { configure } from '{{frameworkName}}'; - -configure(['{{stories}}'], module, false); diff --git a/lib/builder-webpack5/src/utils/entries.ts b/lib/builder-webpack5/src/utils/entries.ts new file mode 100644 index 000000000000..18721986d41c --- /dev/null +++ b/lib/builder-webpack5/src/utils/entries.ts @@ -0,0 +1,139 @@ +import path from 'path'; + +import { handlebars, interpolate, normalizeStories, readTemplate } from '@storybook/core-common'; +import { toRequireContextString, toImportFn } from '@storybook/core-webpack'; +import { BuilderOptions } from '../types'; + +export const getStorybookPaths = (): Record => ({ + global: path.dirname(require.resolve(`global/package.json`)), + ...[ + 'addons', + 'api', + 'store', + 'channels', + 'channel-postmessage', + 'channel-websocket', + 'components', + 'core-events', + 'router', + 'theming', + 'semver', + 'preview-web', + 'client-api', + 'client-logger', + ].reduce( + (acc, sbPackage) => ({ + ...acc, + [`@storybook/${sbPackage}`]: path.dirname( + require.resolve(`@storybook/${sbPackage}/package.json`) + ), + }), + {} + ), +}); + +export async function getModernVirtualEntries({ + configDir, + builderOptions, + isProd, + stories, + configs, + entries: originalEntries, +}: { + configDir: string; + builderOptions: BuilderOptions; + isProd: boolean; + stories: ReturnType; + configs: (string | undefined)[]; + entries: string[]; +}) { + const entries = [...originalEntries]; + const mapping: Record = {}; + const r = (p: string) => path.resolve(path.join(configDir, p)); + + const storiesFileName = 'storybook-stories.js'; + const storiesPath = r(storiesFileName); + + const configEntryFilename = 'storybook-config-entry.js'; + const configEntryPath = r(configEntryFilename); + const data = { + storiesFilename: storiesFileName, + configs, + }; + const template = await readTemplate( + require.resolve('@storybook/builder-webpack5/templates/virtualModuleModernEntry.js.handlebars') + ); + + const needPipelinedImport = !!builderOptions.lazyCompilation && !isProd; + mapping[storiesPath] = toImportFn(stories, { needPipelinedImport }); + // We need to double escape `\` for webpack. We may have some in windows paths + mapping[configEntryPath] = handlebars(template, data).replace(/\\/g, '\\\\'); + + entries.push(configEntryPath); + + return { mapping, entries }; +} + +export async function getLegacyVirtualEntries({ + configDir, + stories, + configs, + entries: originalEntries, + frameworkName, +}: { + configDir: string; + stories: ReturnType; + configs: (string | undefined)[]; + entries: string[]; + frameworkName: string; +}) { + const entries = [...originalEntries]; + const mapping: Record = {}; + const r = (p: string) => path.resolve(path.join(configDir, p)); + const storybookPaths = getStorybookPaths(); + + const frameworkInitEntry = r('storybook-init-framework-entry.mjs'); + mapping[frameworkInitEntry] = `import '${frameworkName}';`; + entries.push(frameworkInitEntry); + + const template = await readTemplate( + require.resolve('@storybook/builder-webpack5/templates/virtualModuleEntry.template.mjs') + ); + + configs.forEach((configFilename: any) => { + const clientApi = storybookPaths['@storybook/client-api']; + const clientLogger = storybookPaths['@storybook/client-logger']; + + const data = { + configFilename, + clientApi, + clientLogger, + }; + const fileName = `${configFilename}-generated-config-entry.js`; + // NOTE: although this file is also from the `dist/cjs` directory, it is actually a ESM + // file, see https://github.com/storybookjs/storybook/pull/16727#issuecomment-986485173 + mapping[fileName] = interpolate(template, data); + entries.push(fileName); + }); + + if (stories.length > 0) { + const storyTemplate = await readTemplate( + require.resolve('@storybook/builder-webpack5/templates/virtualModuleStory.template.js') + ); + // NOTE: this file has a `.cjs` extension as it is a CJS file (from `dist/cjs`) and runs + // in the user's webpack mode, which may be strict about the use of require/import. + // See https://github.com/storybookjs/storybook/issues/14877 + const fileName = r(`generated-stories-entry.cjs`); + const data = { + frameworkName, + }; + mapping[fileName] = interpolate(storyTemplate, data).replace( + // Make sure we also replace quotes for this one + "'{{stories}}'", + stories.map(toRequireContextString).join(',') + ); + entries.push(fileName); + } + + return { mapping, entries }; +} diff --git a/lib/builder-webpack5/src/utils/webpack.ts b/lib/builder-webpack5/src/utils/webpack.ts new file mode 100644 index 000000000000..cdfe2ccbde25 --- /dev/null +++ b/lib/builder-webpack5/src/utils/webpack.ts @@ -0,0 +1,267 @@ +import path from 'path'; +import { dedent } from 'ts-dedent'; +import { DefinePlugin, HotModuleReplacementPlugin, ProgressPlugin, ProvidePlugin } from 'webpack'; +import type { Configuration } from 'webpack'; +import HtmlWebpackPlugin from 'html-webpack-plugin'; +// @ts-ignore // -- this has typings for webpack4 in it, won't work +import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin'; +import TerserWebpackPlugin from 'terser-webpack-plugin'; +import VirtualModulePlugin from 'webpack-virtual-modules'; +import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; + +import type { Options, CoreConfig, StorybookConfig } from '@storybook/core-common'; +import { + getProjectRoot, + stringifyProcessEnvs, + normalizeStories, + getPreviewFile, +} from '@storybook/core-common'; +import type { BuilderOptions, TypescriptOptions } from '../types'; +import { getLegacyVirtualEntries, getModernVirtualEntries, getStorybookPaths } from './entries'; + +export const createWebpackConfig = async (options: Options): Promise => { + const { + outputDir = path.join('.', 'public'), + quiet, + packageJson, + configType, + presets, + previewUrl, + serverChannelUrl, + configDir, + } = options; + + const framework = await presets.apply('framework', undefined); + if (!framework) { + throw new Error(dedent` + You must to specify a framework in '.storybook/main.js' config. + + https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#framework-field-mandatory + `); + } + const frameworkName = typeof framework === 'string' ? framework : framework.name; + const frameworkOptions = presets.apply('frameworkOptions'); + + const { channelOptions, builder } = await presets.apply('core'); + + const isProd = configType === 'PRODUCTION'; + + const envs = presets.apply>('env'); + const logLevel = presets.apply('logLevel'); + const features = options.presets.apply('features'); + const headHtmlSnippet = presets.apply('previewHead'); + const bodyHtmlSnippet = presets.apply('previewBody'); + const template = presets.apply('previewMainTemplate'); + const configs = presets.apply('config', []); + const entriesP = presets.apply('entries', []); + const stories = presets.apply('stories', []); + + const babelOptions = presets.apply('babel', {}); + const builderOptions: BuilderOptions = typeof builder === 'string' ? {} : builder?.options || {}; + const typescriptOptions = options.presets.apply('typescript'); + + const configsFinal = [...(await configs), getPreviewFile(options)].filter(Boolean); + const entriesFinal = await entriesP; + const typescriptOptionsFinal = await typescriptOptions; + const storiesFinal = normalizeStories(await stories, { configDir, workingDir: process.cwd() }); + const featuresFinal = await features; + + const { mapping, entries } = featuresFinal?.storyStoreV7 + ? await getModernVirtualEntries({ + configDir, + stories: storiesFinal, + configs: configsFinal, + entries: entriesFinal, + isProd, + builderOptions, + }) + : await getLegacyVirtualEntries({ + configDir, + stories: storiesFinal, + configs: configsFinal, + entries: entriesFinal, + frameworkName, + }); + + const shouldCheckTs = typescriptOptionsFinal.check && !typescriptOptionsFinal.skipBabel; + const tsCheckOptions = typescriptOptionsFinal.checkOptions || {}; + + return { + name: 'preview', + mode: isProd ? 'production' : 'development', + bail: isProd, + devtool: 'cheap-module-source-map', + entry: entries, + output: { + path: path.resolve(process.cwd(), outputDir), + filename: isProd ? '[name].[contenthash:8].iframe.bundle.js' : '[name].iframe.bundle.js', + publicPath: '', + }, + stats: { + preset: 'none', + logging: 'error', + }, + watchOptions: { + ignored: /node_modules/, + }, + ignoreWarnings: [ + { message: /export '\S+' was not found in 'global'/ }, + { message: /was not found in 'react'/ }, + ], + plugins: [ + ...(Object.keys(mapping).length > 0 ? [new VirtualModulePlugin(mapping)] : []), + new HtmlWebpackPlugin({ + filename: `iframe.html`, + // FIXME: `none` isn't a known option + chunksSortMode: 'none' as any, + alwaysWriteToDisk: true, + inject: false, + template: await template, + templateParameters: { + version: packageJson.version, + globals: { + CONFIG_TYPE: configType, + LOGLEVEL: await logLevel, + FRAMEWORK_OPTIONS: await frameworkOptions, + CHANNEL_OPTIONS: channelOptions, + FEATURES: await features, + PREVIEW_URL: previewUrl, + STORIES: storiesFinal.map((specifier) => ({ + ...specifier, + importPathMatcher: specifier.importPathMatcher.source, + })), + SERVER_CHANNEL_URL: serverChannelUrl, + }, + headHtmlSnippet: await headHtmlSnippet, + bodyHtmlSnippet: await bodyHtmlSnippet, + }, + minify: { + collapseWhitespace: true, + removeComments: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: false, + removeStyleLinkTypeAttributes: true, + useShortDoctype: true, + }, + }), + new DefinePlugin({ + ...stringifyProcessEnvs(await envs), + NODE_ENV: JSON.stringify(process.env.NODE_ENV), + }), + new ProvidePlugin({ process: require.resolve('process/browser.js') }), + ...(isProd ? [] : [new HotModuleReplacementPlugin()]), + new CaseSensitivePathsPlugin(), + ...(quiet ? [] : [new ProgressPlugin({})]), + ...(shouldCheckTs ? [new ForkTsCheckerWebpackPlugin(tsCheckOptions)] : []), + ], + module: { + rules: [ + { + layer: 'storybook_css', + test: /\.css$/, + sideEffects: true, + use: [ + require.resolve('style-loader'), + { + loader: require.resolve('css-loader'), + options: { + importLoaders: 1, + }, + }, + ], + }, + { + layer: 'storybook_media', + test: /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/, + type: 'asset/resource', + generator: { + filename: isProd + ? 'static/media/[name].[contenthash:8][ext]' + : 'static/media/[path][name][ext]', + }, + }, + { + layer: 'storybook_media', + test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/, + type: 'asset', + parser: { + dataUrlCondition: { + maxSize: 10000, + }, + }, + generator: { + filename: isProd + ? 'static/media/[name].[contenthash:8][ext]' + : 'static/media/[path][name][ext]', + }, + }, + { + layer: 'storybook_esm', + test: /\.m?js$/, + type: 'javascript/auto', + resolve: { + fullySpecified: false, + }, + }, + { + test: /\.md$/, + type: 'asset/source', + }, + { + layer: 'storybook_babel', + test: typescriptOptionsFinal.skipBabel ? /\.(mjs|jsx?)$/ : /\.(mjs|tsx?|jsx?)$/, + use: [ + { + loader: require.resolve('babel-loader'), + options: await babelOptions, + }, + ], + include: [getProjectRoot()], + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json', '.cjs'], + modules: ['node_modules'].concat((await envs).NODE_PATH || []), + mainFields: ['browser', 'module', 'main'], + alias: getStorybookPaths(), + fallback: { + path: require.resolve('path-browserify'), + assert: require.resolve('browser-assert'), + util: require.resolve('util'), + crypto: false, + }, + }, + ...(builderOptions.fsCache ? { cache: { type: 'filesystem' as const } } : {}), + experiments: { + ...(builderOptions.lazyCompilation && !isProd ? { lazyCompilation: { entries: false } } : {}), + layers: true, + }, + + optimization: { + splitChunks: { + chunks: 'all', + }, + runtimeChunk: true, + sideEffects: true, + usedExports: isProd, + moduleIds: 'named', + minimizer: isProd + ? [ + new TerserWebpackPlugin({ + parallel: true, + terserOptions: { + sourceMap: true, + mangle: false, + keep_fnames: true, + }, + }), + ] + : [], + }, + performance: { + hints: isProd ? 'warning' : false, + }, + }; +}; diff --git a/lib/builder-webpack5/src/preview/virtualModuleEntry.template.js b/lib/builder-webpack5/templates/virtualModuleEntry.template.mjs similarity index 100% rename from lib/builder-webpack5/src/preview/virtualModuleEntry.template.js rename to lib/builder-webpack5/templates/virtualModuleEntry.template.mjs diff --git a/lib/builder-webpack5/templates/virtualModuleStory.template.js b/lib/builder-webpack5/templates/virtualModuleStory.template.js new file mode 100644 index 000000000000..43e631e4788b --- /dev/null +++ b/lib/builder-webpack5/templates/virtualModuleStory.template.js @@ -0,0 +1,3 @@ +const { configure } = require('{{frameworkName}}'); + +configure(['{{stories}}'], module, false); diff --git a/lib/core-common/src/index.ts b/lib/core-common/src/index.ts index f3d54fd41312..648886335f7f 100644 --- a/lib/core-common/src/index.ts +++ b/lib/core-common/src/index.ts @@ -13,7 +13,7 @@ export * from './utils/get-storybook-configuration'; export * from './utils/get-storybook-info'; export * from './utils/get-storybook-refs'; export * from './utils/load-manager-or-addons-file'; -export * from './utils/load-preview-or-config-file'; +export * from './utils/get-preview-file'; export * from './utils/log-config'; export * from './utils/paths'; export * from './utils/progress-reporting'; diff --git a/lib/core-common/src/presets.ts b/lib/core-common/src/presets.ts index c12ebd0d4933..743631b31749 100644 --- a/lib/core-common/src/presets.ts +++ b/lib/core-common/src/presets.ts @@ -233,10 +233,6 @@ async function loadPresets( return []; } - if (!level) { - logger.info('=> Loading presets'); - } - return ( await Promise.all(presets.map(async (preset) => loadPreset(preset, level, storybookOptions))) ).reduce((acc, loaded) => { @@ -316,16 +312,11 @@ export async function loadAllPresets( LoadOptions & BuilderOptions & { corePresets: string[]; - overridePresets: string[]; } ) { - const { corePresets = [], overridePresets = [], ...restOptions } = options; + const { corePresets = [], ...restOptions } = options; - const presetsConfig: PresetConfig[] = [ - ...corePresets, - ...loadCustomPresets(options), - ...overridePresets, - ]; + const presetsConfig: PresetConfig[] = [...corePresets, ...loadCustomPresets(options)]; // Remove `@storybook/preset-typescript` and add a warning if in use. const filteredPresetConfig = filterPresetsConfig(presetsConfig); diff --git a/lib/core-common/src/types.ts b/lib/core-common/src/types.ts index a5553a50cbc7..f0994883d73d 100644 --- a/lib/core-common/src/types.ts +++ b/lib/core-common/src/types.ts @@ -203,8 +203,6 @@ export interface Builder { startTime: ReturnType; }) => Promise; bail: (e?: Error) => Promise; - corePresets?: string[]; - overridePresets?: string[]; } export interface IndexerOptions { diff --git a/lib/core-common/src/utils/get-preview-file.ts b/lib/core-common/src/utils/get-preview-file.ts new file mode 100644 index 000000000000..990a05a915b4 --- /dev/null +++ b/lib/core-common/src/utils/get-preview-file.ts @@ -0,0 +1,10 @@ +import path from 'path'; +import type { Options } from '../types'; + +import { getInterpretedFile } from './interpret-files'; + +export function getPreviewFile({ configDir }: Options) { + const storybookPreviewPath = getInterpretedFile(path.resolve(configDir, 'preview')); + + return storybookPreviewPath; +} diff --git a/lib/core-common/src/utils/glob-to-regexp.ts b/lib/core-common/src/utils/glob-to-regexp.ts index 2a834518bba8..6ee91331666f 100644 --- a/lib/core-common/src/utils/glob-to-regexp.ts +++ b/lib/core-common/src/utils/glob-to-regexp.ts @@ -4,6 +4,7 @@ export function globToRegexp(glob: string) { const regex = makeRe(glob, { fastpaths: false, noglobstar: false, + dot: true, bash: false, }); diff --git a/lib/core-common/src/utils/load-preview-or-config-file.ts b/lib/core-common/src/utils/load-preview-or-config-file.ts deleted file mode 100644 index 27edc691b9b2..000000000000 --- a/lib/core-common/src/utils/load-preview-or-config-file.ts +++ /dev/null @@ -1,19 +0,0 @@ -import path from 'path'; -import { dedent } from 'ts-dedent'; - -import { getInterpretedFile } from './interpret-files'; - -export function loadPreviewOrConfigFile({ configDir }: { configDir: string }) { - const storybookConfigPath = getInterpretedFile(path.resolve(configDir, 'config')); - const storybookPreviewPath = getInterpretedFile(path.resolve(configDir, 'preview')); - - if (storybookConfigPath && storybookPreviewPath) { - throw new Error(dedent` - You have both a "config.js" and a "preview.js", remove the "config.js" file from your configDir (${path.resolve( - configDir, - 'config' - )})`); - } - - return storybookPreviewPath || storybookConfigPath; -} diff --git a/lib/core-server/package.json b/lib/core-server/package.json index 01ec20158053..53a48e0f0a6a 100644 --- a/lib/core-server/package.json +++ b/lib/core-server/package.json @@ -58,6 +58,7 @@ "core-js": "^3.8.2", "detect-port": "^1.3.0", "express": "^4.17.1", + "find-up": "^5.0.0", "fs-extra": "^9.0.1", "global": "^4.4.0", "globby": "^11.0.2", diff --git a/lib/core-server/src/__snapshots__/html-kitchen-sink_preview-dev-posix b/lib/core-server/src/__snapshots__/html-kitchen-sink_preview-dev-posix index f8012763c738..29107ca32d5a 100644 --- a/lib/core-server/src/__snapshots__/html-kitchen-sink_preview-dev-posix +++ b/lib/core-server/src/__snapshots__/html-kitchen-sink_preview-dev-posix @@ -3,9 +3,7 @@ exports[`html-kitchen-sink preview dev 1`] = ` Object { "entry": Array [ - "ROOT/lib/core-client/dist/esm/globals/globals.js", - "NODE_MODULES/webpack-hot-middleware/client.js?reload=true&quiet=false&noInfo=undefined", - "ROOT/storybook-config-entry.js", + "ROOT/examples/html-kitchen-sink/.storybook/storybook-config-entry.js", ], "keys": Array [ "name", @@ -20,70 +18,88 @@ Object { "plugins", "module", "resolve", + "experiments", "optimization", "performance", - "experiments", ], "module": Object { "rules": Array [ Object { - "test": "/\\\\.m?js$/", - "type": "javascript/auto", + "layer": "storybook_css", + "sideEffects": true, + "test": "/\\\\.css$/", + "use": Array [ + "NODE_MODULES/style-loader/dist/cjs.js", + Object { + "loader": "NODE_MODULES/css-loader/dist/cjs.js", + "options": Object { + "importLoaders": 1, + }, + }, + ], + }, + Object { + "generator": Object { + "filename": "static/media/[path][name][ext]", + }, + "layer": "storybook_media", + "test": "/\\\\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\\\\?.*)?$/", + "type": "asset/resource", }, Object { + "generator": Object { + "filename": "static/media/[path][name][ext]", + }, + "layer": "storybook_media", + "parser": Object { + "dataUrlCondition": Object { + "maxSize": 10000, + }, + }, + "test": "/\\\\.(mp4|webm|wav|mp3|m4a|aac|oga)(\\\\?.*)?$/", + "type": "asset", + }, + Object { + "layer": "storybook_esm", "resolve": Object { "fullySpecified": false, }, "test": "/\\\\.m?js$/", + "type": "javascript/auto", + }, + Object { + "test": "/\\\\.md$/", + "type": "asset/source", }, Object { "exclude": "NODE_MODULES/", "include": Array [ "ROOT", ], + "layer": "storybook_babel", "test": "/\\\\.(mjs|tsx?|jsx?)$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], }, }, ], }, - Object { - "test": "/\\\\.md$/", - "type": "asset/source", - }, Object { "test": "/\\\\.html$/", "use": "NODE_MODULES/html-loader/dist/cjs.js", }, Object { + "layer": "storybook_mdx", "test": "/(stories|story)\\\\.mdx$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "babelrc": false, - "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", "configFile": false, - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], "plugins": Array [ Array [ "NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js", @@ -102,22 +118,14 @@ Object { }, Object { "exclude": "/(stories|story)\\\\.mdx$/", + "layer": "storybook_mdx", "test": "/\\\\.mdx$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "babelrc": false, - "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", "configFile": false, - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], "plugins": Array [ Array [ "NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js", @@ -143,6 +151,7 @@ Object { }, Object { "enforce": "pre", + "layer": "storybook_source", "loader": "ROOT/lib/source-loader/dist/cjs/index.js", "options": Object { "injectStoryParameters": true, @@ -150,26 +159,6 @@ Object { }, "test": "/\\\\.(stories|story)\\\\.[tj]sx?$/", }, - Object { - "sideEffects": true, - "test": "/\\\\.css$/", - "use": Array [ - Object { - "loader": "NODE_MODULES/style-loader/dist/cjs.js", - "options": undefined, - }, - Object { - "loader": "NODE_MODULES/css-loader/dist/cjs.js", - "options": undefined, - }, - Object { - "loader": "NODE_MODULES/postcss-loader/dist/cjs.js", - "options": Object { - "implementation": [Function], - }, - }, - ], - }, Object { "enforce": "pre", "test": Array [ @@ -182,26 +171,6 @@ Object { }, ], }, - Object {}, - Object { - "generator": Object { - "filename": "static/media/[path][name][ext]", - }, - "test": "/\\\\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\\\\?.*)?$/", - "type": "asset/resource", - }, - Object { - "generator": Object { - "filename": "static/media/[path][name][ext]", - }, - "parser": Object { - "dataUrlCondition": Object { - "maxSize": 10000, - }, - }, - "test": "/\\\\.(mp4|webm|wav|mp3|m4a|aac|oga)(\\\\?.*)?$/", - "type": "asset", - }, ], }, "plugins": Array [ diff --git a/lib/core-server/src/__snapshots__/html-kitchen-sink_preview-prod-posix b/lib/core-server/src/__snapshots__/html-kitchen-sink_preview-prod-posix index 963e86506166..1599c6cbb4bf 100644 --- a/lib/core-server/src/__snapshots__/html-kitchen-sink_preview-prod-posix +++ b/lib/core-server/src/__snapshots__/html-kitchen-sink_preview-prod-posix @@ -3,8 +3,7 @@ exports[`html-kitchen-sink preview prod 1`] = ` Object { "entry": Array [ - "ROOT/lib/core-client/dist/esm/globals/globals.js", - "ROOT/storybook-config-entry.js", + "ROOT/examples/html-kitchen-sink/.storybook/storybook-config-entry.js", ], "keys": Array [ "name", @@ -19,70 +18,88 @@ Object { "plugins", "module", "resolve", + "experiments", "optimization", "performance", - "experiments", ], "module": Object { "rules": Array [ Object { - "test": "/\\\\.m?js$/", - "type": "javascript/auto", + "layer": "storybook_css", + "sideEffects": true, + "test": "/\\\\.css$/", + "use": Array [ + "NODE_MODULES/style-loader/dist/cjs.js", + Object { + "loader": "NODE_MODULES/css-loader/dist/cjs.js", + "options": Object { + "importLoaders": 1, + }, + }, + ], + }, + Object { + "generator": Object { + "filename": "static/media/[name].[contenthash:8][ext]", + }, + "layer": "storybook_media", + "test": "/\\\\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\\\\?.*)?$/", + "type": "asset/resource", }, Object { + "generator": Object { + "filename": "static/media/[name].[contenthash:8][ext]", + }, + "layer": "storybook_media", + "parser": Object { + "dataUrlCondition": Object { + "maxSize": 10000, + }, + }, + "test": "/\\\\.(mp4|webm|wav|mp3|m4a|aac|oga)(\\\\?.*)?$/", + "type": "asset", + }, + Object { + "layer": "storybook_esm", "resolve": Object { "fullySpecified": false, }, "test": "/\\\\.m?js$/", + "type": "javascript/auto", + }, + Object { + "test": "/\\\\.md$/", + "type": "asset/source", }, Object { "exclude": "NODE_MODULES/", "include": Array [ "ROOT", ], + "layer": "storybook_babel", "test": "/\\\\.(mjs|tsx?|jsx?)$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], }, }, ], }, - Object { - "test": "/\\\\.md$/", - "type": "asset/source", - }, Object { "test": "/\\\\.html$/", "use": "NODE_MODULES/html-loader/dist/cjs.js", }, Object { + "layer": "storybook_mdx", "test": "/(stories|story)\\\\.mdx$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "babelrc": false, - "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", "configFile": false, - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], "plugins": Array [ Array [ "NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js", @@ -101,22 +118,14 @@ Object { }, Object { "exclude": "/(stories|story)\\\\.mdx$/", + "layer": "storybook_mdx", "test": "/\\\\.mdx$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "babelrc": false, - "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", "configFile": false, - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], "plugins": Array [ Array [ "NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js", @@ -142,6 +151,7 @@ Object { }, Object { "enforce": "pre", + "layer": "storybook_source", "loader": "ROOT/lib/source-loader/dist/cjs/index.js", "options": Object { "injectStoryParameters": true, @@ -149,26 +159,6 @@ Object { }, "test": "/\\\\.(stories|story)\\\\.[tj]sx?$/", }, - Object { - "sideEffects": true, - "test": "/\\\\.css$/", - "use": Array [ - Object { - "loader": "NODE_MODULES/style-loader/dist/cjs.js", - "options": undefined, - }, - Object { - "loader": "NODE_MODULES/css-loader/dist/cjs.js", - "options": undefined, - }, - Object { - "loader": "NODE_MODULES/postcss-loader/dist/cjs.js", - "options": Object { - "implementation": [Function], - }, - }, - ], - }, Object { "enforce": "pre", "test": Array [ @@ -181,26 +171,6 @@ Object { }, ], }, - Object {}, - Object { - "generator": Object { - "filename": "static/media/[name].[contenthash:8][ext]", - }, - "test": "/\\\\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\\\\?.*)?$/", - "type": "asset/resource", - }, - Object { - "generator": Object { - "filename": "static/media/[name].[contenthash:8][ext]", - }, - "parser": Object { - "dataUrlCondition": Object { - "maxSize": 10000, - }, - }, - "test": "/\\\\.(mp4|webm|wav|mp3|m4a|aac|oga)(\\\\?.*)?$/", - "type": "asset", - }, ], }, "plugins": Array [ diff --git a/lib/core-server/src/__snapshots__/vue-3-cli_preview-dev-posix b/lib/core-server/src/__snapshots__/vue-3-cli_preview-dev-posix index 8be7356c9f47..8f6dcbfe0120 100644 --- a/lib/core-server/src/__snapshots__/vue-3-cli_preview-dev-posix +++ b/lib/core-server/src/__snapshots__/vue-3-cli_preview-dev-posix @@ -3,9 +3,7 @@ exports[`vue-3-cli preview dev 1`] = ` Object { "entry": Array [ - "ROOT/lib/core-client/dist/esm/globals/globals.js", - "NODE_MODULES/webpack-hot-middleware/client.js?reload=true&quiet=false&noInfo=undefined", - "ROOT/storybook-config-entry.js", + "ROOT/examples/vue-3-cli/.storybook/storybook-config-entry.js", ], "keys": Array [ "name", @@ -20,49 +18,75 @@ Object { "plugins", "module", "resolve", + "experiments", "optimization", "performance", - "experiments", ], "module": Object { "rules": Array [ Object { - "test": "/\\\\.m?js$/", - "type": "javascript/auto", + "layer": "storybook_css", + "sideEffects": true, + "test": "/\\\\.css$/", + "use": Array [ + "NODE_MODULES/style-loader/dist/cjs.js", + Object { + "loader": "NODE_MODULES/css-loader/dist/cjs.js", + "options": Object { + "importLoaders": 1, + }, + }, + ], + }, + Object { + "generator": Object { + "filename": "static/media/[path][name][ext]", + }, + "layer": "storybook_media", + "test": "/\\\\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\\\\?.*)?$/", + "type": "asset/resource", + }, + Object { + "generator": Object { + "filename": "static/media/[path][name][ext]", + }, + "layer": "storybook_media", + "parser": Object { + "dataUrlCondition": Object { + "maxSize": 10000, + }, + }, + "test": "/\\\\.(mp4|webm|wav|mp3|m4a|aac|oga)(\\\\?.*)?$/", + "type": "asset", }, Object { + "layer": "storybook_esm", "resolve": Object { "fullySpecified": false, }, "test": "/\\\\.m?js$/", + "type": "javascript/auto", + }, + Object { + "test": "/\\\\.md$/", + "type": "asset/source", }, Object { "exclude": "NODE_MODULES/", "include": Array [ "ROOT", ], + "layer": "storybook_babel", "test": "/\\\\.(mjs|tsx?|jsx?)$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], }, }, ], }, - Object { - "test": "/\\\\.md$/", - "type": "asset/source", - }, Object { "loader": "NODE_MODULES/vue-loader/dist/index.js", "options": Object {}, @@ -97,22 +121,14 @@ Object { ], }, Object { + "layer": "storybook_mdx", "test": "/(stories|story)\\\\.mdx$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "babelrc": false, - "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", "configFile": false, - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], "plugins": Array [ Array [ "NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js", @@ -131,22 +147,14 @@ Object { }, Object { "exclude": "/(stories|story)\\\\.mdx$/", + "layer": "storybook_mdx", "test": "/\\\\.mdx$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "babelrc": false, - "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", "configFile": false, - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], "plugins": Array [ Array [ "NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js", @@ -172,6 +180,7 @@ Object { }, Object { "enforce": "pre", + "layer": "storybook_source", "loader": "ROOT/lib/source-loader/dist/cjs/index.js", "options": Object { "injectStoryParameters": true, @@ -179,38 +188,6 @@ Object { }, "test": "/\\\\.(stories|story)\\\\.[tj]sx?$/", }, - Object { - "sideEffects": true, - "test": "/\\\\.css$/", - "use": Array [ - "NODE_MODULES/style-loader/dist/cjs.js", - Object { - "loader": "NODE_MODULES/css-loader/dist/cjs.js", - "options": Object { - "importLoaders": 1, - }, - }, - ], - }, - Object { - "generator": Object { - "filename": "static/media/[path][name][ext]", - }, - "test": "/\\\\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\\\\?.*)?$/", - "type": "asset/resource", - }, - Object { - "generator": Object { - "filename": "static/media/[path][name][ext]", - }, - "parser": Object { - "dataUrlCondition": Object { - "maxSize": 10000, - }, - }, - "test": "/\\\\.(mp4|webm|wav|mp3|m4a|aac|oga)(\\\\?.*)?$/", - "type": "asset", - }, Object { "enforce": "post", "loader": "NODE_MODULES/vue-docgen-loader/lib/index.js", diff --git a/lib/core-server/src/__snapshots__/vue-3-cli_preview-prod-posix b/lib/core-server/src/__snapshots__/vue-3-cli_preview-prod-posix index 2cd4cf1c2b66..c49d5c1b2ab3 100644 --- a/lib/core-server/src/__snapshots__/vue-3-cli_preview-prod-posix +++ b/lib/core-server/src/__snapshots__/vue-3-cli_preview-prod-posix @@ -3,8 +3,7 @@ exports[`vue-3-cli preview prod 1`] = ` Object { "entry": Array [ - "ROOT/lib/core-client/dist/esm/globals/globals.js", - "ROOT/storybook-config-entry.js", + "ROOT/examples/vue-3-cli/.storybook/storybook-config-entry.js", ], "keys": Array [ "name", @@ -19,49 +18,75 @@ Object { "plugins", "module", "resolve", + "experiments", "optimization", "performance", - "experiments", ], "module": Object { "rules": Array [ Object { - "test": "/\\\\.m?js$/", - "type": "javascript/auto", + "layer": "storybook_css", + "sideEffects": true, + "test": "/\\\\.css$/", + "use": Array [ + "NODE_MODULES/style-loader/dist/cjs.js", + Object { + "loader": "NODE_MODULES/css-loader/dist/cjs.js", + "options": Object { + "importLoaders": 1, + }, + }, + ], + }, + Object { + "generator": Object { + "filename": "static/media/[name].[contenthash:8][ext]", + }, + "layer": "storybook_media", + "test": "/\\\\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\\\\?.*)?$/", + "type": "asset/resource", + }, + Object { + "generator": Object { + "filename": "static/media/[name].[contenthash:8][ext]", + }, + "layer": "storybook_media", + "parser": Object { + "dataUrlCondition": Object { + "maxSize": 10000, + }, + }, + "test": "/\\\\.(mp4|webm|wav|mp3|m4a|aac|oga)(\\\\?.*)?$/", + "type": "asset", }, Object { + "layer": "storybook_esm", "resolve": Object { "fullySpecified": false, }, "test": "/\\\\.m?js$/", + "type": "javascript/auto", + }, + Object { + "test": "/\\\\.md$/", + "type": "asset/source", }, Object { "exclude": "NODE_MODULES/", "include": Array [ "ROOT", ], + "layer": "storybook_babel", "test": "/\\\\.(mjs|tsx?|jsx?)$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], }, }, ], }, - Object { - "test": "/\\\\.md$/", - "type": "asset/source", - }, Object { "loader": "NODE_MODULES/vue-loader/dist/index.js", "options": Object {}, @@ -96,22 +121,14 @@ Object { ], }, Object { + "layer": "storybook_mdx", "test": "/(stories|story)\\\\.mdx$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "babelrc": false, - "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", "configFile": false, - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], "plugins": Array [ Array [ "NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js", @@ -130,22 +147,14 @@ Object { }, Object { "exclude": "/(stories|story)\\\\.mdx$/", + "layer": "storybook_mdx", "test": "/\\\\.mdx$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "babelrc": false, - "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", "configFile": false, - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], "plugins": Array [ Array [ "NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js", @@ -171,6 +180,7 @@ Object { }, Object { "enforce": "pre", + "layer": "storybook_source", "loader": "ROOT/lib/source-loader/dist/cjs/index.js", "options": Object { "injectStoryParameters": true, @@ -178,38 +188,6 @@ Object { }, "test": "/\\\\.(stories|story)\\\\.[tj]sx?$/", }, - Object { - "sideEffects": true, - "test": "/\\\\.css$/", - "use": Array [ - "NODE_MODULES/style-loader/dist/cjs.js", - Object { - "loader": "NODE_MODULES/css-loader/dist/cjs.js", - "options": Object { - "importLoaders": 1, - }, - }, - ], - }, - Object { - "generator": Object { - "filename": "static/media/[name].[contenthash:8][ext]", - }, - "test": "/\\\\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\\\\?.*)?$/", - "type": "asset/resource", - }, - Object { - "generator": Object { - "filename": "static/media/[name].[contenthash:8][ext]", - }, - "parser": Object { - "dataUrlCondition": Object { - "maxSize": 10000, - }, - }, - "test": "/\\\\.(mp4|webm|wav|mp3|m4a|aac|oga)(\\\\?.*)?$/", - "type": "asset", - }, Object { "enforce": "post", "loader": "NODE_MODULES/vue-docgen-loader/lib/index.js", diff --git a/lib/core-server/src/__snapshots__/web-components-kitchen-sink_preview-dev-posix b/lib/core-server/src/__snapshots__/web-components-kitchen-sink_preview-dev-posix index e7e98abcf951..a86d3fc8ab49 100644 --- a/lib/core-server/src/__snapshots__/web-components-kitchen-sink_preview-dev-posix +++ b/lib/core-server/src/__snapshots__/web-components-kitchen-sink_preview-dev-posix @@ -3,9 +3,7 @@ exports[`web-components-kitchen-sink preview dev 1`] = ` Object { "entry": Array [ - "ROOT/lib/core-client/dist/esm/globals/globals.js", - "NODE_MODULES/webpack-hot-middleware/client.js?reload=true&quiet=false&noInfo=undefined", - "ROOT/storybook-config-entry.js", + "ROOT/examples/web-components-kitchen-sink/.storybook/storybook-config-entry.js", ], "keys": Array [ "name", @@ -20,49 +18,75 @@ Object { "plugins", "module", "resolve", + "experiments", "optimization", "performance", - "experiments", ], "module": Object { "rules": Array [ Object { - "test": "/\\\\.m?js$/", - "type": "javascript/auto", + "layer": "storybook_css", + "sideEffects": true, + "test": "/\\\\.css$/", + "use": Array [ + "NODE_MODULES/style-loader/dist/cjs.js", + Object { + "loader": "NODE_MODULES/css-loader/dist/cjs.js", + "options": Object { + "importLoaders": 1, + }, + }, + ], + }, + Object { + "generator": Object { + "filename": "static/media/[path][name][ext]", + }, + "layer": "storybook_media", + "test": "/\\\\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\\\\?.*)?$/", + "type": "asset/resource", }, Object { + "generator": Object { + "filename": "static/media/[path][name][ext]", + }, + "layer": "storybook_media", + "parser": Object { + "dataUrlCondition": Object { + "maxSize": 10000, + }, + }, + "test": "/\\\\.(mp4|webm|wav|mp3|m4a|aac|oga)(\\\\?.*)?$/", + "type": "asset", + }, + Object { + "layer": "storybook_esm", "resolve": Object { "fullySpecified": false, }, "test": "/\\\\.m?js$/", + "type": "javascript/auto", + }, + Object { + "test": "/\\\\.md$/", + "type": "asset/source", }, Object { "exclude": "NODE_MODULES/", "include": Array [ "ROOT", ], + "layer": "storybook_babel", "test": "/\\\\.(mjs|tsx?|jsx?)$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], }, }, ], }, - Object { - "test": "/\\\\.md$/", - "type": "asset/source", - }, Object { "test": Array [ "/src(.*)\\\\.js$/", @@ -104,22 +128,14 @@ Object { }, }, Object { + "layer": "storybook_mdx", "test": "/(stories|story)\\\\.mdx$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "babelrc": false, - "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", "configFile": false, - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], "plugins": Array [ Array [ "NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js", @@ -138,22 +154,14 @@ Object { }, Object { "exclude": "/(stories|story)\\\\.mdx$/", + "layer": "storybook_mdx", "test": "/\\\\.mdx$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "babelrc": false, - "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", "configFile": false, - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], "plugins": Array [ Array [ "NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js", @@ -179,6 +187,7 @@ Object { }, Object { "enforce": "pre", + "layer": "storybook_source", "loader": "ROOT/lib/source-loader/dist/cjs/index.js", "options": Object { "injectStoryParameters": true, @@ -198,38 +207,6 @@ Object { }, ], }, - Object { - "sideEffects": true, - "test": "/\\\\.css$/", - "use": Array [ - "NODE_MODULES/style-loader/dist/cjs.js", - Object { - "loader": "NODE_MODULES/css-loader/dist/cjs.js", - "options": Object { - "importLoaders": 1, - }, - }, - ], - }, - Object { - "generator": Object { - "filename": "static/media/[path][name][ext]", - }, - "test": "/\\\\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\\\\?.*)?$/", - "type": "asset/resource", - }, - Object { - "generator": Object { - "filename": "static/media/[path][name][ext]", - }, - "parser": Object { - "dataUrlCondition": Object { - "maxSize": 10000, - }, - }, - "test": "/\\\\.(mp4|webm|wav|mp3|m4a|aac|oga)(\\\\?.*)?$/", - "type": "asset", - }, ], }, "plugins": Array [ diff --git a/lib/core-server/src/__snapshots__/web-components-kitchen-sink_preview-prod-posix b/lib/core-server/src/__snapshots__/web-components-kitchen-sink_preview-prod-posix index e5ea7dbf7ad0..f2e9fc4d4060 100644 --- a/lib/core-server/src/__snapshots__/web-components-kitchen-sink_preview-prod-posix +++ b/lib/core-server/src/__snapshots__/web-components-kitchen-sink_preview-prod-posix @@ -3,8 +3,7 @@ exports[`web-components-kitchen-sink preview prod 1`] = ` Object { "entry": Array [ - "ROOT/lib/core-client/dist/esm/globals/globals.js", - "ROOT/storybook-config-entry.js", + "ROOT/examples/web-components-kitchen-sink/.storybook/storybook-config-entry.js", ], "keys": Array [ "name", @@ -19,49 +18,75 @@ Object { "plugins", "module", "resolve", + "experiments", "optimization", "performance", - "experiments", ], "module": Object { "rules": Array [ Object { - "test": "/\\\\.m?js$/", - "type": "javascript/auto", + "layer": "storybook_css", + "sideEffects": true, + "test": "/\\\\.css$/", + "use": Array [ + "NODE_MODULES/style-loader/dist/cjs.js", + Object { + "loader": "NODE_MODULES/css-loader/dist/cjs.js", + "options": Object { + "importLoaders": 1, + }, + }, + ], + }, + Object { + "generator": Object { + "filename": "static/media/[name].[contenthash:8][ext]", + }, + "layer": "storybook_media", + "test": "/\\\\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\\\\?.*)?$/", + "type": "asset/resource", }, Object { + "generator": Object { + "filename": "static/media/[name].[contenthash:8][ext]", + }, + "layer": "storybook_media", + "parser": Object { + "dataUrlCondition": Object { + "maxSize": 10000, + }, + }, + "test": "/\\\\.(mp4|webm|wav|mp3|m4a|aac|oga)(\\\\?.*)?$/", + "type": "asset", + }, + Object { + "layer": "storybook_esm", "resolve": Object { "fullySpecified": false, }, "test": "/\\\\.m?js$/", + "type": "javascript/auto", + }, + Object { + "test": "/\\\\.md$/", + "type": "asset/source", }, Object { "exclude": "NODE_MODULES/", "include": Array [ "ROOT", ], + "layer": "storybook_babel", "test": "/\\\\.(mjs|tsx?|jsx?)$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], }, }, ], }, - Object { - "test": "/\\\\.md$/", - "type": "asset/source", - }, Object { "test": Array [ "/src(.*)\\\\.js$/", @@ -103,22 +128,14 @@ Object { }, }, Object { + "layer": "storybook_mdx", "test": "/(stories|story)\\\\.mdx$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "babelrc": false, - "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", "configFile": false, - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], "plugins": Array [ Array [ "NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js", @@ -137,22 +154,14 @@ Object { }, Object { "exclude": "/(stories|story)\\\\.mdx$/", + "layer": "storybook_mdx", "test": "/\\\\.mdx$/", "use": Array [ Object { "loader": "NODE_MODULES/babel-loader/lib/index.js", "options": Object { "babelrc": false, - "cacheDirectory": "NODE_MODULES/.cache/storybook/babel", "configFile": false, - "overrides": Array [ - Object { - "plugins": Array [ - "NODE_MODULES/babel-plugin-named-exports-order/index.js", - ], - "test": "/\\\\.(story|stories).*$/", - }, - ], "plugins": Array [ Array [ "NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js", @@ -178,6 +187,7 @@ Object { }, Object { "enforce": "pre", + "layer": "storybook_source", "loader": "ROOT/lib/source-loader/dist/cjs/index.js", "options": Object { "injectStoryParameters": true, @@ -197,38 +207,6 @@ Object { }, ], }, - Object { - "sideEffects": true, - "test": "/\\\\.css$/", - "use": Array [ - "NODE_MODULES/style-loader/dist/cjs.js", - Object { - "loader": "NODE_MODULES/css-loader/dist/cjs.js", - "options": Object { - "importLoaders": 1, - }, - }, - ], - }, - Object { - "generator": Object { - "filename": "static/media/[name].[contenthash:8][ext]", - }, - "test": "/\\\\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\\\\?.*)?$/", - "type": "asset/resource", - }, - Object { - "generator": Object { - "filename": "static/media/[name].[contenthash:8][ext]", - }, - "parser": Object { - "dataUrlCondition": Object { - "maxSize": 10000, - }, - }, - "test": "/\\\\.(mp4|webm|wav|mp3|m4a|aac|oga)(\\\\?.*)?$/", - "type": "asset", - }, ], }, "plugins": Array [ diff --git a/lib/core-server/src/build-dev.ts b/lib/core-server/src/build-dev.ts index 365dafb9a734..2d68118ad739 100644 --- a/lib/core-server/src/build-dev.ts +++ b/lib/core-server/src/build-dev.ts @@ -22,7 +22,7 @@ import { outputStats } from './utils/output-stats'; import { outputStartupInformation } from './utils/output-startup-information'; import { updateCheck } from './utils/update-check'; import { getServerPort, getServerChannelUrl } from './utils/server-address'; -import { getBuilders } from './utils/get-builders'; +import { getPreviewBuilderPath } from './utils/get-builders'; export async function buildDevStandalone(options: CLIOptions & LoadOptions & BuilderOptions) { const { packageJson, versionUpdates, releaseNotes } = options; @@ -61,7 +61,7 @@ export async function buildDevStandalone(options: CLIOptions & LoadOptions & Bui options.serverChannelUrl = getServerChannelUrl(port, options); /* eslint-enable no-param-reassign */ - const { framework } = loadMainConfig(options); + const { framework, core } = loadMainConfig(options); const corePresets = []; const frameworkName = typeof framework === 'string' ? framework : framework?.name; @@ -71,26 +71,29 @@ export async function buildDevStandalone(options: CLIOptions & LoadOptions & Bui logger.warn(`you have not specified a framework in your ${options.configDir}/main.js`); } - logger.info('=> Loading presets'); - let presets = await loadAllPresets({ - corePresets, - overridePresets: [], - ...options, - }); + if (core?.builder) { + if (framework) { + logger.warn( + `You have specified both a framework and a builder. This might conflict, and could be unstable! Configure the builder thru the framework-options instead.` + ); + } - const [previewBuilder, managerBuilder] = await getBuilders({ ...options, presets }); + const builderName = typeof core?.builder === 'string' ? core.builder : core?.builder?.name; + const builderPath = await getPreviewBuilderPath(builderName, options.configDir); + + corePresets.push(join(builderPath, 'preset')); + } - presets = await loadAllPresets({ + const startTime = process.hrtime(); + const presets = await loadAllPresets({ corePresets: [ require.resolve('./presets/common-preset'), - ...managerBuilder.corePresets, - ...previewBuilder.corePresets, ...corePresets, require.resolve('./presets/babel-cache-preset'), ], - overridePresets: previewBuilder.overridePresets, ...options, }); + logger.trace({ message: '=> Loaded presets', time: process.hrtime(startTime) }); const features = await presets.apply('features'); global.FEATURES = features; diff --git a/lib/core-server/src/build-static.ts b/lib/core-server/src/build-static.ts index 6bc5842b62a1..9cf4f854530a 100644 --- a/lib/core-server/src/build-static.ts +++ b/lib/core-server/src/build-static.ts @@ -27,7 +27,7 @@ import { copyAllStaticFiles, copyAllStaticFilesRelativeToMain, } from './utils/copy-all-static-files'; -import { getBuilders } from './utils/get-builders'; +import { getManagerBuilder, getPreviewBuilder, getPreviewBuilderPath } from './utils/get-builders'; import { extractStoriesJson, convertToIndexV3 } from './utils/stories-json'; import { extractStorybookMetadata } from './utils/metadata'; import { StoryIndexGenerator } from './utils/StoryIndexGenerator'; @@ -59,7 +59,7 @@ export async function buildStaticStandalone( await emptyDir(options.outputDir); await ensureDir(options.outputDir); - const { framework } = loadMainConfig(options); + const { framework, core: coreFromMain } = loadMainConfig(options); const corePresets = []; const frameworkName = typeof framework === 'string' ? framework : framework?.name; @@ -69,30 +69,36 @@ export async function buildStaticStandalone( logger.warn(`you have not specified a framework in your ${options.configDir}/main.js`); } - logger.info('=> Loading presets'); - let presets = await loadAllPresets({ - corePresets: [require.resolve('./presets/common-preset'), ...corePresets], - overridePresets: [], - ...options, - }); + if (coreFromMain?.builder) { + if (framework) { + logger.warn( + `You have specified both a framework and a builder. This might conflict, and could be unstable! Configure the builder thru the framework-options instead.` + ); + } - const [previewBuilder, managerBuilder] = await getBuilders({ ...options, presets }); + const builderName = + typeof coreFromMain?.builder === 'string' + ? coreFromMain.builder + : coreFromMain?.builder?.name; + const builderPath = await getPreviewBuilderPath(builderName, options.configDir); + + corePresets.push(join(builderPath, 'preset')); + } - presets = await loadAllPresets({ + const startTime = process.hrtime(); + const presets = await loadAllPresets({ corePresets: [ require.resolve('./presets/common-preset'), - ...(managerBuilder.corePresets || []), - ...(previewBuilder.corePresets || []), ...corePresets, require.resolve('./presets/babel-cache-preset'), ], - overridePresets: previewBuilder.overridePresets || [], ...options, }); + logger.trace({ message: '=> Loaded presets', time: process.hrtime(startTime) }); const [features, core, staticDirs, storyIndexers, stories, docsOptions] = await Promise.all([ presets.apply('features'), - presets.apply('core'), + presets.apply('core', {}), presets.apply('staticDirs'), presets.apply('storyIndexers', []), presets.apply('stories'), @@ -119,6 +125,8 @@ export async function buildStaticStandalone( global.FEATURES = features; + const managerBuilder = await getManagerBuilder(); + await managerBuilder.build({ startTime: process.hrtime(), options: fullOptions }); if (staticDirs) { @@ -165,7 +173,7 @@ export async function buildStaticStandalone( ); } - if (!core?.disableTelemetry) { + if (!core.disableTelemetry) { effects.push( initializedStoryIndexGenerator.then(async (generator) => { if (!generator) { @@ -186,12 +194,18 @@ export async function buildStaticStandalone( ); } - if (!core?.disableProjectJson) { + if (!core.disableProjectJson) { effects.push( extractStorybookMetadata(join(options.outputDir, 'project.json'), options.configDir) ); } + if (!core.builder) { + throw new Error('Builder is not defined'); + } + const builderName = typeof core.builder === 'string' ? core.builder : core.builder.name; + + const previewBuilder = await getPreviewBuilder(builderName, options.configDir); if (options.debugWebpack) { logConfig('Preview webpack config', await previewBuilder.getConfig(fullOptions)); } diff --git a/lib/core-server/src/core-presets.test.ts b/lib/core-server/src/core-presets.test.ts index dc13a72da1bc..1323e45d92de 100644 --- a/lib/core-server/src/core-presets.test.ts +++ b/lib/core-server/src/core-presets.test.ts @@ -21,15 +21,11 @@ const { packageJson } = readUpSync({ cwd: __dirname }); // this only applies to this file jest.setTimeout(10000); -// FIXME: this doesn't work -const skipStoriesJsonPreset = [{ features: { buildStoriesJson: false, storyStoreV7: false } }]; - jest.mock('@storybook/builder-webpack5', () => { const value = jest.fn(() => false); const actualBuilder = jest.requireActual('@storybook/builder-webpack5'); // MUTATION! we couldn't mock webpack5, so we added a level of indirection instead actualBuilder.executor.get = () => value; - actualBuilder.overridePresets = [...actualBuilder.overridePresets, skipStoriesJsonPreset]; return actualBuilder; }); diff --git a/lib/core-server/src/dev-server.ts b/lib/core-server/src/dev-server.ts index 01819ad63200..ef6c8d08d6f0 100644 --- a/lib/core-server/src/dev-server.ts +++ b/lib/core-server/src/dev-server.ts @@ -1,16 +1,18 @@ import express, { Router } from 'express'; import compression from 'compression'; -import { +import type { + Builder, CoreConfig, DocsOptions, Options, + Stats, StorybookConfig, - normalizeStories, - logConfig, } from '@storybook/core-common'; +import { normalizeStories, logConfig } from '@storybook/core-common'; import { telemetry } from '@storybook/telemetry'; + import { getMiddleware } from './utils/middleware'; import { getServerAddresses } from './utils/server-address'; import { getServer } from './utils/server-init'; @@ -18,9 +20,8 @@ import { useStatics } from './utils/server-statics'; import { useStoriesJson } from './utils/stories-json'; import { useStorybookMetadata } from './utils/metadata'; import { getServerChannel } from './utils/get-server-channel'; - import { openInBrowser } from './utils/open-in-browser'; -import { getBuilders } from './utils/get-builders'; +import { getManagerBuilder, getPreviewBuilder } from './utils/get-builders'; import { StoryIndexGenerator } from './utils/StoryIndexGenerator'; // @ts-ignore @@ -28,22 +29,31 @@ export const router: Router = new Router(); export const DEBOUNCE = 100; -export async function storybookDevServer(options: Options) { +type UnPromisify = T extends Promise ? U : T; + +export async function storybookDevServer(options: Options): Promise<{ + address: string; + networkAddress: string; + managerResult: UnPromisify['start']>>; + previewResult: UnPromisify['start']>>; +}> { + const { configDir } = options; const startTime = process.hrtime(); const app = express(); const server = await getServer(app, options); const serverChannel = getServerChannel(server); const features = await options.presets.apply('features'); - const core = await options.presets.apply('core'); + const core = await options.presets.apply('core', {}); + // try get index generator, if failed, send telemetry without storyCount, then rethrow the error let initializedStoryIndexGenerator: Promise = Promise.resolve(undefined); if (features?.buildStoriesJson || features?.storyStoreV7) { try { const workingDir = process.cwd(); const directories = { - configDir: options.configDir, - workingDir, + configDir, + workingDir: process.cwd(), }; const normalizedStories = normalizeStories( await options.presets.apply('stories'), @@ -71,14 +81,14 @@ export async function storybookDevServer(options: Options) { workingDir, }); } catch (err) { - if (!core?.disableTelemetry) { + if (!core.disableTelemetry) { telemetry('start'); } throw err; } } - if (!core?.disableTelemetry) { + if (!core.disableTelemetry) { initializedStoryIndexGenerator.then(async (generator) => { if (!generator) { return; @@ -93,12 +103,12 @@ export async function storybookDevServer(options: Options) { }, } : undefined; - telemetry('start', payload, { configDir: options.configDir }); + telemetry('start', payload, { configDir }); }); } - if (!core?.disableProjectJson) { - useStorybookMetadata(router, options.configDir); + if (!core.disableProjectJson) { + useStorybookMetadata(router, configDir); } app.use(compression({ level: 1 })); @@ -115,7 +125,7 @@ export async function storybookDevServer(options: Options) { next(); }); - if (core?.crossOriginIsolated) { + if (core.crossOriginIsolated) { app.use((req, res, next) => { // These headers are required to enable SharedArrayBuffer // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer @@ -128,7 +138,7 @@ export async function storybookDevServer(options: Options) { // User's own static files await useStatics(router, options); - getMiddleware(options.configDir)(router); + getMiddleware(configDir)(router); app.use(router); const { port, host } = options; @@ -141,7 +151,17 @@ export async function storybookDevServer(options: Options) { server.listen({ port, host }, (error: Error) => (error ? reject(error) : resolve())); }); - const [previewBuilder, managerBuilder] = await getBuilders(options); + const managerBuilder = await getManagerBuilder(); + + const managerResult = await managerBuilder.start({ + startTime, + options, + router, + server, + }); + + const builderName = typeof core.builder === 'string' ? core.builder : core.builder.name; + const previewBuilder = await getPreviewBuilder(builderName, configDir); if (options.debugWebpack) { logConfig('Preview webpack config', await previewBuilder.getConfig(options)); @@ -156,28 +176,11 @@ export async function storybookDevServer(options: Options) { server, }); - const manager = managerBuilder.start({ - startTime, - options, - router, - server, - }); - - const [previewResult, managerResult] = await Promise.all([ + const [previewResult] = await Promise.all([ preview.catch(async (err) => { await managerBuilder?.bail(); throw err; }), - manager - // TODO #13083 Restore this when compiling the preview is fast enough - // .then((result) => { - // if (!options.ci && !options.smokeTest) openInBrowser(address); - // return result; - // }) - .catch(async (err) => { - await previewBuilder?.bail(); - throw err; - }), ]); // TODO #13083 Remove this when compiling the preview is fast enough diff --git a/lib/core-server/src/utils/get-builders.ts b/lib/core-server/src/utils/get-builders.ts index f2309a561cad..354dc717c6af 100644 --- a/lib/core-server/src/utils/get-builders.ts +++ b/lib/core-server/src/utils/get-builders.ts @@ -1,29 +1,31 @@ -import type { Options, CoreConfig, Builder } from '@storybook/core-common'; +import type { Builder, Stats } from '@storybook/core-common'; +import { dirname } from 'path'; +import findUp from 'find-up'; -async function getManagerBuilder() { +export async function getManagerBuilder(): Promise> { return import('@storybook/builder-manager'); } -async function getPreviewBuilder(builderName: string, configDir: string) { - let builderPackage: string; - if (builderName) { - builderPackage = require.resolve( - ['webpack5'].includes(builderName) ? `@storybook/builder-${builderName}` : builderName, - { paths: [configDir] } - ); - } else { - throw new Error('no builder configured!'); - } +export async function getPreviewBuilder( + builderName: string, + configDir: string +): Promise> { + const builderPackage = await getPreviewBuilderPath(builderName, configDir); + const previewBuilder = await import(builderPackage); return previewBuilder; } -export async function getBuilders({ - presets, - configDir, -}: Options): Promise[]> { - const core = await presets.apply('core', undefined); - const builderName = typeof core?.builder === 'string' ? core.builder : core?.builder?.name; +export async function getPreviewBuilderPath(builderName: string, configDir: string) { + if (builderName) { + const location = await findUp('package.json', { + cwd: require.resolve( + ['webpack5'].includes(builderName) ? `@storybook/builder-${builderName}` : builderName, + { paths: [configDir] } + ), + }); - return Promise.all([getPreviewBuilder(builderName, configDir), getManagerBuilder()]); + return dirname(location); + } + throw new Error('no builder configured!'); } diff --git a/presets/react-webpack/src/framework-preset-react.ts b/presets/react-webpack/src/framework-preset-react.ts index 9e2d2da520d9..2d66ab9db7ab 100644 --- a/presets/react-webpack/src/framework-preset-react.ts +++ b/presets/react-webpack/src/framework-preset-react.ts @@ -54,7 +54,9 @@ export const babelDefault: StorybookConfig['babelDefault'] = async (config) => { }; export const webpackFinal: StorybookConfig['webpackFinal'] = async (config, options) => { - if (!(await useFastRefresh(options))) return config; + if (!(await useFastRefresh(options))) { + return config; + } // matches the name of the plugin in CRA. const hasReactRefresh = !!config.plugins?.find( diff --git a/yarn.lock b/yarn.lock index e29baf09b91d..83a3dfaaa6df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7313,16 +7313,16 @@ __metadata: languageName: unknown linkType: soft -"@storybook/addon-postcss@npm:^2.0.0": - version: 2.0.0 - resolution: "@storybook/addon-postcss@npm:2.0.0" +"@storybook/addon-postcss@npm:^3.0.0-alpha.0": + version: 3.0.0-alpha.0 + resolution: "@storybook/addon-postcss@npm:3.0.0-alpha.0" dependencies: "@storybook/node-logger": ^6.1.14 css-loader: ^3.6.0 postcss: ^7.0.35 postcss-loader: ^4.2.0 style-loader: ^1.3.0 - checksum: 48d77633cbe7da6542ba8c04dfda5999ed97919ff602d5b5686f1ec57c579bb89c2e103207bf29b5e60cc8121e803def3229b0584566928e4318b979bd5883be + checksum: 50f3c0374d6666d58dbe3d9ab0ca38d1f080ca604874e8af7e2071cb8de9aaebc4f1752e82a386ac9d9d7908e1859af500c655cf6e7b9d0083922f6f611cb0a6 languageName: node linkType: hard @@ -8353,6 +8353,7 @@ __metadata: core-js: ^3.8.2 detect-port: ^1.3.0 express: ^4.17.1 + find-up: ^5.0.0 fs-extra: ^9.0.1 global: ^4.4.0 globby: ^11.0.2 @@ -25277,7 +25278,7 @@ __metadata: "@storybook/addon-highlight": 7.0.0-alpha.12 "@storybook/addon-jest": 7.0.0-alpha.12 "@storybook/addon-links": 7.0.0-alpha.12 - "@storybook/addon-postcss": ^2.0.0 + "@storybook/addon-postcss": ^3.0.0-alpha.0 "@storybook/addon-storyshots": 7.0.0-alpha.12 "@storybook/addon-storysource": 7.0.0-alpha.12 "@storybook/addon-viewport": 7.0.0-alpha.12 @@ -25292,7 +25293,6 @@ __metadata: format-json: ^1.0.3 global: ^4.4.0 postcss: ^8.2.4 - postcss-color-rebeccapurple: ^6.0.0 storybook: 7.0.0-alpha.12 languageName: unknown linkType: soft @@ -26309,13 +26309,6 @@ __metadata: languageName: node linkType: hard -"ip-regex@npm:^4.1.0": - version: 4.3.0 - resolution: "ip-regex@npm:4.3.0" - checksum: f9ef1f5d0df05b9133a882974e572ae525ccd205260cb103dae337f1fc7451ed783391acc6ad688e56dd2598f769e8e72ecbb650ec34763396af822a91768562 - languageName: node - linkType: hard - "ip@npm:1.1.5": version: 1.1.5 resolution: "ip@npm:1.1.5" @@ -27133,15 +27126,6 @@ __metadata: languageName: node linkType: hard -"is-url-superb@npm:^3.0.0": - version: 3.0.0 - resolution: "is-url-superb@npm:3.0.0" - dependencies: - url-regex: ^5.0.0 - checksum: d242520fc3d6b86d6e87283ba472c89d43dbcf0700da4631c328558e9524d25403b5da042a826efb468df16f61e361c4b405ddd2981cd28bf2c56a2c316ab898 - languageName: node - linkType: hard - "is-utf8@npm:^0.2.0": version: 0.2.1 resolution: "is-utf8@npm:0.2.1" @@ -35308,16 +35292,6 @@ __metadata: languageName: node linkType: hard -"postcss-color-rebeccapurple@npm:^6.0.0": - version: 6.0.0 - resolution: "postcss-color-rebeccapurple@npm:6.0.0" - dependencies: - postcss: ^7.0.27 - postcss-values-parser: ^3.2.0 - checksum: 7893afa08e904c3409ccd97a5c73aede9e1d261e64a4a576b38351d82b74a77a9c94b8bb0312586f744947b9f28969e6c921e301e8d72aeaaf6d6cb5ff25ebbc - languageName: node - linkType: hard - "postcss-color-rebeccapurple@npm:^7.0.2": version: 7.0.2 resolution: "postcss-color-rebeccapurple@npm:7.0.2" @@ -36233,19 +36207,7 @@ __metadata: languageName: node linkType: hard -"postcss-values-parser@npm:^3.2.0": - version: 3.2.1 - resolution: "postcss-values-parser@npm:3.2.1" - dependencies: - color-name: ^1.1.4 - is-url-superb: ^3.0.0 - postcss: ^7.0.5 - url-regex: ^5.0.0 - checksum: 5ff04dcf957fc9cc925b517ad946af547ba525a546578a6d37c8a57964aa93c660b2b96a48ee8388588b5895d93dabb24506704c0d523f5e9b1868d69526abc0 - languageName: node - linkType: hard - -"postcss@npm:7.x.x, postcss@npm:^7.0.14, postcss@npm:^7.0.27, postcss@npm:^7.0.32, postcss@npm:^7.0.35, postcss@npm:^7.0.36, postcss@npm:^7.0.5, postcss@npm:^7.0.6": +"postcss@npm:7.x.x, postcss@npm:^7.0.14, postcss@npm:^7.0.32, postcss@npm:^7.0.35, postcss@npm:^7.0.36, postcss@npm:^7.0.5, postcss@npm:^7.0.6": version: 7.0.39 resolution: "postcss@npm:7.0.39" dependencies: @@ -43011,15 +42973,6 @@ __metadata: languageName: node linkType: hard -"tlds@npm:^1.203.0": - version: 1.231.0 - resolution: "tlds@npm:1.231.0" - bin: - tlds: bin.js - checksum: 0fece96f123c64ba4968dfee1cd3fc6ddb324824ee712cb323531b7f53e0af9be7521fe807bf71017e38c39480ec7a3e34b151701654d8b3d0f6f6405ebdfb40 - languageName: node - linkType: hard - "tmp@npm:0.0.28": version: 0.0.28 resolution: "tmp@npm:0.0.28" @@ -44512,16 +44465,6 @@ __metadata: languageName: node linkType: hard -"url-regex@npm:^5.0.0": - version: 5.0.0 - resolution: "url-regex@npm:5.0.0" - dependencies: - ip-regex: ^4.1.0 - tlds: ^1.203.0 - checksum: 86d7bd8ecf5e8bd6152376292e6953d9a7cab668384d7967ea8201d87edf41799fbda5ff4110a3c16090c7c5b21787e46a6f2b2189a46deb3fd8e0aa9224a347 - languageName: node - linkType: hard - "url-to-options@npm:^1.0.1": version: 1.0.1 resolution: "url-to-options@npm:1.0.1"