From 442b1c1769ae5824db7c2b59d748082305d9c898 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 9 Nov 2023 00:01:44 +0100 Subject: [PATCH 1/3] add documentation about new addon bundling strategy --- docs/addons/writing-addons.md | 7 +- ...storybook-addon-toolkit-tsup-config.ts.mdx | 112 +++++++++++++++--- 2 files changed, 96 insertions(+), 23 deletions(-) diff --git a/docs/addons/writing-addons.md b/docs/addons/writing-addons.md index db951af33832..e2e603b5e3e0 100644 --- a/docs/addons/writing-addons.md +++ b/docs/addons/writing-addons.md @@ -83,12 +83,9 @@ Addons built in the Storybook ecosystem rely on [tsup](https://tsup.egoist.dev/) -When the build scripts run, it will look for the configuration file and pre-bundle the addon's code based on the configuration provided. However, there are a few properties that are worth referencing: +When the build scripts run, it will look for the configuration file and pre-bundle the addon's code based on the configuration provided. Addons can interact with Storybook in various ways. They can define presets to modify the configuration, add behavior to the manager UI, or add behavior to the preview iframe. These different use cases require different bundle outputs because they target different runtimes and environments. Presets are executed in a Node environment. Storybook's manager and preview environments provide certain packages in the global scope, so addons don't need to bundle them or include them as dependencies in their `package.json` file. -- **entry**: Configures the files to be processed by the bundler. It can be extended to include additional files using a regex pattern. -- **format**: Enables the generation of multiple output formats. In this case, we're generating a CommonJS and an ES Module version of our addon. -- **dts**: Auto-generates type definitions for our addon. -- **platform**: Specifies the target platform for our addon. In this case, we're targeting the browser. It can be set to `node` for Node.js environments or `neutral` for universal modules. +The `tsup` configuration handles these complexities by default, but you can customize it according to their requirements. For a detailed explanation of the bundling techniques used, please refer to [the README of the addon-kit](https://github.com/storybookjs/addon-kit#bundling). ## Register the addon diff --git a/docs/snippets/common/storybook-addon-toolkit-tsup-config.ts.mdx b/docs/snippets/common/storybook-addon-toolkit-tsup-config.ts.mdx index bf7c81d23cab..e1a29601d04d 100644 --- a/docs/snippets/common/storybook-addon-toolkit-tsup-config.ts.mdx +++ b/docs/snippets/common/storybook-addon-toolkit-tsup-config.ts.mdx @@ -1,22 +1,98 @@ ```ts // tsup.config.ts -import { defineConfig } from 'tsup'; - -export default defineConfig((options) => ({ - entry: ['src/index.ts', 'src/preview.ts', 'src/manager.ts'], - splitting: false, - minify: !options.watch, - format: ['cjs', 'esm'], - dts: { - resolve: true, - }, - treeshake: true, - sourcemap: true, - clean: true, - platform: 'browser', - esbuildOptions(options) { - options.conditions = ['module']; - }, -})); +import { defineConfig, type Options } from "tsup"; +import { readFile } from "fs/promises"; +import { globalPackages as globalManagerPackages } from "@storybook/manager/globals"; +import { globalPackages as globalPreviewPackages } from "@storybook/preview/globals"; + +// The current browsers supported by Storybook v7 +const BROWSER_TARGET: Options['target'] = ["chrome100", "safari15", "firefox91"]; +const NODE_TARGET: Options['target'] = ["node16"]; + +type BundlerConfig = { + bundler?: { + exportEntries?: string[]; + nodeEntries?: string[]; + managerEntries?: string[]; + previewEntries?: string[]; + }; +}; + +export default defineConfig(async (options) => { + // reading the three types of entries from package.json, which has the following structure: + // { + // ... + // "bundler": { + // "exportEntries": ["./src/index.ts"], + // "managerEntries": ["./src/manager.ts"], + // "previewEntries": ["./src/preview.ts"] + // } + // } + const packageJson = await readFile('./package.json', 'utf8').then(JSON.parse) as BundlerConfig; + const { + bundler: { + exportEntries = [], + managerEntries = [], + previewEntries = [], + } = {}, + } = packageJson; + + const commonConfig: Options = { + splitting: false, + minify: !options.watch, + treeshake: true, + sourcemap: true, + clean: true, + }; + + const configs: Options[] = []; + + // export entries are entries meant to be manually imported by the user + // they are not meant to be loaded by the manager or preview + // they'll be usable in both node and browser environments, depending on which features and modules they depend on + if (exportEntries.length) { + configs.push({ + ...commonConfig, + entry: exportEntries, + dts: { + resolve: true, + }, + format: ["esm", 'cjs'], + target: [...BROWSER_TARGET, ...NODE_TARGET], + platform: "neutral", + external: [...globalManagerPackages, ...globalPreviewPackages], + }); + } + + // manager entries are entries meant to be loaded into the manager UI + // they'll have manager-specific packages externalized and they won't be usable in node + // they won't have types generated for them as they're usually loaded automatically by Storybook + if (managerEntries.length) { + configs.push({ + ...commonConfig, + entry: managerEntries, + format: ["esm"], + target: BROWSER_TARGET, + platform: "browser", + external: globalManagerPackages, + }); + } + + // preview entries are entries meant to be loaded into the preview iframe + // they'll have preview-specific packages externalized and they won't be usable in node + // they won't have types generated for them as they're usually loaded automatically by Storybook + if (previewEntries.length) { + configs.push({ + ...commonConfig, + entry: previewEntries, + format: ["esm"], + target: BROWSER_TARGET, + platform: "browser", + external: globalPreviewPackages, + }); + } + + return configs; +}); ``` From ec8802ce0e73a7111c78411bf899992a13e19fb9 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 9 Nov 2023 09:35:27 +0100 Subject: [PATCH 2/3] remove tsup condig from docs --- docs/addons/writing-addons.md | 12 +-- ...storybook-addon-toolkit-tsup-config.ts.mdx | 98 ------------------- 2 files changed, 1 insertion(+), 109 deletions(-) delete mode 100644 docs/snippets/common/storybook-addon-toolkit-tsup-config.ts.mdx diff --git a/docs/addons/writing-addons.md b/docs/addons/writing-addons.md index e2e603b5e3e0..9083c4210a9e 100644 --- a/docs/addons/writing-addons.md +++ b/docs/addons/writing-addons.md @@ -73,19 +73,9 @@ Clone the repository you just created and install its dependencies. When the ins Addons built in the Storybook ecosystem rely on [tsup](https://tsup.egoist.dev/), a fast, zero-config bundler powered by [esbuild](https://esbuild.github.io/) to transpile your addon's code into modern JavaScript that can run in the browser. Out of the box, the Addon Kit comes with a pre-configured `tsup` configuration file that you can use to customize the build process of your addon. - - - - - - When the build scripts run, it will look for the configuration file and pre-bundle the addon's code based on the configuration provided. Addons can interact with Storybook in various ways. They can define presets to modify the configuration, add behavior to the manager UI, or add behavior to the preview iframe. These different use cases require different bundle outputs because they target different runtimes and environments. Presets are executed in a Node environment. Storybook's manager and preview environments provide certain packages in the global scope, so addons don't need to bundle them or include them as dependencies in their `package.json` file. -The `tsup` configuration handles these complexities by default, but you can customize it according to their requirements. For a detailed explanation of the bundling techniques used, please refer to [the README of the addon-kit](https://github.com/storybookjs/addon-kit#bundling). +The `tsup` configuration handles these complexities by default, but you can customize it according to their requirements. For a detailed explanation of the bundling techniques used, please refer to [the README of the addon-kit](https://github.com/storybookjs/addon-kit#bundling), and check out the default `tsup` configuration [here](https://github.com/storybookjs/addon-kit/blob/main/tsup.config.ts). ## Register the addon diff --git a/docs/snippets/common/storybook-addon-toolkit-tsup-config.ts.mdx b/docs/snippets/common/storybook-addon-toolkit-tsup-config.ts.mdx deleted file mode 100644 index e1a29601d04d..000000000000 --- a/docs/snippets/common/storybook-addon-toolkit-tsup-config.ts.mdx +++ /dev/null @@ -1,98 +0,0 @@ -```ts -// tsup.config.ts - -import { defineConfig, type Options } from "tsup"; -import { readFile } from "fs/promises"; -import { globalPackages as globalManagerPackages } from "@storybook/manager/globals"; -import { globalPackages as globalPreviewPackages } from "@storybook/preview/globals"; - -// The current browsers supported by Storybook v7 -const BROWSER_TARGET: Options['target'] = ["chrome100", "safari15", "firefox91"]; -const NODE_TARGET: Options['target'] = ["node16"]; - -type BundlerConfig = { - bundler?: { - exportEntries?: string[]; - nodeEntries?: string[]; - managerEntries?: string[]; - previewEntries?: string[]; - }; -}; - -export default defineConfig(async (options) => { - // reading the three types of entries from package.json, which has the following structure: - // { - // ... - // "bundler": { - // "exportEntries": ["./src/index.ts"], - // "managerEntries": ["./src/manager.ts"], - // "previewEntries": ["./src/preview.ts"] - // } - // } - const packageJson = await readFile('./package.json', 'utf8').then(JSON.parse) as BundlerConfig; - const { - bundler: { - exportEntries = [], - managerEntries = [], - previewEntries = [], - } = {}, - } = packageJson; - - const commonConfig: Options = { - splitting: false, - minify: !options.watch, - treeshake: true, - sourcemap: true, - clean: true, - }; - - const configs: Options[] = []; - - // export entries are entries meant to be manually imported by the user - // they are not meant to be loaded by the manager or preview - // they'll be usable in both node and browser environments, depending on which features and modules they depend on - if (exportEntries.length) { - configs.push({ - ...commonConfig, - entry: exportEntries, - dts: { - resolve: true, - }, - format: ["esm", 'cjs'], - target: [...BROWSER_TARGET, ...NODE_TARGET], - platform: "neutral", - external: [...globalManagerPackages, ...globalPreviewPackages], - }); - } - - // manager entries are entries meant to be loaded into the manager UI - // they'll have manager-specific packages externalized and they won't be usable in node - // they won't have types generated for them as they're usually loaded automatically by Storybook - if (managerEntries.length) { - configs.push({ - ...commonConfig, - entry: managerEntries, - format: ["esm"], - target: BROWSER_TARGET, - platform: "browser", - external: globalManagerPackages, - }); - } - - // preview entries are entries meant to be loaded into the preview iframe - // they'll have preview-specific packages externalized and they won't be usable in node - // they won't have types generated for them as they're usually loaded automatically by Storybook - if (previewEntries.length) { - configs.push({ - ...commonConfig, - entry: previewEntries, - format: ["esm"], - target: BROWSER_TARGET, - platform: "browser", - external: globalPreviewPackages, - }); - } - - return configs; -}); -``` From c2f02b37d86364e633cba453560f855f3db50c16 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 9 Nov 2023 10:08:17 +0100 Subject: [PATCH 3/3] add migration notes to addon authors --- MIGRATION.md | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index c692f787fa6d..ca14c266b621 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,12 +1,13 @@

Migration

- [From version 7.5.0 to 7.6.0](#from-version-750-to-760) - - [Primary doc block accepts of prop](#primary-doc-block-accepts-of-prop) + - [Primary doc block accepts of prop](#primary-doc-block-accepts-of-prop) + - [Addons no longer need a peer dependency on React](#addons-no-longer-need-a-peer-dependency-on-react) - [From version 7.4.0 to 7.5.0](#from-version-740-to-750) - - [`storyStoreV6` and `storiesOf` is deprecated](#storystorev6-and-storiesof-is-deprecated) - - [`storyIndexers` is replaced with `experimental_indexers`](#storyindexers-is-replaced-with-experimental_indexers) + - [`storyStoreV6` and `storiesOf` is deprecated](#storystorev6-and-storiesof-is-deprecated) + - [`storyIndexers` is replaced with `experimental_indexers`](#storyindexers-is-replaced-with-experimental_indexers) - [From version 7.0.0 to 7.2.0](#from-version-700-to-720) - - [Addon API is more type-strict](#addon-api-is-more-type-strict) + - [Addon API is more type-strict](#addon-api-is-more-type-strict) - [From version 6.5.x to 7.0.0](#from-version-65x-to-700) - [7.0 breaking changes](#70-breaking-changes) - [Dropped support for Node 15 and below](#dropped-support-for-node-15-and-below) @@ -32,7 +33,7 @@ - [Deploying build artifacts](#deploying-build-artifacts) - [Dropped support for file URLs](#dropped-support-for-file-urls) - [Serving with nginx](#serving-with-nginx) - - [Ignore story files from node_modules](#ignore-story-files-from-node_modules) + - [Ignore story files from node\_modules](#ignore-story-files-from-node_modules) - [7.0 Core changes](#70-core-changes) - [7.0 feature flags removed](#70-feature-flags-removed) - [Story context is prepared before for supporting fine grained updates](#story-context-is-prepared-before-for-supporting-fine-grained-updates) @@ -44,7 +45,7 @@ - [Addon-interactions: Interactions debugger is now default](#addon-interactions-interactions-debugger-is-now-default) - [7.0 Vite changes](#70-vite-changes) - [Vite builder uses Vite config automatically](#vite-builder-uses-vite-config-automatically) - - [Vite cache moved to node_modules/.cache/.vite-storybook](#vite-cache-moved-to-node_modulescachevite-storybook) + - [Vite cache moved to node\_modules/.cache/.vite-storybook](#vite-cache-moved-to-node_modulescachevite-storybook) - [7.0 Webpack changes](#70-webpack-changes) - [Webpack4 support discontinued](#webpack4-support-discontinued) - [Babel mode v7 exclusively](#babel-mode-v7-exclusively) @@ -94,7 +95,7 @@ - [Dropped addon-docs manual babel configuration](#dropped-addon-docs-manual-babel-configuration) - [Dropped addon-docs manual configuration](#dropped-addon-docs-manual-configuration) - [Autoplay in docs](#autoplay-in-docs) - - [Removed STORYBOOK_REACT_CLASSES global](#removed-storybook_react_classes-global) + - [Removed STORYBOOK\_REACT\_CLASSES global](#removed-storybook_react_classes-global) - [7.0 Deprecations and default changes](#70-deprecations-and-default-changes) - [storyStoreV7 enabled by default](#storystorev7-enabled-by-default) - [`Story` type deprecated](#story-type-deprecated) @@ -309,10 +310,33 @@ ## From version 7.5.0 to 7.6.0 -##### Primary doc block accepts of prop +#### Primary doc block accepts of prop The `Primary` doc block now also accepts an `of` prop as described in the [Doc Blocks](#doc-blocks) section. It still accepts being passed `name` or no props at all. +#### Addons no longer need a peer dependency on React + +Historically the majority of addons have had a peer dependency on React and a handful of Storybook core packages. In most cases this has not been necessary since 7.0 because the Storybook manager makes those available on the global scope. It has created an unnecessary burden for users in non-React projects. + +We've migrated all the core addons (except for `addon-docs`) to not depend on these packages by: + +1. Moving `react`, `react-dom` and the globalized Storybook packages from `peerDependencies` to `devDependencies` +2. Added the list of globalized packages to the `externals` property in the `tsup` configuration, to ensure they are not part of the bundle. + +As of Storybook 7.6.0 the list of globalized packages can be imported like this: + +```ts +// tsup.config.ts + +import { globalPackages as globalManagerPackages } from '@storybook/manager/globals'; +import { globalPackages as globalPreviewPackages } from '@storybook/preview/globals'; + +const allGlobalPackages = [...globalManagerPackages, ...globalPreviewPackages]; +``` + +We recommend checking out [the updates we've made to the addon-kit](https://github.com/storybookjs/addon-kit/pull/60/files#diff-8fed899bdbc24789a7bb4973574e624ed6207c6ce572338bc3c3e117672b2a20), that can serve as a base for the changes you can do in your own addon. These changes are not necessary for your addon to keep working, but they will remove the need for your users to unnecessary install `react` and `react-dom` to their projects, and they'll significantly reduce the install size of your addon. +These changes should not be breaking for your users, unless you support Storybook pre-v7. + ## From version 7.4.0 to 7.5.0 #### `storyStoreV6` and `storiesOf` is deprecated