diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index 7de9f16085e9..f5ce13675b7d 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,11 @@ +## 10.1.0-beta.6 + +- Angular: Don't kill dev command by using observables - [#33185](https://github.com/storybookjs/storybook/pull/33185), thanks @valentinpalkovic! +- Angular: Replace deprecated import of ApplicationConfig - [#33125](https://github.com/storybookjs/storybook/pull/33125), thanks @EtiennePasteur! +- CLI: Fix passing flags for bun users during init - [#33166](https://github.com/storybookjs/storybook/pull/33166), thanks @valentinpalkovic! +- CLI: Minor improvements - [#33180](https://github.com/storybookjs/storybook/pull/33180), thanks @valentinpalkovic! +- CLI: Update upgrade message - [#33182](https://github.com/storybookjs/storybook/pull/33182), thanks @yannbf! + ## 10.1.0-beta.5 - Checklist: Autocomplete "See what's new" on URL navigation - [#33167](https://github.com/storybookjs/storybook/pull/33167), thanks @ghengeveld! diff --git a/code/core/src/cli/AddonVitestService.test.ts b/code/core/src/cli/AddonVitestService.test.ts index 4085fb8b65fe..8ffa9fb6287d 100644 --- a/code/core/src/cli/AddonVitestService.test.ts +++ b/code/core/src/cli/AddonVitestService.test.ts @@ -28,6 +28,7 @@ describe('AddonVitestService', () => { getAllDependencies: vi.fn(), getInstalledVersion: vi.fn(), runPackageCommand: vi.fn(), + getPackageCommand: vi.fn(), } as Partial as JsPackageManager; service = new AddonVitestService(mockPackageManager); @@ -366,6 +367,10 @@ describe('AddonVitestService', () => { // Mock the logger methods used in installPlaywright vi.mocked(logger.log).mockImplementation(() => {}); vi.mocked(logger.warn).mockImplementation(() => {}); + // Mock getPackageCommand to return a string + vi.mocked(mockPackageManager.getPackageCommand).mockReturnValue( + 'npx playwright install chromium --with-deps' + ); }); it('should install Playwright successfully', async () => { @@ -381,7 +386,7 @@ describe('AddonVitestService', () => { }); expect(prompt.executeTaskWithSpinner).toHaveBeenCalledWith(expect.any(Function), { id: 'playwright-installation', - intro: 'Installing Playwright browser binaries (Press "c" to abort)', + intro: 'Installing Playwright browser binaries (press "c" to abort)', error: expect.stringContaining('An error occurred'), success: 'Playwright browser binaries installed successfully', abortable: true, diff --git a/code/core/src/cli/AddonVitestService.ts b/code/core/src/cli/AddonVitestService.ts index 8a07313d4e5d..ae2218306a47 100644 --- a/code/core/src/cli/AddonVitestService.ts +++ b/code/core/src/cli/AddonVitestService.ts @@ -118,6 +118,7 @@ export class AddonVitestService { const errors: string[] = []; const playwrightCommand = ['playwright', 'install', 'chromium', '--with-deps']; + const playwrightCommandString = this.packageManager.getPackageCommand(playwrightCommand); try { const shouldBeInstalled = options.yes @@ -125,7 +126,7 @@ export class AddonVitestService { : await (async () => { logger.log(dedent` Playwright browser binaries are necessary for @storybook/addon-vitest. The download can take some time. If you don't want to wait, you can skip the installation and run the following command manually later: - ${CLI_COLORS.cta(`npx ${playwrightCommand.join(' ')}`)} + ${CLI_COLORS.cta(playwrightCommandString)} `); return prompt.confirm({ message: 'Do you want to install Playwright with Chromium now?', @@ -143,8 +144,8 @@ export class AddonVitestService { }), { id: 'playwright-installation', - intro: 'Installing Playwright browser binaries (Press "c" to abort)', - error: `An error occurred while installing Playwright browser binaries. Please run the following command later: npx ${playwrightCommand.join(' ')}`, + intro: 'Installing Playwright browser binaries (press "c" to abort)', + error: `An error occurred while installing Playwright browser binaries. Please run the following command later: ${playwrightCommandString}`, success: 'Playwright browser binaries installed successfully', abortable: true, } diff --git a/code/core/src/common/js-package-manager/BUNProxy.ts b/code/core/src/common/js-package-manager/BUNProxy.ts index a2030402413a..19b6d6b2a620 100644 --- a/code/core/src/common/js-package-manager/BUNProxy.ts +++ b/code/core/src/common/js-package-manager/BUNProxy.ts @@ -87,6 +87,10 @@ export class BUNProxy extends JsPackageManager { return `bunx ${pkg}${specifier ? `@${specifier}` : ''} ${args.join(' ')}`; } + getPackageCommand(args: string[]): string { + return `bunx ${args.join(' ')}`; + } + public async getModulePackageJSON(packageName: string): Promise { const wantedPath = join('node_modules', packageName, 'package.json'); const packageJsonPath = find.up(wantedPath, { diff --git a/code/core/src/common/js-package-manager/JsPackageManager.ts b/code/core/src/common/js-package-manager/JsPackageManager.ts index 5afb7b7b77f7..bd5bd4b542d6 100644 --- a/code/core/src/common/js-package-manager/JsPackageManager.ts +++ b/code/core/src/common/js-package-manager/JsPackageManager.ts @@ -101,9 +101,12 @@ export abstract class JsPackageManager { this.primaryPackageJson = this.#getPrimaryPackageJson(); } - /** Runs arbitrary package scripts. */ + /** Runs arbitrary package scripts (as a string for display). */ abstract getRunCommand(command: string): string; + /** Returns the command to run the binary of a local package */ + abstract getPackageCommand(args: string[]): string; + /** Get the package.json file for a given module. */ abstract getModulePackageJSON(packageName: string, cwd?: string): Promise; diff --git a/code/core/src/common/js-package-manager/NPMProxy.ts b/code/core/src/common/js-package-manager/NPMProxy.ts index 71ed0790002e..83d40eb46717 100644 --- a/code/core/src/common/js-package-manager/NPMProxy.ts +++ b/code/core/src/common/js-package-manager/NPMProxy.ts @@ -98,6 +98,10 @@ export class NPMProxy extends JsPackageManager { return this.installArgs; } + public getPackageCommand(args: string[]): string { + return `npx ${args.join(' ')}`; + } + public runPackageCommand( options: Omit & { args: string[] } ): ExecaChildProcess { diff --git a/code/core/src/common/js-package-manager/PNPMProxy.ts b/code/core/src/common/js-package-manager/PNPMProxy.ts index c781d835daa3..b4c6119f0df8 100644 --- a/code/core/src/common/js-package-manager/PNPMProxy.ts +++ b/code/core/src/common/js-package-manager/PNPMProxy.ts @@ -73,6 +73,10 @@ export class PNPMProxy extends JsPackageManager { return this.installArgs; } + getPackageCommand(args: string[]): string { + return `pnpm exec ${args.join(' ')}`; + } + public runPackageCommand({ args, ...options diff --git a/code/core/src/common/js-package-manager/Yarn1Proxy.ts b/code/core/src/common/js-package-manager/Yarn1Proxy.ts index 69164b5d493f..432b1a684d24 100644 --- a/code/core/src/common/js-package-manager/Yarn1Proxy.ts +++ b/code/core/src/common/js-package-manager/Yarn1Proxy.ts @@ -50,6 +50,11 @@ export class Yarn1Proxy extends JsPackageManager { return `yarn ${command}`; } + getPackageCommand(args: string[]): string { + const [command, ...rest] = args; + return `yarn exec ${command} -- ${rest.join(' ')}`; + } + public runPackageCommand({ args, ...options diff --git a/code/core/src/common/js-package-manager/Yarn2Proxy.ts b/code/core/src/common/js-package-manager/Yarn2Proxy.ts index bba32562b962..71f09fedf3d7 100644 --- a/code/core/src/common/js-package-manager/Yarn2Proxy.ts +++ b/code/core/src/common/js-package-manager/Yarn2Proxy.ts @@ -94,6 +94,10 @@ export class Yarn2Proxy extends JsPackageManager { return `yarn ${command}`; } + getPackageCommand(args: string[]): string { + return `yarn exec ${args.join(' ')}`; + } + public runPackageCommand({ args, ...options diff --git a/code/core/src/core-server/withTelemetry.test.ts b/code/core/src/core-server/withTelemetry.test.ts index b642feae1c2b..a46488bbe561 100644 --- a/code/core/src/core-server/withTelemetry.test.ts +++ b/code/core/src/core-server/withTelemetry.test.ts @@ -16,6 +16,7 @@ describe('withTelemetry', () => { beforeEach(() => { vi.resetAllMocks(); vi.mocked(ErrorCollector.getErrors).mockReturnValue([]); + vi.mocked(telemetry).mockResolvedValue(undefined); }); it('works in happy path', async () => { const run = vi.fn(); diff --git a/code/core/src/node-logger/logger/colors.ts b/code/core/src/node-logger/logger/colors.ts index 03d47d47b146..d74dcca728a3 100644 --- a/code/core/src/node-logger/logger/colors.ts +++ b/code/core/src/node-logger/logger/colors.ts @@ -4,7 +4,8 @@ export const CLI_COLORS = { success: picocolors.green, error: picocolors.red, warning: picocolors.yellow, - info: process.platform === 'win32' ? picocolors.cyan : picocolors.blue, + // Improve contrast on dark terminals by using cyan for info on all platforms + info: picocolors.cyan, debug: picocolors.gray, // Only color a link if it is the primary call to action, otherwise links shouldn't be colored cta: picocolors.cyan, diff --git a/code/core/src/telemetry/notify.ts b/code/core/src/telemetry/notify.ts index 40e374ac76d5..634e13905425 100644 --- a/code/core/src/telemetry/notify.ts +++ b/code/core/src/telemetry/notify.ts @@ -19,10 +19,7 @@ export const notify = async () => { if (!(await cache.get(TELEMETRY_KEY_NOTIFY_DATE, null))) { cache.set(TELEMETRY_KEY_NOTIFY_DATE, Date.now()); logger.info( - dedent` - Attention: Storybook collects completely anonymous telemetry regarding usage. This information is used to shape Storybook's roadmap and prioritize features. You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL: - https://storybook.js.org/telemetry - ` + "Storybook collects completely anonymous usage telemetry. We use it to shape Storybook's roadmap and prioritize features. You can learn more, including how to opt out, at https://storybook.js.org/telemetry" ); } } diff --git a/code/frameworks/angular/src/builders/start-storybook/index.ts b/code/frameworks/angular/src/builders/start-storybook/index.ts index e09d6089fb3b..9562ab66ba0c 100644 --- a/code/frameworks/angular/src/builders/start-storybook/index.ts +++ b/code/frameworks/angular/src/builders/start-storybook/index.ts @@ -24,6 +24,7 @@ import type { StyleElement, } from '@angular-devkit/build-angular/src/builders/browser/schema'; import type { JsonObject } from '@angular-devkit/core'; +import { Observable } from 'rxjs'; import * as find from 'empathic/find'; import * as pkg from 'empathic/package'; @@ -72,110 +73,124 @@ export type StorybookBuilderOptions = JsonObject & { export type StorybookBuilderOutput = JsonObject & BuilderOutput & {}; -const commandBuilder: BuilderHandlerFn = async ( +const commandBuilder: BuilderHandlerFn = ( options, context -): Promise => { - logger.intro('Starting Storybook'); - - const { tsConfig } = await setup(options, context); - - const docTSConfig = find.up('tsconfig.doc.json', { - cwd: options.configDir, - last: getProjectRoot(), - }); - - if (options.compodoc) { - await runCompodoc( - { - compodocArgs: [...options.compodocArgs, ...(options.quiet ? ['--silent'] : [])], - tsconfig: docTSConfig ?? tsConfig, - }, - context - ); - } - - getEnvConfig(options, { - port: 'SBCONFIG_PORT', - host: 'SBCONFIG_HOSTNAME', - staticDir: 'SBCONFIG_STATIC_DIR', - configDir: 'SBCONFIG_CONFIG_DIR', - ci: 'CI', +): Observable => { + return new Observable((observer) => { + (async () => { + try { + logger.intro('Starting Storybook'); + + const { tsConfig } = await setup(options, context); + + const docTSConfig = find.up('tsconfig.doc.json', { + cwd: options.configDir, + last: getProjectRoot(), + }); + + if (options.compodoc) { + await runCompodoc( + { + compodocArgs: [...options.compodocArgs, ...(options.quiet ? ['--silent'] : [])], + tsconfig: docTSConfig ?? tsConfig, + }, + context + ); + } + + getEnvConfig(options, { + port: 'SBCONFIG_PORT', + host: 'SBCONFIG_HOSTNAME', + staticDir: 'SBCONFIG_STATIC_DIR', + configDir: 'SBCONFIG_CONFIG_DIR', + ci: 'CI', + }); + + options.port = parseInt(`${options.port}`, 10); + + const { + browserTarget, + stylePreprocessorOptions, + styles, + ci, + configDir, + docs, + host, + https, + port, + quiet, + enableProdMode = false, + smokeTest, + sslCa, + sslCert, + sslKey, + disableTelemetry, + assets, + initialPath, + open, + debugWebpack, + loglevel, + webpackStatsJson, + statsJson, + previewUrl, + sourceMap = false, + preserveSymlinks = false, + experimentalZoneless = !!(VERSION.major && Number(VERSION.major) >= 21), + } = options; + + const packageJsonPath = pkg.up({ cwd: __dirname }); + const packageJson = + packageJsonPath != null ? JSON.parse(readFileSync(packageJsonPath, 'utf8')) : null; + + const standaloneOptions: StandaloneOptions = { + packageJson, + ci, + configDir, + ...(docs ? { docs } : {}), + host, + https, + port, + quiet, + enableProdMode, + smokeTest, + sslCa, + sslCert, + sslKey, + disableTelemetry, + angularBrowserTarget: browserTarget, + angularBuilderContext: context, + angularBuilderOptions: { + ...(stylePreprocessorOptions ? { stylePreprocessorOptions } : {}), + ...(styles ? { styles } : {}), + ...(assets ? { assets } : {}), + preserveSymlinks, + sourceMap, + experimentalZoneless, + }, + tsConfig, + initialPath, + open, + debugWebpack, + webpackStatsJson, + statsJson, + loglevel, + previewUrl, + }; + + const startedPort = await runInstance(standaloneOptions); + + // Emit success output - the dev server is now running + observer.next({ success: true, info: { port: startedPort } } as BuilderOutput); + + // Don't call observer.complete() - this keeps the Observable alive + // so the dev server continues running. Architect will keep subscribing + // until the Observable completes, which allows watch mode to work. + } catch (error) { + observer.error(error); + } + })(); }); - - options.port = parseInt(`${options.port}`, 10); - - const { - browserTarget, - stylePreprocessorOptions, - styles, - ci, - configDir, - docs, - host, - https, - port, - quiet, - enableProdMode = false, - smokeTest, - sslCa, - sslCert, - sslKey, - disableTelemetry, - assets, - initialPath, - open, - debugWebpack, - loglevel, - webpackStatsJson, - statsJson, - previewUrl, - sourceMap = false, - preserveSymlinks = false, - experimentalZoneless = !!(VERSION.major && Number(VERSION.major) >= 21), - } = options; - - const packageJsonPath = pkg.up({ cwd: __dirname }); - const packageJson = - packageJsonPath != null ? JSON.parse(readFileSync(packageJsonPath, 'utf8')) : null; - - const standaloneOptions: StandaloneOptions = { - packageJson, - ci, - configDir, - ...(docs ? { docs } : {}), - host, - https, - port, - quiet, - enableProdMode, - smokeTest, - sslCa, - sslCert, - sslKey, - disableTelemetry, - angularBrowserTarget: browserTarget, - angularBuilderContext: context, - angularBuilderOptions: { - ...(stylePreprocessorOptions ? { stylePreprocessorOptions } : {}), - ...(styles ? { styles } : {}), - ...(assets ? { assets } : {}), - preserveSymlinks, - sourceMap, - experimentalZoneless, - }, - tsConfig, - initialPath, - open, - debugWebpack, - webpackStatsJson, - statsJson, - loglevel, - previewUrl, - }; - - const startedPort = await runInstance(standaloneOptions); - return { success: true, info: { port: startedPort } } as BuilderOutput; }; export default createBuilder(commandBuilder) as DevkitBuilder; diff --git a/code/frameworks/angular/src/client/decorators.ts b/code/frameworks/angular/src/client/decorators.ts index df97880c46fd..f214c708e611 100644 --- a/code/frameworks/angular/src/client/decorators.ts +++ b/code/frameworks/angular/src/client/decorators.ts @@ -1,7 +1,6 @@ import type { DecoratorFunction, StoryContext } from 'storybook/internal/types'; -import type { Type } from '@angular/core'; -import type { ApplicationConfig } from '@angular/platform-browser'; +import type { ApplicationConfig, Type } from '@angular/core'; import { computesTemplateFromComponent } from './angular-beta/ComputesTemplateFromComponent'; import { isComponent } from './angular-beta/utils/NgComponentAnalyzer'; diff --git a/code/frameworks/angular/src/client/types.ts b/code/frameworks/angular/src/client/types.ts index 9bc00bb39339..f1b43189eedb 100644 --- a/code/frameworks/angular/src/client/types.ts +++ b/code/frameworks/angular/src/client/types.ts @@ -4,8 +4,7 @@ import type { WebRenderer, } from 'storybook/internal/types'; -import type { Provider } from '@angular/core'; -import type { ApplicationConfig } from '@angular/platform-browser'; +import type { ApplicationConfig, Provider } from '@angular/core'; export interface NgModuleMetadata { /** List of components, directives, and pipes that belong to your component. */ diff --git a/code/lib/cli-storybook/src/sandbox-templates.ts b/code/lib/cli-storybook/src/sandbox-templates.ts index df497a8569b5..3fa386712eed 100644 --- a/code/lib/cli-storybook/src/sandbox-templates.ts +++ b/code/lib/cli-storybook/src/sandbox-templates.ts @@ -640,7 +640,8 @@ export const baseTemplates = { 'npx -p @angular/cli@next ng new angular-v16 --directory {{beforeDir}} --routing=true --minimal=true --style=scss --strict --skip-git --skip-install --package-manager=yarn --ssr', modifications: { // Angular 21 has introduced a peer dependency requirement on standard-schema via @angular/forms` - extraDependencies: ['@standard-schema/spec@^1', '@angular/forms@next'], + // TODO: use @angular/forms@next as soon as @angular/cli@next points to the same version + extraDependencies: ['@standard-schema/spec@^1', '@angular/forms@^21.0.0'], }, expected: { framework: '@storybook/angular', diff --git a/code/lib/cli-storybook/src/upgrade.ts b/code/lib/cli-storybook/src/upgrade.ts index 9c3d49aaab88..74a9c5d4e404 100644 --- a/code/lib/cli-storybook/src/upgrade.ts +++ b/code/lib/cli-storybook/src/upgrade.ts @@ -253,7 +253,7 @@ function logUpgradeResults( if (automigrationLinks.length > 0) { const automigrationLinksMessage = [ - 'If you want to learn more about the automigrations that executed in your project(s), please check the following links:\n', + 'If you want to learn more about the automigrations that executed in your project(s), please check the following links:', ...automigrationLinks, ].join('\n'); diff --git a/code/lib/create-storybook/src/commands/UserPreferencesCommand.ts b/code/lib/create-storybook/src/commands/UserPreferencesCommand.ts index 0b5a7a72afd5..1937263b9cd0 100644 --- a/code/lib/create-storybook/src/commands/UserPreferencesCommand.ts +++ b/code/lib/create-storybook/src/commands/UserPreferencesCommand.ts @@ -155,8 +155,8 @@ export class UserPreferencesCommand { let installType: InstallType = 'recommended'; const recommendedLabel = isTestFeatureAvailable - ? `Recommended: Includes component development, docs and testing features.` - : `Recommended: Includes component development and docs`; + ? `Recommended: Component development, docs, and testing features.` + : `Recommended: Component development and docs`; if (!skipPrompt) { installType = await prompt.select({ diff --git a/code/lib/create-storybook/src/initiate.ts b/code/lib/create-storybook/src/initiate.ts index c600e13c72cf..61be6ce2028d 100644 --- a/code/lib/create-storybook/src/initiate.ts +++ b/code/lib/create-storybook/src/initiate.ts @@ -1,5 +1,9 @@ import { ProjectType } from 'storybook/internal/cli'; -import { type JsPackageManager, executeCommand } from 'storybook/internal/common'; +import { + type JsPackageManager, + PackageManagerName, + executeCommand, +} from 'storybook/internal/common'; import { withTelemetry } from 'storybook/internal/core-server'; import { logTracker, logger } from 'storybook/internal/node-logger'; import { ErrorCollector } from 'storybook/internal/telemetry'; @@ -181,7 +185,11 @@ async function runStorybookDev(result: { // npm needs extra -- to pass flags to the command // in the case of Angular, we are calling `ng run` which doesn't need the extra `--` - if (packageManager.type === 'npm' && projectType !== ProjectType.ANGULAR) { + const doesNeedExtraDash = + packageManager.type === PackageManagerName.NPM || + packageManager.type === PackageManagerName.BUN; + + if (doesNeedExtraDash && projectType !== ProjectType.ANGULAR) { flags.push('--'); } diff --git a/code/lib/create-storybook/src/services/TelemetryService.test.ts b/code/lib/create-storybook/src/services/TelemetryService.test.ts index d993bb462a19..f05f27c421f1 100644 --- a/code/lib/create-storybook/src/services/TelemetryService.test.ts +++ b/code/lib/create-storybook/src/services/TelemetryService.test.ts @@ -14,6 +14,7 @@ vi.mock('process-ancestry', { spy: true }); describe('TelemetryService', () => { beforeEach(() => { vi.clearAllMocks(); + vi.mocked(telemetry).mockResolvedValue(undefined); }); describe('when telemetry is enabled', () => { diff --git a/code/package.json b/code/package.json index 7a279df74dfb..3f8bd079c66d 100644 --- a/code/package.json +++ b/code/package.json @@ -286,5 +286,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "10.1.0-beta.6" } diff --git a/docs/versions/next.json b/docs/versions/next.json index 181a8ab491f9..696ce4ddcd88 100644 --- a/docs/versions/next.json +++ b/docs/versions/next.json @@ -1 +1 @@ -{"version":"10.1.0-beta.5","info":{"plain":"- Checklist: Autocomplete \\\"See what's new\\\" on URL navigation - [#33167](https://github.com/storybookjs/storybook/pull/33167), thanks @ghengeveld!\n- Core: Fix testing widget focus outline - [#33172](https://github.com/storybookjs/storybook/pull/33172), thanks @ghengeveld!\n- Core: Rename `Listbox` component to `ActionList` and use it in `TagsFilterPanel` - [#33140](https://github.com/storybookjs/storybook/pull/33140), thanks @ghengeveld!\n- UI: Add padding for ArgsTable shadow in TabbedArgsTable - [#33034](https://github.com/storybookjs/storybook/pull/33034), thanks @Sidnioulz!\n- UI: Fix crashes in Select when passed falsy non-string options - [#33164](https://github.com/storybookjs/storybook/pull/33164), thanks @Sidnioulz!\n- UI: Fix regression on addon panel empty content fontsize - [#33021](https://github.com/storybookjs/storybook/pull/33021), thanks @Sidnioulz!\n- UI: Fix trivial RefBlocks ARIA violations - [#33026](https://github.com/storybookjs/storybook/pull/33026), thanks @Sidnioulz!\n- UI: Refocus search input after clearing it - [#33165](https://github.com/storybookjs/storybook/pull/33165), thanks @Sidnioulz!\n- UI: Rework default background of Color swatch for dark mode - [#33023](https://github.com/storybookjs/storybook/pull/33023), thanks @Sidnioulz!"}} \ No newline at end of file +{"version":"10.1.0-beta.6","info":{"plain":"- Angular: Don't kill dev command by using observables - [#33185](https://github.com/storybookjs/storybook/pull/33185), thanks @valentinpalkovic!\n- Angular: Replace deprecated import of ApplicationConfig - [#33125](https://github.com/storybookjs/storybook/pull/33125), thanks @EtiennePasteur!\n- CLI: Fix passing flags for bun users during init - [#33166](https://github.com/storybookjs/storybook/pull/33166), thanks @valentinpalkovic!\n- CLI: Minor improvements - [#33180](https://github.com/storybookjs/storybook/pull/33180), thanks @valentinpalkovic!\n- CLI: Update upgrade message - [#33182](https://github.com/storybookjs/storybook/pull/33182), thanks @yannbf!"}} \ No newline at end of file