diff --git a/MIGRATION.md b/MIGRATION.md
index e5dd732ef7b6..76c01e2cf7e2 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -1,5 +1,7 @@
Migration
+- [From version 6.3.x to 6.4.0](#from-version-63x-to-640)
+ - [Babel mode v7](#babel-mode-v7)
- [From version 6.2.x to 6.3.0](#from-version-62x-to-630)
- [Webpack 5 manager build](#webpack-5-manager-build)
- [Angular 12 upgrade](#angular-12-upgrade)
@@ -162,6 +164,39 @@
- [Packages renaming](#packages-renaming)
- [Deprecated embedded addons](#deprecated-embedded-addons)
+## From version 6.3.x to 6.4.0
+
+### Babel mode v7
+
+SB6.4 introduces an opt-in feature flag, `features.babelModeV7`, that reworks the way Babel is configured in Storybook to make it more consistent with the Babel is configured in your app. This breaking change will become the default in SB 7.0, but we encourage you to migrate today.
+
+> NOTE: CRA apps using `@storybook/preset-create-react-app` use CRA's handling, so the new flag has no effect on CRA apps.
+
+In SB6.x and earlier, Storybook provided its own default configuration and inconsistently handled configurations from the user's babelrc file. This resulted in a final configuration that differs from your application's configuration AND is difficult to debug.
+
+In `babelModeV7`, Storybook no longer provides its own default configuration and is primarily configured via babelrc file, with small, incremental updates from Storybook addons.
+
+In 6.x, Storybook supported a `.storybook/babelrc` configuration option. This is no longer supported and it's up to you to reconcile this with your project babelrc.
+
+To activate the v7 mode set the feature flag in your `.storybook/main.js` config:
+
+```js
+module.exports = {
+ // ... your existing config
+ features: {
+ babelModeV7: true,
+ },
+};
+```
+
+In the new mode, Storybook expects you to provide a configuration file. If you want a configuration file that's equivalent to the 6.x default, you can run the following command in your project directory:
+
+```sh
+npx sb@next babelrc
+```
+
+This will create a `.babelrc.json` file. This file includes a bunch of babel plugins, so you may need to add new package devDependencies accordingly.
+
## From version 6.2.x to 6.3.0
### Webpack 5 manager build
diff --git a/app/react/src/server/framework-preset-react.ts b/app/react/src/server/framework-preset-react.ts
index 2e8b75682e4b..71d6816a366b 100644
--- a/app/react/src/server/framework-preset-react.ts
+++ b/app/react/src/server/framework-preset-react.ts
@@ -50,11 +50,11 @@ export async function babelDefault(config: TransformOptions) {
return {
...config,
presets: [
- ...config.presets,
+ ...(config?.presets || []),
[require.resolve('@babel/preset-react'), presetReactOptions],
require.resolve('@babel/preset-flow'),
],
- plugins: [...(config.plugins || []), require.resolve('babel-plugin-add-react-displayname')],
+ plugins: [...(config?.plugins || []), require.resolve('babel-plugin-add-react-displayname')],
};
}
diff --git a/app/web-components/src/server/framework-preset-web-components.ts b/app/web-components/src/server/framework-preset-web-components.ts
index 8e14b519defe..6e17cad29f5a 100644
--- a/app/web-components/src/server/framework-preset-web-components.ts
+++ b/app/web-components/src/server/framework-preset-web-components.ts
@@ -1,7 +1,9 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { Configuration } from 'webpack';
+import type { Options } from '@storybook/core-common';
-export function webpack(config: Configuration) {
+export function webpack(config: Configuration, options: Options) {
+ const babelrcOptions = options.features?.babelModeV7 ? null : { babelrc: false };
config.module.rules.push({
test: [
new RegExp(`src(.*)\\.js$`),
@@ -30,7 +32,7 @@ export function webpack(config: Configuration) {
},
],
],
- babelrc: false,
+ ...babelrcOptions,
},
},
});
diff --git a/docs/configure/babel.md b/docs/configure/babel.md
index 7e3b2dd07926..b102819c277a 100644
--- a/docs/configure/babel.md
+++ b/docs/configure/babel.md
@@ -2,7 +2,21 @@
title: 'Babel'
---
-Storybook’s webpack config by [default](#default-configuration) sets up [Babel](https://babeljs.io/) for ES6 transpiling. Storybook works with evergreen browsers by default.
+Storybook’s webpack config by [default](#default-configuration) sets up [Babel](https://babeljs.io/) for ES6 transpiling.
+
+It has three different modes:
+
+- **CRA** - the mode for Create React App apps specifically
+- **V6** - the default mode for version 6.x and below
+- **V7** - a new mode slated to become the default in SB7.x
+
+## CRA mode
+
+CRA apps configured with `@storybook/preset-create-react-app` use CRA's babel handling to behave as close as possible to your actual application. None of the other documentation on this page applies.
+
+## V6 mode
+
+Storybook works with evergreen browsers by default.
If you want to run Storybook in IE11, make sure to [disable](../essentials/introduction#disabling-addons) the docs-addon that is part of `@storybook/addon-essentials`, as this currently [causes issues in IE11](https://github.com/storybookjs/storybook/issues/8884).
@@ -37,3 +51,54 @@ module.exports = {
}),
};
```
+
+## V7 Mode
+
+V7 mode is a new option available in Storybook 6.4+ behind a feature flag.
+
+Its goal is to make Babel configuration simpler, less buggy, easier to troubleshoot, and more consistent with the rest of the JS ecosystem.
+
+In V7 mode, you are responsible for configuring Babel using your `.babelrc` file and Storybook does not provide any default. Storybook's frameworks and addons may provide small programmatic modifications to the babel configuration.
+
+### Activating
+
+To activate V7 mode, set the feature flag in your `.storybook/main.js` config:
+
+```js
+module.exports = {
+ // ... your existing config
+ features: {
+ babelModeV7: true,
+ },
+};
+```
+
+### Migrating from V6
+
+For detailed instructions on how to migrate from `V6` mode please see [MIGRATION.md](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#babel-mode-v7).
+
+### Generate a babelrc
+
+If your app does not use a babelrc and you need one, you can generate a babelrc file by running the following command in your project directory:
+
+```sh
+npx sb@next babelrc
+```
+
+This will create a `.babelrc.json` file. You may need to add package dependencies.
+
+### Troubleshooting
+
+To troubleshoot your babel configuration, set the `BABEL_SHOW_CONFIG_FOR` environment variable.
+
+For example, to see how Storybook is transpiling your `.storybook/preview.js` config:
+
+```sh
+BABEL_SHOW_CONFIG_FOR=.storybook/preview.js yarn storybook
+```
+
+This will print out the babel configuration for `.storybook/preview.js`, which can be used to debug when files fail to transpile or transpile incorrectly.
+
+> NOTE: Due to what appears to be a Babel bug, setting this flag causes Babel transpilation to fail on the file provided. Thus you cannot actually _RUN_ storybook using this command. However, it will print out the configuration information as advertised and thus you can use this to debug your Storybook. You'll need to remove the flag to actually run your Storybook.
+
+For more info, please refer to the [Babel documentation](https://babeljs.io/docs/en/configuration#print-effective-configs).
diff --git a/examples/react-ts/main.ts b/examples/react-ts/main.ts
index 9460c8989714..bf60ccabbd75 100644
--- a/examples/react-ts/main.ts
+++ b/examples/react-ts/main.ts
@@ -22,6 +22,7 @@ const config: StorybookConfig = {
postcss: false,
previewCsfV3: true,
buildStoriesJson: true,
+ babelModeV7: true,
},
};
diff --git a/lib/builder-webpack4/src/preview/base-webpack.config.ts b/lib/builder-webpack4/src/preview/base-webpack.config.ts
index bb37afd9b10d..996978f58f58 100644
--- a/lib/builder-webpack4/src/preview/base-webpack.config.ts
+++ b/lib/builder-webpack4/src/preview/base-webpack.config.ts
@@ -4,7 +4,7 @@ import path from 'path';
import { logger } from '@storybook/node-logger';
import deprecate from 'util-deprecate';
import dedent from 'ts-dedent';
-import type { BuilderOptions, LoadedPreset, Options } from '@storybook/core-common';
+import type { LoadedPreset, Options } from '@storybook/core-common';
const warnImplicitPostcssPlugins = deprecate(
() => ({
diff --git a/lib/cli/package.json b/lib/cli/package.json
index b51f3aabcefc..16eade2f6997 100644
--- a/lib/cli/package.json
+++ b/lib/cli/package.json
@@ -49,6 +49,7 @@
"@babel/core": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@storybook/codemod": "6.4.0-alpha.32",
+ "@storybook/core-common": "6.4.0-alpha.32",
"@storybook/node-logger": "6.4.0-alpha.32",
"@storybook/semver": "^7.3.2",
"boxen": "^4.2.0",
diff --git a/lib/cli/src/babel-config.ts b/lib/cli/src/babel-config.ts
new file mode 100644
index 000000000000..ff5dd2b6261b
--- /dev/null
+++ b/lib/cli/src/babel-config.ts
@@ -0,0 +1,41 @@
+import { writeFile, access } from 'fs-extra';
+import { logger } from '@storybook/node-logger';
+import { getStorybookBabelConfig } from '@storybook/core-common';
+import path from 'path';
+import prompts from 'prompts';
+
+export const generateStorybookBabelConfigInCWD = async () => {
+ const target = process.cwd();
+ return generateStorybookBabelConfig({ target });
+};
+export const generateStorybookBabelConfig = async ({ target }: { target: string }) => {
+ logger.info(`Generating the storybook default babel config at ${target}`);
+
+ const config = getStorybookBabelConfig({ local: true });
+ const contents = JSON.stringify(config, null, 2);
+
+ const fileName = '.babelrc.json';
+ const location = path.join(target, fileName);
+
+ const exists = await access(location).then(
+ () => true,
+ () => false
+ );
+
+ if (exists) {
+ const { overwrite } = await prompts({
+ type: 'confirm',
+ initial: true,
+ name: 'overwrite',
+ message: `${fileName} already exists. Would you like overwrite it?`,
+ });
+
+ if (overwrite === false) {
+ logger.warn(`Cancelled, babel config file was NOT written to file-system.`);
+ return;
+ }
+ }
+
+ logger.info(`Writing file to ${location}`);
+ await writeFile(location, contents);
+};
diff --git a/lib/cli/src/generate.ts b/lib/cli/src/generate.ts
index 01209a82f6fd..b514be300d9b 100644
--- a/lib/cli/src/generate.ts
+++ b/lib/cli/src/generate.ts
@@ -4,13 +4,14 @@ import chalk from 'chalk';
import envinfo from 'envinfo';
import leven from 'leven';
import { sync } from 'read-pkg-up';
-import initiate from './initiate';
+import { initiate } from './initiate';
import { add } from './add';
import { migrate } from './migrate';
import { extract } from './extract';
import { upgrade } from './upgrade';
import { repro } from './repro';
import { link } from './link';
+import { generateStorybookBabelConfigInCWD } from './babel-config';
const pkg = sync({ cwd: __dirname }).packageJson;
@@ -37,6 +38,11 @@ program
.option('-s --skip-postinstall', 'Skip package specific postinstall config modifications')
.action((addonName, options) => add(addonName, options));
+program
+ .command('babelrc')
+ .description('generate the default storybook babel config into your current working directory')
+ .action(() => generateStorybookBabelConfigInCWD());
+
program
.command('upgrade')
.description('Upgrade your Storybook packages to the latest')
diff --git a/lib/cli/src/generators/baseGenerator.ts b/lib/cli/src/generators/baseGenerator.ts
index 6f3634a7333d..b3a7395d30c0 100644
--- a/lib/cli/src/generators/baseGenerator.ts
+++ b/lib/cli/src/generators/baseGenerator.ts
@@ -1,3 +1,5 @@
+import fse from 'fs-extra';
+import { getStorybookBabelDependencies } from '@storybook/core-common';
import { NpmOptions } from '../NpmOptions';
import {
StoryFormat,
@@ -9,6 +11,7 @@ import {
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;
@@ -91,6 +94,11 @@ export async function baseGenerator(
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));
@@ -129,6 +137,10 @@ export async function baseGenerator(
}
const babelDependencies = addBabel ? await getBabelDependencies(packageManager, packageJson) : [];
+ if (isNewFolder) {
+ babelDependencies.push(...getStorybookBabelDependencies());
+ await generateStorybookBabelConfigInCWD();
+ }
packageManager.addDependencies({ ...npmOptions, packageJson }, [
...versionedPackages,
...babelDependencies,
diff --git a/lib/cli/src/initiate.ts b/lib/cli/src/initiate.ts
index 95bcb5a11c5a..dce515f83bb0 100644
--- a/lib/cli/src/initiate.ts
+++ b/lib/cli/src/initiate.ts
@@ -298,7 +298,7 @@ const projectTypeInquirer = async (options: { yes?: boolean }) => {
return Promise.resolve();
};
-export default function (options: CommandOptions, pkg: Package): Promise {
+export function initiate(options: CommandOptions, pkg: Package): Promise {
const welcomeMessage = 'sb init - the simplest way to add a Storybook to your project.';
logger.log(chalk.inverse(`\n ${welcomeMessage} \n`));
diff --git a/lib/core-common/src/types.ts b/lib/core-common/src/types.ts
index ed34b1c21f2a..d9054b9cb57a 100644
--- a/lib/core-common/src/types.ts
+++ b/lib/core-common/src/types.ts
@@ -265,6 +265,11 @@ export interface StorybookConfig {
* Activate preview of CSF v3.0
*/
previewCsfV3?: boolean;
+
+ /**
+ * Use Storybook 7.0 babel config scheme
+ */
+ babelModeV7?: boolean;
};
/**
* Tells Storybook where to find stories.
diff --git a/lib/core-common/src/utils/babel.ts b/lib/core-common/src/utils/babel.ts
index c7af63107681..8d0416274fb5 100644
--- a/lib/core-common/src/utils/babel.ts
+++ b/lib/core-common/src/utils/babel.ts
@@ -1,56 +1,78 @@
import { TransformOptions } from '@babel/core';
-const plugins = [
- require.resolve('@babel/plugin-transform-shorthand-properties'),
- require.resolve('@babel/plugin-transform-block-scoping'),
- /*
- * Added for TypeScript experimental decorator support
- * https://babeljs.io/docs/en/babel-plugin-transform-typescript#typescript-compiler-options
- */
- [require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
- [require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }],
- [require.resolve('@babel/plugin-proposal-private-methods'), { loose: true }],
- require.resolve('@babel/plugin-proposal-export-default-from'),
- require.resolve('@babel/plugin-syntax-dynamic-import'),
- [
- require.resolve('@babel/plugin-proposal-object-rest-spread'),
- { loose: true, useBuiltIns: true },
- ],
- require.resolve('@babel/plugin-transform-classes'),
- require.resolve('@babel/plugin-transform-arrow-functions'),
- require.resolve('@babel/plugin-transform-parameters'),
- require.resolve('@babel/plugin-transform-destructuring'),
- require.resolve('@babel/plugin-transform-spread'),
- require.resolve('@babel/plugin-transform-for-of'),
- require.resolve('babel-plugin-macros'),
- /*
- * Optional chaining and nullish coalescing are supported in
- * @babel/preset-env, but not yet supported in Webpack due to support
- * missing from acorn. These can be removed once Webpack has support.
- * See https://github.com/facebook/create-react-app/issues/8445#issuecomment-588512250
- */
- require.resolve('@babel/plugin-proposal-optional-chaining'),
- require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'),
- [
- require.resolve('babel-plugin-polyfill-corejs3'),
- {
- method: 'usage-global',
- absoluteImports: require.resolve('core-js'),
- // eslint-disable-next-line global-require
- version: require('core-js/package.json').version,
- },
- ],
-];
-
-const presets = [
- [require.resolve('@babel/preset-env'), { shippedProposals: true, loose: true }],
- require.resolve('@babel/preset-typescript'),
-];
+const r = (s: string, local: boolean) => {
+ return local ? s : require.resolve(s);
+};
-export const babelConfig: () => TransformOptions = () => {
+export const getStorybookBabelConfig = ({ local = false }: { local?: boolean } = {}) => {
return {
sourceType: 'unambiguous',
- presets: [...presets],
- plugins: [...plugins],
- };
+ presets: [
+ [r('@babel/preset-env', local), { shippedProposals: true, loose: true }],
+ r('@babel/preset-typescript', local),
+ ],
+ plugins: [
+ r('@babel/plugin-transform-shorthand-properties', local),
+ r('@babel/plugin-transform-block-scoping', local),
+ /*
+ * Added for TypeScript experimental decorator support
+ * https://babeljs.io/docs/en/babel-plugin-transform-typescript#typescript-compiler-options
+ */
+ [r('@babel/plugin-proposal-decorators', local), { legacy: true }],
+ [r('@babel/plugin-proposal-class-properties', local), { loose: true }],
+ [r('@babel/plugin-proposal-private-methods', local), { loose: true }],
+ r('@babel/plugin-proposal-export-default-from', local),
+ r('@babel/plugin-syntax-dynamic-import', local),
+ [r('@babel/plugin-proposal-object-rest-spread', local), { loose: true, useBuiltIns: true }],
+ r('@babel/plugin-transform-classes', local),
+ r('@babel/plugin-transform-arrow-functions', local),
+ r('@babel/plugin-transform-parameters', local),
+ r('@babel/plugin-transform-destructuring', local),
+ r('@babel/plugin-transform-spread', local),
+ r('@babel/plugin-transform-for-of', local),
+ r('babel-plugin-macros', local),
+ /*
+ * Optional chaining and nullish coalescing are supported in
+ * @babel/preset-env, but not yet supported in Webpack due to support
+ * missing from acorn. These can be removed once Webpack has support.
+ * See https://github.com/facebook/create-react-app/issues/8445#issuecomment-588512250
+ */
+ r('@babel/plugin-proposal-optional-chaining', local),
+ r('@babel/plugin-proposal-nullish-coalescing-operator', local),
+ [
+ r('babel-plugin-polyfill-corejs3', local),
+ {
+ method: 'usage-global',
+ absoluteImports: r('core-js', local),
+ // eslint-disable-next-line global-require
+ version: require('core-js/package.json').version,
+ },
+ ],
+ ],
+ } as TransformOptions;
};
+
+export const getStorybookBabelDependencies = () => [
+ '@babel/preset-env',
+ '@babel/preset-typescript',
+ '@babel/plugin-transform-shorthand-properties',
+ '@babel/plugin-transform-block-scoping',
+ '@babel/plugin-proposal-decorators',
+ '@babel/plugin-proposal-class-properties',
+ '@babel/plugin-proposal-private-methods',
+ '@babel/plugin-proposal-export-default-from',
+ '@babel/plugin-syntax-dynamic-import',
+ '@babel/plugin-proposal-object-rest-spread',
+ '@babel/plugin-transform-classes',
+ '@babel/plugin-transform-arrow-functions',
+ '@babel/plugin-transform-parameters',
+ '@babel/plugin-transform-destructuring',
+ '@babel/plugin-transform-spread',
+ '@babel/plugin-transform-for-of',
+ 'babel-plugin-macros',
+ '@babel/plugin-proposal-optional-chaining',
+ '@babel/plugin-proposal-nullish-coalescing-operator',
+ 'babel-plugin-polyfill-corejs3',
+ 'babel-loader',
+ 'core-js',
+];
diff --git a/lib/core-common/src/utils/es6Transpiler.ts b/lib/core-common/src/utils/es6Transpiler.ts
index 07f9337a7997..5404e76bdc31 100644
--- a/lib/core-common/src/utils/es6Transpiler.ts
+++ b/lib/core-common/src/utils/es6Transpiler.ts
@@ -1,7 +1,7 @@
import { RuleSetRule } from 'webpack';
-import { babelConfig } from './babel';
+import { getStorybookBabelConfig } from './babel';
-const { plugins } = babelConfig();
+const { plugins } = getStorybookBabelConfig();
const nodeModulesThatNeedToBeParsedBecauseTheyExposeES6 = [
'@storybook[\\\\/]node_logger',
diff --git a/lib/core-server/src/__snapshots__/cra-ts-essentials_manager-dev b/lib/core-server/src/__snapshots__/cra-ts-essentials_manager-dev
index 4e178a11ece1..08a97245e468 100644
--- a/lib/core-server/src/__snapshots__/cra-ts-essentials_manager-dev
+++ b/lib/core-server/src/__snapshots__/cra-ts-essentials_manager-dev
@@ -44,6 +44,8 @@ Object {
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
+ "babelrc": false,
+ "configFile": false,
"plugins": Array [
"NODE_MODULES/@babel/plugin-transform-shorthand-properties/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-block-scoping/lib/index.js",
diff --git a/lib/core-server/src/__snapshots__/cra-ts-essentials_manager-prod b/lib/core-server/src/__snapshots__/cra-ts-essentials_manager-prod
index db6851a6b30e..91c113945e6c 100644
--- a/lib/core-server/src/__snapshots__/cra-ts-essentials_manager-prod
+++ b/lib/core-server/src/__snapshots__/cra-ts-essentials_manager-prod
@@ -44,6 +44,8 @@ Object {
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
+ "babelrc": false,
+ "configFile": false,
"plugins": Array [
"NODE_MODULES/@babel/plugin-transform-shorthand-properties/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-block-scoping/lib/index.js",
diff --git a/lib/core-server/src/__snapshots__/html-kitchen-sink_manager-dev b/lib/core-server/src/__snapshots__/html-kitchen-sink_manager-dev
index 4658aa09a26c..766eb604b77c 100644
--- a/lib/core-server/src/__snapshots__/html-kitchen-sink_manager-dev
+++ b/lib/core-server/src/__snapshots__/html-kitchen-sink_manager-dev
@@ -46,6 +46,8 @@ Object {
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
+ "babelrc": false,
+ "configFile": false,
"plugins": Array [
"NODE_MODULES/@babel/plugin-transform-shorthand-properties/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-block-scoping/lib/index.js",
diff --git a/lib/core-server/src/__snapshots__/html-kitchen-sink_manager-prod b/lib/core-server/src/__snapshots__/html-kitchen-sink_manager-prod
index 22bd1485f828..41b8889625aa 100644
--- a/lib/core-server/src/__snapshots__/html-kitchen-sink_manager-prod
+++ b/lib/core-server/src/__snapshots__/html-kitchen-sink_manager-prod
@@ -46,6 +46,8 @@ Object {
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
+ "babelrc": false,
+ "configFile": false,
"plugins": Array [
"NODE_MODULES/@babel/plugin-transform-shorthand-properties/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-block-scoping/lib/index.js",
diff --git a/lib/core-server/src/__snapshots__/vue-3-cli_manager-dev b/lib/core-server/src/__snapshots__/vue-3-cli_manager-dev
index be6c5b62a0d3..0c04036fe091 100644
--- a/lib/core-server/src/__snapshots__/vue-3-cli_manager-dev
+++ b/lib/core-server/src/__snapshots__/vue-3-cli_manager-dev
@@ -46,6 +46,8 @@ Object {
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
+ "babelrc": false,
+ "configFile": false,
"plugins": Array [
"NODE_MODULES/@babel/plugin-transform-shorthand-properties/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-block-scoping/lib/index.js",
diff --git a/lib/core-server/src/__snapshots__/vue-3-cli_manager-prod b/lib/core-server/src/__snapshots__/vue-3-cli_manager-prod
index b86aeecbfb37..9e196e4220fb 100644
--- a/lib/core-server/src/__snapshots__/vue-3-cli_manager-prod
+++ b/lib/core-server/src/__snapshots__/vue-3-cli_manager-prod
@@ -46,6 +46,8 @@ Object {
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
+ "babelrc": false,
+ "configFile": false,
"plugins": Array [
"NODE_MODULES/@babel/plugin-transform-shorthand-properties/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-block-scoping/lib/index.js",
diff --git a/lib/core-server/src/__snapshots__/web-components-kitchen-sink_manager-dev b/lib/core-server/src/__snapshots__/web-components-kitchen-sink_manager-dev
index a34185d641e1..f7b97a41cabc 100644
--- a/lib/core-server/src/__snapshots__/web-components-kitchen-sink_manager-dev
+++ b/lib/core-server/src/__snapshots__/web-components-kitchen-sink_manager-dev
@@ -46,6 +46,8 @@ Object {
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
+ "babelrc": false,
+ "configFile": false,
"plugins": Array [
"NODE_MODULES/@babel/plugin-transform-shorthand-properties/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-block-scoping/lib/index.js",
diff --git a/lib/core-server/src/__snapshots__/web-components-kitchen-sink_manager-prod b/lib/core-server/src/__snapshots__/web-components-kitchen-sink_manager-prod
index 99029c68161c..bad2f198819b 100644
--- a/lib/core-server/src/__snapshots__/web-components-kitchen-sink_manager-prod
+++ b/lib/core-server/src/__snapshots__/web-components-kitchen-sink_manager-prod
@@ -46,6 +46,8 @@ Object {
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
+ "babelrc": false,
+ "configFile": false,
"plugins": Array [
"NODE_MODULES/@babel/plugin-transform-shorthand-properties/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-block-scoping/lib/index.js",
diff --git a/lib/core-server/src/presets/common-preset.ts b/lib/core-server/src/presets/common-preset.ts
index 4382b106b6a0..4ddfcf914598 100644
--- a/lib/core-server/src/presets/common-preset.ts
+++ b/lib/core-server/src/presets/common-preset.ts
@@ -4,17 +4,20 @@ import {
getManagerMainTemplate,
getPreviewMainTemplate,
loadCustomBabelConfig,
- babelConfig,
+ getStorybookBabelConfig,
loadEnvs,
Options,
} from '@storybook/core-common';
export const babel = async (_: unknown, options: Options) => {
const { configDir, presets } = options;
+ if (options.features?.babelModeV7) {
+ return presets.apply('babelDefault', {}, options);
+ }
return loadCustomBabelConfig(
configDir,
- () => presets.apply('babelDefault', babelConfig(), options) as any
+ () => presets.apply('babelDefault', getStorybookBabelConfig(), options) as any
);
};
diff --git a/lib/manager-webpack4/src/babel-loader-manager.ts b/lib/manager-webpack4/src/babel-loader-manager.ts
deleted file mode 100644
index d5837ce29092..000000000000
--- a/lib/manager-webpack4/src/babel-loader-manager.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { RuleSetRule } from 'webpack';
-import { getProjectRoot, babelConfig } from '@storybook/core-common';
-
-const { plugins, presets } = babelConfig();
-
-export const babelLoader: () => RuleSetRule = () => ({
- test: /\.(mjs|tsx?|jsx?)$/,
- use: [
- {
- loader: require.resolve('babel-loader'),
- options: {
- sourceType: 'unambiguous',
- presets: [...presets, require.resolve('@babel/preset-react')],
- plugins: [
- ...plugins,
- // Should only be done on manager. Template literals are not meant to be
- // transformed for frameworks like ember
- require.resolve('@babel/plugin-transform-template-literals'),
- ],
- },
- },
- ],
- include: [getProjectRoot()],
- exclude: [/node_modules/, /dist/],
-});
diff --git a/lib/manager-webpack4/src/manager-config.ts b/lib/manager-webpack4/src/manager-config.ts
index 0fa322e05a53..dd42e9bb3064 100644
--- a/lib/manager-webpack4/src/manager-config.ts
+++ b/lib/manager-webpack4/src/manager-config.ts
@@ -70,8 +70,6 @@ const deprecatedDefinedRefDisabled = deprecate(
export async function getManagerWebpackConfig(options: Options): Promise {
const { presets } = options;
- const typescriptOptions = await presets.apply('typescript', {}, options);
- const babelOptions = await presets.apply('babel', {}, { ...options, typescriptOptions });
const definedRefs: Record | undefined = await presets.apply(
'refs',
@@ -145,5 +143,5 @@ export async function getManagerWebpackConfig(options: Options): Promise => {
- const envs = await presets.apply>('env');
- const logLevel = await presets.apply('logLevel', undefined);
- const template = await presets.apply('managerMainTemplate', getManagerMainTemplate());
-
- const headHtmlSnippet = await presets.apply(
- 'managerHead',
- getManagerHeadTemplate(configDir, process.env)
- );
- const isProd = configType === 'PRODUCTION';
- const refsTemplate = fse.readFileSync(path.join(__dirname, 'virtualModuleRef.template.js'), {
- encoding: 'utf8',
- });
- const {
- packageJson: { version },
- } = await readPackage({ cwd: __dirname });
-
- // @ts-ignore
- // const { BundleAnalyzerPlugin } = await import('webpack-bundle-analyzer').catch(() => ({}));
-
- return {
- name: 'manager',
- mode: isProd ? 'production' : 'development',
- bail: isProd,
- devtool: false,
- entry: entries,
- output: {
- path: outputDir,
- filename: isProd ? '[name].[contenthash].manager.bundle.js' : '[name].manager.bundle.js',
- publicPath: '',
- },
- watchOptions: {
- ignored: /node_modules/,
- },
- plugins: [
- refs
- ? ((new VirtualModulePlugin({
- [path.resolve(path.join(configDir, `generated-refs.js`))]: refsTemplate.replace(
- `'{{refs}}'`,
- JSON.stringify(refs)
- ),
- }) as any) as WebpackPluginInstance)
- : null,
- (new HtmlWebpackPlugin({
- filename: `index.html`,
- // FIXME: `none` isn't a known option
- chunksSortMode: 'none' as any,
- alwaysWriteToDisk: true,
- inject: false,
- templateParameters: (compilation, files, options) => ({
- compilation,
- files,
- options,
- version,
- globals: {
- CONFIG_TYPE: configType,
- LOGLEVEL: logLevel,
- VERSIONCHECK: JSON.stringify(versionCheck),
- RELEASE_NOTES_DATA: JSON.stringify(releaseNotesData),
- DOCS_MODE: docsMode, // global docs mode
- PREVIEW_URL: previewUrl, // global preview URL
- },
- headHtmlSnippet,
- }),
- template,
- }) as any) as WebpackPluginInstance,
- (new CaseSensitivePathsPlugin() as any) as WebpackPluginInstance,
- (new Dotenv({ silent: true }) as any) as WebpackPluginInstance,
- // graphql sources check process variable
- new DefinePlugin({
- 'process.env': stringifyEnvs(envs),
- NODE_ENV: JSON.stringify(envs.NODE_ENV),
- }) as WebpackPluginInstance,
- // isProd &&
- // BundleAnalyzerPlugin &&
- // new BundleAnalyzerPlugin({ analyzerMode: 'static', openAnalyzer: false }),
- ].filter(Boolean),
- module: {
- rules: [
- babelLoader(),
- es6Transpiler() as any,
- {
- test: /\.css$/,
- use: [
- require.resolve('style-loader'),
- {
- loader: require.resolve('css-loader'),
- options: {
- importLoaders: 1,
- },
- },
- ],
- },
- {
- test: /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/,
- loader: require.resolve('file-loader'),
- options: {
- name: isProd
- ? 'static/media/[name].[contenthash:8].[ext]'
- : 'static/media/[path][name].[ext]',
- },
- },
- {
- test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
- loader: require.resolve('url-loader'),
- options: {
- limit: 10000,
- name: isProd
- ? 'static/media/[name].[contenthash:8].[ext]'
- : 'static/media/[path][name].[ext]',
- },
- },
- ],
- },
- resolve: {
- extensions: ['.mjs', '.js', '.jsx', '.json', '.cjs', '.ts', '.tsx'],
- modules: ['node_modules'].concat(envs.NODE_PATH || []),
- mainFields: [modern ? 'sbmodern' : null, 'browser', 'module', 'main'].filter(Boolean),
- alias: {
- ...themingPaths,
- ...uiPaths,
- },
- plugins: [
- // Transparently resolve packages via PnP when needed; noop otherwise
- PnpWebpackPlugin,
- ],
- },
- resolveLoader: {
- plugins: [PnpWebpackPlugin.moduleLoader(module)],
- },
- recordsPath: resolvePathInStorybookCache('public/records.json'),
- performance: {
- hints: false,
- },
- optimization: {
- splitChunks: {
- chunks: 'all',
- },
- runtimeChunk: true,
- sideEffects: true,
- usedExports: true,
- concatenateModules: true,
- minimizer: isProd
- ? [
- new TerserWebpackPlugin({
- parallel: true,
- terserOptions: {
- mangle: false,
- sourceMap: true,
- keep_fnames: true,
- },
- }),
- ]
- : [],
- },
- };
-};
diff --git a/lib/manager-webpack4/src/presets/babel-loader-manager.ts b/lib/manager-webpack4/src/presets/babel-loader-manager.ts
new file mode 100644
index 000000000000..07f9f43118ec
--- /dev/null
+++ b/lib/manager-webpack4/src/presets/babel-loader-manager.ts
@@ -0,0 +1,29 @@
+import { RuleSetRule } from 'webpack';
+import { getProjectRoot, getStorybookBabelConfig } from '@storybook/core-common';
+
+export const babelLoader = () => {
+ const { plugins, presets } = getStorybookBabelConfig();
+
+ return {
+ test: /\.(mjs|tsx?|jsx?)$/,
+ use: [
+ {
+ loader: require.resolve('babel-loader'),
+ options: {
+ sourceType: 'unambiguous',
+ presets: [...presets, require.resolve('@babel/preset-react')],
+ plugins: [
+ ...plugins,
+ // Should only be done on manager. Template literals are not meant to be
+ // transformed for frameworks like ember
+ require.resolve('@babel/plugin-transform-template-literals'),
+ ],
+ babelrc: false,
+ configFile: false,
+ },
+ },
+ ],
+ include: [getProjectRoot()],
+ exclude: [/node_modules/, /dist/],
+ } as RuleSetRule;
+};
diff --git a/lib/manager-webpack4/src/presets/manager-preset.ts b/lib/manager-webpack4/src/presets/manager-preset.ts
index bb31b2f08554..5da0e64a9a3c 100644
--- a/lib/manager-webpack4/src/presets/manager-preset.ts
+++ b/lib/manager-webpack4/src/presets/manager-preset.ts
@@ -1,12 +1,204 @@
-import { Configuration } from 'webpack';
-import { loadManagerOrAddonsFile, ManagerWebpackOptions, Options } from '@storybook/core-common';
-import createDevConfig from '../manager-webpack.config';
+import path from 'path';
+import fse from 'fs-extra';
+import { DefinePlugin, Configuration, WebpackPluginInstance } from 'webpack';
+import Dotenv from 'dotenv-webpack';
+import HtmlWebpackPlugin from 'html-webpack-plugin';
+import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
+import PnpWebpackPlugin from 'pnp-webpack-plugin';
+import VirtualModulePlugin from 'webpack-virtual-modules';
+import TerserWebpackPlugin from 'terser-webpack-plugin';
+
+import themingPaths from '@storybook/theming/paths';
+import uiPaths from '@storybook/ui/paths';
+
+import readPackage from 'read-pkg-up';
+import {
+ loadManagerOrAddonsFile,
+ resolvePathInStorybookCache,
+ stringifyEnvs,
+ es6Transpiler,
+ getManagerHeadTemplate,
+ getManagerMainTemplate,
+ Options,
+ ManagerWebpackOptions,
+} from '@storybook/core-common';
+
+import { babelLoader } from './babel-loader-manager';
export async function managerWebpack(
_: Configuration,
- options: Options & ManagerWebpackOptions
+ {
+ configDir,
+ configType,
+ docsMode,
+ entries,
+ refs,
+ outputDir,
+ previewUrl,
+ versionCheck,
+ releaseNotesData,
+ presets,
+ modern,
+ }: Options & ManagerWebpackOptions
): Promise {
- return createDevConfig(options);
+ const envs = await presets.apply>('env');
+ const logLevel = await presets.apply('logLevel', undefined);
+ const template = await presets.apply('managerMainTemplate', getManagerMainTemplate());
+
+ const headHtmlSnippet = await presets.apply(
+ 'managerHead',
+ getManagerHeadTemplate(configDir, process.env)
+ );
+ const isProd = configType === 'PRODUCTION';
+ const refsTemplate = fse.readFileSync(
+ path.join(__dirname, '..', 'virtualModuleRef.template.js'),
+ {
+ encoding: 'utf8',
+ }
+ );
+ const {
+ packageJson: { version },
+ } = await readPackage({ cwd: __dirname });
+
+ // @ts-ignore
+ // const { BundleAnalyzerPlugin } = await import('webpack-bundle-analyzer').catch(() => ({}));
+
+ return {
+ name: 'manager',
+ mode: isProd ? 'production' : 'development',
+ bail: isProd,
+ devtool: false,
+ entry: entries,
+ output: {
+ path: outputDir,
+ filename: isProd ? '[name].[contenthash].manager.bundle.js' : '[name].manager.bundle.js',
+ publicPath: '',
+ },
+ watchOptions: {
+ ignored: /node_modules/,
+ },
+ plugins: [
+ refs
+ ? ((new VirtualModulePlugin({
+ [path.resolve(path.join(configDir, `generated-refs.js`))]: refsTemplate.replace(
+ `'{{refs}}'`,
+ JSON.stringify(refs)
+ ),
+ }) as any) as WebpackPluginInstance)
+ : null,
+ (new HtmlWebpackPlugin({
+ filename: `index.html`,
+ // FIXME: `none` isn't a known option
+ chunksSortMode: 'none' as any,
+ alwaysWriteToDisk: true,
+ inject: false,
+ templateParameters: (compilation, files, options) => ({
+ compilation,
+ files,
+ options,
+ version,
+ globals: {
+ CONFIG_TYPE: configType,
+ LOGLEVEL: logLevel,
+ VERSIONCHECK: JSON.stringify(versionCheck),
+ RELEASE_NOTES_DATA: JSON.stringify(releaseNotesData),
+ DOCS_MODE: docsMode, // global docs mode
+ PREVIEW_URL: previewUrl, // global preview URL
+ },
+ headHtmlSnippet,
+ }),
+ template,
+ }) as any) as WebpackPluginInstance,
+ (new CaseSensitivePathsPlugin() as any) as WebpackPluginInstance,
+ (new Dotenv({ silent: true }) as any) as WebpackPluginInstance,
+ // graphql sources check process variable
+ new DefinePlugin({
+ 'process.env': stringifyEnvs(envs),
+ NODE_ENV: JSON.stringify(envs.NODE_ENV),
+ }) as WebpackPluginInstance,
+ // isProd &&
+ // BundleAnalyzerPlugin &&
+ // new BundleAnalyzerPlugin({ analyzerMode: 'static', openAnalyzer: false }),
+ ].filter(Boolean),
+ module: {
+ rules: [
+ babelLoader(),
+ es6Transpiler() as any,
+ {
+ test: /\.css$/,
+ use: [
+ require.resolve('style-loader'),
+ {
+ loader: require.resolve('css-loader'),
+ options: {
+ importLoaders: 1,
+ },
+ },
+ ],
+ },
+ {
+ test: /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/,
+ loader: require.resolve('file-loader'),
+ options: {
+ name: isProd
+ ? 'static/media/[name].[contenthash:8].[ext]'
+ : 'static/media/[path][name].[ext]',
+ },
+ },
+ {
+ test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
+ loader: require.resolve('url-loader'),
+ options: {
+ limit: 10000,
+ name: isProd
+ ? 'static/media/[name].[contenthash:8].[ext]'
+ : 'static/media/[path][name].[ext]',
+ },
+ },
+ ],
+ },
+ resolve: {
+ extensions: ['.mjs', '.js', '.jsx', '.json', '.cjs', '.ts', '.tsx'],
+ modules: ['node_modules'].concat(envs.NODE_PATH || []),
+ mainFields: [modern ? 'sbmodern' : null, 'browser', 'module', 'main'].filter(Boolean),
+ alias: {
+ ...themingPaths,
+ ...uiPaths,
+ },
+ plugins: [
+ // Transparently resolve packages via PnP when needed; noop otherwise
+ PnpWebpackPlugin,
+ ],
+ },
+ resolveLoader: {
+ plugins: [PnpWebpackPlugin.moduleLoader(module)],
+ },
+ recordsPath: resolvePathInStorybookCache('public/records.json'),
+ performance: {
+ hints: false,
+ },
+ optimization: {
+ splitChunks: {
+ chunks: 'all',
+ },
+ runtimeChunk: true,
+ sideEffects: true,
+ usedExports: true,
+ concatenateModules: true,
+ minimizer: isProd
+ ? [
+ new TerserWebpackPlugin({
+ parallel: true,
+ terserOptions: {
+ mangle: false,
+ sourceMap: true,
+ keep_fnames: true,
+ },
+ }),
+ ]
+ : [],
+ },
+ };
}
export async function managerEntries(
diff --git a/lib/manager-webpack5/src/babel-loader-manager.ts b/lib/manager-webpack5/src/babel-loader-manager.ts
deleted file mode 100644
index d5837ce29092..000000000000
--- a/lib/manager-webpack5/src/babel-loader-manager.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { RuleSetRule } from 'webpack';
-import { getProjectRoot, babelConfig } from '@storybook/core-common';
-
-const { plugins, presets } = babelConfig();
-
-export const babelLoader: () => RuleSetRule = () => ({
- test: /\.(mjs|tsx?|jsx?)$/,
- use: [
- {
- loader: require.resolve('babel-loader'),
- options: {
- sourceType: 'unambiguous',
- presets: [...presets, require.resolve('@babel/preset-react')],
- plugins: [
- ...plugins,
- // Should only be done on manager. Template literals are not meant to be
- // transformed for frameworks like ember
- require.resolve('@babel/plugin-transform-template-literals'),
- ],
- },
- },
- ],
- include: [getProjectRoot()],
- exclude: [/node_modules/, /dist/],
-});
diff --git a/lib/manager-webpack5/src/manager-config.ts b/lib/manager-webpack5/src/manager-config.ts
index 0fa322e05a53..dd42e9bb3064 100644
--- a/lib/manager-webpack5/src/manager-config.ts
+++ b/lib/manager-webpack5/src/manager-config.ts
@@ -70,8 +70,6 @@ const deprecatedDefinedRefDisabled = deprecate(
export async function getManagerWebpackConfig(options: Options): Promise {
const { presets } = options;
- const typescriptOptions = await presets.apply('typescript', {}, options);
- const babelOptions = await presets.apply('babel', {}, { ...options, typescriptOptions });
const definedRefs: Record | undefined = await presets.apply(
'refs',
@@ -145,5 +143,5 @@ export async function getManagerWebpackConfig(options: Options): Promise => {
- const envs = await presets.apply>('env');
- const logLevel = await presets.apply('logLevel', undefined);
- const template = await presets.apply('managerMainTemplate', getManagerMainTemplate());
-
- const headHtmlSnippet = await presets.apply(
- 'managerHead',
- getManagerHeadTemplate(configDir, process.env)
- );
- const isProd = configType === 'PRODUCTION';
- const refsTemplate = fse.readFileSync(path.join(__dirname, 'virtualModuleRef.template.js'), {
- encoding: 'utf8',
- });
- const {
- packageJson: { version },
- } = await readPackage({ cwd: __dirname });
-
- // @ts-ignore
- // const { BundleAnalyzerPlugin } = await import('webpack-bundle-analyzer').catch(() => ({}));
-
- return {
- name: 'manager',
- mode: isProd ? 'production' : 'development',
- bail: isProd,
- devtool: false,
- entry: entries,
- output: {
- path: outputDir,
- filename: isProd ? '[name].[contenthash].manager.bundle.js' : '[name].manager.bundle.js',
- publicPath: '',
- },
- watchOptions: {
- ignored: /node_modules/,
- },
- plugins: [
- refs
- ? ((new VirtualModulePlugin({
- [path.resolve(path.join(configDir, `generated-refs.js`))]: refsTemplate.replace(
- `'{{refs}}'`,
- JSON.stringify(refs)
- ),
- }) as any) as WebpackPluginInstance)
- : null,
- (new HtmlWebpackPlugin({
- filename: `index.html`,
- // FIXME: `none` isn't a known option
- chunksSortMode: 'none' as any,
- alwaysWriteToDisk: true,
- inject: false,
- templateParameters: (compilation, files, options) => ({
- compilation,
- files,
- options,
- version,
- globals: {
- CONFIG_TYPE: configType,
- LOGLEVEL: logLevel,
- VERSIONCHECK: JSON.stringify(versionCheck),
- RELEASE_NOTES_DATA: JSON.stringify(releaseNotesData),
- DOCS_MODE: docsMode, // global docs mode
- PREVIEW_URL: previewUrl, // global preview URL
- },
- headHtmlSnippet,
- }),
- template,
- }) as any) as WebpackPluginInstance,
- (new CaseSensitivePathsPlugin() as any) as WebpackPluginInstance,
- hasDotenv() ? new Dotenv({ silent: true }) : null,
- // graphql sources check process variable
- new DefinePlugin({
- 'process.env': stringifyEnvs(envs),
- NODE_ENV: JSON.stringify(envs.NODE_ENV),
- }) as WebpackPluginInstance,
- // isProd &&
- // BundleAnalyzerPlugin &&
- // new BundleAnalyzerPlugin({ analyzerMode: 'static', openAnalyzer: false }),
- ].filter(Boolean),
- module: {
- rules: [
- babelLoader(),
- es6Transpiler() as any,
- {
- test: /\.css$/,
- use: [
- require.resolve('style-loader'),
- {
- loader: require.resolve('css-loader'),
- options: {
- importLoaders: 1,
- },
- },
- ],
- },
- {
- test: /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/,
- loader: require.resolve('file-loader'),
- options: {
- name: isProd
- ? 'static/media/[name].[contenthash:8].[ext]'
- : 'static/media/[path][name].[ext]',
- },
- },
- {
- test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
- loader: require.resolve('url-loader'),
- options: {
- limit: 10000,
- name: isProd
- ? 'static/media/[name].[contenthash:8].[ext]'
- : 'static/media/[path][name].[ext]',
- },
- },
- ],
- },
- resolve: {
- extensions: ['.mjs', '.js', '.jsx', '.json', '.cjs', '.ts', '.tsx'],
- modules: ['node_modules'].concat(envs.NODE_PATH || []),
- mainFields: [modern ? 'sbmodern' : null, 'browser', 'module', 'main'].filter(Boolean),
- alias: {
- ...themingPaths,
- ...uiPaths,
- },
- },
- recordsPath: resolvePathInStorybookCache('public/records.json'),
- performance: {
- hints: false,
- },
- optimization: {
- splitChunks: {
- chunks: 'all',
- },
- runtimeChunk: true,
- sideEffects: true,
- usedExports: true,
- concatenateModules: true,
- minimizer: isProd
- ? [
- new TerserWebpackPlugin({
- parallel: true,
- terserOptions: {
- mangle: false,
- sourceMap: true,
- keep_fnames: true,
- },
- }),
- ]
- : [],
- },
- };
-};
diff --git a/lib/manager-webpack5/src/presets/babel-loader-manager.ts b/lib/manager-webpack5/src/presets/babel-loader-manager.ts
new file mode 100644
index 000000000000..07f9f43118ec
--- /dev/null
+++ b/lib/manager-webpack5/src/presets/babel-loader-manager.ts
@@ -0,0 +1,29 @@
+import { RuleSetRule } from 'webpack';
+import { getProjectRoot, getStorybookBabelConfig } from '@storybook/core-common';
+
+export const babelLoader = () => {
+ const { plugins, presets } = getStorybookBabelConfig();
+
+ return {
+ test: /\.(mjs|tsx?|jsx?)$/,
+ use: [
+ {
+ loader: require.resolve('babel-loader'),
+ options: {
+ sourceType: 'unambiguous',
+ presets: [...presets, require.resolve('@babel/preset-react')],
+ plugins: [
+ ...plugins,
+ // Should only be done on manager. Template literals are not meant to be
+ // transformed for frameworks like ember
+ require.resolve('@babel/plugin-transform-template-literals'),
+ ],
+ babelrc: false,
+ configFile: false,
+ },
+ },
+ ],
+ include: [getProjectRoot()],
+ exclude: [/node_modules/, /dist/],
+ } as RuleSetRule;
+};
diff --git a/lib/manager-webpack5/src/presets/manager-preset.ts b/lib/manager-webpack5/src/presets/manager-preset.ts
index bb31b2f08554..a4e6dafa40e0 100644
--- a/lib/manager-webpack5/src/presets/manager-preset.ts
+++ b/lib/manager-webpack5/src/presets/manager-preset.ts
@@ -1,12 +1,197 @@
-import { Configuration } from 'webpack';
-import { loadManagerOrAddonsFile, ManagerWebpackOptions, Options } from '@storybook/core-common';
-import createDevConfig from '../manager-webpack.config';
+import path from 'path';
+import fse from 'fs-extra';
+import { DefinePlugin, Configuration, WebpackPluginInstance } from 'webpack';
+import Dotenv from 'dotenv-webpack';
+import HtmlWebpackPlugin from 'html-webpack-plugin';
+import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
+import VirtualModulePlugin from 'webpack-virtual-modules';
+import TerserWebpackPlugin from 'terser-webpack-plugin';
+
+import themingPaths from '@storybook/theming/paths';
+import uiPaths from '@storybook/ui/paths';
+
+import readPackage from 'read-pkg-up';
+import {
+ loadManagerOrAddonsFile,
+ resolvePathInStorybookCache,
+ stringifyEnvs,
+ es6Transpiler,
+ getManagerHeadTemplate,
+ getManagerMainTemplate,
+ Options,
+ ManagerWebpackOptions,
+ hasDotenv,
+} from '@storybook/core-common';
+
+import { babelLoader } from './babel-loader-manager';
export async function managerWebpack(
_: Configuration,
- options: Options & ManagerWebpackOptions
+ {
+ configDir,
+ configType,
+ docsMode,
+ entries,
+ refs,
+ outputDir,
+ previewUrl,
+ versionCheck,
+ releaseNotesData,
+ presets,
+ modern,
+ }: Options & ManagerWebpackOptions
): Promise {
- return createDevConfig(options);
+ const envs = await presets.apply>('env');
+ const logLevel = await presets.apply('logLevel', undefined);
+ const template = await presets.apply('managerMainTemplate', getManagerMainTemplate());
+
+ const headHtmlSnippet = await presets.apply(
+ 'managerHead',
+ getManagerHeadTemplate(configDir, process.env)
+ );
+ const isProd = configType === 'PRODUCTION';
+ const refsTemplate = fse.readFileSync(
+ path.join(__dirname, '..', 'virtualModuleRef.template.js'),
+ {
+ encoding: 'utf8',
+ }
+ );
+ const {
+ packageJson: { version },
+ } = await readPackage({ cwd: __dirname });
+
+ // @ts-ignore
+ // const { BundleAnalyzerPlugin } = await import('webpack-bundle-analyzer').catch(() => ({}));
+
+ return {
+ name: 'manager',
+ mode: isProd ? 'production' : 'development',
+ bail: isProd,
+ devtool: false,
+ entry: entries,
+ output: {
+ path: outputDir,
+ filename: isProd ? '[name].[contenthash].manager.bundle.js' : '[name].manager.bundle.js',
+ publicPath: '',
+ },
+ watchOptions: {
+ ignored: /node_modules/,
+ },
+ plugins: [
+ refs
+ ? ((new VirtualModulePlugin({
+ [path.resolve(path.join(configDir, `generated-refs.js`))]: refsTemplate.replace(
+ `'{{refs}}'`,
+ JSON.stringify(refs)
+ ),
+ }) as any) as WebpackPluginInstance)
+ : null,
+ (new HtmlWebpackPlugin({
+ filename: `index.html`,
+ // FIXME: `none` isn't a known option
+ chunksSortMode: 'none' as any,
+ alwaysWriteToDisk: true,
+ inject: false,
+ templateParameters: (compilation, files, options) => ({
+ compilation,
+ files,
+ options,
+ version,
+ globals: {
+ CONFIG_TYPE: configType,
+ LOGLEVEL: logLevel,
+ VERSIONCHECK: JSON.stringify(versionCheck),
+ RELEASE_NOTES_DATA: JSON.stringify(releaseNotesData),
+ DOCS_MODE: docsMode, // global docs mode
+ PREVIEW_URL: previewUrl, // global preview URL
+ },
+ headHtmlSnippet,
+ }),
+ template,
+ }) as any) as WebpackPluginInstance,
+ (new CaseSensitivePathsPlugin() as any) as WebpackPluginInstance,
+ hasDotenv() ? new Dotenv({ silent: true }) : null,
+ // graphql sources check process variable
+ new DefinePlugin({
+ 'process.env': stringifyEnvs(envs),
+ NODE_ENV: JSON.stringify(envs.NODE_ENV),
+ }) as WebpackPluginInstance,
+ // isProd &&
+ // BundleAnalyzerPlugin &&
+ // new BundleAnalyzerPlugin({ analyzerMode: 'static', openAnalyzer: false }),
+ ].filter(Boolean),
+ module: {
+ rules: [
+ babelLoader(),
+ es6Transpiler() as any,
+ {
+ test: /\.css$/,
+ use: [
+ require.resolve('style-loader'),
+ {
+ loader: require.resolve('css-loader'),
+ options: {
+ importLoaders: 1,
+ },
+ },
+ ],
+ },
+ {
+ test: /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/,
+ loader: require.resolve('file-loader'),
+ options: {
+ name: isProd
+ ? 'static/media/[name].[contenthash:8].[ext]'
+ : 'static/media/[path][name].[ext]',
+ },
+ },
+ {
+ test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
+ loader: require.resolve('url-loader'),
+ options: {
+ limit: 10000,
+ name: isProd
+ ? 'static/media/[name].[contenthash:8].[ext]'
+ : 'static/media/[path][name].[ext]',
+ },
+ },
+ ],
+ },
+ resolve: {
+ extensions: ['.mjs', '.js', '.jsx', '.json', '.cjs', '.ts', '.tsx'],
+ modules: ['node_modules'].concat(envs.NODE_PATH || []),
+ mainFields: [modern ? 'sbmodern' : null, 'browser', 'module', 'main'].filter(Boolean),
+ alias: {
+ ...themingPaths,
+ ...uiPaths,
+ },
+ },
+ recordsPath: resolvePathInStorybookCache('public/records.json'),
+ performance: {
+ hints: false,
+ },
+ optimization: {
+ splitChunks: {
+ chunks: 'all',
+ },
+ runtimeChunk: true,
+ sideEffects: true,
+ usedExports: true,
+ concatenateModules: true,
+ minimizer: isProd
+ ? [
+ new TerserWebpackPlugin({
+ parallel: true,
+ terserOptions: {
+ mangle: false,
+ sourceMap: true,
+ keep_fnames: true,
+ },
+ }),
+ ]
+ : [],
+ },
+ };
}
export async function managerEntries(
diff --git a/lib/node-logger/src/index.ts b/lib/node-logger/src/index.ts
index 07beaa620bbb..aa5c0769c00f 100644
--- a/lib/node-logger/src/index.ts
+++ b/lib/node-logger/src/index.ts
@@ -27,3 +27,15 @@ export const logger = {
};
export { npmLog as instance };
+
+const logged = new Set();
+export const once = (type: 'info' | 'warn' | 'error') => (message: string) => {
+ if (logged.has(message)) return undefined;
+ logged.add(message);
+ return logger[type](message);
+};
+
+once.clear = () => logged.clear();
+once.info = once('info');
+once.warn = once('warn');
+once.error = once('error');
diff --git a/yarn.lock b/yarn.lock
index 3c96867ce639..735a000450e0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7775,6 +7775,7 @@ __metadata:
"@babel/preset-env": ^7.12.11
"@storybook/client-api": 6.4.0-alpha.32
"@storybook/codemod": 6.4.0-alpha.32
+ "@storybook/core-common": 6.4.0-alpha.32
"@storybook/node-logger": 6.4.0-alpha.32
"@storybook/semver": ^7.3.2
"@types/cross-spawn": ^6.0.2