From 5bd78d30a742e439b9b023b493b1ee99bd213f71 Mon Sep 17 00:00:00 2001 From: Vytautas Date: Wed, 2 Aug 2023 10:42:15 +0800 Subject: [PATCH 01/44] fix: table of contents leak into stories --- code/ui/blocks/src/components/TableOfContents.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/code/ui/blocks/src/components/TableOfContents.tsx b/code/ui/blocks/src/components/TableOfContents.tsx index 892f1e137f20..7d7063f3d343 100644 --- a/code/ui/blocks/src/components/TableOfContents.tsx +++ b/code/ui/blocks/src/components/TableOfContents.tsx @@ -139,14 +139,13 @@ export const TableOfContents = ({ tocSelector: '.toc-wrapper', contentSelector: contentsSelector ?? '.sbdocs-content', headingSelector: headingSelector ?? 'h3', - ignoreSelector: ignoreSelector ?? '.skip-toc', - headingsOffset: 40, - scrollSmoothOffset: -40, /** * Ignore headings that did not * come from the main markdown code. */ - // ignoreSelector: ':not(.sbdocs), .hide-from-toc', + ignoreSelector: ignoreSelector ?? '.docblock-source *, .docs-story *, .skip-toc', + headingsOffset: 40, + scrollSmoothOffset: -40, orderedList: false, /** * Prevent default linking behavior, From 93a5503a6b9fc7db91e1a26db0a38aed39f86ae2 Mon Sep 17 00:00:00 2001 From: Vytautas Date: Wed, 2 Aug 2023 10:59:22 +0800 Subject: [PATCH 02/44] docs: update TOC docs --- docs/writing-docs/autodocs.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/writing-docs/autodocs.md b/docs/writing-docs/autodocs.md index 45f886d0c770..2a0969431f5b 100644 --- a/docs/writing-docs/autodocs.md +++ b/docs/writing-docs/autodocs.md @@ -130,14 +130,14 @@ Storybook's auto-generated documentation pages can be quite long and difficult t By default, the table of contents on the documentation page will only show the `h3` headings that are automatically generated. However, if you want to customize the table of contents, you can add more parameters to the `toc` property. The following options and examples of how to use them are available. -| Option | Description | -| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -| `contentsSelector` | Defines the container's CSS selector for search for the headings
`toc: { contentsSelector: '.sbdocs-content' }` | -| `disable` | Hides the table of contents for the documentation pages
`toc: { disable: true }` | -| `headingSelector` | Defines the list of headings to feature in the table of contents
`toc: { headingSelector: 'h1, h2, h3' }` | -| `ignoreSelector` | Configures the table of contents to ignore specific headings or stories
`toc: { ignoreSelector: 'h2' }` | -| `title` | Defines a title caption for the table of contents.
Accepts one of: `string`, `null`, React element
`toc: { title: 'Table of Contents' }` | -| `unsafeTocbotOptions` | Provides additional [`TocBot`](https://tscanlin.github.io/tocbot/) configuration options
`toc: { unsafeTocbotOptions: { orderedList: true } }` | +| Option | Description | +| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `contentsSelector` | Defines the container's CSS selector for search for the headings
`toc: { contentsSelector: '.sbdocs-content' }` | +| `disable` | Hides the table of contents for the documentation pages
`toc: { disable: true }` | +| `headingSelector` | Defines the list of headings to feature in the table of contents
`toc: { headingSelector: 'h1, h2, h3' }` | +| `ignoreSelector` | Configures the table of contents to ignore specific headings or stories. By default, the table of contents will ignore all content placed within Story blocks
`toc: { ignoreSelector: '.docs-story h2' }` | +| `title` | Defines a title caption for the table of contents.
Accepts one of: `string`, `null`, React element
`toc: { title: 'Table of Contents' }` | +| `unsafeTocbotOptions` | Provides additional [`TocBot`](https://tscanlin.github.io/tocbot/) configuration options
`toc: { unsafeTocbotOptions: { orderedList: true } }` |
From c8ecec6af942aade41666bbcaaadd65f337b18ec Mon Sep 17 00:00:00 2001 From: Vytautas Date: Wed, 2 Aug 2023 10:59:58 +0800 Subject: [PATCH 03/44] fix: update TOC ignoreSelector --- code/ui/blocks/src/components/TableOfContents.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ui/blocks/src/components/TableOfContents.tsx b/code/ui/blocks/src/components/TableOfContents.tsx index 7d7063f3d343..3aaced0d1651 100644 --- a/code/ui/blocks/src/components/TableOfContents.tsx +++ b/code/ui/blocks/src/components/TableOfContents.tsx @@ -143,7 +143,7 @@ export const TableOfContents = ({ * Ignore headings that did not * come from the main markdown code. */ - ignoreSelector: ignoreSelector ?? '.docblock-source *, .docs-story *, .skip-toc', + ignoreSelector: ignoreSelector ?? '.docs-story *, .skip-toc', headingsOffset: 40, scrollSmoothOffset: -40, orderedList: false, From c125ec60c5fef06ad3f72c2bb13a12c74e089588 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Wed, 7 Feb 2024 12:46:04 +0100 Subject: [PATCH 04/44] Autogenerate SupportedFrameworks --- code/lib/cli/src/dirs.ts | 3 +- code/lib/cli/src/generators/baseGenerator.ts | 3 +- code/lib/cli/src/helpers.ts | 34 +++++++++++++----- code/lib/cli/src/project_types.ts | 4 +-- .../src/utils/get-storybook-info.ts | 4 +-- code/lib/types/package.json | 1 + .../scripts/generate-available-frameworks.js | 36 +++++++++++++++++++ code/lib/types/src/index.ts | 1 + code/lib/types/src/modules/frameworks.ts | 24 +++++++++++++ 9 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 code/lib/types/scripts/generate-available-frameworks.js create mode 100644 code/lib/types/src/modules/frameworks.ts diff --git a/code/lib/cli/src/dirs.ts b/code/lib/cli/src/dirs.ts index 213b1877aaee..1f41620b4ea6 100644 --- a/code/lib/cli/src/dirs.ts +++ b/code/lib/cli/src/dirs.ts @@ -6,9 +6,10 @@ import * as tempy from 'tempy'; import invariant from 'tiny-invariant'; import { externalFrameworks } from './project_types'; -import type { SupportedFrameworks, SupportedRenderers } from './project_types'; +import type { SupportedRenderers } from './project_types'; import type { JsPackageManager } from '@storybook/core-common'; import { versions } from '@storybook/core-common'; +import type { SupportedFrameworks } from '@storybook/types'; export function getCliDir() { return dirname(require.resolve('@storybook/cli/package.json')); diff --git a/code/lib/cli/src/generators/baseGenerator.ts b/code/lib/cli/src/generators/baseGenerator.ts index 3c3cae9e5e5e..2f85d0af4549 100644 --- a/code/lib/cli/src/generators/baseGenerator.ts +++ b/code/lib/cli/src/generators/baseGenerator.ts @@ -5,8 +5,9 @@ import ora from 'ora'; import invariant from 'tiny-invariant'; import type { JsPackageManager } from '@storybook/core-common'; import { getPackageDetails, versions as packageVersions } from '@storybook/core-common'; +import type { SupportedFrameworks } from '@storybook/types'; import type { NpmOptions } from '../NpmOptions'; -import type { SupportedRenderers, SupportedFrameworks, Builder } from '../project_types'; +import type { SupportedRenderers, Builder } from '../project_types'; import { SupportedLanguage, externalFrameworks } from '../project_types'; import { copyTemplateFiles } from '../helpers'; import { configureMain, configurePreview } from './configure'; diff --git a/code/lib/cli/src/helpers.ts b/code/lib/cli/src/helpers.ts index 95c22ee624f1..167340059828 100644 --- a/code/lib/cli/src/helpers.ts +++ b/code/lib/cli/src/helpers.ts @@ -13,7 +13,8 @@ import type { PackageJson, PackageJsonWithDepsAndDevDeps, } from '@storybook/core-common'; -import type { SupportedFrameworks, SupportedRenderers } from './project_types'; +import type { SupportedFrameworks } from '@storybook/types'; +import type { SupportedRenderers } from './project_types'; import { SupportedLanguage } from './project_types'; import { versions as storybookMonorepoPackages } from '@storybook/core-common'; @@ -134,19 +135,36 @@ const frameworkToRenderer: Record< SupportedFrameworks | SupportedRenderers, SupportedRenderers | 'vue' > = { + // frameworks angular: 'angular', ember: 'ember', - html: 'html', + 'html-vite': 'html', + 'html-webpack5': 'html', nextjs: 'react', - preact: 'preact', + 'preact-vite': 'preact', + 'preact-webpack5': 'preact', qwik: 'qwik', - react: 'react', - 'react-native': 'react', - server: 'react', + 'react-vite': 'react', + 'react-webpack5': 'react', + 'server-webpack5': 'server', solid: 'solid', - svelte: 'svelte', + 'svelte-vite': 'svelte', + 'svelte-webpack5': 'svelte', sveltekit: 'svelte', - vue3: 'vue', + 'vue-vite': 'vue', + 'vue-webpack5': 'vue', + 'vue3-vite': 'vue3', + 'vue3-webpack5': 'vue3', + 'web-components-vite': 'web-components', + 'web-components-webpack5': 'web-components', + // renderers + html: 'html', + preact: 'preact', + 'react-native': 'react-native', + react: 'react', + server: 'server', + svelte: 'svelte', + vue3: 'vue3', 'web-components': 'web-components', }; diff --git a/code/lib/cli/src/project_types.ts b/code/lib/cli/src/project_types.ts index 0a0073d84664..e781b3e16db8 100644 --- a/code/lib/cli/src/project_types.ts +++ b/code/lib/cli/src/project_types.ts @@ -1,4 +1,5 @@ import { minVersion, validRange } from 'semver'; +import type { SupportedFrameworks } from '@storybook/types'; function eqMajor(versionRange: string, major: number) { // Uses validRange to avoid a throw from minVersion if an invalid range gets passed @@ -21,9 +22,6 @@ export const externalFrameworks: ExternalFramework[] = [ { name: 'solid', frameworks: ['storybook-solidjs-vite'], renderer: 'storybook-solidjs' }, ]; -// Should match @storybook/ -export type SupportedFrameworks = 'nextjs' | 'angular' | 'sveltekit' | 'qwik' | 'solid' | 'ember'; - // Should match @storybook/ export type SupportedRenderers = | 'react' diff --git a/code/lib/core-common/src/utils/get-storybook-info.ts b/code/lib/core-common/src/utils/get-storybook-info.ts index dd462a6b0370..6dda1cd20699 100644 --- a/code/lib/core-common/src/utils/get-storybook-info.ts +++ b/code/lib/core-common/src/utils/get-storybook-info.ts @@ -1,6 +1,6 @@ import path from 'path'; import fse from 'fs-extra'; -import type { CoreCommon_StorybookInfo, PackageJson } from '@storybook/types'; +import type { CoreCommon_StorybookInfo, PackageJson, SupportedFrameworks } from '@storybook/types'; import { getStorybookConfiguration } from './get-storybook-configuration'; export const rendererPackages: Record = { @@ -19,7 +19,7 @@ export const rendererPackages: Record = { 'storybook-solidjs': 'solid', }; -export const frameworkPackages: Record = { +export const frameworkPackages: Record = { '@storybook/angular': 'angular', '@storybook/ember': 'ember', '@storybook/html-vite': 'html-vite', diff --git a/code/lib/types/package.json b/code/lib/types/package.json index e2b155001ae7..6d1f4b281635 100644 --- a/code/lib/types/package.json +++ b/code/lib/types/package.json @@ -58,6 +58,7 @@ "access": "public" }, "bundler": { + "pre": "./scripts/generate-available-frameworks.js", "entries": [ "./src/index.ts" ] diff --git a/code/lib/types/scripts/generate-available-frameworks.js b/code/lib/types/scripts/generate-available-frameworks.js new file mode 100644 index 000000000000..f3f0af79f6f1 --- /dev/null +++ b/code/lib/types/scripts/generate-available-frameworks.js @@ -0,0 +1,36 @@ +// read ./code/frameworks subfolders and generate a list of available frameworks +// save this list into ./code/lib/cli/src/frameworks.ts and export it as a union type. The name of the type is `SupportedFrameworks`. Add additionally 'qwik' and `solid` to that list. + +import { readdir, writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import prettier from 'prettier'; +import dedent from 'ts-dedent'; + +const thirdPartyFrameworks = ['qwik', 'solid']; + +const run = async () => { + const frameworks = await readdir(path.join(__dirname, '..', '..', '..', 'frameworks')); + const supportedFrameworks = frameworks.map((framework) => `'${framework}'`).join(' | '); + const fileContent = dedent` + // auto generated file, do not edit + // the file gets generated by the script ./code/lib/types/scripts/generate-available-frameworks.js + export type SupportedFrameworks = ${supportedFrameworks} | ${thirdPartyFrameworks + .map((framework) => `'${framework}'`) + .join(' | ')}; +`; + // format fileContent by prettier + const frameworksFile = path.join(__dirname, '..', 'src', 'modules', 'frameworks.ts'); + const prettierConfig = await prettier.resolveConfig(frameworksFile); + + const formattedFileContent = await prettier.format(fileContent, { + ...prettierConfig, + parser: 'typescript', + }); + + await writeFile(frameworksFile, formattedFileContent); +}; + +run().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/code/lib/types/src/index.ts b/code/lib/types/src/index.ts index 756b3d2634cb..0eb7547ccb6b 100644 --- a/code/lib/types/src/index.ts +++ b/code/lib/types/src/index.ts @@ -10,3 +10,4 @@ export * from './modules/api-stories'; export * from './modules/indexer'; export * from './modules/composedStory'; export * from './modules/channelApi'; +export * from './modules/frameworks'; diff --git a/code/lib/types/src/modules/frameworks.ts b/code/lib/types/src/modules/frameworks.ts new file mode 100644 index 000000000000..0785763f46e5 --- /dev/null +++ b/code/lib/types/src/modules/frameworks.ts @@ -0,0 +1,24 @@ +// auto generated file, do not edit +// the file gets generated by the script ./code/lib/types/scripts/generate-available-frameworks.js +export type SupportedFrameworks = + | 'angular' + | 'ember' + | 'html-vite' + | 'html-webpack5' + | 'nextjs' + | 'preact-vite' + | 'preact-webpack5' + | 'react-vite' + | 'react-webpack5' + | 'server-webpack5' + | 'svelte-vite' + | 'svelte-webpack5' + | 'sveltekit' + | 'vue-vite' + | 'vue-webpack5' + | 'vue3-vite' + | 'vue3-webpack5' + | 'web-components-vite' + | 'web-components-webpack5' + | 'qwik' + | 'solid'; From d351504a58df41f9cb96edbeee90bd5f6d30a2c1 Mon Sep 17 00:00:00 2001 From: jonniebigodes Date: Thu, 8 Feb 2024 21:17:33 +0000 Subject: [PATCH 05/44] Docs: Remove references to the storiesOf API --- code/lib/preview-api/README-client-api.md | 6 +- code/lib/preview-api/docs/storiesOf.md | 97 --------- docs/api/main-config-indexers.md | 4 +- docs/configure/frameworks-feature-support.md | 10 +- docs/faq.md | 206 +++++++++---------- 5 files changed, 112 insertions(+), 211 deletions(-) delete mode 100644 code/lib/preview-api/docs/storiesOf.md diff --git a/code/lib/preview-api/README-client-api.md b/code/lib/preview-api/README-client-api.md index c459b4c8d425..84861e4f2ca9 100644 --- a/code/lib/preview-api/README-client-api.md +++ b/code/lib/preview-api/README-client-api.md @@ -1,10 +1,12 @@ -# `@storybook/client-api` -- Deprecated Story APIs (`storiesOf`) +# `@storybook/client-api` -- Removed Story APIs (`storiesOf`) **NOTE** This API is deprecated, and the CSF format is preferred for all stories. ## `storiesOf` API -The `@storybook/client` API provides the [`storiesOf()` API](./docs/storiesOf.md), which is proxied through to the CSF API. +The `@storybook/client` package provided backward compatibility for the `storiesOf` API supported until the previous stable Storybook version. With the release of Storybook 8 the `storiesOf` API was removed and is no longer supported or maintained. + + ### Internals diff --git a/code/lib/preview-api/docs/storiesOf.md b/code/lib/preview-api/docs/storiesOf.md deleted file mode 100644 index 08e7009a9d0d..000000000000 --- a/code/lib/preview-api/docs/storiesOf.md +++ /dev/null @@ -1,97 +0,0 @@ -## storiesOf (Legacy) API - -`storiesOf` is Storybook's Legacy API for adding stories. Up until Storybook 5.2, it has been the primary way to create stories in Storybook. - -In Storybook 5.2 we introduced a simpler and more portable [Component Story Format](https://storybook.js.org/docs/react/api/csf), and all future tools and infrastructure will be oriented towards CSF. Therefore, we recommend migrating your stories out of `storiesOf` API, and even provide [automated tools to do this](#component-story-format-migration). - -That said, the `storiesOf` API is no longer actively maintained and has been removed as part of the Storybook 8 release. If you're working with a custom indexer or similar tooling that implements this API, we encourage using custom story indexers instead. Read our [Indexer API documentation](https://storybook.js.org/docs/api/main-config-indexers) for more information. - -## storiesOf API - -A Storybook is a collection of stories. Each story represents a single visual state of a component. - -Here's a basic story file in the `storiesOf` API: - -```js -import React from 'react'; -import { storiesOf } from '@storybook/react'; -import { action } from '@storybook/addon-actions'; -import Button from '../components/Button'; - -storiesOf('Button', module) - .add('with text', () => ) - .add('with some emoji', () => ( - - )); -``` - -The string argument to `storiesOf` is the component title. If you pass a string like `'Widgets|Button/Button'` it can also be used to position your component's story within Storybook's story hierarchy. - -The second argument of `storiesOf` is a webpack `module`, which is available on the global (per-file) scope. Storybook needs it to enable hot-module-replacement. If it's not included you'll need to refresh your browser with each change you make. - -Each `.add` call takes a story name, a story function that returns a renderable object (JSX in the case of React), and optionally some parameters, which are described below. - -## Decorators and parameters - -[Decorators](https://storybook.js.org/docs/react/writing-stories/decorators) and [parameters](https://storybook.js.org/docs/react/writing-stories/parameters) can be specified globally, at the component level, or locally at the story level. - -In the `storiesOf` API, story-level parameters are provided as a third argument to `.add`: - -```js -storiesOf('Button', module).add( - 'with text', - () => , - { notes: someNotes } -); -``` - -Story-level decorators are provided via parameters: - -```js -storiesOf('Button', module).add( - 'with text', - () => , - { decorators: [withKnobs] } -); -``` - -We can control how the component's stories will render with parameters and decorators. You can use as many `.addDecorators` as you need (but make sure you add them all before your first story), but you can only use one `.addParameters`, as you can see in the example below: - -```js -storiesOf('Button', module) - .addParameters({ backgrounds: { values: [{ name: 'red', value: '#f00' }] } }) - .addDecorator((Story) => ( -
- -
- )) - .addDecorator((Story) => ( -
- -
- )) - .add('with text', () => ) - .add('with some emoji', () => ( - - )); -``` - -Parameters and decorators can also be used globally, you can define them in your .storybook/preview.js. Take a look [here](https://storybook.js.org/docs/react/writing-stories/parameters#global-parameters) to learn more about global parameters and [here](https://storybook.js.org/docs/react/writing-stories/decorators#global-decorators) for global decorators. - -## Component Story Format migration - -To make it easier to adopt the new [Component Story Format (CSF)](https://storybook.js.org/docs/react/api/csf), we've created an automatic migration tool to transform `storiesOf` API to Module format. - -```sh -sb migrate storiesof-to-csf --glob=src/**/*.stories.js -``` - -For more information, see the CLI's [Codemod README](https://github.com/storybookjs/storybook/tree/next/code/lib/codemod). diff --git a/docs/api/main-config-indexers.md b/docs/api/main-config-indexers.md index 256dd9b2c990..79e7fa2f2651 100644 --- a/docs/api/main-config-indexers.md +++ b/docs/api/main-config-indexers.md @@ -351,9 +351,7 @@ function JsonStoriesPlugin(): PluginOption { Generating stories with an alternative API -You can use a custom indexer and builder plugin to create your own API for defining stories, such as imperatively defining stories similar to the legacy [`storiesOf`](https://github.com/storybookjs/storybook/blob/main/code/lib/preview-api/docs/storiesOf.md) format. - -The [dynamic stories proof of concept](https://stackblitz.com/edit/github-h2rgfk?file=README.md) is an elaborate, functional example of doing just that. It contains everything needed to support such a feature, including the indexer, a Vite plugin and a Webpack loader. +You can use a custom indexer and builder plugin to create your API to define stories extending the CSF format. To learn more, see the following [proof of concept](https://stackblitz.com/edit/github-h2rgfk?file=README.md) to set up a custom indexer to generate stories dynamically. It contains everything needed to support such a feature, including the indexer, a Vite plugin, and a Webpack loader. diff --git a/docs/configure/frameworks-feature-support.md b/docs/configure/frameworks-feature-support.md index 7ae41614e248..79013ad8fc4b 100644 --- a/docs/configure/frameworks-feature-support.md +++ b/docs/configure/frameworks-feature-support.md @@ -110,8 +110,8 @@ Community frameworks have fewer contributors which means they may not be as up t To align the Storybook ecosystem with the current state of frontend development, the following features and addons are now deprecated, no longer maintained, and will be removed in future versions of Storybook -| Feature | Status | -| -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [Knobs](https://github.com/storybookjs/addon-knobs) | The Knobs addon was officially deprecated with the release of Storybook 6.3 and is no longer actively maintained. We recommend using the [controls](../essentials/controls.md) instead. | -| [Storyshots](../writing-tests/snapshot-testing.md) | The Storyshots addon was officially deprecated with the release of Storybook 7.6, is no longer actively maintained and was removed in Storybook 8. See the [migration guide](../writing-tests/storyshots-migration-guide.md) for the available alternatives. | -| [`StoriesOf`](https://github.com/storybookjs/storybook/blob/next/code/lib/preview-api/docs/storiesOf.md) | The `storiesOf` API was officially removed with the release of Storybook 8 and is no longer maintained. We recommend using the [CSF API](../api/csf.md) instead for writing stories.
See the [migration guide](../migration-guide.md#storiesof-to-csf) for more information. | +| Feature | Status | +| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Knobs](https://github.com/storybookjs/addon-knobs) | The Knobs addon was officially deprecated with the release of Storybook 6.3 and is no longer actively maintained. We recommend using the [controls](../essentials/controls.md) instead. | +| [Storyshots](../writing-tests/snapshot-testing.md) | The Storyshots addon was officially deprecated with the release of Storybook 7.6, is no longer actively maintained and was removed in Storybook 8. See the [migration guide](../writing-tests/storyshots-migration-guide.md) for the available alternatives. | +| StoriesOf | The `storiesOf` API was officially removed with the release of Storybook 8 and is no longer maintained. We recommend using the [CSF API](../api/csf.md) instead for writing stories.
See the [migration guide](../migration-guide.md#storiesof-to-csf) for more information. | diff --git a/docs/faq.md b/docs/faq.md index 6e6ce176787a..895f556160d6 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -230,112 +230,110 @@ With the release of version 6.0, we updated our documentation as well. That does We're only covering versions 5.3 and 5.0 as they were important milestones for Storybook. If you want to go back in time a little more, you'll have to check the specific release in the monorepo. -| Section | Page | Current Location | Version 5.3 location | Version 5.0 location | -| ---------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| N/A | Why Storybook | [See current documentation](./why-storybook.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| Get started | Install | [See current documentation](./get-started/install.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides/quick-start-guide) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/guides/quick-start-guide) | -| | What's a story | [See current documentation](./get-started/whats-a-story.md) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/guides) | -| | Browse Stories | [See current documentation](./get-started/browse-stories.md) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/blob/release/5.0/docs/src/pages/guides) | -| | Setup | [See current documentation](./get-started/setup.md) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/guides) | -| Write stories | Introduction | [See current documentation](./writing-stories/index.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories) | -| | Parameters | [See current documentation](./writing-stories/parameters.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories/index.md#parameters) | Non existing feature or undocumented | -| | Decorators | [See current documentation](./writing-stories/decorators.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories/index.md#decorators) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories/index.md#using-decorators) | -| | Naming components and hierarchy | [See current documentation](./writing-stories/naming-components-and-hierarchy.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories) | -| | Build pages and screens | [See current documentation](./writing-stories/build-pages-with-storybook.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Stories for multiple components | [See current documentation](./writing-stories/stories-for-multiple-components.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| Write docs | Autodocs | [See current documentation](./writing-docs/autodocs.md) | See versioned addon documentation | Non existing feature or undocumented | -| | MDX | [See current documentation](./writing-docs/mdx.md) | See versioned addon documentation | Non existing feature or undocumented | -| | Doc Blocks | [See current documentation](./writing-docs/doc-blocks.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Preview and build docs | [See current documentation](./writing-docs/build-documentation.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| Testing | Visual tests | [See current documentation](./writing-tests/visual-testing.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/automated-visual-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/automated-visual-testing) | -| | Accessibility tests | [See current documentation](./writing-tests/accessibility-testing.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Interaction tests | [See current documentation](./writing-tests/interaction-testing.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/interaction-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/interaction-testing) | -| | Snapshot tests | [See current documentation](./writing-tests/snapshot-testing.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/structural-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/structural-testing) | -| | Import stories in tests/Unit tests | [See current documentation](./writing-tests/stories-in-unit-tests.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/react-ui-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/react-ui-testing) | -| | Import stories in tests/End-to-end testing | [See current documentation](./writing-tests/stories-in-end-to-end-tests.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/react-ui-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/react-ui-testing) | -| Sharing | Publish Storybook | [See current documentation](./sharing/publish-storybook.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/exporting-storybook) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/exporting-storybook) | -| | Embed | [See current documentation](./sharing/embed.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Composition | [See current documentation](./sharing/storybook-composition.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Package Composition | [See current documentation](./sharing/package-composition.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| Essential addons | Controls | [See current documentation](./essentials/controls.md) | Controls are specific to version 6.0 see [Knobs versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/knobs) | Controls are specific to version 6.0 see [Knobs versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/knobs) | -| | Actions | [See current documentation](./essentials/actions.md) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/actions) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/actions) | -| | Viewport | [See current documentation](./essentials/viewport.md) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/viewport) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/viewport) | -| | Backgrounds | [See current documentation](./essentials/backgrounds.md) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/backgrounds) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/backgrounds) | -| | Toolbars and globals | [See current documentation](./essentials/toolbars-and-globals.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/toolbar-guide) | Non existing feature or undocumented | -| Configure | Overview | [See current documentation](./configure/index.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/overview) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories) | -| | Integration/Frameworks | [See current documentation](./configure/frameworks.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Integration/Framework support for frameworks | [See current documentation](./configure/frameworks-feature-support.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Integration/Compilers | [See current documentation](./configure/compilers.md) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/custom-babel-config) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/custom-babel-config) | -| | Integration/Typescript | [See current documentation](./configure/typescript.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/typescript-config) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/typescript-config) | -| | Integration/Styling and CSS | [See current documentation](./configure/styling-and-css.md) | See versioned documentation | See versioned documentation | -| | Integration/Images and assets | [See current documentation](./configure/images-and-assets.md) | See versioned documentation | See versioned documentation | -| | Story rendering | [See current documentation](./configure/story-rendering.md) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/add-custom-head-tags) and [here](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/add-custom-body) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/add-custom-head-tags) | -| | Story Layout | [See current documentation](./configure/story-layout.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | User Interface/Features and behavior | [See current documentation](./configure/features-and-behavior.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/options-parameter) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/options-parameter) | -| | User Interface/Theming | [See current documentation](./configure/theming.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/theming) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/theming) | -| | User Interface/Sidebar & URLS | [See current documentation](./configure/sidebar-and-urls.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/options-parameter) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/options-parameter) | -| | Environment variables | [See current documentation](./configure/environment-variables.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/env-vars) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/env-vars) | -| Builders | Introduction | [See current documentation](./builders/index.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Vite | [See current documentation](./builders/vite.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Webpack | [See current documentation](./builders/webpack.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/custom-webpack-config/index.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/custom-webpack-config/index.md) | -| | Builder API | [See current documentation](./builders/builder-api.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| Addons | Introduction | [See current documentation](./addons/index.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/writing-addons) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/writing-addons) | -| | Install addons | [See current documentation](./addons/install-addons.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/using-addons/) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/using-addons/) | -| | Writing Addons | [See current documentation](./addons/writing-addons.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/writing-addons) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/writing-addons) | -| | Writing Presets | [See current documentation](./addons/writing-presets.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/presets/writing-presets) | Non existing feature or undocumented | -| | Addons Knowledge Base | [See current documentation](./addons/addon-knowledge-base.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/writing-addons) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/writing-addons) | -| | Types of addons | [See current documentation](./addons/addon-types.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Addons API | [See current documentation](./addons/addons-api.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/api) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/api) | -| API | @storybook/blocks/ArgTypes | [See current documentation](./api/doc-block-argtypes.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Canvas | [See current documentation](./api/doc-block-canvas.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/ColorPalette | [See current documentation](./api/doc-block-colorpalette.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Controls | [See current documentation](./api/doc-block-controls.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Description | [See current documentation](./api/doc-description.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/IconGallery | [See current documentation](./api/doc-block-icongallery.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Markdown | [See current documentation](./api/doc-block-markdown.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Meta | [See current documentation](./api/doc-block-meta.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Primary | [See current documentation](./api/doc-block-primary.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Source | [See current documentation](./api/doc-block-source.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Stories | [See current documentation](./api/doc-block-stories.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Story | [See current documentation](./api/doc-block-story.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Subtitle | [See current documentation](./api/doc-block-subtitle.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Title | [See current documentation](./api/doc-block-title.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Typeset | [See current documentation](./api/doc-block-typeset.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/Unstyled | [See current documentation](./api/doc-block-unstyled.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | @storybook/blocks/useOf | [See current documentation](./api/doc-block-useof.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Stories/Component Story Format | [See current documentation](./api/csf.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/formats/component-story-format) | Non existing feature or undocumented | -| | Stories/StoriesOF format (see note below) | [See current documentation](https://github.com/storybookjs/storybook/blob/main/code/lib/preview-api/docs/storiesOf.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/formats/storiesof-api) | Non existing feature or undocumented | -| | ArgTypes | [See current documentation](./api/arg-types.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/Overview | [See current documentation](./api/main-config.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/framework | [See current documentation](./api/main-config-framework.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/stories | [See current documentation](./api/main-config-stories.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/addons | [See current documentation](./api/main-config-addons.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/babel | [See current documentation](./api/main-config-babel.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/babelDefault | [See current documentation](./api/main-config-babel-default.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/build | [See current documentation](./api/main-config-build.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/core | [See current documentation](./api/main-config-core.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/docs | [See current documentation](./api/main-config-docs.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/env | [See current documentation](./api/main-config-env.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/features | [See current documentation](./api/main-config-features.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/indexers | [See current documentation](./api/main-config-indexers.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/logLevel | [See current documentation](./api/main-config-log-level.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/managerHead | [See current documentation](./api/main-config-manager-head.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/previewAnnotations | [See current documentation](./api/main-config-preview-annotations.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/previewBody | [See current documentation](./api/main-config-preview-body.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/previewHead | [See current documentation](./api/main-config-preview-head.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/refs | [See current documentation](./api/main-config-refs.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/staticDirs | [See current documentation](./api/main-config-static-dirs.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/swc | [See current documentation](./api/main-config-swc.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/typescript | [See current documentation](./api/main-config-typescript.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/viteFinal | [See current documentation](./api/main-config-vite-final.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | `main.js` configuration/webpackFinal | [See current documentation](./api/main-config-webpack-final.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | Frameworks | [See current documentation](./api/new-frameworks.md) | Non existing feature or undocumented | Non existing feature or undocumented | -| | CLI options | [See current documentation](./api/cli-options.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/cli-options) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/cli-options) | - - +| Section | Page | Current Location | Version 5.3 location | Version 5.0 location | +| ---------------- | ----------------------------------------------- | --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| N/A | Why Storybook | [See current documentation](./why-storybook.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| Get started | Install | [See current documentation](./get-started/install.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides/quick-start-guide) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/guides/quick-start-guide) | +| | What's a story | [See current documentation](./get-started/whats-a-story.md) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/guides) | +| | Browse Stories | [See current documentation](./get-started/browse-stories.md) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/blob/release/5.0/docs/src/pages/guides) | +| | Setup | [See current documentation](./get-started/setup.md) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/guides) | [See versioned documentation for your framework](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/guides) | +| Write stories | Introduction | [See current documentation](./writing-stories/index.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories) | +| | Parameters | [See current documentation](./writing-stories/parameters.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories/index.md#parameters) | Non existing feature or undocumented | +| | Decorators | [See current documentation](./writing-stories/decorators.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories/index.md#decorators) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories/index.md#using-decorators) | +| | Naming components and hierarchy | [See current documentation](./writing-stories/naming-components-and-hierarchy.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/writing-stories) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories) | +| | Build pages and screens | [See current documentation](./writing-stories/build-pages-with-storybook.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Stories for multiple components | [See current documentation](./writing-stories/stories-for-multiple-components.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| Write docs | Autodocs | [See current documentation](./writing-docs/autodocs.md) | See versioned addon documentation | Non existing feature or undocumented | +| | MDX | [See current documentation](./writing-docs/mdx.md) | See versioned addon documentation | Non existing feature or undocumented | +| | Doc Blocks | [See current documentation](./writing-docs/doc-blocks.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Preview and build docs | [See current documentation](./writing-docs/build-documentation.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| Testing | Visual tests | [See current documentation](./writing-tests/visual-testing.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/automated-visual-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/automated-visual-testing) | +| | Accessibility tests | [See current documentation](./writing-tests/accessibility-testing.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Interaction tests | [See current documentation](./writing-tests/interaction-testing.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/interaction-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/interaction-testing) | +| | Snapshot tests | [See current documentation](./writing-tests/snapshot-testing.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/structural-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/structural-testing) | +| | Import stories in tests/Unit tests | [See current documentation](./writing-tests/stories-in-unit-tests.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/react-ui-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/react-ui-testing) | +| | Import stories in tests/End-to-end testing | [See current documentation](./writing-tests/stories-in-end-to-end-tests.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/testing/react-ui-testing) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/testing/react-ui-testing) | +| Sharing | Publish Storybook | [See current documentation](./sharing/publish-storybook.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/exporting-storybook) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/exporting-storybook) | +| | Embed | [See current documentation](./sharing/embed.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Composition | [See current documentation](./sharing/storybook-composition.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Package Composition | [See current documentation](./sharing/package-composition.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| Essential addons | Controls | [See current documentation](./essentials/controls.md) | Controls are specific to version 6.0 see [Knobs versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/knobs) | Controls are specific to version 6.0 see [Knobs versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/knobs) | +| | Actions | [See current documentation](./essentials/actions.md) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/actions) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/actions) | +| | Viewport | [See current documentation](./essentials/viewport.md) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/viewport) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/viewport) | +| | Backgrounds | [See current documentation](./essentials/backgrounds.md) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/addons/backgrounds) | [See addon versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/addons/backgrounds) | +| | Toolbars and globals | [See current documentation](./essentials/toolbars-and-globals.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/basics/toolbar-guide) | Non existing feature or undocumented | +| Configure | Overview | [See current documentation](./configure/index.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/overview) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/basics/writing-stories) | +| | Integration/Frameworks | [See current documentation](./configure/frameworks.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Integration/Framework support for frameworks | [See current documentation](./configure/frameworks-feature-support.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Integration/Compilers | [See current documentation](./configure/compilers.md) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/custom-babel-config) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/custom-babel-config) | +| | Integration/Typescript | [See current documentation](./configure/typescript.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/typescript-config) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/typescript-config) | +| | Integration/Styling and CSS | [See current documentation](./configure/styling-and-css.md) | See versioned documentation | See versioned documentation | +| | Integration/Images and assets | [See current documentation](./configure/images-and-assets.md) | See versioned documentation | See versioned documentation | +| | Story rendering | [See current documentation](./configure/story-rendering.md) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/add-custom-head-tags) and [here](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/add-custom-body) | See versioned documentation [here](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/add-custom-head-tags) | +| | Story Layout | [See current documentation](./configure/story-layout.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | User Interface/Features and behavior | [See current documentation](./configure/features-and-behavior.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/options-parameter) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/options-parameter) | +| | User Interface/Theming | [See current documentation](./configure/theming.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/theming) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/theming) | +| | User Interface/Sidebar & URLS | [See current documentation](./configure/sidebar-and-urls.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/options-parameter) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/options-parameter) | +| | Environment variables | [See current documentation](./configure/environment-variables.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/env-vars) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/env-vars) | +| Builders | Introduction | [See current documentation](./builders/index.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Vite | [See current documentation](./builders/vite.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Webpack | [See current documentation](./builders/webpack.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/custom-webpack-config/index.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/custom-webpack-config/index.md) | +| | Builder API | [See current documentation](./builders/builder-api.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| Addons | Introduction | [See current documentation](./addons/index.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/writing-addons) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/writing-addons) | +| | Install addons | [See current documentation](./addons/install-addons.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/using-addons/) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/using-addons/) | +| | Writing Addons | [See current documentation](./addons/writing-addons.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/writing-addons) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/writing-addons) | +| | Writing Presets | [See current documentation](./addons/writing-presets.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/presets/writing-presets) | Non existing feature or undocumented | +| | Addons Knowledge Base | [See current documentation](./addons/addon-knowledge-base.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/writing-addons) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/writing-addons) | +| | Types of addons | [See current documentation](./addons/addon-types.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Addons API | [See current documentation](./addons/addons-api.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/addons/api) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/addons/api) | +| API | @storybook/blocks/ArgTypes | [See current documentation](./api/doc-block-argtypes.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Canvas | [See current documentation](./api/doc-block-canvas.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/ColorPalette | [See current documentation](./api/doc-block-colorpalette.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Controls | [See current documentation](./api/doc-block-controls.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Description | [See current documentation](./api/doc-description.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/IconGallery | [See current documentation](./api/doc-block-icongallery.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Markdown | [See current documentation](./api/doc-block-markdown.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Meta | [See current documentation](./api/doc-block-meta.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Primary | [See current documentation](./api/doc-block-primary.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Source | [See current documentation](./api/doc-block-source.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Stories | [See current documentation](./api/doc-block-stories.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Story | [See current documentation](./api/doc-block-story.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Subtitle | [See current documentation](./api/doc-block-subtitle.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Title | [See current documentation](./api/doc-block-title.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Typeset | [See current documentation](./api/doc-block-typeset.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/Unstyled | [See current documentation](./api/doc-block-unstyled.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | @storybook/blocks/useOf | [See current documentation](./api/doc-block-useof.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Stories/Component Story Format (see note below) | [See current documentation](./api/csf.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/formats/component-story-format) | Non existing feature or undocumented | +| | ArgTypes | [See current documentation](./api/arg-types.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/Overview | [See current documentation](./api/main-config.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/framework | [See current documentation](./api/main-config-framework.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/stories | [See current documentation](./api/main-config-stories.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/addons | [See current documentation](./api/main-config-addons.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/babel | [See current documentation](./api/main-config-babel.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/babelDefault | [See current documentation](./api/main-config-babel-default.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/build | [See current documentation](./api/main-config-build.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/core | [See current documentation](./api/main-config-core.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/docs | [See current documentation](./api/main-config-docs.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/env | [See current documentation](./api/main-config-env.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/features | [See current documentation](./api/main-config-features.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/indexers | [See current documentation](./api/main-config-indexers.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/logLevel | [See current documentation](./api/main-config-log-level.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/managerHead | [See current documentation](./api/main-config-manager-head.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/previewAnnotations | [See current documentation](./api/main-config-preview-annotations.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/previewBody | [See current documentation](./api/main-config-preview-body.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/previewHead | [See current documentation](./api/main-config-preview-head.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/refs | [See current documentation](./api/main-config-refs.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/staticDirs | [See current documentation](./api/main-config-static-dirs.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/swc | [See current documentation](./api/main-config-swc.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/typescript | [See current documentation](./api/main-config-typescript.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/viteFinal | [See current documentation](./api/main-config-vite-final.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | `main.js` configuration/webpackFinal | [See current documentation](./api/main-config-webpack-final.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | Frameworks | [See current documentation](./api/new-frameworks.md) | Non existing feature or undocumented | Non existing feature or undocumented | +| | CLI options | [See current documentation](./api/cli-options.md) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/cli-options) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/cli-options) | + -With the release of version 5.3, we've updated how you can write your stories more compactly and easily. It doesn't mean that the `storiesOf` format has been removed. For the time being, we're still supporting it, and we have documentation for it. But be advised that this is bound to change in the future. +If you have stories written with the older `storiesOf` format, it was removed in Storybook 8.0 and is no longer maintained. We recommend that you migrate your stories to CSF. See the [migration guide](./migration-guide.md#storiesof-to-csf) for more information. However, if you need, you can still access the old documentation for `storiesOf` [here](https://github.com/storybookjs/storybook/blob/release/5.3/docs/src/pages/formats/storiesof-api/index.md). From 9c8805234f7886c204fc8df2b5bb858d78d7effc Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 12 Feb 2024 14:49:34 +0100 Subject: [PATCH 06/44] Add Webpack5 Compiler automigration --- code/lib/cli/src/automigrate/fixes/index.ts | 2 + .../fixes/webpack5-compiler-setup.test.ts | 441 ++++++++++++++++++ .../fixes/webpack5-compiler-setup.ts | 180 +++++++ .../src/automigrate/helpers/mainConfigFile.ts | 24 +- code/lib/cli/src/automigrate/index.ts | 2 +- code/lib/cli/src/helpers.ts | 25 + code/lib/cli/src/project_types.ts | 15 + code/lib/types/src/modules/core-common.ts | 3 + 8 files changed, 690 insertions(+), 2 deletions(-) create mode 100644 code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts create mode 100644 code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts diff --git a/code/lib/cli/src/automigrate/fixes/index.ts b/code/lib/cli/src/automigrate/fixes/index.ts index 27f8a80b3140..606e87221192 100644 --- a/code/lib/cli/src/automigrate/fixes/index.ts +++ b/code/lib/cli/src/automigrate/fixes/index.ts @@ -20,6 +20,7 @@ import { wrapRequire } from './wrap-require'; import { reactDocgen } from './react-docgen'; import { removeReactDependency } from './prompt-remove-react'; import { storyshotsMigration } from './storyshots-migration'; +import { webpack5Migration } from './webpack5-compiler-setup'; export * from '../types'; @@ -44,6 +45,7 @@ export const allFixes: Fix[] = [ reactDocgen, storyshotsMigration, removeReactDependency, + webpack5Migration, ]; export const initFixes: Fix[] = [eslintPlugin]; diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts new file mode 100644 index 000000000000..580a5b8324e6 --- /dev/null +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts @@ -0,0 +1,441 @@ +import { vi, describe, it, expect, afterEach, beforeEach } from 'vitest'; +import type { StorybookConfig } from '@storybook/types'; +import type { JsPackageManager } from '@storybook/core-common'; +import { webpack5Migration } from './webpack5-compiler-setup'; +import { CoreWebpackCompilers } from '../../project_types'; + +const check = async ({ + packageManager, + mainConfig, + storybookVersion = '8.0.0', +}: { + packageManager?: Partial; + main?: Partial & Record; + storybookVersion?: string; + mainConfig?: Partial; +}) => { + return webpack5Migration.check({ + packageManager: packageManager as any, + configDir: '', + storybookVersion, + mainConfig: mainConfig as any, + }); +}; + +const promptMocks = vi.hoisted(() => { + return { + default: vi.fn(), + }; +}); + +vi.mock('prompts', () => { + return { + default: promptMocks.default, + }; +}); + +// mock chalk yellow and cyan +vi.mock('chalk', () => { + return { + default: { + yellow: (str: string) => str, + cyan: (str: string) => str, + }, + }; +}); + +describe('check function', () => { + describe('webpack5Migration check function', () => { + beforeEach(() => { + promptMocks.default.mockResolvedValue({ + compiler: 'swc', + }); + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + + describe('return null', async () => { + it('should return null if the builder is not webpack5', async () => { + const result = check({ + mainConfig: { + framework: { + name: '@storybook/react-vite', + }, + }, + }); + + await expect(result).resolves.toBeNull(); + }); + + it('should return null if the framework is Angular', async () => { + const result = check({ + mainConfig: { + framework: '@storybook/angular', + }, + }); + + await expect(result).resolves.toBeNull(); + // ... + }); + + it('should return null if the framework is Ember', async () => { + const result = check({ + mainConfig: { + framework: { + name: '@storybook/ember', + }, + }, + }); + + await expect(result).resolves.toBeNull(); + }); + + it('should return null if the framework is Webpack5 based but a different framework builder is used', async () => { + const result = check({ + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + options: { + builder: '@storybook/builder-vite', + }, + }, + }, + }); + + await expect(result).resolves.toBeNull(); + }); + + it('should return null if the framework is Webpack5 based but a different core builder is used', async () => { + const result = check({ + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + }, + core: { + builder: '@storybook/builder-vite', + }, + }, + }); + + await expect(result).resolves.toBeNull(); + }); + + it('should return null if the framework is CRA based', async () => { + const result = check({ + packageManager: { + getPackageVersion: (name) => { + if (name === 'react-scripts') { + return Promise.resolve('5.0.0'); + } + + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + }, + }, + }); + + await expect(result).resolves.toBeNull(); + }); + }); + + describe('useSWC', () => { + it('should return shouldRemoveSWCFlag: true when useSWC flag is set to true', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + options: { + builder: { + useSWC: true, + }, + }, + }, + }, + }); + + expect(result).contains({ + shouldRemoveSWCFlag: true, + }); + }); + + it('should return shouldRemoveSWCFlag: true when useSWC flag is set to false', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + options: { + builder: { + useSWC: false, + }, + }, + }, + }, + }); + + expect(result).contains({ + shouldRemoveSWCFlag: true, + }); + }); + + it('should return shouldRemoveSWCFlag: false when useSWC flag is not set', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + options: { + builder: {}, + }, + }, + }, + }); + + expect(result).contains({ + shouldRemoveSWCFlag: false, + }); + }); + }); + + describe('Next.js', () => { + it('should return isNextJs: true when the framework is nextjs', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/nextjs', + }, + }, + }); + + expect(result).contains({ + isNextJs: true, + }); + }); + + it('should return isNextJs: false when the framework is not nextjs', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + }, + }, + }); + + expect(result).contains({ + isNextJs: false, + }); + }); + }); + + describe('return options', () => { + it('should return compiler: babel when useSWC flag is not set and the user selects babel during prompt', async () => { + promptMocks.default.mockResolvedValue({ + compiler: 'babel', + }); + + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + }, + }, + }); + + expect(result).contains({ + compiler: 'babel', + compilerPackageName: '@storybook/addon-webpack5-compiler-babel', + }); + }); + + describe('user selects swc', () => { + it('should return compiler: swc when useSWC flag is not set and the user selects swc during prompt', async () => { + promptMocks.default.mockResolvedValue({ + compiler: 'swc', + }); + + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + }, + }, + }); + + expect(result).contains({ + compiler: 'swc', + compilerPackageName: '@storybook/addon-webpack5-compiler-swc', + }); + }); + }); + + it('should return compiler: swc when useSWC flag is set', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + options: { + builder: { + useSWC: true, + }, + }, + }, + }, + }); + + expect(result).contains({ + compiler: 'swc', + compilerPackageName: '@storybook/addon-webpack5-compiler-swc', + }); + }); + + it('should return options if the framework is unknown but webpack5 was detected', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + if (name === 'webpack') { + return Promise.resolve('5.0.0'); + } + + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/unknown', + }, + }, + }); + + expect(result).not.toBeNull(); + }); + }); + }); + + describe('prompt', () => { + it('shouldRemoveSWCFlag = true', async () => { + const prompt = webpack5Migration.prompt({ + shouldRemoveSWCFlag: true, + isNextJs: false, + compilerPackageName: '@storybook/addon-webpack5-compiler-swc', + compiler: CoreWebpackCompilers.SWC, + }); + + expect(prompt).toMatchInlineSnapshot(` + "We need to update your Storybook configuration for Webpack 5. + The framework.options.builder.useSWC flag will be removed. + + The @storybook/addon-webpack5-compiler-swc addon will be added to your project. It adds SWC as the compiler for your Storybook. + After the migration, you can switch Webpack5 compilers by swapping the addon in your project. + You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" + `); + }); + + it('shouldRemoveSWCFlag = false', async () => { + const prompt = webpack5Migration.prompt({ + shouldRemoveSWCFlag: false, + isNextJs: false, + compilerPackageName: '@storybook/addon-webpack5-compiler-swc', + compiler: CoreWebpackCompilers.SWC, + }); + + expect(prompt).toMatchInlineSnapshot(` + "The @storybook/addon-webpack5-compiler-swc addon will be added to your project. It adds SWC as the compiler for your Storybook. + After the migration, you can switch Webpack5 compilers by swapping the addon in your project. + You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" + `); + }); + + it('isNextJs = true', () => { + const prompt = webpack5Migration.prompt({ + shouldRemoveSWCFlag: true, + isNextJs: true, + compilerPackageName: undefined, + compiler: undefined, + }); + + expect(prompt).toMatchInlineSnapshot(` + "We need to update your Storybook configuration for Webpack 5. + The framework.options.builder.useSWC flag will be removed. + + Storybook now detects whether it should use Babel or SWC as a compiler by applying the same logic as Next.js itself. + If you have a babel.config.js file in your project, Storybook will use Babel as the compiler. + If you have a babel.config.js file in your project and you have set experimental.forceSwcTransforms = true in your next.config.js file, Storybook will use SWC as the compiler. + If you don't have a babel.config.js file in your project, Storybook will use SWC as the compiler." + `); + }); + + it('isNextjs = false AND compilerPackageName = @storybook/addon-webpack5-compiler-swc AND compiler = swc', () => { + const prompt = webpack5Migration.prompt({ + shouldRemoveSWCFlag: false, + isNextJs: false, + compilerPackageName: '@storybook/addon-webpack5-compiler-swc', + compiler: CoreWebpackCompilers.SWC, + }); + + expect(prompt).toMatchInlineSnapshot(` + "The @storybook/addon-webpack5-compiler-swc addon will be added to your project. It adds SWC as the compiler for your Storybook. + After the migration, you can switch Webpack5 compilers by swapping the addon in your project. + You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" + `); + }); + + it('isNextjs = false AND compilerPackageName = @storybook/addon-webpack5-compiler-babel AND compiler = babel', () => { + const prompt = webpack5Migration.prompt({ + shouldRemoveSWCFlag: false, + isNextJs: false, + compilerPackageName: '@storybook/addon-webpack5-compiler-babel', + compiler: CoreWebpackCompilers.Babel, + }); + + expect(prompt).toMatchInlineSnapshot(` + "The @storybook/addon-webpack5-compiler-babel addon will be added to your project. It adds Babel as the compiler for your Storybook. + After the migration, you can switch Webpack5 compilers by swapping the addon in your project. + You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" + `); + }); + }); +}); diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts new file mode 100644 index 000000000000..b895d4b6ef12 --- /dev/null +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts @@ -0,0 +1,180 @@ +import prompts from 'prompts'; +import type { SupportedFrameworks } from '@storybook/types'; +import { frameworkPackages } from '@storybook/core-common'; +import type { Fix } from '../types'; +import { + getBuilderPackageName, + getFrameworkOptions, + getFrameworkPackageName, + updateMainConfig, +} from '../helpers/mainConfigFile'; +import { frameworkToDefaultBuilder } from '../../helpers'; +import { + CoreBuilder, + CoreWebpackCompilers, + builderNameToCoreBuilder, + compilerNameToCoreCompiler, +} from '../../project_types'; +import dedent from 'ts-dedent'; +import chalk from 'chalk'; +import { add } from '../../add'; + +type Options = { + compiler?: CoreWebpackCompilers; + compilerPackageName?: keyof typeof compilerNameToCoreCompiler | undefined; + shouldRemoveSWCFlag: boolean; + isNextJs: boolean; +}; + +export const webpack5Migration: Fix = { + id: 'webpack5-compiler-setup', + + async check({ mainConfig, packageManager }) { + const frameworkName = Object.entries(frameworkPackages).find( + ([name]) => name === getFrameworkPackageName(mainConfig) + )?.[1]; + + const builderPackageName = getBuilderPackageName(mainConfig); + const customCoreBuilder = builderPackageName + ? builderNameToCoreBuilder[builderPackageName] + : null; + + const defaultCoreBuilder = frameworkName + ? frameworkToDefaultBuilder[frameworkName] + : await (async () => { + const webpackVersion = await packageManager.getPackageVersion('webpack'); + return !!webpackVersion ? CoreBuilder.Webpack5 : CoreBuilder.Vite; + })(); + + const builder = customCoreBuilder || defaultCoreBuilder; + + if (builder !== CoreBuilder.Webpack5) { + return null; + } + + const excludedFrameworks: SupportedFrameworks[] = ['angular', 'ember']; + + const isExcludedFramework = frameworkName ? excludedFrameworks.includes(frameworkName) : false; + + if (isExcludedFramework) { + return null; + } + + const hasReactScriptsDependency = !!(await packageManager.getPackageVersion('react-scripts')); + + if (hasReactScriptsDependency) { + return null; + } + + const frameworkOptions = getFrameworkOptions(mainConfig); + + const defaultCompiler = frameworkOptions?.builder?.useSWC + ? CoreWebpackCompilers.SWC + : CoreWebpackCompilers.Babel; + + const compiler: CoreWebpackCompilers = + defaultCompiler === CoreWebpackCompilers.Babel + ? await askUserForCompilerChoice() + : CoreWebpackCompilers.SWC; + + const compilerPackageName = Object.entries(compilerNameToCoreCompiler).find( + ([, coreCompiler]) => coreCompiler === compiler + )?.[0]; + + if (frameworkName === 'nextjs') { + return { + compiler: undefined, + compilerPackageName: undefined, + shouldRemoveSWCFlag: false, + isNextJs: true, + }; + } + + return { + compiler, + compilerPackageName, + shouldRemoveSWCFlag: frameworkOptions?.builder ? 'useSWC' in frameworkOptions.builder : false, + isNextJs: false, + }; + }, + + prompt({ compiler, compilerPackageName, shouldRemoveSWCFlag, isNextJs }) { + const message = []; + + if (shouldRemoveSWCFlag) { + message.push(dedent` + We need to update your Storybook configuration for Webpack 5. + The ${chalk.yellow('framework.options.builder.useSWC')} flag will be removed.`); + } + + if (isNextJs) { + message.push(dedent` + Storybook now detects whether it should use Babel or SWC as a compiler by applying the same logic as Next.js itself. + If you have a ${chalk.yellow( + 'babel.config.js' + )} file in your project, Storybook will use Babel as the compiler. + If you have a ${chalk.yellow( + 'babel.config.js' + )} file in your project and you have set ${chalk.yellow( + 'experimental.forceSwcTransforms = true' + )} in your next.config.js file, Storybook will use SWC as the compiler. + If you don't have a babel.config.js file in your project, Storybook will use SWC as the compiler. + `); + } else if (compilerPackageName) { + message.push(dedent` + The ${chalk.cyan(compilerPackageName)} addon will be added to your project. It adds ${ + compiler === CoreWebpackCompilers.Babel ? 'Babel' : 'SWC' + } as the compiler for your Storybook. + After the migration, you can switch Webpack5 compilers by swapping the addon in your project. + You can find more information here: ${chalk.yellow( + 'https://storybook.js.org/docs/8.0/builders/webpack#compiler-support' + )} + `); + } + + return message.join('\n\n'); + }, + + async run({ result, mainConfigPath, packageManager, skipInstall, dryRun }) { + const { compilerPackageName, shouldRemoveSWCFlag, isNextJs } = result; + + if (shouldRemoveSWCFlag) { + await updateMainConfig({ mainConfigPath, dryRun: !!dryRun }, (main) => { + main.removeField(['framework', 'options', 'builder', 'useSWC']); + }); + } + + if (!isNextJs && compilerPackageName) { + await add(compilerPackageName, { + packageManager: packageManager.type, + skipPostinstall: !!skipInstall, + }); + } + }, +}; + +async function askUserForCompilerChoice() { + const response = await prompts<'compiler'>({ + type: 'select', + name: 'compiler', + message: `Storybook's Webpack5 builder is now compiler agnostic, meaning you can choose a compiler addon that best fits your project: +Babel - A vast ecosystem and is battle-tested. It's a robust choice if you have an extensive Babel setup or need specific Babel plugins for your project. +SWC - Fast and easy to configure. Ideal if you want faster builds and have a straightforward configuration without the need for Babel's extensibility. +Which compiler would you like to use?`, + choices: [ + { + title: 'Babel', + description: 'Choose Babel for a vast ecosystem and battle-tested reliability.', + value: CoreWebpackCompilers.Babel, + }, + { + title: 'SWC', + description: 'Choose SWC for fast builds and simple configuration.', + value: CoreWebpackCompilers.SWC, + }, + ], + initial: 0, + }); + + return response.compiler as CoreWebpackCompilers; +} diff --git a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts index c721dae39a31..491bcbe219ef 100644 --- a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts +++ b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts @@ -42,20 +42,42 @@ export const getFrameworkPackageName = (mainConfig?: StorybookConfigRaw) => { * @returns - The package name of the builder. If not found, returns null. */ export const getBuilderPackageName = (mainConfig?: StorybookConfigRaw) => { - const packageNameOrPath = + const frameworkOptions = getFrameworkOptions(mainConfig); + + const frameworkBuilder = frameworkOptions?.builder; + + const frameworkBuilderName = + typeof frameworkBuilder === 'string' ? frameworkBuilder : frameworkBuilder?.options?.name; + + const coreBuilderName = typeof mainConfig?.core?.builder === 'string' ? mainConfig.core.builder : mainConfig?.core?.builder?.name; + const packageNameOrPath = coreBuilderName ?? frameworkBuilderName; + if (!packageNameOrPath) { return null; } + console.log({ packageNameOrPath }); + const normalizedPath = path.normalize(packageNameOrPath).replace(new RegExp(/\\/, 'g'), '/'); return builderPackages.find((pkg) => normalizedPath.endsWith(pkg)) || packageNameOrPath; }; +/** + * Given a Storybook configuration object, retrieves the configuration for the framework. + * @param mainConfig - The main Storybook configuration object to lookup. + * @returns - The configuration for the framework. If not found, returns null. + */ +export const getFrameworkOptions = ( + mainConfig?: StorybookConfigRaw +): Record | null => { + return typeof mainConfig?.framework === 'string' ? null : mainConfig?.framework?.options ?? null; +}; + /** * Returns a renderer package name given a framework package name. * @param frameworkPackageName - The package name of the framework to lookup. diff --git a/code/lib/cli/src/automigrate/index.ts b/code/lib/cli/src/automigrate/index.ts index 3adeff5e0ead..f63e77e79773 100644 --- a/code/lib/cli/src/automigrate/index.ts +++ b/code/lib/cli/src/automigrate/index.ts @@ -231,7 +231,7 @@ export async function runFixes({ if (result) { logger.info(`\n🔎 found a '${chalk.cyan(f.id)}' migration:`); - const message = f.prompt(result); + const message = await f.prompt(result); logger.info( boxen(message, { diff --git a/code/lib/cli/src/helpers.ts b/code/lib/cli/src/helpers.ts index 167340059828..5659216412a7 100644 --- a/code/lib/cli/src/helpers.ts +++ b/code/lib/cli/src/helpers.ts @@ -15,6 +15,7 @@ import type { } from '@storybook/core-common'; import type { SupportedFrameworks } from '@storybook/types'; import type { SupportedRenderers } from './project_types'; +import { CoreBuilder } from './project_types'; import { SupportedLanguage } from './project_types'; import { versions as storybookMonorepoPackages } from '@storybook/core-common'; @@ -168,6 +169,30 @@ const frameworkToRenderer: Record< 'web-components': 'web-components', }; +export const frameworkToDefaultBuilder: Record = { + angular: CoreBuilder.Webpack5, + ember: CoreBuilder.Webpack5, + 'html-vite': CoreBuilder.Vite, + 'html-webpack5': CoreBuilder.Webpack5, + nextjs: CoreBuilder.Webpack5, + 'preact-vite': CoreBuilder.Vite, + 'preact-webpack5': CoreBuilder.Webpack5, + qwik: CoreBuilder.Vite, + 'react-vite': CoreBuilder.Vite, + 'react-webpack5': CoreBuilder.Webpack5, + 'server-webpack5': CoreBuilder.Webpack5, + solid: CoreBuilder.Vite, + 'svelte-vite': CoreBuilder.Vite, + 'svelte-webpack5': CoreBuilder.Webpack5, + sveltekit: CoreBuilder.Vite, + 'vue-vite': CoreBuilder.Vite, + 'vue-webpack5': CoreBuilder.Webpack5, + 'vue3-vite': CoreBuilder.Vite, + 'vue3-webpack5': CoreBuilder.Webpack5, + 'web-components-vite': CoreBuilder.Vite, + 'web-components-webpack5': CoreBuilder.Webpack5, +}; + export async function copyTemplateFiles({ packageManager, renderer, diff --git a/code/lib/cli/src/project_types.ts b/code/lib/cli/src/project_types.ts index e781b3e16db8..3a5cda3781ef 100644 --- a/code/lib/cli/src/project_types.ts +++ b/code/lib/cli/src/project_types.ts @@ -77,6 +77,21 @@ export enum CoreBuilder { Vite = 'vite', } +export enum CoreWebpackCompilers { + Babel = 'babel', + SWC = 'swc', +} + +export const compilerNameToCoreCompiler: Record = { + '@storybook/addon-webpack5-compiler-babel': CoreWebpackCompilers.Babel, + '@storybook/addon-webpack5-compiler-swc': CoreWebpackCompilers.SWC, +}; + +export const builderNameToCoreBuilder: Record = { + '@storybook/builder-webpack5': CoreBuilder.Webpack5, + '@storybook/builder-vite': CoreBuilder.Vite, +}; + // The `& {}` bit allows for auto-complete, see: https://github.com/microsoft/TypeScript/issues/29729 export type Builder = CoreBuilder | (string & {}); diff --git a/code/lib/types/src/modules/core-common.ts b/code/lib/types/src/modules/core-common.ts index 7a6bd8a99699..cc03d0ca7089 100644 --- a/code/lib/types/src/modules/core-common.ts +++ b/code/lib/types/src/modules/core-common.ts @@ -19,6 +19,9 @@ interface ServerChannel { } export interface CoreConfig { + /** + * @deprecated Use `framework.options.builder` instead. This will be removed in Storybook 9.0 + */ builder?: | BuilderName | { From 7c77bf9056d6f539f9b4daf2a17ccd25c3d374d0 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 12 Feb 2024 14:50:26 +0100 Subject: [PATCH 07/44] Adjust vitest.workspace.ts to fix Wallaby tooling --- code/vitest.workspace.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/vitest.workspace.ts b/code/vitest.workspace.ts index fda0ab58e871..c6d2efbc51ad 100644 --- a/code/vitest.workspace.ts +++ b/code/vitest.workspace.ts @@ -15,7 +15,7 @@ export default defineWorkspace([ export const vitestCommonConfig = defineConfig({ test: { clearMocks: true, - setupFiles: [resolve('./vitest-setup.ts')], + setupFiles: [resolve(__dirname, './vitest-setup.ts')], globals: true, }, }); From 02a7b1610d8a54588291b2bdede79eef017cd601 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 12 Feb 2024 14:52:25 +0100 Subject: [PATCH 08/44] Remove console.log --- code/lib/cli/src/automigrate/helpers/mainConfigFile.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts index 491bcbe219ef..2ba805c95614 100644 --- a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts +++ b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts @@ -60,8 +60,6 @@ export const getBuilderPackageName = (mainConfig?: StorybookConfigRaw) => { return null; } - console.log({ packageNameOrPath }); - const normalizedPath = path.normalize(packageNameOrPath).replace(new RegExp(/\\/, 'g'), '/'); return builderPackages.find((pkg) => normalizedPath.endsWith(pkg)) || packageNameOrPath; From 82355f657006fae41bbff4d6642a6ca115601f05 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 12 Feb 2024 14:53:09 +0100 Subject: [PATCH 09/44] Undo prompt awaiting --- code/lib/cli/src/automigrate/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/lib/cli/src/automigrate/index.ts b/code/lib/cli/src/automigrate/index.ts index f63e77e79773..3adeff5e0ead 100644 --- a/code/lib/cli/src/automigrate/index.ts +++ b/code/lib/cli/src/automigrate/index.ts @@ -231,7 +231,7 @@ export async function runFixes({ if (result) { logger.info(`\n🔎 found a '${chalk.cyan(f.id)}' migration:`); - const message = await f.prompt(result); + const message = f.prompt(result); logger.info( boxen(message, { From 14d3d12c7c07abe9783ce70f8f7ec46ffda05baf Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 12 Feb 2024 14:54:06 +0100 Subject: [PATCH 10/44] Remove unnecessary comment --- code/lib/types/scripts/generate-available-frameworks.js | 1 - 1 file changed, 1 deletion(-) diff --git a/code/lib/types/scripts/generate-available-frameworks.js b/code/lib/types/scripts/generate-available-frameworks.js index f3f0af79f6f1..dced66c06707 100644 --- a/code/lib/types/scripts/generate-available-frameworks.js +++ b/code/lib/types/scripts/generate-available-frameworks.js @@ -18,7 +18,6 @@ const run = async () => { .map((framework) => `'${framework}'`) .join(' | ')}; `; - // format fileContent by prettier const frameworksFile = path.join(__dirname, '..', 'src', 'modules', 'frameworks.ts'); const prettierConfig = await prettier.resolveConfig(frameworksFile); From 50ec83be51805490be5a15e2155633d1ae16763a Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 12 Feb 2024 14:54:42 +0100 Subject: [PATCH 11/44] Remove deprecation --- code/lib/types/src/modules/core-common.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/lib/types/src/modules/core-common.ts b/code/lib/types/src/modules/core-common.ts index cc03d0ca7089..7a6bd8a99699 100644 --- a/code/lib/types/src/modules/core-common.ts +++ b/code/lib/types/src/modules/core-common.ts @@ -19,9 +19,6 @@ interface ServerChannel { } export interface CoreConfig { - /** - * @deprecated Use `framework.options.builder` instead. This will be removed in Storybook 9.0 - */ builder?: | BuilderName | { From 074d5daca1c94729455e05f7f7ebc9abdf21b80c Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Mon, 12 Feb 2024 15:06:05 +0100 Subject: [PATCH 12/44] Add remove argtypes-regex automigration --- code/lib/cli/package.json | 1 + code/lib/cli/src/automigrate/fixes/index.ts | 2 + .../fixes/remove-argtypes-regex.ts | 78 +++++++++++++++++++ code/lib/codemod/package.json | 2 + .../src/transforms/find-implicit-spies.ts | 50 ++++++++++++ code/yarn.lock | 1 + 6 files changed, 134 insertions(+) create mode 100644 code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts create mode 100644 code/lib/codemod/src/transforms/find-implicit-spies.ts diff --git a/code/lib/cli/package.json b/code/lib/cli/package.json index 2ea8e20d58b0..d691e0323f7f 100644 --- a/code/lib/cli/package.json +++ b/code/lib/cli/package.json @@ -56,6 +56,7 @@ "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { + "@babel/core": "^7.23.0", "@babel/types": "^7.23.0", "@ndelangen/get-tarball": "^3.0.7", "@storybook/codemod": "workspace:*", diff --git a/code/lib/cli/src/automigrate/fixes/index.ts b/code/lib/cli/src/automigrate/fixes/index.ts index 27f8a80b3140..ff060341a7c8 100644 --- a/code/lib/cli/src/automigrate/fixes/index.ts +++ b/code/lib/cli/src/automigrate/fixes/index.ts @@ -20,6 +20,7 @@ import { wrapRequire } from './wrap-require'; import { reactDocgen } from './react-docgen'; import { removeReactDependency } from './prompt-remove-react'; import { storyshotsMigration } from './storyshots-migration'; +import { removeArgtypesRegex } from './remove-argtypes-regex'; export * from '../types'; @@ -34,6 +35,7 @@ export const allFixes: Fix[] = [ sbBinary, sbScripts, incompatibleAddons, + removeArgtypesRegex, removedGlobalClientAPIs, mdx1to2, mdxgfm, diff --git a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts new file mode 100644 index 000000000000..5bdc46071881 --- /dev/null +++ b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts @@ -0,0 +1,78 @@ +import type { Fix } from '../types'; +import * as fs from 'node:fs/promises'; +import * as babel from '@babel/core'; +import { BabelFile, NodePath } from '@babel/core'; +import { babelParse } from '@storybook/csf-tools'; +import dedent from 'ts-dedent'; +import chalk from 'chalk'; + +export const removeArgtypesRegex: Fix<{ argTypesRegex: NodePath; previewConfigPath: string }> = { + id: 'remove-argtypes-regex', + promptOnly: true, + async check({ previewConfigPath }) { + if (!previewConfigPath) return null; + + const previewFile = await fs.readFile(previewConfigPath, { encoding: 'utf-8' }); + + // @ts-expect-error File is not yet exposed, see https://github.com/babel/babel/issues/11350#issuecomment-644118606 + const file: BabelFile = new babel.File( + { filename: previewConfigPath }, + { code: previewFile, ast: babelParse(previewFile) } + ); + + let argTypesRegex; + + file.path.traverse({ + Identifier: (path) => { + if (path.node.name === 'argTypesRegex') { + argTypesRegex = path; + } + }, + }); + + return argTypesRegex ? { argTypesRegex, previewConfigPath } : null; + }, + prompt({ argTypesRegex, previewConfigPath }) { + const snippet = dedent` + import { fn } from '@storybook/test'; + export default { + args: { onClick: fn() }, // will log to the action panel when clicked + };`; + + // @ts-expect-error File is not yet exposed, see https://github.com/babel/babel/issues/11350#issuecomment-644118606 + const file: BabelFile = new babel.File( + { file: 'story.tsx' }, + { code: snippet, ast: babelParse(snippet) } + ); + + let formattedSnippet; + file.path.traverse({ + Identifier: (path) => { + if (path.node.name === 'fn') { + formattedSnippet = path.buildCodeFrameError(``).message; + } + }, + }); + + return dedent` + ${chalk.bold('Attention')}: We've detected that you're using argTypesRegex: + + ${argTypesRegex.buildCodeFrameError(`${previewConfigPath}`).message} + + In Storybook 8, we recommend removing this regex, and assigning explicit spies with the ${chalk.cyan( + 'fn' + )} function instead: + ${formattedSnippet} + + Even if you keep using the argTypesRegex, you will need to above pattern if you want to use spies in your play function. + Implicit spies (based on a combination of argTypesRegex and docgen) is not supported anymore in Storybook 8. + + Use the following command to check for spy usages in your play functions: + ${chalk.cyan( + 'npx storybook migrate find-implicit-spies --glob="**/*.stories.@(js|jsx|ts|tsx)"' + )} + + Make sure to assign an explicit ${chalk.cyan('fn')} to your args for those usages. + `; + }, +}; diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json index 04fa46508190..303ab36f49de 100644 --- a/code/lib/codemod/package.json +++ b/code/lib/codemod/package.json @@ -29,6 +29,7 @@ "./dist/transforms/add-component-parameters.js": "./dist/transforms/add-component-parameters.js", "./dist/transforms/csf-2-to-3.js": "./dist/transforms/csf-2-to-3.js", "./dist/transforms/csf-hoist-story-annotations.js": "./dist/transforms/csf-hoist-story-annotations.js", + "./dist/transforms/find-implicit-spies.js": "./dist/transforms/find-implicit-spies.js", "./dist/transforms/move-builtin-addons.js": "./dist/transforms/move-builtin-addons.js", "./dist/transforms/mdx-to-csf.js": "./dist/transforms/mdx-to-csf.js", "./dist/transforms/storiesof-to-csf.js": "./dist/transforms/storiesof-to-csf.js", @@ -92,6 +93,7 @@ "./src/transforms/add-component-parameters.js", "./src/transforms/csf-2-to-3.ts", "./src/transforms/csf-hoist-story-annotations.js", + "./src/transforms/find-implicit-spies.ts", "./src/transforms/mdx-to-csf.ts", "./src/transforms/move-builtin-addons.js", "./src/transforms/storiesof-to-csf.js", diff --git a/code/lib/codemod/src/transforms/find-implicit-spies.ts b/code/lib/codemod/src/transforms/find-implicit-spies.ts new file mode 100644 index 000000000000..594bb1cfc1c8 --- /dev/null +++ b/code/lib/codemod/src/transforms/find-implicit-spies.ts @@ -0,0 +1,50 @@ +/* eslint-disable no-underscore-dangle */ +import type { FileInfo } from 'jscodeshift'; +import { loadCsf } from '@storybook/csf-tools'; +import type { BabelFile } from '@babel/core'; +import * as babel from '@babel/core'; + +function findImplicitSpies(path: babel.NodePath, file: string) { + path.traverse({ + Identifier: (identifier) => { + if (/^on[A-Z].*/.test(identifier.node.name)) { + console.warn(identifier.buildCodeFrameError(`${file} Possible implicit spy found`).message); + } + }, + }); +} + +export default async function transform(info: FileInfo) { + const csf = loadCsf(info.source, { makeTitle: (title) => title }); + const fileNode = csf._ast; + // @ts-expect-error File is not yet exposed, see https://github.com/babel/babel/issues/11350#issuecomment-644118606 + const file: BabelFile = new babel.File( + { filename: info.path }, + { code: info.source, ast: fileNode } + ); + + file.path.traverse({ + // CSF2 play function Story.play = + AssignmentExpression: (path) => { + const left = path.get('left'); + if (!left.isMemberExpression()) return; + + const property = left.get('property'); + if (property.isIdentifier() && property.node.name === 'play') { + findImplicitSpies(path, info.path); + } + }, + + // CSF3 play function: const Story = {play: () => {} }; + ObjectProperty: (path) => { + const key = path.get('key'); + if (key.isIdentifier() && key.node.name === 'play') { + findImplicitSpies(path, info.path); + } + }, + }); + + return; +} + +export const parser = 'tsx'; diff --git a/code/yarn.lock b/code/yarn.lock index 755a51216c17..d8200e4a3cd4 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5354,6 +5354,7 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/cli@workspace:lib/cli" dependencies: + "@babel/core": "npm:^7.23.0" "@babel/types": "npm:^7.23.0" "@ndelangen/get-tarball": "npm:^3.0.7" "@storybook/codemod": "workspace:*" From ce77d9f97e220af288d151e510f0383afb136522 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Mon, 12 Feb 2024 15:27:28 +0100 Subject: [PATCH 13/44] Add a test --- .../__tests__/find-implicit-spies.test.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 code/lib/codemod/src/transforms/__tests__/find-implicit-spies.test.ts diff --git a/code/lib/codemod/src/transforms/__tests__/find-implicit-spies.test.ts b/code/lib/codemod/src/transforms/__tests__/find-implicit-spies.test.ts new file mode 100644 index 000000000000..0d4cbbe3cee6 --- /dev/null +++ b/code/lib/codemod/src/transforms/__tests__/find-implicit-spies.test.ts @@ -0,0 +1,43 @@ +import { beforeEach, expect, test, vi } from 'vitest'; +import transform from '../find-implicit-spies'; +import dedent from 'ts-dedent'; +import ansiRegex from 'ansi-regex'; + +expect.addSnapshotSerializer({ + print: (val, print) => print((val as string).replace(ansiRegex(), '')), + test: (value) => typeof value === 'string' && ansiRegex().test(value), +}); + +const tsTransform = async (source: string) => transform({ source, path: 'Component.stories.tsx' }); + +const warn = vi.spyOn(console, 'warn'); + +beforeEach(() => { + warn.mockImplementation(() => {}); +}); + +test('replace jest and testing-library with the test package', async () => { + const input = dedent` + Interactions.play = async ({ args }) => { + await userEvent.click(screen.getByRole("button")); + await expect(args.onButtonIconClick).toHaveBeenCalled(); + }; + + `; + + await tsTransform(input); + + expect(warn.mock.calls).toMatchInlineSnapshot(` + [ + [ + "Component.stories.tsx Possible implicit spy found + 1 | Interactions.play = async ({ args }) => { + 2 | await userEvent.click(screen.getByRole("button")); + > 3 | await expect(args.onButtonIconClick).toHaveBeenCalled(); + | ^^^^^^^^^^^^^^^^^ + 4 | }; + 5 |", + ], + ] + `); +}); From dcb87ef8fbb929fb4134bcc9dbf09d0cef6e9c4c Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Mon, 12 Feb 2024 15:43:47 +0100 Subject: [PATCH 14/44] Fix eslint --- code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts index 5bdc46071881..153d988630c9 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts @@ -1,7 +1,7 @@ import type { Fix } from '../types'; import * as fs from 'node:fs/promises'; import * as babel from '@babel/core'; -import { BabelFile, NodePath } from '@babel/core'; +import type { BabelFile, NodePath } from '@babel/core'; import { babelParse } from '@storybook/csf-tools'; import dedent from 'ts-dedent'; import chalk from 'chalk'; From 24f232dd4b981da83f59a313966bacc2e0e8b785 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 12 Feb 2024 16:11:52 +0100 Subject: [PATCH 15/44] Update frameworks --- code/lib/types/src/modules/frameworks.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/lib/types/src/modules/frameworks.ts b/code/lib/types/src/modules/frameworks.ts index 0785763f46e5..c1ea25abe7b0 100644 --- a/code/lib/types/src/modules/frameworks.ts +++ b/code/lib/types/src/modules/frameworks.ts @@ -14,8 +14,6 @@ export type SupportedFrameworks = | 'svelte-vite' | 'svelte-webpack5' | 'sveltekit' - | 'vue-vite' - | 'vue-webpack5' | 'vue3-vite' | 'vue3-webpack5' | 'web-components-vite' From 000b7dec927df519735cb8bb67fb0dcf9d46aaff Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 12 Feb 2024 16:56:29 +0100 Subject: [PATCH 16/44] Update helpers to remove vue-vite vue-webpack5 keys from frameworkToRenderer --- code/lib/cli/src/helpers.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/code/lib/cli/src/helpers.ts b/code/lib/cli/src/helpers.ts index 5659216412a7..09249f2e5523 100644 --- a/code/lib/cli/src/helpers.ts +++ b/code/lib/cli/src/helpers.ts @@ -152,8 +152,6 @@ const frameworkToRenderer: Record< 'svelte-vite': 'svelte', 'svelte-webpack5': 'svelte', sveltekit: 'svelte', - 'vue-vite': 'vue', - 'vue-webpack5': 'vue', 'vue3-vite': 'vue3', 'vue3-webpack5': 'vue3', 'web-components-vite': 'web-components', @@ -185,8 +183,6 @@ export const frameworkToDefaultBuilder: Record 'svelte-vite': CoreBuilder.Vite, 'svelte-webpack5': CoreBuilder.Webpack5, sveltekit: CoreBuilder.Vite, - 'vue-vite': CoreBuilder.Vite, - 'vue-webpack5': CoreBuilder.Webpack5, 'vue3-vite': CoreBuilder.Vite, 'vue3-webpack5': CoreBuilder.Webpack5, 'web-components-vite': CoreBuilder.Vite, From dbd4ea5a10f43755a4f95632b271afb9309332ae Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Tue, 13 Feb 2024 10:55:59 +0100 Subject: [PATCH 17/44] Pass shouldRemoveSWCFlag option also when Next.js is detected --- .../fixes/webpack5-compiler-setup.test.ts | 25 ++++++++++++++++ .../fixes/webpack5-compiler-setup.ts | 30 +++++++++++-------- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts index 580a5b8324e6..d7f7e9564389 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts @@ -236,6 +236,31 @@ describe('check function', () => { }); }); + it('should return isNextJs: true AND should return shouldRemoveSWCFlag: true when useSWC flag is set', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/nextjs', + options: { + builder: { + useSWC: true, + }, + }, + }, + }, + }); + + expect(result).contains({ + shouldRemoveSWCFlag: true, + isNextJs: true, + }); + }); + it('should return isNextJs: false when the framework is not nextjs', async () => { const result = await check({ packageManager: { diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts index b895d4b6ef12..b72e21ac2ca1 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts @@ -81,11 +81,15 @@ export const webpack5Migration: Fix = { ([, coreCompiler]) => coreCompiler === compiler )?.[0]; + const shouldRemoveSWCFlag = frameworkOptions?.builder + ? 'useSWC' in frameworkOptions.builder + : false; + if (frameworkName === 'nextjs') { return { compiler: undefined, compilerPackageName: undefined, - shouldRemoveSWCFlag: false, + shouldRemoveSWCFlag, isNextJs: true, }; } @@ -93,7 +97,7 @@ export const webpack5Migration: Fix = { return { compiler, compilerPackageName, - shouldRemoveSWCFlag: frameworkOptions?.builder ? 'useSWC' in frameworkOptions.builder : false, + shouldRemoveSWCFlag, isNextJs: false, }; }, @@ -109,16 +113,18 @@ export const webpack5Migration: Fix = { if (isNextJs) { message.push(dedent` - Storybook now detects whether it should use Babel or SWC as a compiler by applying the same logic as Next.js itself. - If you have a ${chalk.yellow( - 'babel.config.js' - )} file in your project, Storybook will use Babel as the compiler. - If you have a ${chalk.yellow( - 'babel.config.js' - )} file in your project and you have set ${chalk.yellow( - 'experimental.forceSwcTransforms = true' - )} in your next.config.js file, Storybook will use SWC as the compiler. - If you don't have a babel.config.js file in your project, Storybook will use SWC as the compiler. + Storybook now detects whether it should use Babel or SWC as a compiler by applying the same logic as Next.js itself:\n + - If you have a ${chalk.yellow( + 'babel.config.js' + )} file in your project, Storybook will use Babel as the compiler. + - If you have a ${chalk.yellow('babel.config.js')} file in your project and you have set + ${chalk.yellow('experimental.forceSwcTransforms = true')} in your ${chalk.yellow( + 'next.config.js' + )} file, + Storybook will use SWC as the compiler. + - If you don't have a ${chalk.yellow( + 'babel.config.js' + )} file in your project, Storybook will use SWC as the compiler. `); } else if (compilerPackageName) { message.push(dedent` From 359f4dc8dbc7b2a25cdbad25ee65105f1dd957c2 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Tue, 13 Feb 2024 12:55:16 +0100 Subject: [PATCH 18/44] Update snapshot --- .../automigrate/fixes/webpack5-compiler-setup.test.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts index d7f7e9564389..1de040d2a6c6 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts @@ -426,10 +426,13 @@ describe('check function', () => { "We need to update your Storybook configuration for Webpack 5. The framework.options.builder.useSWC flag will be removed. - Storybook now detects whether it should use Babel or SWC as a compiler by applying the same logic as Next.js itself. - If you have a babel.config.js file in your project, Storybook will use Babel as the compiler. - If you have a babel.config.js file in your project and you have set experimental.forceSwcTransforms = true in your next.config.js file, Storybook will use SWC as the compiler. - If you don't have a babel.config.js file in your project, Storybook will use SWC as the compiler." + Storybook now detects whether it should use Babel or SWC as a compiler by applying the same logic as Next.js itself: + + - If you have a babel.config.js file in your project, Storybook will use Babel as the compiler. + - If you have a babel.config.js file in your project and you have set + experimental.forceSwcTransforms = true in your next.config.js file, + Storybook will use SWC as the compiler. + - If you don't have a babel.config.js file in your project, Storybook will use SWC as the compiler." `); }); From 64a160b7db2b44c0b02482e48374a60e5ebd4947 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Tue, 13 Feb 2024 13:59:01 +0100 Subject: [PATCH 19/44] Text formatting --- .../cli/src/automigrate/fixes/webpack5-compiler-setup.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts index b72e21ac2ca1..1411b30ddfcf 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts @@ -163,9 +163,9 @@ async function askUserForCompilerChoice() { const response = await prompts<'compiler'>({ type: 'select', name: 'compiler', - message: `Storybook's Webpack5 builder is now compiler agnostic, meaning you can choose a compiler addon that best fits your project: -Babel - A vast ecosystem and is battle-tested. It's a robust choice if you have an extensive Babel setup or need specific Babel plugins for your project. -SWC - Fast and easy to configure. Ideal if you want faster builds and have a straightforward configuration without the need for Babel's extensibility. + message: `Storybook's Webpack5 builder is now compiler agnostic, meaning you can choose a compiler addon that best fits your project:\n + - Babel: A vast ecosystem and is battle-tested. It's a robust choice if you have an extensive Babel setup or need specific Babel plugins for your project. + - SWC: Fast and easy to configure. Ideal if you want faster builds and have a straightforward configuration without the need for Babel's extensibility.\n Which compiler would you like to use?`, choices: [ { From e08fa518a9b48b8ad6c1163323aa08ab61bbc28c Mon Sep 17 00:00:00 2001 From: jonniebigodes Date: Thu, 15 Feb 2024 14:22:26 +0000 Subject: [PATCH 20/44] Feedback addressed --- code/lib/preview-api/README-client-api.md | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/code/lib/preview-api/README-client-api.md b/code/lib/preview-api/README-client-api.md index 84861e4f2ca9..80a88f3b0cab 100644 --- a/code/lib/preview-api/README-client-api.md +++ b/code/lib/preview-api/README-client-api.md @@ -1,18 +1,3 @@ # `@storybook/client-api` -- Removed Story APIs (`storiesOf`) -**NOTE** This API is deprecated, and the CSF format is preferred for all stories. - -## `storiesOf` API - -The `@storybook/client` package provided backward compatibility for the `storiesOf` API supported until the previous stable Storybook version. With the release of Storybook 8 the `storiesOf` API was removed and is no longer supported or maintained. - - - -### Internals - -In order to appear to the store like the CSF API, a call to `storiesOf().add()` does the following: - -- Tracks the story added in a synthetic `StoryIndex` data structure -- Constructs a `moduleExports` object that is equivalent to the exports from a CSF file that produced the same stories. - -In order to achieve the old `storySort` functionality, the client API also needs access to the project annotations. +**NOTE** This API is deprecated, and the CSF format is preferred for all stories. \ No newline at end of file From 5131e1f4a85176bd7afb1c6049bd02ee89279bfe Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Fri, 16 Feb 2024 11:54:13 +1100 Subject: [PATCH 21/44] Fix bug that meant we always warned about TS plugin --- code/builders/builder-vite/src/build.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/builders/builder-vite/src/build.ts b/code/builders/builder-vite/src/build.ts index ebf4f030e8b8..1cd14d1b2d8d 100644 --- a/code/builders/builder-vite/src/build.ts +++ b/code/builders/builder-vite/src/build.ts @@ -41,7 +41,7 @@ export async function build(options: Options) { const turbosnapPluginName = 'rollup-plugin-turbosnap'; const hasTurbosnapPlugin = - finalConfig.plugins && hasVitePlugins(finalConfig.plugins, [turbosnapPluginName]); + finalConfig.plugins && (await hasVitePlugins(finalConfig.plugins, [turbosnapPluginName])); if (hasTurbosnapPlugin) { logger.warn(dedent`Found '${turbosnapPluginName}' which is now included by default in Storybook 8. Removing from your plugins list. Ensure you pass \`--webpack-stats-json\` to generate stats. From 65916d37508fcab983497ff604b16e6e59cef5ff Mon Sep 17 00:00:00 2001 From: jonniebigodes Date: Fri, 16 Feb 2024 12:52:57 +0000 Subject: [PATCH 22/44] additional feedback addressed --- code/lib/preview-api/README-client-api.md | 3 --- code/lib/preview-api/README.md | 1 - docs/faq.md | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) delete mode 100644 code/lib/preview-api/README-client-api.md diff --git a/code/lib/preview-api/README-client-api.md b/code/lib/preview-api/README-client-api.md deleted file mode 100644 index 80a88f3b0cab..000000000000 --- a/code/lib/preview-api/README-client-api.md +++ /dev/null @@ -1,3 +0,0 @@ -# `@storybook/client-api` -- Removed Story APIs (`storiesOf`) - -**NOTE** This API is deprecated, and the CSF format is preferred for all stories. \ No newline at end of file diff --git a/code/lib/preview-api/README.md b/code/lib/preview-api/README.md index 864bbbb11e7f..cb5788bbb0f4 100644 --- a/code/lib/preview-api/README.md +++ b/code/lib/preview-api/README.md @@ -7,7 +7,6 @@ TODO write proper documentation of this package This package used to be multiple packages (they have been combined into this one): - `@storybook/addons` [read (old) docs](./README-addons.md) -- `@storybook/client-api` [read (old) docs](./README-client-api.md) - `@storybook/core-client` [read (old) docs](./README-core-client.md) - `@storybook/preview-web` [read (old) docs](./README-preview-web.md) - `@storybook/store` [read (old) docs](./README-store.md) diff --git a/docs/faq.md b/docs/faq.md index 895f556160d6..1cceba208102 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -333,7 +333,7 @@ We're only covering versions 5.3 and 5.0 as they were important milestones for S -If you have stories written with the older `storiesOf` format, it was removed in Storybook 8.0 and is no longer maintained. We recommend that you migrate your stories to CSF. See the [migration guide](./migration-guide.md#storiesof-to-csf) for more information. However, if you need, you can still access the old documentation for `storiesOf` [here](https://github.com/storybookjs/storybook/blob/release/5.3/docs/src/pages/formats/storiesof-api/index.md). +If you have stories written with the older `storiesOf` format, it was removed in Storybook 8.0 and is no longer maintained. We recommend that you migrate your stories to CSF. See the [migration guide](./migration-guide.md#storiesof-to-csf) for more information. However, if you need, you can still access the old `storiesOf` [documentation](https://github.com/storybookjs/storybook/blob/release/5.3/docs/src/pages/formats/storiesof-api/index.md) for reference. From c9e988fa5cab2f5afe3e33ce805bfe1e879633c2 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 16 Feb 2024 14:45:21 +0100 Subject: [PATCH 23/44] Update webpack5Migration to webpack5CompilerSetup --- code/lib/cli/src/automigrate/fixes/index.ts | 4 ++-- .../fixes/webpack5-compiler-setup.test.ts | 14 +++++++------- .../automigrate/fixes/webpack5-compiler-setup.ts | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/index.ts b/code/lib/cli/src/automigrate/fixes/index.ts index 12006cd619b3..d1612045cd7f 100644 --- a/code/lib/cli/src/automigrate/fixes/index.ts +++ b/code/lib/cli/src/automigrate/fixes/index.ts @@ -21,7 +21,7 @@ import { wrapRequire } from './wrap-require'; import { reactDocgen } from './react-docgen'; import { removeReactDependency } from './prompt-remove-react'; import { storyshotsMigration } from './storyshots-migration'; -import { webpack5Migration } from './webpack5-compiler-setup'; +import { webpack5CompilerSetup } from './webpack5-compiler-setup'; import { removeJestTestingLibrary } from './remove-jest-testing-library'; export * from '../types'; @@ -49,7 +49,7 @@ export const allFixes: Fix[] = [ reactDocgen, storyshotsMigration, removeReactDependency, - webpack5Migration, + webpack5CompilerSetup, ]; export const initFixes: Fix[] = [eslintPlugin]; diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts index 1de040d2a6c6..a7768b9b10be 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts @@ -1,7 +1,7 @@ import { vi, describe, it, expect, afterEach, beforeEach } from 'vitest'; import type { StorybookConfig } from '@storybook/types'; import type { JsPackageManager } from '@storybook/core-common'; -import { webpack5Migration } from './webpack5-compiler-setup'; +import { webpack5CompilerSetup } from './webpack5-compiler-setup'; import { CoreWebpackCompilers } from '../../project_types'; const check = async ({ @@ -14,7 +14,7 @@ const check = async ({ storybookVersion?: string; mainConfig?: Partial; }) => { - return webpack5Migration.check({ + return webpack5CompilerSetup.check({ packageManager: packageManager as any, configDir: '', storybookVersion, @@ -382,7 +382,7 @@ describe('check function', () => { describe('prompt', () => { it('shouldRemoveSWCFlag = true', async () => { - const prompt = webpack5Migration.prompt({ + const prompt = webpack5CompilerSetup.prompt({ shouldRemoveSWCFlag: true, isNextJs: false, compilerPackageName: '@storybook/addon-webpack5-compiler-swc', @@ -400,7 +400,7 @@ describe('check function', () => { }); it('shouldRemoveSWCFlag = false', async () => { - const prompt = webpack5Migration.prompt({ + const prompt = webpack5CompilerSetup.prompt({ shouldRemoveSWCFlag: false, isNextJs: false, compilerPackageName: '@storybook/addon-webpack5-compiler-swc', @@ -415,7 +415,7 @@ describe('check function', () => { }); it('isNextJs = true', () => { - const prompt = webpack5Migration.prompt({ + const prompt = webpack5CompilerSetup.prompt({ shouldRemoveSWCFlag: true, isNextJs: true, compilerPackageName: undefined, @@ -437,7 +437,7 @@ describe('check function', () => { }); it('isNextjs = false AND compilerPackageName = @storybook/addon-webpack5-compiler-swc AND compiler = swc', () => { - const prompt = webpack5Migration.prompt({ + const prompt = webpack5CompilerSetup.prompt({ shouldRemoveSWCFlag: false, isNextJs: false, compilerPackageName: '@storybook/addon-webpack5-compiler-swc', @@ -452,7 +452,7 @@ describe('check function', () => { }); it('isNextjs = false AND compilerPackageName = @storybook/addon-webpack5-compiler-babel AND compiler = babel', () => { - const prompt = webpack5Migration.prompt({ + const prompt = webpack5CompilerSetup.prompt({ shouldRemoveSWCFlag: false, isNextJs: false, compilerPackageName: '@storybook/addon-webpack5-compiler-babel', diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts index 1411b30ddfcf..647707c9d10c 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts @@ -26,7 +26,7 @@ type Options = { isNextJs: boolean; }; -export const webpack5Migration: Fix = { +export const webpack5CompilerSetup: Fix = { id: 'webpack5-compiler-setup', async check({ mainConfig, packageManager }) { From 5b988d0f35072a4da18d9eec65db110a3b4a8b96 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 16 Feb 2024 14:53:16 +0100 Subject: [PATCH 24/44] Don't warn if explicit arg or argType is found --- .../__tests__/find-implicit-spies.test.ts | 48 +++++-- .../src/transforms/find-implicit-spies.ts | 132 +++++++++++++++--- 2 files changed, 151 insertions(+), 29 deletions(-) diff --git a/code/lib/codemod/src/transforms/__tests__/find-implicit-spies.test.ts b/code/lib/codemod/src/transforms/__tests__/find-implicit-spies.test.ts index 0d4cbbe3cee6..c3885605e8ed 100644 --- a/code/lib/codemod/src/transforms/__tests__/find-implicit-spies.test.ts +++ b/code/lib/codemod/src/transforms/__tests__/find-implicit-spies.test.ts @@ -16,13 +16,30 @@ beforeEach(() => { warn.mockImplementation(() => {}); }); -test('replace jest and testing-library with the test package', async () => { +test('Warn for possible implicit actions', async () => { const input = dedent` - Interactions.play = async ({ args }) => { + export default { title: 'foo/bar', args: {onClick: fn() }, argTypes: { onHover: {action: true} } }; + const Template = (args) => { }; + export const A = Template.bind({}); + A.args = { onBla: fn() }; + A.play = async ({ args }) => { await userEvent.click(screen.getByRole("button")); - await expect(args.onButtonIconClick).toHaveBeenCalled(); + await expect(args.onImplicit).toHaveBeenCalled(); + await expect(args.onClick).toHaveBeenCalled(); + await expect(args.onHover).toHaveBeenCalled(); + await expect(args.onBla).toHaveBeenCalled(); + }; + + export const B = { + args: {onBla: fn() }, + play: async ({ args }) => { + await userEvent.click(screen.getByRole("button")); + await expect(args.onImplicit).toHaveBeenCalled(); + await expect(args.onClick).toHaveBeenCalled(); + await expect(args.onHover).toHaveBeenCalled(); + await expect(args.onBla).toHaveBeenCalled(); + } }; - `; await tsTransform(input); @@ -31,12 +48,23 @@ test('replace jest and testing-library with the test package', async () => { [ [ "Component.stories.tsx Possible implicit spy found - 1 | Interactions.play = async ({ args }) => { - 2 | await userEvent.click(screen.getByRole("button")); - > 3 | await expect(args.onButtonIconClick).toHaveBeenCalled(); - | ^^^^^^^^^^^^^^^^^ - 4 | }; - 5 |", + 5 | A.play = async ({ args }) => { + 6 | await userEvent.click(screen.getByRole("button")); + > 7 | await expect(args.onImplicit).toHaveBeenCalled(); + | ^^^^^^^^^^ + 8 | await expect(args.onClick).toHaveBeenCalled(); + 9 | await expect(args.onHover).toHaveBeenCalled(); + 10 | await expect(args.onBla).toHaveBeenCalled();", + ], + [ + "Component.stories.tsx Possible implicit spy found + 15 | play: async ({ args }) => { + 16 | await userEvent.click(screen.getByRole("button")); + > 17 | await expect(args.onImplicit).toHaveBeenCalled(); + | ^^^^^^^^^^ + 18 | await expect(args.onClick).toHaveBeenCalled(); + 19 | await expect(args.onHover).toHaveBeenCalled(); + 20 | await expect(args.onBla).toHaveBeenCalled();", ], ] `); diff --git a/code/lib/codemod/src/transforms/find-implicit-spies.ts b/code/lib/codemod/src/transforms/find-implicit-spies.ts index 594bb1cfc1c8..558cde383aa7 100644 --- a/code/lib/codemod/src/transforms/find-implicit-spies.ts +++ b/code/lib/codemod/src/transforms/find-implicit-spies.ts @@ -3,47 +3,141 @@ import type { FileInfo } from 'jscodeshift'; import { loadCsf } from '@storybook/csf-tools'; import type { BabelFile } from '@babel/core'; import * as babel from '@babel/core'; +import { isIdentifier, isObjectExpression, isObjectProperty } from '@babel/types'; -function findImplicitSpies(path: babel.NodePath, file: string) { +function findImplicitSpies(path: babel.NodePath, file: string, keys: string[]) { path.traverse({ Identifier: (identifier) => { - if (/^on[A-Z].*/.test(identifier.node.name)) { + if (!keys.includes(identifier.node.name) && /^on[A-Z].*/.test(identifier.node.name)) { console.warn(identifier.buildCodeFrameError(`${file} Possible implicit spy found`).message); } }, }); } -export default async function transform(info: FileInfo) { - const csf = loadCsf(info.source, { makeTitle: (title) => title }); - const fileNode = csf._ast; - // @ts-expect-error File is not yet exposed, see https://github.com/babel/babel/issues/11350#issuecomment-644118606 - const file: BabelFile = new babel.File( - { filename: info.path }, - { code: info.source, ast: fileNode } - ); +function getAnnotationKeys(file: BabelFile, storyName: string, annotationName: string) { + const argKeys: string[] = []; file.path.traverse({ - // CSF2 play function Story.play = + // CSF2 play function Story.args = AssignmentExpression: (path) => { const left = path.get('left'); if (!left.isMemberExpression()) return; + const object = left.get('object'); + + if (!(object.isIdentifier() && object.node.name === storyName)) return; const property = left.get('property'); - if (property.isIdentifier() && property.node.name === 'play') { - findImplicitSpies(path, info.path); + const right = path.get('right'); + if ( + property.isIdentifier() && + property.node.name === annotationName && + right.isObjectExpression() + ) { + argKeys.push( + ...right.node.properties.flatMap((value) => + isObjectProperty(value) && isIdentifier(value.key) ? [value.key.name] : [] + ) + ); } }, + // CSF3 const Story = {args: () => {} }; + VariableDeclarator: (path) => { + const id = path.get('id'); + const init = path.get('init'); + if (!(id.isIdentifier() && id.node.name === storyName) || !init.isObjectExpression()) return; - // CSF3 play function: const Story = {play: () => {} }; - ObjectProperty: (path) => { - const key = path.get('key'); - if (key.isIdentifier() && key.node.name === 'play') { - findImplicitSpies(path, info.path); - } + const args = init + .get('properties') + .flatMap((it) => (it.isObjectProperty() ? [it] : [])) + .find((it) => { + const argKey = it.get('key'); + return argKey.isIdentifier() && argKey.node.name === annotationName; + }); + + if (!args) return; + const argsValue = args.get('value'); + + if (!argsValue || !argsValue.isObjectExpression()) return; + argKeys.push( + ...argsValue.node.properties.flatMap((value) => + isObjectProperty(value) && isIdentifier(value.key) ? [value.key.name] : [] + ) + ); }, }); + return argKeys; +} + +const getObjectExpressionKeys = (node: babel.Node | undefined) => { + return isObjectExpression(node) + ? node.properties.flatMap((value) => + isObjectProperty(value) && isIdentifier(value.key) ? [value.key.name] : [] + ) + : []; +}; + +export default async function transform(info: FileInfo) { + const csf = loadCsf(info.source, { makeTitle: (title) => title }); + const fileNode = csf._ast; + // @ts-expect-error File is not yet exposed, see https://github.com/babel/babel/issues/11350#issuecomment-644118606 + const file: BabelFile = new babel.File( + { filename: info.path }, + { code: info.source, ast: fileNode } + ); + + csf.parse(); + + const metaKeys = [ + ...getObjectExpressionKeys(csf._metaAnnotations.args), + ...getObjectExpressionKeys(csf._metaAnnotations.argTypes), + ]; + + Object.entries(csf.stories).forEach(([key, { name }]) => { + if (!name) return; + const allKeys = [ + ...metaKeys, + ...getAnnotationKeys(file, name, 'args'), + ...getAnnotationKeys(file, name, 'argTypes'), + ]; + + file.path.traverse({ + // CSF2 play function Story.play = + AssignmentExpression: (path) => { + const left = path.get('left'); + if (!left.isMemberExpression()) return; + const object = left.get('object'); + + if (!(object.isIdentifier() && object.node.name === name)) return; + + const property = left.get('property'); + if (property.isIdentifier() && property.node.name === 'play') { + findImplicitSpies(path, info.path, allKeys); + } + }, + + // CSF3 play function: const Story = {play: () => {} }; + VariableDeclarator: (path) => { + const id = path.get('id'); + const init = path.get('init'); + if (!(id.isIdentifier() && id.node.name === name) || !init.isObjectExpression()) return; + + const play = init + .get('properties') + .flatMap((it) => (it.isObjectProperty() ? [it] : [])) + .find((it) => { + const argKey = it.get('key'); + return argKey.isIdentifier() && argKey.node.name === 'play'; + }); + + if (play) { + findImplicitSpies(play, info.path, allKeys); + } + }, + }); + }); + return; } From 6261cdfd59a560f01e7a161674d1d22a6f37203c Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 16 Feb 2024 15:30:33 +0100 Subject: [PATCH 25/44] Prompt the user at the end to select a specific compiler --- .../fixes/webpack5-compiler-setup.test.ts | 161 ++++++++++-------- .../fixes/webpack5-compiler-setup.ts | 66 ++++--- 2 files changed, 132 insertions(+), 95 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts index a7768b9b10be..0b0ddc98e2fd 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts @@ -1,4 +1,4 @@ -import { vi, describe, it, expect, afterEach, beforeEach } from 'vitest'; +import { vi, describe, it, expect } from 'vitest'; import type { StorybookConfig } from '@storybook/types'; import type { JsPackageManager } from '@storybook/core-common'; import { webpack5CompilerSetup } from './webpack5-compiler-setup'; @@ -46,17 +46,30 @@ vi.mock('chalk', () => { describe('check function', () => { describe('webpack5Migration check function', () => { - beforeEach(() => { - promptMocks.default.mockResolvedValue({ - compiler: 'swc', - }); - }); + describe('return null', async () => { + it('should return null if one of the addons is already installed', async () => { + const result = check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); + }, + }, + mainConfig: { + addons: ['@storybook/addon-webpack5-compiler-swc'], + framework: { + name: '@storybook/react-webpack5', + options: { + builder: { + useSWC: true, + }, + }, + }, + }, + }); - afterEach(() => { - vi.resetAllMocks(); - }); + expect(result).resolves.toBeNull(); + }); - describe('return null', async () => { it('should return null if the builder is not webpack5', async () => { const result = check({ mainConfig: { @@ -256,8 +269,8 @@ describe('check function', () => { }); expect(result).contains({ - shouldRemoveSWCFlag: true, isNextJs: true, + shouldRemoveSWCFlag: true, }); }); @@ -282,11 +295,7 @@ describe('check function', () => { }); describe('return options', () => { - it('should return compiler: babel when useSWC flag is not set and the user selects babel during prompt', async () => { - promptMocks.default.mockResolvedValue({ - compiler: 'babel', - }); - + it('should return defaultCompiler: babel when useSWC flag is not set', async () => { const result = await check({ packageManager: { getPackageVersion: (name) => { @@ -301,17 +310,12 @@ describe('check function', () => { }); expect(result).contains({ - compiler: 'babel', - compilerPackageName: '@storybook/addon-webpack5-compiler-babel', + defaultCompiler: CoreWebpackCompilers.Babel, }); }); describe('user selects swc', () => { - it('should return compiler: swc when useSWC flag is not set and the user selects swc during prompt', async () => { - promptMocks.default.mockResolvedValue({ - compiler: 'swc', - }); - + it('should return defaultCompiler: swc when useSWC flag is set', async () => { const result = await check({ packageManager: { getPackageVersion: (name) => { @@ -321,42 +325,21 @@ describe('check function', () => { mainConfig: { framework: { name: '@storybook/react-webpack5', + options: { + builder: { + useSWC: true, + }, + }, }, }, }); expect(result).contains({ - compiler: 'swc', - compilerPackageName: '@storybook/addon-webpack5-compiler-swc', + defaultCompiler: CoreWebpackCompilers.SWC, }); }); }); - it('should return compiler: swc when useSWC flag is set', async () => { - const result = await check({ - packageManager: { - getPackageVersion: (name) => { - return Promise.resolve(null); - }, - }, - mainConfig: { - framework: { - name: '@storybook/react-webpack5', - options: { - builder: { - useSWC: true, - }, - }, - }, - }, - }); - - expect(result).contains({ - compiler: 'swc', - compilerPackageName: '@storybook/addon-webpack5-compiler-swc', - }); - }); - it('should return options if the framework is unknown but webpack5 was detected', async () => { const result = await check({ packageManager: { @@ -385,30 +368,58 @@ describe('check function', () => { const prompt = webpack5CompilerSetup.prompt({ shouldRemoveSWCFlag: true, isNextJs: false, - compilerPackageName: '@storybook/addon-webpack5-compiler-swc', - compiler: CoreWebpackCompilers.SWC, + defaultCompiler: CoreWebpackCompilers.SWC, }); expect(prompt).toMatchInlineSnapshot(` - "We need to update your Storybook configuration for Webpack 5. - The framework.options.builder.useSWC flag will be removed. + "We need to update your Storybook configuration for Webpack 5. + The framework.options.builder.useSWC flag will be removed. + + Storybook's Webpack5 builder is now compiler agnostic, meaning you have to install an additional addon to set up a compiler for Webpack5. + + We have detected, that you want to use SWC as the compiler for Webpack5. - The @storybook/addon-webpack5-compiler-swc addon will be added to your project. It adds SWC as the compiler for your Storybook. - After the migration, you can switch Webpack5 compilers by swapping the addon in your project. - You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" - `); + In the next step, Storybook will install @storybook/addon-webpack5-compiler-swc and will add it to your addons list in your Storybook config. + + After the migration, you can switch Webpack5 compilers by swapping the addon in your project. + You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" + `); }); it('shouldRemoveSWCFlag = false', async () => { const prompt = webpack5CompilerSetup.prompt({ shouldRemoveSWCFlag: false, isNextJs: false, - compilerPackageName: '@storybook/addon-webpack5-compiler-swc', - compiler: CoreWebpackCompilers.SWC, + defaultCompiler: CoreWebpackCompilers.SWC, }); expect(prompt).toMatchInlineSnapshot(` - "The @storybook/addon-webpack5-compiler-swc addon will be added to your project. It adds SWC as the compiler for your Storybook. + "Storybook's Webpack5 builder is now compiler agnostic, meaning you have to install an additional addon to set up a compiler for Webpack5. + + We have detected, that you want to use SWC as the compiler for Webpack5. + + In the next step, Storybook will install @storybook/addon-webpack5-compiler-swc and will add it to your addons list in your Storybook config. + + After the migration, you can switch Webpack5 compilers by swapping the addon in your project. + You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" + `); + }); + + it('defaultCompiler = babel', async () => { + const prompt = webpack5CompilerSetup.prompt({ + shouldRemoveSWCFlag: false, + isNextJs: false, + defaultCompiler: CoreWebpackCompilers.Babel, + }); + + expect(prompt).toMatchInlineSnapshot(` + "Storybook's Webpack5 builder is now compiler agnostic, meaning you can choose a compiler addon that best fits your project: + + - Babel: A vast ecosystem and is battle-tested. It's a robust choice if you have an extensive Babel setup or need specific Babel plugins for your project. + - SWC: Fast and easy to configure. Ideal if you want faster builds and have a straightforward configuration without the need for Babel's extensibility. + + In the next step, Storybook will ask you to choose a compiler to automatically set it up for you. + After the migration, you can switch Webpack5 compilers by swapping the addon in your project. You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" `); @@ -418,8 +429,7 @@ describe('check function', () => { const prompt = webpack5CompilerSetup.prompt({ shouldRemoveSWCFlag: true, isNextJs: true, - compilerPackageName: undefined, - compiler: undefined, + defaultCompiler: CoreWebpackCompilers.SWC, }); expect(prompt).toMatchInlineSnapshot(` @@ -436,31 +446,40 @@ describe('check function', () => { `); }); - it('isNextjs = false AND compilerPackageName = @storybook/addon-webpack5-compiler-swc AND compiler = swc', () => { + it('isNextjs = false AND defaultCompiler = swc', () => { const prompt = webpack5CompilerSetup.prompt({ shouldRemoveSWCFlag: false, isNextJs: false, - compilerPackageName: '@storybook/addon-webpack5-compiler-swc', - compiler: CoreWebpackCompilers.SWC, + defaultCompiler: CoreWebpackCompilers.SWC, }); expect(prompt).toMatchInlineSnapshot(` - "The @storybook/addon-webpack5-compiler-swc addon will be added to your project. It adds SWC as the compiler for your Storybook. + "Storybook's Webpack5 builder is now compiler agnostic, meaning you have to install an additional addon to set up a compiler for Webpack5. + + We have detected, that you want to use SWC as the compiler for Webpack5. + + In the next step, Storybook will install @storybook/addon-webpack5-compiler-swc and will add it to your addons list in your Storybook config. + After the migration, you can switch Webpack5 compilers by swapping the addon in your project. You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" `); }); - it('isNextjs = false AND compilerPackageName = @storybook/addon-webpack5-compiler-babel AND compiler = babel', () => { + it('isNextjs = false AND defaultCompiler = babel', () => { const prompt = webpack5CompilerSetup.prompt({ shouldRemoveSWCFlag: false, isNextJs: false, - compilerPackageName: '@storybook/addon-webpack5-compiler-babel', - compiler: CoreWebpackCompilers.Babel, + defaultCompiler: CoreWebpackCompilers.Babel, }); expect(prompt).toMatchInlineSnapshot(` - "The @storybook/addon-webpack5-compiler-babel addon will be added to your project. It adds Babel as the compiler for your Storybook. + "Storybook's Webpack5 builder is now compiler agnostic, meaning you can choose a compiler addon that best fits your project: + + - Babel: A vast ecosystem and is battle-tested. It's a robust choice if you have an extensive Babel setup or need specific Babel plugins for your project. + - SWC: Fast and easy to configure. Ideal if you want faster builds and have a straightforward configuration without the need for Babel's extensibility. + + In the next step, Storybook will ask you to choose a compiler to automatically set it up for you. + After the migration, you can switch Webpack5 compilers by swapping the addon in your project. You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" `); diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts index 647707c9d10c..778b099f272a 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts @@ -3,6 +3,7 @@ import type { SupportedFrameworks } from '@storybook/types'; import { frameworkPackages } from '@storybook/core-common'; import type { Fix } from '../types'; import { + getAddonNames, getBuilderPackageName, getFrameworkOptions, getFrameworkPackageName, @@ -20,8 +21,7 @@ import chalk from 'chalk'; import { add } from '../../add'; type Options = { - compiler?: CoreWebpackCompilers; - compilerPackageName?: keyof typeof compilerNameToCoreCompiler | undefined; + defaultCompiler?: CoreWebpackCompilers; shouldRemoveSWCFlag: boolean; isNextJs: boolean; }; @@ -30,6 +30,17 @@ export const webpack5CompilerSetup: Fix = { id: 'webpack5-compiler-setup', async check({ mainConfig, packageManager }) { + const addons = getAddonNames(mainConfig); + + if ( + addons.find( + (addon) => + addon.includes(CoreWebpackCompilers.Babel) || addon.includes(CoreWebpackCompilers.SWC) + ) + ) { + return null; + } + const frameworkName = Object.entries(frameworkPackages).find( ([name]) => name === getFrameworkPackageName(mainConfig) )?.[1]; @@ -72,15 +83,6 @@ export const webpack5CompilerSetup: Fix = { ? CoreWebpackCompilers.SWC : CoreWebpackCompilers.Babel; - const compiler: CoreWebpackCompilers = - defaultCompiler === CoreWebpackCompilers.Babel - ? await askUserForCompilerChoice() - : CoreWebpackCompilers.SWC; - - const compilerPackageName = Object.entries(compilerNameToCoreCompiler).find( - ([, coreCompiler]) => coreCompiler === compiler - )?.[0]; - const shouldRemoveSWCFlag = frameworkOptions?.builder ? 'useSWC' in frameworkOptions.builder : false; @@ -95,14 +97,13 @@ export const webpack5CompilerSetup: Fix = { } return { - compiler, - compilerPackageName, + defaultCompiler, shouldRemoveSWCFlag, isNextJs: false, }; }, - prompt({ compiler, compilerPackageName, shouldRemoveSWCFlag, isNextJs }) { + prompt({ defaultCompiler, shouldRemoveSWCFlag, isNextJs }) { const message = []; if (shouldRemoveSWCFlag) { @@ -126,11 +127,22 @@ export const webpack5CompilerSetup: Fix = { 'babel.config.js' )} file in your project, Storybook will use SWC as the compiler. `); - } else if (compilerPackageName) { + } else if (defaultCompiler === CoreWebpackCompilers.Babel) { message.push(dedent` - The ${chalk.cyan(compilerPackageName)} addon will be added to your project. It adds ${ - compiler === CoreWebpackCompilers.Babel ? 'Babel' : 'SWC' - } as the compiler for your Storybook. + Storybook's Webpack5 builder is now compiler agnostic, meaning you can choose a compiler addon that best fits your project:\n + - Babel: A vast ecosystem and is battle-tested. It's a robust choice if you have an extensive Babel setup or need specific Babel plugins for your project. + - SWC: Fast and easy to configure. Ideal if you want faster builds and have a straightforward configuration without the need for Babel's extensibility.\n + In the next step, Storybook will ask you to choose a compiler to automatically set it up for you.\n + After the migration, you can switch Webpack5 compilers by swapping the addon in your project. + You can find more information here: ${chalk.yellow( + 'https://storybook.js.org/docs/8.0/builders/webpack#compiler-support' + )} + `); + } else { + message.push(dedent` + Storybook's Webpack5 builder is now compiler agnostic, meaning you have to install an additional addon to set up a compiler for Webpack5.\n + We have detected, that you want to use SWC as the compiler for Webpack5.\n + In the next step, Storybook will install @storybook/addon-webpack5-compiler-swc and will add it to your addons list in your Storybook config.\n After the migration, you can switch Webpack5 compilers by swapping the addon in your project. You can find more information here: ${chalk.yellow( 'https://storybook.js.org/docs/8.0/builders/webpack#compiler-support' @@ -142,7 +154,7 @@ export const webpack5CompilerSetup: Fix = { }, async run({ result, mainConfigPath, packageManager, skipInstall, dryRun }) { - const { compilerPackageName, shouldRemoveSWCFlag, isNextJs } = result; + const { defaultCompiler, shouldRemoveSWCFlag, isNextJs } = result; if (shouldRemoveSWCFlag) { await updateMainConfig({ mainConfigPath, dryRun: !!dryRun }, (main) => { @@ -150,7 +162,16 @@ export const webpack5CompilerSetup: Fix = { }); } - if (!isNextJs && compilerPackageName) { + if (!isNextJs) { + const compiler: CoreWebpackCompilers = + defaultCompiler === CoreWebpackCompilers.Babel + ? await askUserForCompilerChoice() + : CoreWebpackCompilers.SWC; + + const compilerPackageName = Object.entries(compilerNameToCoreCompiler).find( + ([, coreCompiler]) => coreCompiler === compiler + )![0]; + await add(compilerPackageName, { packageManager: packageManager.type, skipPostinstall: !!skipInstall, @@ -163,10 +184,7 @@ async function askUserForCompilerChoice() { const response = await prompts<'compiler'>({ type: 'select', name: 'compiler', - message: `Storybook's Webpack5 builder is now compiler agnostic, meaning you can choose a compiler addon that best fits your project:\n - - Babel: A vast ecosystem and is battle-tested. It's a robust choice if you have an extensive Babel setup or need specific Babel plugins for your project. - - SWC: Fast and easy to configure. Ideal if you want faster builds and have a straightforward configuration without the need for Babel's extensibility.\n -Which compiler would you like to use?`, + message: `Which compiler would you like to use?`, choices: [ { title: 'Babel', From c4db489a4b35da39f6a926747f033d14d939f40a Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 16 Feb 2024 18:32:05 +0100 Subject: [PATCH 26/44] Re-enable flaky tests --- code/e2e-tests/addon-docs.spec.ts | 3 +-- code/e2e-tests/tags.spec.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/code/e2e-tests/addon-docs.spec.ts b/code/e2e-tests/addon-docs.spec.ts index 9be08c4b674a..4ae3da33e99e 100644 --- a/code/e2e-tests/addon-docs.spec.ts +++ b/code/e2e-tests/addon-docs.spec.ts @@ -34,8 +34,7 @@ test.describe('addon-docs', () => { await expect(anotherStory).toContainText('Another button, just to show multiple stories'); }); - // FIXME - get rid of the flake - test.skip('should show source=code view for stories', async ({ page }) => { + test('should show source=code view for stories', async ({ page }) => { const skipped = [ // SSv6 does not render stories in the correct order in our sandboxes 'internal\\/ssv6', diff --git a/code/e2e-tests/tags.spec.ts b/code/e2e-tests/tags.spec.ts index e3d79723c8f8..37fb76fb814c 100644 --- a/code/e2e-tests/tags.spec.ts +++ b/code/e2e-tests/tags.spec.ts @@ -3,8 +3,7 @@ import { SbPage } from './util'; const storybookUrl = process.env.STORYBOOK_URL || 'http://localhost:8001'; -// FIXME - get rid of the flake -test.describe.skip('tags', () => { +test.describe('tags', () => { test.beforeEach(async ({ page }) => { await page.goto(storybookUrl); await new SbPage(page).waitUntilLoaded(); From aea7ea3dbee02aec0bc769b8dfab58f6c8d5c4fc Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Sat, 17 Feb 2024 14:55:21 +0800 Subject: [PATCH 27/44] Core: Add addon removal telemetry --- code/lib/cli/src/generate.ts | 21 +++++++++++++++---- .../core-server/src/presets/common-preset.ts | 16 +++++++++----- code/lib/telemetry/src/types.ts | 3 ++- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/code/lib/cli/src/generate.ts b/code/lib/cli/src/generate.ts index 250cd200d206..b4e02c322b46 100644 --- a/code/lib/cli/src/generate.ts +++ b/code/lib/cli/src/generate.ts @@ -6,13 +6,19 @@ import { sync as readUpSync } from 'read-pkg-up'; import invariant from 'tiny-invariant'; import { logger } from '@storybook/node-logger'; -import { addToGlobalContext } from '@storybook/telemetry'; -import { parseList, getEnvConfig, JsPackageManagerFactory, versions } from '@storybook/core-common'; +import { addToGlobalContext, telemetry } from '@storybook/telemetry'; +import { + parseList, + getEnvConfig, + JsPackageManagerFactory, + versions, + removeAddon as remove, +} from '@storybook/core-common'; +import { withTelemetry } from '@storybook/core-server'; import type { CommandOptions } from './generators/types'; import { initiate } from './initiate'; import { add } from './add'; -import { removeAddon as remove } from '@storybook/core-common'; import { migrate } from './migrate'; import { upgrade, type UpgradeOptions } from './upgrade'; import { sandbox } from './sandbox'; @@ -71,7 +77,14 @@ command('remove ') '--package-manager ', 'Force package manager for installing dependencies' ) - .action((addonName: string, options: any) => remove(addonName, options)); + .action((addonName: string, options: any) => + withTelemetry('remove', { cliOptions: options }, async () => { + await remove(addonName, options); + if (!options.disableTelemetry) { + await telemetry('remove', { addon: addonName, source: 'cli' }); + } + }) + ); command('upgrade') .description(`Upgrade your Storybook packages to v${versions.storybook}`) diff --git a/code/lib/core-server/src/presets/common-preset.ts b/code/lib/core-server/src/presets/common-preset.ts index 4e0c183ea7c1..6dcd6366eff0 100644 --- a/code/lib/core-server/src/presets/common-preset.ts +++ b/code/lib/core-server/src/presets/common-preset.ts @@ -7,7 +7,7 @@ import { getPreviewBodyTemplate, getPreviewHeadTemplate, loadEnvs, - removeAddon, + removeAddon as removeAddonBase, } from '@storybook/core-common'; import type { CLIOptions, @@ -162,10 +162,16 @@ const optionalEnvToBoolean = (input: string | undefined): boolean | undefined => }; // eslint-disable-next-line @typescript-eslint/naming-convention -export const experimental_serverAPI = (extension: Record) => ({ - ...extension, - removeAddon, -}); +export const experimental_serverAPI = (extension: Record, options: Options) => { + let removeAddon = removeAddonBase; + if (!options.disableTelemetry) { + removeAddon = async (id: string, opts: any) => { + await telemetry('remove', { addon: id, source: 'api' }); + return removeAddonBase(id, opts); + }; + } + return { ...extension, removeAddon }; +}; /** * If for some reason this config is not applied, the reason is that diff --git a/code/lib/telemetry/src/types.ts b/code/lib/telemetry/src/types.ts index 8f091703bcae..846e7adb9556 100644 --- a/code/lib/telemetry/src/types.ts +++ b/code/lib/telemetry/src/types.ts @@ -15,7 +15,8 @@ export type EventType = | 'error' | 'error-metadata' | 'version-update' - | 'core-config'; + | 'core-config' + | 'remove'; export interface Dependency { version: string | undefined; From b34b44d6ef7805a730b0a70dc2b2168edf3da808 Mon Sep 17 00:00:00 2001 From: Hendrik Schmitz Date: Mon, 19 Feb 2024 08:50:28 +0100 Subject: [PATCH 28/44] Core: Update ip version to fix CVE-2023-42282 --- code/lib/core-server/package.json | 2 +- code/yarn.lock | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json index ac629c9e7099..ff37db230991 100644 --- a/code/lib/core-server/package.json +++ b/code/lib/core-server/package.json @@ -83,7 +83,7 @@ "express": "^4.17.3", "fs-extra": "^11.1.0", "globby": "^11.0.2", - "ip": "^2.0.0", + "ip": "^2.0.1", "lodash": "^4.17.21", "open": "^8.4.0", "pretty-hrtime": "^1.0.3", diff --git a/code/yarn.lock b/code/yarn.lock index 2c786bd21e03..86f09cd405d2 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5571,7 +5571,7 @@ __metadata: express: "npm:^4.17.3" fs-extra: "npm:^11.1.0" globby: "npm:^11.0.2" - ip: "npm:^2.0.0" + ip: "npm:^2.0.1" lodash: "npm:^4.17.21" node-fetch: "npm:^3.3.1" open: "npm:^8.4.0" @@ -17277,6 +17277,13 @@ __metadata: languageName: node linkType: hard +"ip@npm:^2.0.1": + version: 2.0.1 + resolution: "ip@npm:2.0.1" + checksum: cab8eb3e88d0abe23e4724829621ec4c4c5cb41a7f936a2e626c947128c1be16ed543448d42af7cca95379f9892bfcacc1ccd8d09bc7e8bea0e86d492ce33616 + languageName: node + linkType: hard + "ipaddr.js@npm:1.9.1": version: 1.9.1 resolution: "ipaddr.js@npm:1.9.1" From e0e439e713444c653aa101ae4665452b55d97c26 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 19 Feb 2024 09:31:02 +0100 Subject: [PATCH 29/44] Update automigrate fixes prompt types --- .../fixes/angular-builders-multiproject.ts | 2 +- .../automigrate/fixes/incompatible-addons.ts | 2 +- .../automigrate/fixes/prompt-remove-react.ts | 2 +- .../fixes/remove-global-client-apis.ts | 2 +- .../fixes/remove-jest-testing-library.ts | 2 +- .../automigrate/fixes/storyshots-migration.ts | 2 +- .../fixes/webpack5-compiler-setup.ts | 4 ++ code/lib/cli/src/automigrate/index.ts | 39 ++++++++++++++++--- code/lib/cli/src/automigrate/types.ts | 10 ++++- 9 files changed, 53 insertions(+), 12 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts b/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts index 9bf74a288416..d57fbd28a10c 100644 --- a/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts +++ b/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts @@ -10,7 +10,7 @@ interface AngularBuildersMultiprojectRunOptions {} export const angularBuildersMultiproject: Fix = { id: 'angular-builders-multiproject', - promptOnly: true, + promptType: 'manual', async check({ packageManager, mainConfig }) { // Skip in case of NX diff --git a/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts b/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts index ce9d01ccd96a..469383834cc1 100644 --- a/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts +++ b/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts @@ -9,7 +9,7 @@ interface IncompatibleAddonsOptions { export const incompatibleAddons: Fix = { id: 'incompatible-addons', - promptOnly: true, + promptType: 'manual', async check({ mainConfig, packageManager }) { const incompatibleAddonList = await getIncompatibleAddons(mainConfig, packageManager); diff --git a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts index eb93ce015b08..a7586a03ff15 100644 --- a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts +++ b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts @@ -5,7 +5,7 @@ import type { Fix } from '../types'; export const removeReactDependency: Fix<{}> = { id: 'remove-react-dependency', - promptOnly: true, + promptType: 'manual', async check({ packageManager, mainConfig, storybookVersion }) { // when the user is using the react renderer, we should not prompt them to remove react diff --git a/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts b/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts index 4a1304dae1a6..2f25e5277096 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts @@ -19,7 +19,7 @@ interface GlobalClientAPIOptions { export const removedGlobalClientAPIs: Fix = { id: 'removedglobalclientapis', - promptOnly: true, + promptType: 'manual', async check({ previewConfigPath }) { if (previewConfigPath) { diff --git a/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.ts b/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.ts index 87cf964468b3..ba238dd2740a 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.ts @@ -4,7 +4,7 @@ import type { Fix } from '../types'; export const removeJestTestingLibrary: Fix<{ incompatiblePackages: string[] }> = { id: 'remove-jest-testing-library', - promptOnly: true, + promptType: 'manual', async check({ mainConfig, packageManager }) { const deps = await packageManager.getAllDependencies(); diff --git a/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts b/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts index 6d047f88449f..6d132037c3e0 100644 --- a/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts +++ b/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts @@ -4,7 +4,7 @@ import type { Fix } from '../types'; export const storyshotsMigration: Fix = { id: 'storyshots-migration', - promptOnly: true, + promptType: 'manual', async check({ mainConfig, packageManager }) { const allDeps = await packageManager.getAllDependencies(); diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts index 778b099f272a..192b6ca54f70 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts @@ -29,6 +29,10 @@ type Options = { export const webpack5CompilerSetup: Fix = { id: 'webpack5-compiler-setup', + promptType(result) { + return result.isNextJs && !result.shouldRemoveSWCFlag ? 'notified' : 'auto'; + }, + async check({ mainConfig, packageManager }) { const addons = getAddonNames(mainConfig); diff --git a/code/lib/cli/src/automigrate/index.ts b/code/lib/cli/src/automigrate/index.ts index 19d4ee8922e9..f29a5263160a 100644 --- a/code/lib/cli/src/automigrate/index.ts +++ b/code/lib/cli/src/automigrate/index.ts @@ -20,6 +20,7 @@ import type { FixSummary, PreCheckFailure, AutofixOptionsFromCLI, + Prompt, } from './fixes'; import { FixStatus, allFixes } from './fixes'; import { cleanLog } from './helpers/cleanLog'; @@ -217,15 +218,29 @@ export async function runFixes({ } if (result) { + const promptType: Prompt = + typeof f.promptType === 'function' ? await f.promptType(result) : f.promptType ?? 'auto'; + logger.info(`\n🔎 found a '${chalk.cyan(f.id)}' migration:`); const message = f.prompt(result); + const getTitle = () => { + switch (promptType) { + case 'auto': + return 'Automigration detected'; + case 'manual': + return 'Manual migration detected'; + case 'notified': + return 'Migration notification'; + } + }; + logger.info( boxen(message, { borderStyle: 'round', padding: 1, borderColor: '#F1618C', - title: f.promptOnly ? 'Manual migration detected' : 'Automigration detected', + title: getTitle(), }) ); @@ -236,11 +251,11 @@ export async function runFixes({ runAnswer = { fix: false }; } else if (yes) { runAnswer = { fix: true }; - if (f.promptOnly) { + if (promptType === 'manual') { fixResults[f.id] = FixStatus.MANUAL_SUCCEEDED; fixSummary.manual.push(f.id); } - } else if (f.promptOnly) { + } else if (promptType === 'manual') { fixResults[f.id] = FixStatus.MANUAL_SUCCEEDED; fixSummary.manual.push(f.id); @@ -266,7 +281,7 @@ export async function runFixes({ fixResults[f.id] = FixStatus.MANUAL_SKIPPED; break; } - } else { + } else if (promptType === 'auto') { runAnswer = await prompts( { type: 'confirm', @@ -280,12 +295,26 @@ export async function runFixes({ }, } ); + } else if (promptType === 'notified') { + runAnswer = await prompts( + { + type: 'confirm', + name: 'fix', + message: `Do you want to continue?`, + initial: true, + }, + { + onCancel: () => { + throw new Error(); + }, + } + ); } } catch (err) { break; } - if (!f.promptOnly) { + if (promptType === 'auto') { invariant(runAnswer, 'runAnswer must be defined if not promptOnly'); if (runAnswer.fix) { try { diff --git a/code/lib/cli/src/automigrate/types.ts b/code/lib/cli/src/automigrate/types.ts index 97d20c09dc45..6ef4f243b9ea 100644 --- a/code/lib/cli/src/automigrate/types.ts +++ b/code/lib/cli/src/automigrate/types.ts @@ -19,9 +19,17 @@ export interface RunOptions { skipInstall?: boolean; } +/** + * promptType defines how the user will be prompted to apply an automigration fix + * - auto: the fix will be applied automatically + * - manual: the user will be prompted to apply the fix + * - notified: the user will be notified about the some changes. A fix isn't required + */ +export type Prompt = 'auto' | 'manual' | 'notified'; + export interface Fix { id: string; - promptOnly?: boolean; + promptType?: Prompt | ((result: ResultType) => Promise | Prompt); check: (options: CheckOptions) => Promise; prompt: (result: ResultType) => string; run?: (options: RunOptions) => Promise; From ece437c3fc502638313b3bb00b917e8c0f234f04 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 19 Feb 2024 09:40:09 +0100 Subject: [PATCH 30/44] Add tests --- .../fixes/webpack5-compiler-setup.test.ts | 582 +++++++++--------- .../fixes/webpack5-compiler-setup.ts | 4 +- 2 files changed, 308 insertions(+), 278 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts index 0b0ddc98e2fd..db437fbb13df 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts @@ -44,258 +44,277 @@ vi.mock('chalk', () => { }; }); -describe('check function', () => { - describe('webpack5Migration check function', () => { - describe('return null', async () => { - it('should return null if one of the addons is already installed', async () => { - const result = check({ - packageManager: { - getPackageVersion: (name) => { - return Promise.resolve(null); - }, +describe('webpack5Migration check function', () => { + describe('return null', async () => { + it('should return null if one of the addons is already installed', async () => { + const result = check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); }, - mainConfig: { - addons: ['@storybook/addon-webpack5-compiler-swc'], - framework: { - name: '@storybook/react-webpack5', - options: { - builder: { - useSWC: true, - }, + }, + mainConfig: { + addons: ['@storybook/addon-webpack5-compiler-swc'], + framework: { + name: '@storybook/react-webpack5', + options: { + builder: { + useSWC: true, }, }, }, - }); - - expect(result).resolves.toBeNull(); + }, }); - it('should return null if the builder is not webpack5', async () => { - const result = check({ - mainConfig: { - framework: { - name: '@storybook/react-vite', - }, - }, - }); - - await expect(result).resolves.toBeNull(); - }); + expect(result).resolves.toBeNull(); + }); - it('should return null if the framework is Angular', async () => { - const result = check({ - mainConfig: { - framework: '@storybook/angular', + it('should return null if the builder is not webpack5', async () => { + const result = check({ + mainConfig: { + framework: { + name: '@storybook/react-vite', }, - }); - - await expect(result).resolves.toBeNull(); - // ... + }, }); - it('should return null if the framework is Ember', async () => { - const result = check({ - mainConfig: { - framework: { - name: '@storybook/ember', - }, - }, - }); + await expect(result).resolves.toBeNull(); + }); - await expect(result).resolves.toBeNull(); + it('should return null if the framework is Angular', async () => { + const result = check({ + mainConfig: { + framework: '@storybook/angular', + }, }); - it('should return null if the framework is Webpack5 based but a different framework builder is used', async () => { - const result = check({ - mainConfig: { - framework: { - name: '@storybook/react-webpack5', - options: { - builder: '@storybook/builder-vite', - }, - }, - }, - }); + await expect(result).resolves.toBeNull(); + // ... + }); - await expect(result).resolves.toBeNull(); + it('should return null if the framework is Ember', async () => { + const result = check({ + mainConfig: { + framework: { + name: '@storybook/ember', + }, + }, }); - it('should return null if the framework is Webpack5 based but a different core builder is used', async () => { - const result = check({ - mainConfig: { - framework: { - name: '@storybook/react-webpack5', - }, - core: { + await expect(result).resolves.toBeNull(); + }); + + it('should return null if the framework is Webpack5 based but a different framework builder is used', async () => { + const result = check({ + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + options: { builder: '@storybook/builder-vite', }, }, - }); - - await expect(result).resolves.toBeNull(); + }, }); - it('should return null if the framework is CRA based', async () => { - const result = check({ - packageManager: { - getPackageVersion: (name) => { - if (name === 'react-scripts') { - return Promise.resolve('5.0.0'); - } + await expect(result).resolves.toBeNull(); + }); - return Promise.resolve(null); - }, + it('should return null if the framework is Webpack5 based but a different core builder is used', async () => { + const result = check({ + mainConfig: { + framework: { + name: '@storybook/react-webpack5', }, - mainConfig: { - framework: { - name: '@storybook/react-webpack5', - }, + core: { + builder: '@storybook/builder-vite', }, - }); + }, + }); + + await expect(result).resolves.toBeNull(); + }); + + it('should return null if the framework is CRA based', async () => { + const result = check({ + packageManager: { + getPackageVersion: (name) => { + if (name === 'react-scripts') { + return Promise.resolve('5.0.0'); + } - await expect(result).resolves.toBeNull(); + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + }, + }, }); + + await expect(result).resolves.toBeNull(); }); + }); - describe('useSWC', () => { - it('should return shouldRemoveSWCFlag: true when useSWC flag is set to true', async () => { - const result = await check({ - packageManager: { - getPackageVersion: (name) => { - return Promise.resolve(null); - }, + describe('useSWC', () => { + it('should return shouldRemoveSWCFlag: true when useSWC flag is set to true', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); }, - mainConfig: { - framework: { - name: '@storybook/react-webpack5', - options: { - builder: { - useSWC: true, - }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + options: { + builder: { + useSWC: true, }, }, }, - }); + }, + }); - expect(result).contains({ - shouldRemoveSWCFlag: true, - }); + expect(result).contains({ + shouldRemoveSWCFlag: true, }); + }); - it('should return shouldRemoveSWCFlag: true when useSWC flag is set to false', async () => { - const result = await check({ - packageManager: { - getPackageVersion: (name) => { - return Promise.resolve(null); - }, + it('should return shouldRemoveSWCFlag: true when useSWC flag is set to false', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); }, - mainConfig: { - framework: { - name: '@storybook/react-webpack5', - options: { - builder: { - useSWC: false, - }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + options: { + builder: { + useSWC: false, }, }, }, - }); + }, + }); - expect(result).contains({ - shouldRemoveSWCFlag: true, - }); + expect(result).contains({ + shouldRemoveSWCFlag: true, }); + }); - it('should return shouldRemoveSWCFlag: false when useSWC flag is not set', async () => { - const result = await check({ - packageManager: { - getPackageVersion: (name) => { - return Promise.resolve(null); - }, + it('should return shouldRemoveSWCFlag: false when useSWC flag is not set', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); }, - mainConfig: { - framework: { - name: '@storybook/react-webpack5', - options: { - builder: {}, - }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + options: { + builder: {}, }, }, - }); + }, + }); - expect(result).contains({ - shouldRemoveSWCFlag: false, - }); + expect(result).contains({ + shouldRemoveSWCFlag: false, }); }); + }); - describe('Next.js', () => { - it('should return isNextJs: true when the framework is nextjs', async () => { - const result = await check({ - packageManager: { - getPackageVersion: (name) => { - return Promise.resolve(null); - }, + describe('Next.js', () => { + it('should return isNextJs: true when the framework is nextjs', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); }, - mainConfig: { - framework: { - name: '@storybook/nextjs', - }, + }, + mainConfig: { + framework: { + name: '@storybook/nextjs', }, - }); + }, + }); - expect(result).contains({ - isNextJs: true, - }); + expect(result).contains({ + isNextJs: true, }); + }); - it('should return isNextJs: true AND should return shouldRemoveSWCFlag: true when useSWC flag is set', async () => { - const result = await check({ - packageManager: { - getPackageVersion: (name) => { - return Promise.resolve(null); - }, + it('should return isNextJs: true AND should return shouldRemoveSWCFlag: true when useSWC flag is set', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); }, - mainConfig: { - framework: { - name: '@storybook/nextjs', - options: { - builder: { - useSWC: true, - }, + }, + mainConfig: { + framework: { + name: '@storybook/nextjs', + options: { + builder: { + useSWC: true, }, }, }, - }); + }, + }); - expect(result).contains({ - isNextJs: true, - shouldRemoveSWCFlag: true, - }); + expect(result).contains({ + isNextJs: true, + shouldRemoveSWCFlag: true, }); + }); - it('should return isNextJs: false when the framework is not nextjs', async () => { - const result = await check({ - packageManager: { - getPackageVersion: (name) => { - return Promise.resolve(null); - }, + it('should return isNextJs: false when the framework is not nextjs', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); }, - mainConfig: { - framework: { - name: '@storybook/react-webpack5', - }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', }, - }); + }, + }); - expect(result).contains({ - isNextJs: false, - }); + expect(result).contains({ + isNextJs: false, }); }); + }); + + describe('return options', () => { + it('should return defaultCompiler: babel when useSWC flag is not set', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/react-webpack5', + }, + }, + }); - describe('return options', () => { - it('should return defaultCompiler: babel when useSWC flag is not set', async () => { + expect(result).contains({ + defaultCompiler: CoreWebpackCompilers.Babel, + }); + }); + + describe('user selects swc', () => { + it('should return defaultCompiler: swc when useSWC flag is set', async () => { const result = await check({ packageManager: { getPackageVersion: (name) => { @@ -305,73 +324,85 @@ describe('check function', () => { mainConfig: { framework: { name: '@storybook/react-webpack5', + options: { + builder: { + useSWC: true, + }, + }, }, }, }); expect(result).contains({ - defaultCompiler: CoreWebpackCompilers.Babel, + defaultCompiler: CoreWebpackCompilers.SWC, }); }); + }); - describe('user selects swc', () => { - it('should return defaultCompiler: swc when useSWC flag is set', async () => { - const result = await check({ - packageManager: { - getPackageVersion: (name) => { - return Promise.resolve(null); - }, - }, - mainConfig: { - framework: { - name: '@storybook/react-webpack5', - options: { - builder: { - useSWC: true, - }, - }, - }, - }, - }); + it('should return options if the framework is unknown but webpack5 was detected', async () => { + const result = await check({ + packageManager: { + getPackageVersion: (name) => { + if (name === 'webpack') { + return Promise.resolve('5.0.0'); + } - expect(result).contains({ - defaultCompiler: CoreWebpackCompilers.SWC, - }); - }); + return Promise.resolve(null); + }, + }, + mainConfig: { + framework: { + name: '@storybook/unknown', + }, + }, }); - it('should return options if the framework is unknown but webpack5 was detected', async () => { - const result = await check({ - packageManager: { - getPackageVersion: (name) => { - if (name === 'webpack') { - return Promise.resolve('5.0.0'); - } + expect(result).not.toBeNull(); + }); + }); +}); - return Promise.resolve(null); - }, - }, - mainConfig: { - framework: { - name: '@storybook/unknown', - }, - }, - }); +describe('promptOnly', () => { + it('should return notified if isNextJs = true and shouldRemoveSWCFlag = false', () => { + const promptType = webpack5CompilerSetup.promptType({ + isNextJs: true, + shouldRemoveSWCFlag: false, + defaultCompiler: undefined, + }); - expect(result).not.toBeNull(); - }); + expect(promptType).toBe('notified'); + }); + + it('should return auto if isNextJs = false', () => { + const promptType = webpack5CompilerSetup.promptType({ + isNextJs: false, + shouldRemoveSWCFlag: false, + defaultCompiler: undefined, }); + + expect(promptType).toBe('auto'); }); - describe('prompt', () => { - it('shouldRemoveSWCFlag = true', async () => { - const prompt = webpack5CompilerSetup.prompt({ - shouldRemoveSWCFlag: true, - isNextJs: false, - defaultCompiler: CoreWebpackCompilers.SWC, - }); + it('should return auto if shouldRemoveSWCFlag is true', () => { + const promptType = webpack5CompilerSetup.promptType({ + isNextJs: true, + shouldRemoveSWCFlag: true, + defaultCompiler: undefined, + }); - expect(prompt).toMatchInlineSnapshot(` + expect(promptType).toBe('auto'); + }); +}); + +describe('prompt', () => { + it('shouldRemoveSWCFlag = true', async () => { + const prompt = webpack5CompilerSetup.prompt({ + shouldRemoveSWCFlag: true, + isNextJs: false, + defaultCompiler: CoreWebpackCompilers.SWC, + }); + + expect(prompt).toMatchInlineSnapshot(` "We need to update your Storybook configuration for Webpack 5. The framework.options.builder.useSWC flag will be removed. @@ -384,16 +415,16 @@ describe('check function', () => { After the migration, you can switch Webpack5 compilers by swapping the addon in your project. You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" `); - }); + }); - it('shouldRemoveSWCFlag = false', async () => { - const prompt = webpack5CompilerSetup.prompt({ - shouldRemoveSWCFlag: false, - isNextJs: false, - defaultCompiler: CoreWebpackCompilers.SWC, - }); + it('shouldRemoveSWCFlag = false', async () => { + const prompt = webpack5CompilerSetup.prompt({ + shouldRemoveSWCFlag: false, + isNextJs: false, + defaultCompiler: CoreWebpackCompilers.SWC, + }); - expect(prompt).toMatchInlineSnapshot(` + expect(prompt).toMatchInlineSnapshot(` "Storybook's Webpack5 builder is now compiler agnostic, meaning you have to install an additional addon to set up a compiler for Webpack5. We have detected, that you want to use SWC as the compiler for Webpack5. @@ -403,16 +434,16 @@ describe('check function', () => { After the migration, you can switch Webpack5 compilers by swapping the addon in your project. You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" `); - }); + }); - it('defaultCompiler = babel', async () => { - const prompt = webpack5CompilerSetup.prompt({ - shouldRemoveSWCFlag: false, - isNextJs: false, - defaultCompiler: CoreWebpackCompilers.Babel, - }); + it('defaultCompiler = babel', async () => { + const prompt = webpack5CompilerSetup.prompt({ + shouldRemoveSWCFlag: false, + isNextJs: false, + defaultCompiler: CoreWebpackCompilers.Babel, + }); - expect(prompt).toMatchInlineSnapshot(` + expect(prompt).toMatchInlineSnapshot(` "Storybook's Webpack5 builder is now compiler agnostic, meaning you can choose a compiler addon that best fits your project: - Babel: A vast ecosystem and is battle-tested. It's a robust choice if you have an extensive Babel setup or need specific Babel plugins for your project. @@ -423,16 +454,16 @@ describe('check function', () => { After the migration, you can switch Webpack5 compilers by swapping the addon in your project. You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" `); - }); + }); - it('isNextJs = true', () => { - const prompt = webpack5CompilerSetup.prompt({ - shouldRemoveSWCFlag: true, - isNextJs: true, - defaultCompiler: CoreWebpackCompilers.SWC, - }); + it('isNextJs = true', () => { + const prompt = webpack5CompilerSetup.prompt({ + shouldRemoveSWCFlag: true, + isNextJs: true, + defaultCompiler: CoreWebpackCompilers.SWC, + }); - expect(prompt).toMatchInlineSnapshot(` + expect(prompt).toMatchInlineSnapshot(` "We need to update your Storybook configuration for Webpack 5. The framework.options.builder.useSWC flag will be removed. @@ -444,16 +475,16 @@ describe('check function', () => { Storybook will use SWC as the compiler. - If you don't have a babel.config.js file in your project, Storybook will use SWC as the compiler." `); - }); + }); - it('isNextjs = false AND defaultCompiler = swc', () => { - const prompt = webpack5CompilerSetup.prompt({ - shouldRemoveSWCFlag: false, - isNextJs: false, - defaultCompiler: CoreWebpackCompilers.SWC, - }); + it('isNextjs = false AND defaultCompiler = swc', () => { + const prompt = webpack5CompilerSetup.prompt({ + shouldRemoveSWCFlag: false, + isNextJs: false, + defaultCompiler: CoreWebpackCompilers.SWC, + }); - expect(prompt).toMatchInlineSnapshot(` + expect(prompt).toMatchInlineSnapshot(` "Storybook's Webpack5 builder is now compiler agnostic, meaning you have to install an additional addon to set up a compiler for Webpack5. We have detected, that you want to use SWC as the compiler for Webpack5. @@ -463,16 +494,16 @@ describe('check function', () => { After the migration, you can switch Webpack5 compilers by swapping the addon in your project. You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" `); - }); + }); - it('isNextjs = false AND defaultCompiler = babel', () => { - const prompt = webpack5CompilerSetup.prompt({ - shouldRemoveSWCFlag: false, - isNextJs: false, - defaultCompiler: CoreWebpackCompilers.Babel, - }); + it('isNextjs = false AND defaultCompiler = babel', () => { + const prompt = webpack5CompilerSetup.prompt({ + shouldRemoveSWCFlag: false, + isNextJs: false, + defaultCompiler: CoreWebpackCompilers.Babel, + }); - expect(prompt).toMatchInlineSnapshot(` + expect(prompt).toMatchInlineSnapshot(` "Storybook's Webpack5 builder is now compiler agnostic, meaning you can choose a compiler addon that best fits your project: - Babel: A vast ecosystem and is battle-tested. It's a robust choice if you have an extensive Babel setup or need specific Babel plugins for your project. @@ -483,6 +514,5 @@ describe('check function', () => { After the migration, you can switch Webpack5 compilers by swapping the addon in your project. You can find more information here: https://storybook.js.org/docs/8.0/builders/webpack#compiler-support" `); - }); }); }); diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts index 192b6ca54f70..c4ef5740412d 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts @@ -26,7 +26,7 @@ type Options = { isNextJs: boolean; }; -export const webpack5CompilerSetup: Fix = { +export const webpack5CompilerSetup = { id: 'webpack5-compiler-setup', promptType(result) { @@ -182,7 +182,7 @@ export const webpack5CompilerSetup: Fix = { }); } }, -}; +} satisfies Fix; async function askUserForCompilerChoice() { const response = await prompts<'compiler'>({ From fd6a748190f09d9fee906357daaead829ca4243f Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Mon, 19 Feb 2024 09:46:34 +0100 Subject: [PATCH 31/44] rename incorrectly named type interfaces --- code/lib/cli/src/automigrate/fixes/vite-config-file.ts | 4 ++-- code/lib/cli/src/automigrate/fixes/vite4.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/vite-config-file.ts b/code/lib/cli/src/automigrate/fixes/vite-config-file.ts index f8047a839af9..1719035fc220 100644 --- a/code/lib/cli/src/automigrate/fixes/vite-config-file.ts +++ b/code/lib/cli/src/automigrate/fixes/vite-config-file.ts @@ -5,7 +5,7 @@ import { getFrameworkPackageName } from '../helpers/mainConfigFile'; import { frameworkToRenderer } from '../../helpers'; import { frameworkPackages } from '@storybook/core-common'; -interface Webpack5RunOptions { +interface ViteConfigFileRunOptions { plugins: string[]; existed: boolean; } @@ -108,4 +108,4 @@ export const viteConfigFile = { This change was necessary to support newer versions of Vite. `; }, -} satisfies Fix; +} satisfies Fix; diff --git a/code/lib/cli/src/automigrate/fixes/vite4.ts b/code/lib/cli/src/automigrate/fixes/vite4.ts index d04c4abd10d7..f85cc4d57cef 100644 --- a/code/lib/cli/src/automigrate/fixes/vite4.ts +++ b/code/lib/cli/src/automigrate/fixes/vite4.ts @@ -5,7 +5,7 @@ import type { Fix } from '../types'; const logger = console; -interface Webpack5RunOptions { +interface Vite4RunOptions { viteVersion: string | null; } @@ -40,4 +40,4 @@ export const vite4 = { await packageManager.addDependencies({ installAsDevDependencies: true }, deps); } }, -} satisfies Fix; +} satisfies Fix; From aeba7508d366ef7624d7d144e7376beff8baf77f Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 19 Feb 2024 10:53:49 +0100 Subject: [PATCH 32/44] Support both supported babel.config.js files in Next.js --- code/frameworks/nextjs/src/preset.ts | 3 ++- .../src/automigrate/fixes/webpack5-compiler-setup.ts | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/code/frameworks/nextjs/src/preset.ts b/code/frameworks/nextjs/src/preset.ts index bb7ac0ddf153..e76db8104d1b 100644 --- a/code/frameworks/nextjs/src/preset.ts +++ b/code/frameworks/nextjs/src/preset.ts @@ -117,7 +117,8 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = async (baseConfig, }); const babelRCPath = join(getProjectRoot(), '.babelrc'); - const hasBabelConfig = fs.existsSync(babelRCPath); + const babelConfigPath = join(getProjectRoot(), 'babel.config.js'); + const hasBabelConfig = fs.existsSync(babelRCPath) || fs.existsSync(babelConfigPath); const nextjsVersion = getNextjsVersion(); const isDevelopment = options.configType !== 'PRODUCTION'; diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts index c4ef5740412d..0f95772ba214 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts @@ -120,16 +120,18 @@ export const webpack5CompilerSetup = { message.push(dedent` Storybook now detects whether it should use Babel or SWC as a compiler by applying the same logic as Next.js itself:\n - If you have a ${chalk.yellow( - 'babel.config.js' + '.babelrc' )} file in your project, Storybook will use Babel as the compiler. - - If you have a ${chalk.yellow('babel.config.js')} file in your project and you have set + - If you have a ${chalk.yellow('.babelrc')} (or ${chalk.yellow( + 'babel.config.js' + )}) file in your project and you have set ${chalk.yellow('experimental.forceSwcTransforms = true')} in your ${chalk.yellow( 'next.config.js' )} file, Storybook will use SWC as the compiler. - - If you don't have a ${chalk.yellow( + - If you don't have a ${chalk.yellow('.babelrc')} (or ${chalk.yellow( 'babel.config.js' - )} file in your project, Storybook will use SWC as the compiler. + )}) file in your project, Storybook will use SWC as the compiler. `); } else if (defaultCompiler === CoreWebpackCompilers.Babel) { message.push(dedent` From cbe6f3136e5b774a70f01dd55e028d3dbd54964e Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 19 Feb 2024 10:54:23 +0100 Subject: [PATCH 33/44] Update prompt value from 'notified' to 'notification' --- code/lib/cli/src/automigrate/index.ts | 2 +- code/lib/cli/src/automigrate/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/lib/cli/src/automigrate/index.ts b/code/lib/cli/src/automigrate/index.ts index f29a5263160a..0a5980230c76 100644 --- a/code/lib/cli/src/automigrate/index.ts +++ b/code/lib/cli/src/automigrate/index.ts @@ -230,7 +230,7 @@ export async function runFixes({ return 'Automigration detected'; case 'manual': return 'Manual migration detected'; - case 'notified': + case 'notification': return 'Migration notification'; } }; diff --git a/code/lib/cli/src/automigrate/types.ts b/code/lib/cli/src/automigrate/types.ts index 6ef4f243b9ea..98c9524afe87 100644 --- a/code/lib/cli/src/automigrate/types.ts +++ b/code/lib/cli/src/automigrate/types.ts @@ -25,7 +25,7 @@ export interface RunOptions { * - manual: the user will be prompted to apply the fix * - notified: the user will be notified about the some changes. A fix isn't required */ -export type Prompt = 'auto' | 'manual' | 'notified'; +export type Prompt = 'auto' | 'manual' | 'notification'; export interface Fix { id: string; From bbcaba152c02aa0f413ec9f14bc4f1d4796a05ae Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 19 Feb 2024 10:55:22 +0100 Subject: [PATCH 34/44] Update tests --- .../fixes/webpack5-compiler-setup.test.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts index db437fbb13df..426496fcac22 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts @@ -464,17 +464,17 @@ describe('prompt', () => { }); expect(prompt).toMatchInlineSnapshot(` - "We need to update your Storybook configuration for Webpack 5. - The framework.options.builder.useSWC flag will be removed. + "We need to update your Storybook configuration for Webpack 5. + The framework.options.builder.useSWC flag will be removed. - Storybook now detects whether it should use Babel or SWC as a compiler by applying the same logic as Next.js itself: + Storybook now detects whether it should use Babel or SWC as a compiler by applying the same logic as Next.js itself: - - If you have a babel.config.js file in your project, Storybook will use Babel as the compiler. - - If you have a babel.config.js file in your project and you have set - experimental.forceSwcTransforms = true in your next.config.js file, - Storybook will use SWC as the compiler. - - If you don't have a babel.config.js file in your project, Storybook will use SWC as the compiler." - `); + - If you have a .babelrc file in your project, Storybook will use Babel as the compiler. + - If you have a .babelrc (or babel.config.js) file in your project and you have set + experimental.forceSwcTransforms = true in your next.config.js file, + Storybook will use SWC as the compiler. + - If you don't have a .babelrc (or babel.config.js) file in your project, Storybook will use SWC as the compiler." + `); }); it('isNextjs = false AND defaultCompiler = swc', () => { From cbed15010ff22fd13b701365bec68971323bdb55 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 19 Feb 2024 10:56:56 +0100 Subject: [PATCH 35/44] Update webpack5 compiler setup to use Babel or SWC based on project configuration --- .../src/automigrate/fixes/webpack5-compiler-setup.test.ts | 2 +- .../cli/src/automigrate/fixes/webpack5-compiler-setup.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts index 426496fcac22..5cc0d34dd892 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts @@ -469,7 +469,7 @@ describe('prompt', () => { Storybook now detects whether it should use Babel or SWC as a compiler by applying the same logic as Next.js itself: - - If you have a .babelrc file in your project, Storybook will use Babel as the compiler. + - If you have a .babelrc (or babel.config.js) file in your project, Storybook will use Babel as the compiler. - If you have a .babelrc (or babel.config.js) file in your project and you have set experimental.forceSwcTransforms = true in your next.config.js file, Storybook will use SWC as the compiler. diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts index 0f95772ba214..99e70d8bec6a 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts @@ -119,9 +119,9 @@ export const webpack5CompilerSetup = { if (isNextJs) { message.push(dedent` Storybook now detects whether it should use Babel or SWC as a compiler by applying the same logic as Next.js itself:\n - - If you have a ${chalk.yellow( - '.babelrc' - )} file in your project, Storybook will use Babel as the compiler. + - If you have a ${chalk.yellow('.babelrc')} (or ${chalk.yellow( + 'babel.config.js' + )}) file in your project, Storybook will use Babel as the compiler. - If you have a ${chalk.yellow('.babelrc')} (or ${chalk.yellow( 'babel.config.js' )}) file in your project and you have set From 675c289f3a0dd61b32012b61f8acacf0c2a0859c Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 19 Feb 2024 11:42:21 +0100 Subject: [PATCH 36/44] Fix promptType value in webpack5-compiler-setup --- .../cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts | 4 ++-- code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts | 2 +- code/lib/cli/src/automigrate/index.ts | 2 +- code/lib/cli/src/automigrate/types.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts index 5cc0d34dd892..dcebc1f28b8b 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts @@ -363,14 +363,14 @@ describe('webpack5Migration check function', () => { }); describe('promptOnly', () => { - it('should return notified if isNextJs = true and shouldRemoveSWCFlag = false', () => { + it('should return notification if isNextJs = true and shouldRemoveSWCFlag = false', () => { const promptType = webpack5CompilerSetup.promptType({ isNextJs: true, shouldRemoveSWCFlag: false, defaultCompiler: undefined, }); - expect(promptType).toBe('notified'); + expect(promptType).toBe('notification'); }); it('should return auto if isNextJs = false', () => { diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts index 99e70d8bec6a..a22f8a55fdd0 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts @@ -30,7 +30,7 @@ export const webpack5CompilerSetup = { id: 'webpack5-compiler-setup', promptType(result) { - return result.isNextJs && !result.shouldRemoveSWCFlag ? 'notified' : 'auto'; + return result.isNextJs && !result.shouldRemoveSWCFlag ? 'notification' : 'auto'; }, async check({ mainConfig, packageManager }) { diff --git a/code/lib/cli/src/automigrate/index.ts b/code/lib/cli/src/automigrate/index.ts index 0a5980230c76..1e78d57863ce 100644 --- a/code/lib/cli/src/automigrate/index.ts +++ b/code/lib/cli/src/automigrate/index.ts @@ -295,7 +295,7 @@ export async function runFixes({ }, } ); - } else if (promptType === 'notified') { + } else if (promptType === 'notification') { runAnswer = await prompts( { type: 'confirm', diff --git a/code/lib/cli/src/automigrate/types.ts b/code/lib/cli/src/automigrate/types.ts index 98c9524afe87..510cdcda24c1 100644 --- a/code/lib/cli/src/automigrate/types.ts +++ b/code/lib/cli/src/automigrate/types.ts @@ -23,7 +23,7 @@ export interface RunOptions { * promptType defines how the user will be prompted to apply an automigration fix * - auto: the fix will be applied automatically * - manual: the user will be prompted to apply the fix - * - notified: the user will be notified about the some changes. A fix isn't required + * - notification: the user will be notified about the some changes. A fix isn't required */ export type Prompt = 'auto' | 'manual' | 'notification'; From f090f7abf58b81404ca908a1843228888ec97a11 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Mon, 19 Feb 2024 21:44:44 +1100 Subject: [PATCH 37/44] Fix name of Vite TS plugin --- code/builders/builder-vite/src/build.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/builders/builder-vite/src/build.ts b/code/builders/builder-vite/src/build.ts index 1cd14d1b2d8d..ff8f46c8bed3 100644 --- a/code/builders/builder-vite/src/build.ts +++ b/code/builders/builder-vite/src/build.ts @@ -53,6 +53,9 @@ export async function build(options: Options) { await viteBuild(await sanitizeEnvVars(options, finalConfig)); - const statsPlugin = findPlugin(finalConfig, 'rollup-plugin-webpack-stats') as WebpackStatsPlugin; + const statsPlugin = findPlugin( + finalConfig, + 'storybook:rollup-plugin-webpack-stats' + ) as WebpackStatsPlugin; return statsPlugin?.storybookGetStats(); } From d47fe18e54b1bc8dbf227ddb5e856be9f5f79089 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 19 Feb 2024 12:28:27 +0100 Subject: [PATCH 38/44] Update promptType to 'manual' in removeArgtypesRegex fix --- code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts index 153d988630c9..6e5d26583453 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts @@ -8,7 +8,7 @@ import chalk from 'chalk'; export const removeArgtypesRegex: Fix<{ argTypesRegex: NodePath; previewConfigPath: string }> = { id: 'remove-argtypes-regex', - promptOnly: true, + promptType: 'manual', async check({ previewConfigPath }) { if (!previewConfigPath) return null; From 952dba5774039c78c2c62ab8ff421b360de278a9 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Mon, 19 Feb 2024 12:08:07 +0100 Subject: [PATCH 39/44] Warn when argTypesRegex is used together with the visual test addon --- .../src/utils/get-storybook-info.ts | 2 +- code/lib/core-server/package.json | 1 + code/lib/core-server/src/build-dev.ts | 9 +- .../src/utils/warnWhenUsingArgTypesRegex.ts | 54 ++++++++++++ code/yarn.lock | 84 +++++++++++++++++++ 5 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 code/lib/core-server/src/utils/warnWhenUsingArgTypesRegex.ts diff --git a/code/lib/core-common/src/utils/get-storybook-info.ts b/code/lib/core-common/src/utils/get-storybook-info.ts index 642db99a7485..2c90d90fd674 100644 --- a/code/lib/core-common/src/utils/get-storybook-info.ts +++ b/code/lib/core-common/src/utils/get-storybook-info.ts @@ -94,7 +94,7 @@ export const findConfigFile = (prefix: string, configDir: string) => { return extension ? `${filePrefix}.${extension}` : null; }; -const getConfigInfo = (packageJson: PackageJson, configDir?: string) => { +export const getConfigInfo = (packageJson: PackageJson, configDir?: string) => { let storybookConfigDir = configDir ?? '.storybook'; const storybookScript = packageJson.scripts?.['storybook']; if (storybookScript && !configDir) { diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json index ff37db230991..dc024488a7f8 100644 --- a/code/lib/core-server/package.json +++ b/code/lib/core-server/package.json @@ -56,6 +56,7 @@ }, "dependencies": { "@aw-web-design/x-default-browser": "1.4.126", + "@babel/core": "^7.23.9", "@discoveryjs/json-ext": "^0.5.3", "@storybook/builder-manager": "workspace:*", "@storybook/channels": "workspace:*", diff --git a/code/lib/core-server/src/build-dev.ts b/code/lib/core-server/src/build-dev.ts index 67990ff2d430..cfab99ef9f2e 100644 --- a/code/lib/core-server/src/build-dev.ts +++ b/code/lib/core-server/src/build-dev.ts @@ -11,7 +11,7 @@ import { import prompts from 'prompts'; import invariant from 'tiny-invariant'; import { global } from '@storybook/global'; -import { telemetry, oneWayHash } from '@storybook/telemetry'; +import { oneWayHash, telemetry } from '@storybook/telemetry'; import { join, relative, resolve } from 'path'; import { deprecate } from '@storybook/node-logger'; @@ -22,9 +22,10 @@ import { storybookDevServer } from './dev-server'; 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 { getServerChannelUrl, getServerPort } from './utils/server-address'; import { getManagerBuilder, getPreviewBuilder } from './utils/get-builders'; import { warnOnIncompatibleAddons } from './utils/warnOnIncompatibleAddons'; +import { warnWhenUsingArgTypesRegex } from './utils/warnWhenUsingArgTypesRegex'; import { buildOrThrow } from './utils/build-or-throw'; export async function buildDevStandalone( @@ -93,6 +94,10 @@ export async function buildDevStandalone( console.warn('Storybook failed to check addon compatibility', e); } + try { + await warnWhenUsingArgTypesRegex(packageJson, configDir, config); + } catch (e) {} + // Load first pass: We need to determine the builder // We need to do this because builders might introduce 'overridePresets' which we need to take into account // We hope to remove this in SB8 diff --git a/code/lib/core-server/src/utils/warnWhenUsingArgTypesRegex.ts b/code/lib/core-server/src/utils/warnWhenUsingArgTypesRegex.ts new file mode 100644 index 000000000000..1518dd3f8aca --- /dev/null +++ b/code/lib/core-server/src/utils/warnWhenUsingArgTypesRegex.ts @@ -0,0 +1,54 @@ +import type { PackageJson, StorybookConfig } from '@storybook/types'; +import { getConfigInfo } from '@storybook/core-common'; +import { readFile } from 'fs-extra'; +import * as babel from '@babel/core'; +import type { BabelFile } from '@babel/core'; +import { babelParse } from '@storybook/csf-tools'; +import dedent from 'ts-dedent'; +import chalk from 'chalk'; + +export async function warnWhenUsingArgTypesRegex( + packageJson: PackageJson, + configDir: string, + config: StorybookConfig +) { + const { previewConfig } = getConfigInfo(packageJson, configDir); + const previewContent = previewConfig ? await readFile(previewConfig, 'utf8') : ''; + + const hasVisualTestAddon = + config?.addons?.some((it) => + typeof it === 'string' + ? it === '@chromatic-com/storybook' + : it.name === '@chromatic-com/storybook' + ) ?? false; + + if (hasVisualTestAddon && previewConfig && previewContent.includes('argTypesRegex')) { + // @ts-expect-error File is not yet exposed, see https://github.com/babel/babel/issues/11350#issuecomment-644118606 + const file: BabelFile = new babel.File( + { filename: previewConfig }, + { code: previewContent, ast: babelParse(previewContent) } + ); + + file.path.traverse({ + Identifier: (path) => { + if (path.node.name === 'argTypesRegex') { + const message = dedent` + ${chalk.bold('Attention')}: We've detected that you're using ${chalk.cyan( + 'actions.argTypesRegex' + )} together with the visual test addon: + + ${path.buildCodeFrameError(previewConfig).message} + + We recommend removing the ${chalk.cyan( + 'argTypesRegex' + )} and assigning explicit action with the ${chalk.cyan( + 'fn' + )} function from ${chalk.cyan('@storybook/test')} instead. + The build used by the addon for snapshot testing doesn't take the regex into account, which can cause hard to debug problems when a snapshot depends on the presence of action props. + `; + console.warn(message); + } + }, + }); + } +} diff --git a/code/yarn.lock b/code/yarn.lock index 86f09cd405d2..8b5a956afd3f 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -467,6 +467,29 @@ __metadata: languageName: node linkType: hard +"@babel/core@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/core@npm:7.23.9" + dependencies: + "@ampproject/remapping": "npm:^2.2.0" + "@babel/code-frame": "npm:^7.23.5" + "@babel/generator": "npm:^7.23.6" + "@babel/helper-compilation-targets": "npm:^7.23.6" + "@babel/helper-module-transforms": "npm:^7.23.3" + "@babel/helpers": "npm:^7.23.9" + "@babel/parser": "npm:^7.23.9" + "@babel/template": "npm:^7.23.9" + "@babel/traverse": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 03883300bf1252ab4c9ba5b52f161232dd52873dbe5cde9289bb2bb26e935c42682493acbac9194a59a3b6cbd17f4c4c84030db8d6d482588afe64531532ff9b + languageName: node + linkType: hard + "@babel/generator@npm:7.23.0": version: 7.23.0 resolution: "@babel/generator@npm:7.23.0" @@ -740,6 +763,17 @@ __metadata: languageName: node linkType: hard +"@babel/helpers@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/helpers@npm:7.23.9" + dependencies: + "@babel/template": "npm:^7.23.9" + "@babel/traverse": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" + checksum: f69fd0aca96a6fb8bd6dd044cd8a5c0f1851072d4ce23355345b9493c4032e76d1217f86b70df795e127553cf7f3fcd1587ede9d1b03b95e8b62681ca2165b87 + languageName: node + linkType: hard + "@babel/highlight@npm:^7.23.4": version: 7.23.4 resolution: "@babel/highlight@npm:7.23.4" @@ -760,6 +794,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/parser@npm:7.23.9" + bin: + parser: ./bin/babel-parser.js + checksum: 7df97386431366d4810538db4b9ec538f4377096f720c0591c7587a16f6810e62747e9fbbfa1ff99257fd4330035e4fb1b5b77c7bd3b97ce0d2e3780a6618975 + languageName: node + linkType: hard + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.15, @babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.23.3": version: 7.23.3 resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.23.3" @@ -2220,6 +2263,17 @@ __metadata: languageName: node linkType: hard +"@babel/template@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/template@npm:7.23.9" + dependencies: + "@babel/code-frame": "npm:^7.23.5" + "@babel/parser": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" + checksum: 0e8b60119433787742bc08ae762bbd8d6755611c4cabbcb7627b292ec901a55af65d93d1c88572326069efb64136ef151ec91ffb74b2df7689bbab237030833a + languageName: node + linkType: hard + "@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.23.2, @babel/traverse@npm:^7.23.7, @babel/traverse@npm:^7.4.5": version: 7.23.7 resolution: "@babel/traverse@npm:7.23.7" @@ -2238,6 +2292,24 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/traverse@npm:7.23.9" + dependencies: + "@babel/code-frame": "npm:^7.23.5" + "@babel/generator": "npm:^7.23.6" + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-function-name": "npm:^7.23.0" + "@babel/helper-hoist-variables": "npm:^7.22.5" + "@babel/helper-split-export-declaration": "npm:^7.22.6" + "@babel/parser": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" + debug: "npm:^4.3.1" + globals: "npm:^11.1.0" + checksum: d1615d1d02f04d47111a7ea4446a1a6275668ca39082f31d51f08380de9502e19862be434eaa34b022ce9a17dbb8f9e2b73a746c654d9575f3a680a7ffdf5630 + languageName: node + linkType: hard + "@babel/types@npm:^7.0.0, @babel/types@npm:^7.11.5, @babel/types@npm:^7.18.9, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.4, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.4, @babel/types@npm:^7.23.6, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.6.1, @babel/types@npm:^7.7.2, @babel/types@npm:^7.8.3, @babel/types@npm:^7.9.6": version: 7.23.6 resolution: "@babel/types@npm:7.23.6" @@ -2249,6 +2321,17 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/types@npm:7.23.9" + dependencies: + "@babel/helper-string-parser": "npm:^7.23.4" + "@babel/helper-validator-identifier": "npm:^7.22.20" + to-fast-properties: "npm:^2.0.0" + checksum: edc7bb180ce7e4d2aea10c6972fb10474341ac39ba8fdc4a27ffb328368dfdfbf40fca18e441bbe7c483774500d5c05e222cec276c242e952853dcaf4eb884f7 + languageName: node + linkType: hard + "@base2/pretty-print-object@npm:1.0.1": version: 1.0.1 resolution: "@base2/pretty-print-object@npm:1.0.1" @@ -5538,6 +5621,7 @@ __metadata: resolution: "@storybook/core-server@workspace:lib/core-server" dependencies: "@aw-web-design/x-default-browser": "npm:1.4.126" + "@babel/core": "npm:^7.23.9" "@discoveryjs/json-ext": "npm:^0.5.3" "@storybook/addon-docs": "workspace:*" "@storybook/builder-manager": "workspace:*" From 88cea6fc73e9556a7a2dbbd417574b82ca6423b3 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Mon, 19 Feb 2024 14:36:46 +0100 Subject: [PATCH 40/44] Update code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts Co-authored-by: Valentin Palkovic --- code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts index 6e5d26583453..0402773df5ea 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts @@ -73,6 +73,8 @@ export const removeArgtypesRegex: Fix<{ argTypesRegex: NodePath; previewConfigPa )} Make sure to assign an explicit ${chalk.cyan('fn')} to your args for those usages. + + For more information please visit our migration guide: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#implicit-actions-can-not-be-used-during-rendering-for-example-in-the-play-function `; }, }; From 15c84372baaf9a27d5186bb547a1e3888542f7ac Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Mon, 19 Feb 2024 14:59:55 +0100 Subject: [PATCH 41/44] Add link to docs --- code/lib/core-server/src/utils/warnWhenUsingArgTypesRegex.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/lib/core-server/src/utils/warnWhenUsingArgTypesRegex.ts b/code/lib/core-server/src/utils/warnWhenUsingArgTypesRegex.ts index 1518dd3f8aca..fa336373a8a4 100644 --- a/code/lib/core-server/src/utils/warnWhenUsingArgTypesRegex.ts +++ b/code/lib/core-server/src/utils/warnWhenUsingArgTypesRegex.ts @@ -43,7 +43,9 @@ export async function warnWhenUsingArgTypesRegex( 'argTypesRegex' )} and assigning explicit action with the ${chalk.cyan( 'fn' - )} function from ${chalk.cyan('@storybook/test')} instead. + )} function from ${chalk.cyan('@storybook/test')} instead: + https://storybook.js.org/docs/essentials/actions#via-storybooktest-fn-spy-function + The build used by the addon for snapshot testing doesn't take the regex into account, which can cause hard to debug problems when a snapshot depends on the presence of action props. `; console.warn(message); From 8422756247169a123dc1222b5b05e3a345a7166f Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Mon, 19 Feb 2024 15:44:39 +0100 Subject: [PATCH 42/44] remove the logging to file feature from autoblockers --- .../src/autoblock/block-dependencies-versions.ts | 3 --- code/lib/cli/src/autoblock/block-node-version.ts | 3 --- code/lib/cli/src/autoblock/block-stories-mdx.ts | 11 +++++------ code/lib/cli/src/autoblock/block-storystorev6.ts | 5 ----- code/lib/cli/src/autoblock/index.ts | 16 +--------------- code/lib/cli/src/autoblock/types.ts | 7 ------- 6 files changed, 6 insertions(+), 39 deletions(-) diff --git a/code/lib/cli/src/autoblock/block-dependencies-versions.ts b/code/lib/cli/src/autoblock/block-dependencies-versions.ts index 284562aa9f6d..fb052e60c62e 100644 --- a/code/lib/cli/src/autoblock/block-dependencies-versions.ts +++ b/code/lib/cli/src/autoblock/block-dependencies-versions.ts @@ -43,9 +43,6 @@ export const blocker = createBlocker({ return acc; }, false); }, - message(options, data) { - return `Found ${data.packageName} version: ${data.installedVersion}, please upgrade to ${data.minimumVersion} or higher.`; - }, log(options, data) { switch (data.packageName) { case 'react-scripts': diff --git a/code/lib/cli/src/autoblock/block-node-version.ts b/code/lib/cli/src/autoblock/block-node-version.ts index 220b29823e4e..8d67cdd51018 100644 --- a/code/lib/cli/src/autoblock/block-node-version.ts +++ b/code/lib/cli/src/autoblock/block-node-version.ts @@ -11,9 +11,6 @@ export const blocker = createBlocker({ } return false; }, - message(options, data) { - return `Please use Node.js v18 or higher.`; - }, log(options, data) { return dedent` We've detected you're using Node.js v${data.nodeVersion}. diff --git a/code/lib/cli/src/autoblock/block-stories-mdx.ts b/code/lib/cli/src/autoblock/block-stories-mdx.ts index b868d913ecd0..3c1fadeda351 100644 --- a/code/lib/cli/src/autoblock/block-stories-mdx.ts +++ b/code/lib/cli/src/autoblock/block-stories-mdx.ts @@ -11,12 +11,7 @@ export const blocker = createBlocker({ } return { files }; }, - message(options, data) { - return `Found ${data.files.length} stories.mdx ${ - data.files.length === 1 ? 'file' : 'files' - }, these must be migrated.`; - }, - log() { + log(options, data) { return dedent` Support for *.stories.mdx files has been removed. Please see the migration guide for more information: @@ -26,6 +21,10 @@ export const blocker = createBlocker({ Check the migration guide for more information: https://mdxjs.com/blog/v3/ + Found ${data.files.length} stories.mdx ${ + data.files.length === 1 ? 'file' : 'files' + }, these must be migrated. + Manually run the migration script to convert your stories.mdx files to CSF format documented here: https://storybook.js.org/docs/migration-guide#storiesmdx-to-mdxcsf `; diff --git a/code/lib/cli/src/autoblock/block-storystorev6.ts b/code/lib/cli/src/autoblock/block-storystorev6.ts index 40a9f8822ac9..cd9eaffb6a7a 100644 --- a/code/lib/cli/src/autoblock/block-storystorev6.ts +++ b/code/lib/cli/src/autoblock/block-storystorev6.ts @@ -1,4 +1,3 @@ -import { relative } from 'path'; import { createBlocker } from './types'; import { dedent } from 'ts-dedent'; import type { StorybookConfigRaw } from '@storybook/types'; @@ -15,10 +14,6 @@ export const blocker = createBlocker({ } return false; }, - message(options, data) { - const mainConfigPath = relative(process.cwd(), options.mainConfigPath); - return `StoryStoreV7 feature must be removed from ${mainConfigPath}`; - }, log() { return dedent` StoryStoreV7 feature must be removed from your Storybook configuration. diff --git a/code/lib/cli/src/autoblock/index.ts b/code/lib/cli/src/autoblock/index.ts index ca8116d890cb..a6c45a2318ba 100644 --- a/code/lib/cli/src/autoblock/index.ts +++ b/code/lib/cli/src/autoblock/index.ts @@ -2,7 +2,6 @@ import type { AutoblockOptions, Blocker } from './types'; import { logger } from '@storybook/node-logger'; import chalk from 'chalk'; import boxen from 'boxen'; -import { writeFile } from 'node:fs/promises'; const excludesFalse = (x: T | false): x is T => x !== false; @@ -34,7 +33,6 @@ export const autoblock = async ( return { id: blocker.id, value: true, - message: blocker.message(options, result), log: blocker.log(options, result), }; } else { @@ -46,12 +44,9 @@ export const autoblock = async ( const faults = out.filter(excludesFalse); if (faults.length > 0) { - const LOG_FILE_NAME = 'migration-storybook.log'; - const messages = { welcome: `Blocking your upgrade because of the following issues:`, reminder: chalk.yellow('Fix the above issues and try running the upgrade command again.'), - logfile: chalk.yellow(`You can find more details in ./${LOG_FILE_NAME}.`), }; const borderColor = '#FC521F'; @@ -59,22 +54,13 @@ export const autoblock = async ( logger.plain( boxen( [messages.welcome] - .concat(faults.map((i) => i.message)) + .concat(faults.map((i) => i.log)) .concat([messages.reminder]) - .concat([messages.logfile]) .join('\n\n'), { borderStyle: 'round', padding: 1, borderColor } ) ); - await writeFile( - LOG_FILE_NAME, - faults.map((i) => '(' + i.id + '):\n' + i.log).join('\n\n----\n\n'), - { - encoding: 'utf-8', - } - ); - return faults[0].id; } diff --git a/code/lib/cli/src/autoblock/types.ts b/code/lib/cli/src/autoblock/types.ts index 62be9625c76e..39e6c728921a 100644 --- a/code/lib/cli/src/autoblock/types.ts +++ b/code/lib/cli/src/autoblock/types.ts @@ -21,13 +21,6 @@ export interface Blocker { * @returns A truthy value to activate the block, return false to proceed. */ check: (options: AutoblockOptions) => Promise; - /** - * Format a message to be printed to the log-file. - * @param context - * @param data returned from the check method. - * @returns The string to print to the terminal. - */ - message: (options: AutoblockOptions, data: T) => string; /** * Format a message to be printed to the log-file. * @param context From 16311805589b376f2efa6a28ea3049ccd9da6d41 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Mon, 19 Feb 2024 15:45:20 +0100 Subject: [PATCH 43/44] Revert "remove the logging to file feature from autoblockers" This reverts commit 8422756247169a123dc1222b5b05e3a345a7166f. --- .../src/autoblock/block-dependencies-versions.ts | 3 +++ code/lib/cli/src/autoblock/block-node-version.ts | 3 +++ code/lib/cli/src/autoblock/block-stories-mdx.ts | 11 ++++++----- code/lib/cli/src/autoblock/block-storystorev6.ts | 5 +++++ code/lib/cli/src/autoblock/index.ts | 16 +++++++++++++++- code/lib/cli/src/autoblock/types.ts | 7 +++++++ 6 files changed, 39 insertions(+), 6 deletions(-) diff --git a/code/lib/cli/src/autoblock/block-dependencies-versions.ts b/code/lib/cli/src/autoblock/block-dependencies-versions.ts index fb052e60c62e..284562aa9f6d 100644 --- a/code/lib/cli/src/autoblock/block-dependencies-versions.ts +++ b/code/lib/cli/src/autoblock/block-dependencies-versions.ts @@ -43,6 +43,9 @@ export const blocker = createBlocker({ return acc; }, false); }, + message(options, data) { + return `Found ${data.packageName} version: ${data.installedVersion}, please upgrade to ${data.minimumVersion} or higher.`; + }, log(options, data) { switch (data.packageName) { case 'react-scripts': diff --git a/code/lib/cli/src/autoblock/block-node-version.ts b/code/lib/cli/src/autoblock/block-node-version.ts index 8d67cdd51018..220b29823e4e 100644 --- a/code/lib/cli/src/autoblock/block-node-version.ts +++ b/code/lib/cli/src/autoblock/block-node-version.ts @@ -11,6 +11,9 @@ export const blocker = createBlocker({ } return false; }, + message(options, data) { + return `Please use Node.js v18 or higher.`; + }, log(options, data) { return dedent` We've detected you're using Node.js v${data.nodeVersion}. diff --git a/code/lib/cli/src/autoblock/block-stories-mdx.ts b/code/lib/cli/src/autoblock/block-stories-mdx.ts index 3c1fadeda351..b868d913ecd0 100644 --- a/code/lib/cli/src/autoblock/block-stories-mdx.ts +++ b/code/lib/cli/src/autoblock/block-stories-mdx.ts @@ -11,7 +11,12 @@ export const blocker = createBlocker({ } return { files }; }, - log(options, data) { + message(options, data) { + return `Found ${data.files.length} stories.mdx ${ + data.files.length === 1 ? 'file' : 'files' + }, these must be migrated.`; + }, + log() { return dedent` Support for *.stories.mdx files has been removed. Please see the migration guide for more information: @@ -21,10 +26,6 @@ export const blocker = createBlocker({ Check the migration guide for more information: https://mdxjs.com/blog/v3/ - Found ${data.files.length} stories.mdx ${ - data.files.length === 1 ? 'file' : 'files' - }, these must be migrated. - Manually run the migration script to convert your stories.mdx files to CSF format documented here: https://storybook.js.org/docs/migration-guide#storiesmdx-to-mdxcsf `; diff --git a/code/lib/cli/src/autoblock/block-storystorev6.ts b/code/lib/cli/src/autoblock/block-storystorev6.ts index cd9eaffb6a7a..40a9f8822ac9 100644 --- a/code/lib/cli/src/autoblock/block-storystorev6.ts +++ b/code/lib/cli/src/autoblock/block-storystorev6.ts @@ -1,3 +1,4 @@ +import { relative } from 'path'; import { createBlocker } from './types'; import { dedent } from 'ts-dedent'; import type { StorybookConfigRaw } from '@storybook/types'; @@ -14,6 +15,10 @@ export const blocker = createBlocker({ } return false; }, + message(options, data) { + const mainConfigPath = relative(process.cwd(), options.mainConfigPath); + return `StoryStoreV7 feature must be removed from ${mainConfigPath}`; + }, log() { return dedent` StoryStoreV7 feature must be removed from your Storybook configuration. diff --git a/code/lib/cli/src/autoblock/index.ts b/code/lib/cli/src/autoblock/index.ts index a6c45a2318ba..ca8116d890cb 100644 --- a/code/lib/cli/src/autoblock/index.ts +++ b/code/lib/cli/src/autoblock/index.ts @@ -2,6 +2,7 @@ import type { AutoblockOptions, Blocker } from './types'; import { logger } from '@storybook/node-logger'; import chalk from 'chalk'; import boxen from 'boxen'; +import { writeFile } from 'node:fs/promises'; const excludesFalse = (x: T | false): x is T => x !== false; @@ -33,6 +34,7 @@ export const autoblock = async ( return { id: blocker.id, value: true, + message: blocker.message(options, result), log: blocker.log(options, result), }; } else { @@ -44,9 +46,12 @@ export const autoblock = async ( const faults = out.filter(excludesFalse); if (faults.length > 0) { + const LOG_FILE_NAME = 'migration-storybook.log'; + const messages = { welcome: `Blocking your upgrade because of the following issues:`, reminder: chalk.yellow('Fix the above issues and try running the upgrade command again.'), + logfile: chalk.yellow(`You can find more details in ./${LOG_FILE_NAME}.`), }; const borderColor = '#FC521F'; @@ -54,13 +59,22 @@ export const autoblock = async ( logger.plain( boxen( [messages.welcome] - .concat(faults.map((i) => i.log)) + .concat(faults.map((i) => i.message)) .concat([messages.reminder]) + .concat([messages.logfile]) .join('\n\n'), { borderStyle: 'round', padding: 1, borderColor } ) ); + await writeFile( + LOG_FILE_NAME, + faults.map((i) => '(' + i.id + '):\n' + i.log).join('\n\n----\n\n'), + { + encoding: 'utf-8', + } + ); + return faults[0].id; } diff --git a/code/lib/cli/src/autoblock/types.ts b/code/lib/cli/src/autoblock/types.ts index 39e6c728921a..62be9625c76e 100644 --- a/code/lib/cli/src/autoblock/types.ts +++ b/code/lib/cli/src/autoblock/types.ts @@ -21,6 +21,13 @@ export interface Blocker { * @returns A truthy value to activate the block, return false to proceed. */ check: (options: AutoblockOptions) => Promise; + /** + * Format a message to be printed to the log-file. + * @param context + * @param data returned from the check method. + * @returns The string to print to the terminal. + */ + message: (options: AutoblockOptions, data: T) => string; /** * Format a message to be printed to the log-file. * @param context From e20a8c2c0e7fba8f86324d587ab48d34bc7661a6 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Mon, 19 Feb 2024 16:19:49 +0100 Subject: [PATCH 44/44] Avoid long lines, as wrapping lines is buggy in boxen --- .../src/automigrate/fixes/remove-argtypes-regex.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts index 153d988630c9..273c0c648e78 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts @@ -59,13 +59,14 @@ export const removeArgtypesRegex: Fix<{ argTypesRegex: NodePath; previewConfigPa ${argTypesRegex.buildCodeFrameError(`${previewConfigPath}`).message} - In Storybook 8, we recommend removing this regex, and assigning explicit spies with the ${chalk.cyan( - 'fn' - )} function instead: + In Storybook 8, we recommend removing this regex. + Assign explicit spies with the ${chalk.cyan('fn')} function instead: ${formattedSnippet} - Even if you keep using the argTypesRegex, you will need to above pattern if you want to use spies in your play function. - Implicit spies (based on a combination of argTypesRegex and docgen) is not supported anymore in Storybook 8. + The above pattern is needed when using spies in the play function, ${chalk.bold( + 'even' + )} if you keep using argTypesRegex. + Implicit spies (based on a combination of argTypesRegex and docgen) is not supported in Storybook 8. Use the following command to check for spy usages in your play functions: ${chalk.cyan(