= React.memo(
Caught exception in play function
-
- {printSerializedError(caughtException)}
-
+
)}
{unhandledErrors && (
diff --git a/code/addons/interactions/src/components/MatcherResult.tsx b/code/addons/interactions/src/components/MatcherResult.tsx
index beae8b2146ff..46b5e540ad8d 100644
--- a/code/addons/interactions/src/components/MatcherResult.tsx
+++ b/code/addons/interactions/src/components/MatcherResult.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import { styled, typography } from 'storybook/internal/theming';
+import { useAnsiToHtmlFilter } from '../utils';
import { Node } from './MethodCall';
const getParams = (line: string, fromIndex = 0): string => {
@@ -59,6 +60,7 @@ export const MatcherResult = ({
message: string;
style?: React.CSSProperties;
}) => {
+ const filter = useAnsiToHtmlFilter();
const lines = message.split('\n');
return (
{line},
];
+ return [
+ ,
+
,
+ ];
})}
);
diff --git a/code/addons/interactions/src/preview.ts b/code/addons/interactions/src/preview.ts
index 6abc30f63b93..482b6933279f 100644
--- a/code/addons/interactions/src/preview.ts
+++ b/code/addons/interactions/src/preview.ts
@@ -1,6 +1,9 @@
import type { PlayFunction, StepLabel, StoryContext } from 'storybook/internal/types';
import { instrument } from '@storybook/instrumenter';
+// This makes sure that storybook test loaders are always loaded when addon-interactions is used
+// For 9.0 we want to merge storybook/test and addon-interactions into one addon.
+import '@storybook/test';
export const { step: runStep } = instrument(
{
diff --git a/code/addons/interactions/src/utils.ts b/code/addons/interactions/src/utils.ts
index 1b08eca12a24..d80d9f4cdbee 100644
--- a/code/addons/interactions/src/utils.ts
+++ b/code/addons/interactions/src/utils.ts
@@ -1,3 +1,7 @@
+import { type StorybookTheme, useTheme } from 'storybook/internal/theming';
+
+import Filter from 'ansi-to-html';
+
export function isTestAssertionError(error: unknown) {
return isChaiError(error) || isJestError(error);
}
@@ -21,3 +25,15 @@ export function isJestError(error: unknown) {
error.message.startsWith('expect(')
);
}
+
+export function createAnsiToHtmlFilter(theme: StorybookTheme) {
+ return new Filter({
+ fg: theme.color.defaultText,
+ bg: theme.background.content,
+ });
+}
+
+export function useAnsiToHtmlFilter() {
+ const theme = useTheme();
+ return createAnsiToHtmlFilter(theme);
+}
diff --git a/code/addons/jest/package.json b/code/addons/jest/package.json
index 73e977da3e4d..b994a205e0f2 100644
--- a/code/addons/jest/package.json
+++ b/code/addons/jest/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/addon-jest",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "React storybook addon that show component jest report",
"keywords": [
"addon",
diff --git a/code/addons/jest/project.json b/code/addons/jest/project.json
index e7852e6415fc..226ea5597330 100644
--- a/code/addons/jest/project.json
+++ b/code/addons/jest/project.json
@@ -1,5 +1,5 @@
{
- "name": "jest",
+ "name": "addon-jest",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {
diff --git a/code/addons/links/package.json b/code/addons/links/package.json
index 7f339327da93..e55afc0b5630 100644
--- a/code/addons/links/package.json
+++ b/code/addons/links/package.json
@@ -1,10 +1,9 @@
{
"name": "@storybook/addon-links",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Link stories together to build demos and prototypes with your UI components",
"keywords": [
- "addon",
- "storybook",
+ "storybook-addons",
"organize"
],
"homepage": "https://github.com/storybookjs/storybook/tree/next/code/addons/links",
@@ -71,7 +70,6 @@
"ts-dedent": "^2.0.0"
},
"devDependencies": {
- "fs-extra": "^11.1.0",
"typescript": "^5.3.2"
},
"peerDependencies": {
diff --git a/code/addons/links/project.json b/code/addons/links/project.json
index 60e9238ad5ad..7f246ffb677b 100644
--- a/code/addons/links/project.json
+++ b/code/addons/links/project.json
@@ -1,5 +1,5 @@
{
- "name": "links",
+ "name": "addon-links",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {
diff --git a/code/addons/links/scripts/fix-preview-api-reference.ts b/code/addons/links/scripts/fix-preview-api-reference.ts
index 71ca667999e2..90d54704768d 100644
--- a/code/addons/links/scripts/fix-preview-api-reference.ts
+++ b/code/addons/links/scripts/fix-preview-api-reference.ts
@@ -1,4 +1,4 @@
-import { readFile, writeFile } from 'fs-extra';
+import { readFile, writeFile } from 'node:fs/promises';
/* I wish this wasn't needed..
* There seems to be some bug in tsup / the unlaying lib that does DTS bundling
diff --git a/code/addons/measure/package.json b/code/addons/measure/package.json
index 59d95f5fcfb0..0d568a6a8fe2 100644
--- a/code/addons/measure/package.json
+++ b/code/addons/measure/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/addon-measure",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Inspect layouts by visualizing the box model",
"keywords": [
"storybook-addons",
diff --git a/code/addons/measure/project.json b/code/addons/measure/project.json
index deaa4529c04d..60e8867d2e88 100644
--- a/code/addons/measure/project.json
+++ b/code/addons/measure/project.json
@@ -1,5 +1,5 @@
{
- "name": "measure",
+ "name": "addon-measure",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {
diff --git a/code/addons/onboarding/package.json b/code/addons/onboarding/package.json
index 70ff24eb52a4..8e37d1da57b9 100644
--- a/code/addons/onboarding/package.json
+++ b/code/addons/onboarding/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/addon-onboarding",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook Addon Onboarding - Introduces a new onboarding experience",
"keywords": [
"storybook-addons",
diff --git a/code/addons/onboarding/project.json b/code/addons/onboarding/project.json
index f2f83ffd6d64..75697db7494a 100644
--- a/code/addons/onboarding/project.json
+++ b/code/addons/onboarding/project.json
@@ -1,5 +1,5 @@
{
- "name": "onboarding",
+ "name": "addon-onboarding",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {
diff --git a/code/addons/outline/package.json b/code/addons/outline/package.json
index 21593b46384d..5070ddcb9d1a 100644
--- a/code/addons/outline/package.json
+++ b/code/addons/outline/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/addon-outline",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Outline all elements with CSS to help with layout placement and alignment",
"keywords": [
"storybook-addons",
diff --git a/code/addons/outline/project.json b/code/addons/outline/project.json
index 5183adf1235e..ed8081003447 100644
--- a/code/addons/outline/project.json
+++ b/code/addons/outline/project.json
@@ -1,5 +1,5 @@
{
- "name": "outline",
+ "name": "addon-outline",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {
diff --git a/code/addons/storysource/package.json b/code/addons/storysource/package.json
index 20ac10370da3..84df2eeab95d 100644
--- a/code/addons/storysource/package.json
+++ b/code/addons/storysource/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/addon-storysource",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "View a story’s source code to see how it works and paste into your app",
"keywords": [
"addon",
diff --git a/code/addons/storysource/project.json b/code/addons/storysource/project.json
index 9ebd0d3c0829..21fe2fc0731b 100644
--- a/code/addons/storysource/project.json
+++ b/code/addons/storysource/project.json
@@ -1,5 +1,5 @@
{
- "name": "storysource",
+ "name": "addon-storysource",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {
diff --git a/code/addons/test/package.json b/code/addons/test/package.json
index ca52df1494d3..a892d6918b80 100644
--- a/code/addons/test/package.json
+++ b/code/addons/test/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/experimental-addon-test",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Integrate Vitest with Storybook",
"keywords": [
"storybook-addons",
@@ -77,6 +77,7 @@
"@vitest/browser": "^2.0.0",
"boxen": "^8.0.1",
"find-up": "^7.0.0",
+ "picocolors": "^1.1.0",
"semver": "^7.6.3",
"tinyrainbow": "^1.2.0",
"ts-dedent": "^2.2.0",
diff --git a/code/addons/test/project.json b/code/addons/test/project.json
index 96314d1efc94..c23ed17fc57b 100644
--- a/code/addons/test/project.json
+++ b/code/addons/test/project.json
@@ -1,5 +1,5 @@
{
- "name": "addon-test",
+ "name": "experimental-addon-test",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {
diff --git a/code/addons/test/src/postinstall.ts b/code/addons/test/src/postinstall.ts
index a868330b94e7..ac64b80bdc22 100644
--- a/code/addons/test/src/postinstall.ts
+++ b/code/addons/test/src/postinstall.ts
@@ -14,6 +14,7 @@ import {
import { colors, logger } from 'storybook/internal/node-logger';
import { findUp } from 'find-up';
+import picocolors from 'picocolors';
import { coerce, satisfies } from 'semver';
import c from 'tinyrainbow';
import { dedent } from 'ts-dedent';
@@ -30,7 +31,7 @@ export default async function postInstall(options: PostinstallOptions) {
printSuccess(
'👋 Howdy!',
dedent`
- I'm the installation helper for ${colors.pink.bold(ADDON_NAME)}
+ I'm the installation helper for ${colors.pink(ADDON_NAME)}
Hold on for a moment while I look at your project and get it set up...
`
@@ -78,14 +79,14 @@ export default async function postInstall(options: PostinstallOptions) {
if (!isRendererSupported) {
reasons.push(dedent`
- • The addon cannot yet be used with ${colors.pink.bold(info.frameworkPackageName)}
+ • The addon cannot yet be used with ${picocolors.bold(colors.pink(info.frameworkPackageName))}
`);
}
if (coercedVitestVersion && !satisfies(coercedVitestVersion, '>=2.0.0')) {
reasons.push(`
• The addon requires Vitest 2.0.0 or later. You are currently using ${vitestVersionSpecifier}.
- Please update your ${colors.pink.bold('vitest')} dependency and try again.
+ Please update your ${picocolors.bold(colors.pink('vitest'))} dependency and try again.
`);
}
@@ -93,7 +94,7 @@ export default async function postInstall(options: PostinstallOptions) {
const nextVersion = await packageManager.getInstalledVersion('next');
if (!nextVersion) {
reasons.push(dedent`
- • You are using ${colors.pink.bold('@storybook/nextjs')} without having ${colors.pink.bold('next')} installed.
+ • You are using ${picocolors.bold(colors.pink('@storybook/nextjs'))} without having ${picocolors.bold(colors.pink('next'))} installed.
Please install "next" or use a different Storybook framework integration and try again.
`);
}
@@ -105,7 +106,7 @@ export default async function postInstall(options: PostinstallOptions) {
);
reasons.push(
dedent`
- To roll back the installation, remove ${colors.pink.bold(ADDON_NAME)} from the "addons" array
+ To roll back the installation, remove ${picocolors.bold(colors.pink(ADDON_NAME))} from the "addons" array
in your main Storybook config file and remove the dependency from your package.json file.
`
);
@@ -114,14 +115,14 @@ export default async function postInstall(options: PostinstallOptions) {
reasons.push(
dedent`
Please check the documentation for more information about its requirements and installation:
- ${c.cyan`https://storybook.js.org/docs/writing-tests/test-runner-with-vitest`}
+ ${c.cyan`https://storybook.js.org/docs/writing-tests/vitest-plugin`}
`
);
} else {
reasons.push(
dedent`
Fear not, however, you can follow the manual installation process instead at:
- ${c.cyan`https://storybook.js.org/docs/writing-tests/test-runner-with-vitest#manual`}
+ ${c.cyan`https://storybook.js.org/docs/writing-tests/vitest-plugin#manual`}
`
);
}
@@ -148,7 +149,7 @@ export default async function postInstall(options: PostinstallOptions) {
dedent`
It looks like you're using Next.js.
- Adding ${colors.pink.bold(`@storybook/experimental-nextjs-vite/vite-plugin`)} so you can use it with Vitest.
+ Adding ${picocolors.bold(colors.pink(`@storybook/experimental-nextjs-vite/vite-plugin`))} so you can use it with Vitest.
More info about the plugin at: ${c.cyan`https://github.com/storybookjs/vite-plugin-storybook-nextjs`}
`
@@ -197,7 +198,7 @@ export default async function postInstall(options: PostinstallOptions) {
${colors.gray(vitestSetupFile)}
Please refer to the documentation to complete the setup manually:
- ${c.cyan`https://storybook.js.org/docs/writing-tests/test-runner-with-vitest#manual`}
+ ${c.cyan`https://storybook.js.org/docs/writing-tests/vitest-plugin#manual`}
`
);
logger.line(1);
@@ -240,7 +241,7 @@ export default async function postInstall(options: PostinstallOptions) {
your existing workspace file automatically, you must do it yourself. This was the last step.
Please refer to the documentation to complete the setup manually:
- ${c.cyan`https://storybook.js.org/docs/writing-tests/test-runner-with-vitest#manual`}
+ ${c.cyan`https://storybook.js.org/docs/writing-tests/vitest-plugin#manual`}
`
);
logger.line(1);
@@ -256,13 +257,13 @@ export default async function postInstall(options: PostinstallOptions) {
'🚨 Oh no!',
dedent`
You seem to have an existing test configuration in your Vite config file:
- ${colors.gray(vitestWorkspaceFile)}
+ ${colors.gray(vitestWorkspaceFile || '')}
I was able to configure most of the addon but could not safely extend
your existing workspace file automatically, you must do it yourself. This was the last step.
Please refer to the documentation to complete the setup manually:
- ${c.cyan`https://storybook.js.org/docs/writing-tests/test-runner-with-vitest#manual`}
+ ${c.cyan`https://storybook.js.org/docs/writing-tests/vitest-plugin#manual`}
`
);
logger.line(1);
@@ -288,13 +289,13 @@ export default async function postInstall(options: PostinstallOptions) {
import { defineWorkspace } from 'vitest/config';
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';${vitestInfo.frameworkPluginImport}
- // More info at: https://storybook.js.org/docs/writing-tests/test-runner-with-vitest
+ // More info at: https://storybook.js.org/docs/writing-tests/vitest-plugin
export default defineWorkspace([
'${relative(dirname(browserWorkspaceFile), rootConfig)}',
{
extends: '${viteConfigFile ? relative(dirname(browserWorkspaceFile), viteConfigFile) : ''}',
plugins: [
- // See options at: https://storybook.js.org/docs/writing-tests/test-runner-with-vitest#storybooktest
+ // See options at: https://storybook.js.org/docs/writing-tests/vitest-plugin#storybooktest
storybookTest(),${vitestInfo.frameworkPluginDocs + vitestInfo.frameworkPluginCall}
],
test: {
@@ -327,10 +328,10 @@ export default async function postInstall(options: PostinstallOptions) {
import { defineConfig } from 'vitest/config';
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';${vitestInfo.frameworkPluginImport}
- // More info at: https://storybook.js.org/docs/writing-tests/test-runner-with-vitest
+ // More info at: https://storybook.js.org/docs/writing-tests/vitest-plugin
export default defineConfig({
plugins: [
- // See options at: https://storybook.js.org/docs/writing-tests/test-runner-with-vitest#storybooktest
+ // See options at: https://storybook.js.org/docs/writing-tests/vitest-plugin#storybooktest
storybookTest(),${vitestInfo.frameworkPluginDocs + vitestInfo.frameworkPluginCall}
],
test: {
@@ -362,7 +363,7 @@ export default async function postInstall(options: PostinstallOptions) {
• When using the Vitest extension in your editor, all of your stories will be shown as tests!
Check the documentation for more information about its features and options at:
- ${c.cyan`https://storybook.js.org/docs/writing-tests/test-runner-with-vitest`}
+ ${c.cyan`https://storybook.js.org/docs/writing-tests/vitest-plugin`}
`
);
logger.line(1);
diff --git a/code/addons/themes/docs/api.md b/code/addons/themes/docs/api.md
index 143178b00047..a45818ab724e 100644
--- a/code/addons/themes/docs/api.md
+++ b/code/addons/themes/docs/api.md
@@ -148,7 +148,7 @@ export const myCustomDecorator = ({ themes, defaultState, ...rest }) => {
Let's use Vuetify as an example. Vuetify uses it's own global state to know which theme to render. To build a custom decorator to accommodate this method we'll need to do the following
```js
-// .storybook/withVeutifyTheme.decorator.js
+// .storybook/withVuetifyTheme.decorator.js
import { DecoratorHelpers } from '@storybook/addon-themes';
import { useTheme } from 'vuetify';
diff --git a/code/addons/themes/package.json b/code/addons/themes/package.json
index e83374efdf5e..53cc7b812f63 100644
--- a/code/addons/themes/package.json
+++ b/code/addons/themes/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/addon-themes",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Switch between multiple themes for you components in Storybook",
"keywords": [
"css",
diff --git a/code/addons/themes/project.json b/code/addons/themes/project.json
index ea17cd02b7b9..48d062a5ffe8 100644
--- a/code/addons/themes/project.json
+++ b/code/addons/themes/project.json
@@ -1,5 +1,5 @@
{
- "name": "themes",
+ "name": "addon-themes",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"targets": {
"build": {}
diff --git a/code/addons/toolbars/package.json b/code/addons/toolbars/package.json
index af631dca4c59..1dd14fb03d41 100644
--- a/code/addons/toolbars/package.json
+++ b/code/addons/toolbars/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/addon-toolbars",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Create your own toolbar items that control story rendering",
"keywords": [
"addon",
diff --git a/code/addons/toolbars/project.json b/code/addons/toolbars/project.json
index aa7476d22fd8..382c8c0ed590 100644
--- a/code/addons/toolbars/project.json
+++ b/code/addons/toolbars/project.json
@@ -1,5 +1,5 @@
{
- "name": "toolbars",
+ "name": "addon-toolbars",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {
diff --git a/code/addons/viewport/package.json b/code/addons/viewport/package.json
index faafc88f0e63..f6a0bccfba5f 100644
--- a/code/addons/viewport/package.json
+++ b/code/addons/viewport/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/addon-viewport",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Build responsive components by adjusting Storybook’s viewport size and orientation",
"keywords": [
"addon",
diff --git a/code/addons/viewport/project.json b/code/addons/viewport/project.json
index 94fc39e648d0..3d5b204b276a 100644
--- a/code/addons/viewport/project.json
+++ b/code/addons/viewport/project.json
@@ -1,5 +1,5 @@
{
- "name": "viewport",
+ "name": "addon-viewport",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {
diff --git a/code/addons/viewport/src/defaults.ts b/code/addons/viewport/src/defaults.ts
index 4190ccb7f3cc..d29e380ac230 100644
--- a/code/addons/viewport/src/defaults.ts
+++ b/code/addons/viewport/src/defaults.ts
@@ -1,6 +1,6 @@
import type { ViewportMap } from './types';
-export const INITIAL_VIEWPORTS: ViewportMap = {
+const INITIAL_VIEWPORTS_DATA = {
iphone5: {
name: 'iPhone 5',
styles: {
@@ -225,7 +225,12 @@ export const INITIAL_VIEWPORTS: ViewportMap = {
},
type: 'mobile',
},
-};
+} as const;
+
+export type InitialViewportKeys = keyof typeof INITIAL_VIEWPORTS_DATA;
+
+export const INITIAL_VIEWPORTS: ViewportMap = INITIAL_VIEWPORTS_DATA;
+
export const DEFAULT_VIEWPORT = 'responsive';
export const MINIMAL_VIEWPORTS: ViewportMap = {
diff --git a/code/builders/builder-vite/package.json b/code/builders/builder-vite/package.json
index c5b98d2d4697..b8c3d1b93568 100644
--- a/code/builders/builder-vite/package.json
+++ b/code/builders/builder-vite/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/builder-vite",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "A plugin to run and build Storybooks with Vite",
"homepage": "https://github.com/storybookjs/storybook/tree/next/code/builders/builder-vite/#readme",
"bugs": {
@@ -49,7 +49,6 @@
"es-module-lexer": "^1.5.0",
"express": "^4.19.2",
"find-cache-dir": "^3.0.0",
- "fs-extra": "^11.1.0",
"knitwork": "^1.1.0",
"magic-string": "^0.30.0",
"pathe": "^1.1.2",
diff --git a/code/builders/builder-vite/src/codegen-modern-iframe-script.ts b/code/builders/builder-vite/src/codegen-modern-iframe-script.ts
index 2de92617befa..4abf4c22f315 100644
--- a/code/builders/builder-vite/src/codegen-modern-iframe-script.ts
+++ b/code/builders/builder-vite/src/codegen-modern-iframe-script.ts
@@ -2,7 +2,7 @@ import { getFrameworkName, loadPreviewOrConfigFile } from 'storybook/internal/co
import type { Options, PreviewAnnotation } from 'storybook/internal/types';
import { processPreviewAnnotation } from './utils/process-preview-annotation';
-import { virtualAddonSetupFile, virtualStoriesFile } from './virtual-file-names';
+import { SB_VIRTUAL_FILES, getResolvedVirtualModuleId } from './virtual-file-names';
export async function generateModernIframeScriptCode(options: Options, projectRoot: string) {
const { presets, configDir } = options;
@@ -27,7 +27,7 @@ export async function generateModernIframeScriptCode(options: Options, projectRo
.map(
(previewAnnotation, index) =>
// Prefer the updated module from an HMR update, otherwise import the original module
- `hmrPreviewAnnotationModules.at(${index}) ?? import('${previewAnnotation}')`
+ `hmrPreviewAnnotationModules[${index}] ?? import('${previewAnnotation}')`
)
.join(',\n')}])
return composeConfigs(configs);
@@ -45,7 +45,7 @@ export async function generateModernIframeScriptCode(options: Options, projectRo
return `
if (import.meta.hot) {
- import.meta.hot.accept('${virtualStoriesFile}', (newModule) => {
+ import.meta.hot.accept('${getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE)}', (newModule) => {
// importFn has changed so we need to patch the new one in
window.__STORYBOOK_PREVIEW__.onStoriesChanged({ importFn: newModule.importFn });
});
@@ -68,8 +68,8 @@ export async function generateModernIframeScriptCode(options: Options, projectRo
*/
const code = `
import { composeConfigs, PreviewWeb, ClientApi } from 'storybook/internal/preview-api';
- import '${virtualAddonSetupFile}';
- import { importFn } from '${virtualStoriesFile}';
+ import '${SB_VIRTUAL_FILES.VIRTUAL_ADDON_SETUP_FILE}';
+ import { importFn } from '${SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE}';
${getPreviewAnnotationsFunction}
diff --git a/code/builders/builder-vite/src/index.ts b/code/builders/builder-vite/src/index.ts
index a8a775a13344..0962e1676e4e 100644
--- a/code/builders/builder-vite/src/index.ts
+++ b/code/builders/builder-vite/src/index.ts
@@ -1,4 +1,5 @@
// noinspection JSUnusedGlobalSymbols
+import { cp, readFile } from 'node:fs/promises';
import { join, parse } from 'node:path';
import { NoStatsForViteDevError } from 'storybook/internal/server-errors';
@@ -6,7 +7,6 @@ import type { Options } from 'storybook/internal/types';
import type { RequestHandler } from 'express';
import express from 'express';
-import * as fs from 'fs-extra';
import { corePath } from 'storybook/core-path';
import type { ViteDevServer } from 'vite';
@@ -34,10 +34,9 @@ function iframeMiddleware(options: Options, server: ViteDevServer): RequestHandl
return;
}
- const indexHtml = await fs.readFile(
- require.resolve('@storybook/builder-vite/input/iframe.html'),
- 'utf-8'
- );
+ const indexHtml = await readFile(require.resolve('@storybook/builder-vite/input/iframe.html'), {
+ encoding: 'utf8',
+ });
const generated = await transformIframeHtml(indexHtml, options);
const transformed = await server.transformIndexHtml('/iframe.html', generated);
res.setHeader('Content-Type', 'text/html');
@@ -85,7 +84,7 @@ export const build: ViteBuilder['build'] = async ({ options }) => {
const previewDirOrigin = previewResolvedDir;
const previewDirTarget = join(options.outputDir || '', `sb-preview`);
- const previewFiles = fs.copy(previewDirOrigin, previewDirTarget, {
+ const previewFiles = cp(previewDirOrigin, previewDirTarget, {
filter: (src) => {
const { ext } = parse(src);
if (ext) {
@@ -93,6 +92,7 @@ export const build: ViteBuilder['build'] = async ({ options }) => {
}
return true;
},
+ recursive: true,
});
const [out] = await Promise.all([viteCompilation, previewFiles]);
diff --git a/code/builders/builder-vite/src/optimizeDeps.ts b/code/builders/builder-vite/src/optimizeDeps.ts
index 0e81d7d5afc4..cbade7a268a6 100644
--- a/code/builders/builder-vite/src/optimizeDeps.ts
+++ b/code/builders/builder-vite/src/optimizeDeps.ts
@@ -32,6 +32,7 @@ const INCLUDE_CANDIDATES = [
'fast-deep-equal',
'html-tags',
'isobject',
+ 'jsdoc-type-pratt-parser', // TODO: Remove this once it's converted to ESM: https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/173
'loader-utils',
'lodash/camelCase.js',
'lodash/camelCase',
diff --git a/code/builders/builder-vite/src/plugins/code-generator-plugin.ts b/code/builders/builder-vite/src/plugins/code-generator-plugin.ts
index 89f6430f7584..77509b97cf93 100644
--- a/code/builders/builder-vite/src/plugins/code-generator-plugin.ts
+++ b/code/builders/builder-vite/src/plugins/code-generator-plugin.ts
@@ -8,12 +8,7 @@ import { generateImportFnScriptCode } from '../codegen-importfn-script';
import { generateModernIframeScriptCode } from '../codegen-modern-iframe-script';
import { generateAddonSetupCode } from '../codegen-set-addon-channel';
import { transformIframeHtml } from '../transform-iframe-html';
-import {
- virtualAddonSetupFile,
- virtualFileId,
- virtualPreviewFile,
- virtualStoriesFile,
-} from '../virtual-file-names';
+import { SB_VIRTUAL_FILES, getResolvedVirtualModuleId } from '../virtual-file-names';
export function codeGeneratorPlugin(options: Options): Plugin {
const iframePath = require.resolve('@storybook/builder-vite/input/iframe.html');
@@ -28,11 +23,15 @@ export function codeGeneratorPlugin(options: Options): Plugin {
// invalidate the whole vite-app.js script on every file change.
// (this might be a little too aggressive?)
server.watcher.on('change', () => {
- const appModule = server.moduleGraph.getModuleById(virtualFileId);
+ const appModule = server.moduleGraph.getModuleById(
+ getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_APP_FILE)
+ );
if (appModule) {
server.moduleGraph.invalidateModule(appModule);
}
- const storiesModule = server.moduleGraph.getModuleById(virtualStoriesFile);
+ const storiesModule = server.moduleGraph.getModuleById(
+ getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE)
+ );
if (storiesModule) {
server.moduleGraph.invalidateModule(storiesModule);
}
@@ -45,7 +44,7 @@ export function codeGeneratorPlugin(options: Options): Plugin {
// TODO maybe use the stories declaration in main
if (/\.stories\.([tj])sx?$/.test(path) || /\.mdx$/.test(path)) {
// We need to emit a change event to trigger HMR
- server.watcher.emit('change', virtualStoriesFile);
+ server.watcher.emit('change', SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE);
}
});
},
@@ -69,34 +68,34 @@ export function codeGeneratorPlugin(options: Options): Plugin {
iframeId = `${config.root}/iframe.html`;
},
resolveId(source) {
- if (source === virtualFileId) {
- return `${virtualFileId}`;
+ if (source === SB_VIRTUAL_FILES.VIRTUAL_APP_FILE) {
+ return getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_APP_FILE);
}
if (source === iframePath) {
return iframeId;
}
- if (source === virtualStoriesFile) {
- return `${virtualStoriesFile}`;
+ if (source === SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE) {
+ return getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE);
}
- if (source === virtualPreviewFile) {
- return virtualPreviewFile;
+ if (source === SB_VIRTUAL_FILES.VIRTUAL_PREVIEW_FILE) {
+ return getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_PREVIEW_FILE);
}
- if (source === virtualAddonSetupFile) {
- return `${virtualAddonSetupFile}`;
+ if (source === SB_VIRTUAL_FILES.VIRTUAL_ADDON_SETUP_FILE) {
+ return getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_ADDON_SETUP_FILE);
}
return undefined;
},
async load(id, config) {
- if (id === `${virtualStoriesFile}`) {
+ if (id === getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_STORIES_FILE)) {
return generateImportFnScriptCode(options);
}
- if (id === `${virtualAddonSetupFile}`) {
+ if (id === getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_ADDON_SETUP_FILE)) {
return generateAddonSetupCode();
}
- if (id === `${virtualFileId}`) {
+ if (id === getResolvedVirtualModuleId(SB_VIRTUAL_FILES.VIRTUAL_APP_FILE)) {
return generateModernIframeScriptCode(options, projectRoot);
}
diff --git a/code/builders/builder-vite/src/plugins/external-globals-plugin.test.ts b/code/builders/builder-vite/src/plugins/external-globals-plugin.test.ts
index 9555a4dd6b86..67b62690da43 100644
--- a/code/builders/builder-vite/src/plugins/external-globals-plugin.test.ts
+++ b/code/builders/builder-vite/src/plugins/external-globals-plugin.test.ts
@@ -12,6 +12,22 @@ const cases = [
input: `import { Rain, Jour as Day, Nuit as Night, Sun } from "${packageName}"`,
output: `const { Rain, Jour: Day, Nuit: Night, Sun } = ${globals[packageName]}`,
},
+ {
+ globals,
+ packageName,
+ input: `import {
+ Rain,
+ Jour as Day,
+ Nuit as Night,
+ Sun
+ } from "${packageName}"`,
+ output: `const {
+ Rain,
+ Jour: Day,
+ Nuit: Night,
+ Sun
+ } = ${globals[packageName]}`,
+ },
{
globals,
packageName,
diff --git a/code/builders/builder-vite/src/plugins/external-globals-plugin.ts b/code/builders/builder-vite/src/plugins/external-globals-plugin.ts
index 1c7ada46b7be..d6767372a64f 100644
--- a/code/builders/builder-vite/src/plugins/external-globals-plugin.ts
+++ b/code/builders/builder-vite/src/plugins/external-globals-plugin.ts
@@ -1,8 +1,9 @@
-import { join } from 'node:path';
+import { existsSync } from 'node:fs';
+import { mkdir, writeFile } from 'node:fs/promises';
+import { dirname, join } from 'node:path';
import { init, parse } from 'es-module-lexer';
import findCacheDirectory from 'find-cache-dir';
-import { ensureFile, writeFile } from 'fs-extra';
import MagicString from 'magic-string';
import type { Alias, Plugin } from 'vite';
@@ -59,7 +60,10 @@ export async function externalGlobalsPlugin(externals: Record) {
(Object.keys(externals) as Array).map(async (externalKey) => {
const externalCachePath = join(cachePath, `${externalKey}.js`);
newAlias.push({ find: new RegExp(`^${externalKey}$`), replacement: externalCachePath });
- await ensureFile(externalCachePath);
+ if (!existsSync(externalCachePath)) {
+ const directory = dirname(externalCachePath);
+ await mkdir(directory, { recursive: true });
+ }
await writeFile(externalCachePath, `module.exports = ${externals[externalKey]};`);
})
);
@@ -91,10 +95,7 @@ export async function externalGlobalsPlugin(externals: Record) {
return {
code: src.toString(),
- map: src.generateMap({
- source: id,
- hires: true,
- }),
+ map: null,
};
},
} satisfies Plugin;
diff --git a/code/builders/builder-vite/src/plugins/webpack-stats-plugin.ts b/code/builders/builder-vite/src/plugins/webpack-stats-plugin.ts
index 3b8deebc8111..7e6f596e022f 100644
--- a/code/builders/builder-vite/src/plugins/webpack-stats-plugin.ts
+++ b/code/builders/builder-vite/src/plugins/webpack-stats-plugin.ts
@@ -6,6 +6,12 @@ import type { BuilderStats } from 'storybook/internal/types';
import slash from 'slash';
import type { Plugin } from 'vite';
+import {
+ SB_VIRTUAL_FILES,
+ getOriginalVirtualModuleId,
+ getResolvedVirtualModuleId,
+} from '../virtual-file-names';
+
/*
* Reason, Module are copied from chromatic types
* https://github.com/chromaui/chromatic-cli/blob/145a5e295dde21042e96396c7e004f250d842182/bin-src/types.ts#L265-L276
@@ -34,11 +40,18 @@ function stripQueryParams(filePath: string): string {
/** We only care about user code, not node_modules, vite files, or (most) virtual files. */
function isUserCode(moduleName: string) {
+ if (!moduleName) {
+ return false;
+ }
+
+ // keep Storybook's virtual files because they import the story files, so they are essential to the module graph
+ if (Object.values(SB_VIRTUAL_FILES).includes(getOriginalVirtualModuleId(moduleName))) {
+ return true;
+ }
+
return Boolean(
- moduleName &&
- !moduleName.startsWith('vite/') &&
- !moduleName.startsWith('\x00') &&
- !moduleName.startsWith('\u0000') &&
+ !moduleName.startsWith('vite/') &&
+ !moduleName.startsWith('\0') &&
moduleName !== 'react/jsx-runtime' &&
!moduleName.match(/node_modules\//)
);
@@ -53,6 +66,14 @@ export function pluginWebpackStats({ workingDir }: WebpackStatsPluginOptions): W
if (filename.startsWith('/virtual:')) {
return filename;
}
+ // ! Maintain backwards compatibility with the old virtual file names
+ // ! to ensure that the stats file doesn't change between the versions
+ // ! Turbosnap is also only compatible with the old virtual file names
+ // ! the old virtual file names did not start with the obligatory \0 character
+ if (Object.values(SB_VIRTUAL_FILES).includes(getOriginalVirtualModuleId(filename))) {
+ return getOriginalVirtualModuleId(filename);
+ }
+
// Otherwise, we need them in the format `./path/to/file.js`.
else {
const relativePath = relative(workingDir, stripQueryParams(filename));
@@ -82,25 +103,27 @@ export function pluginWebpackStats({ workingDir }: WebpackStatsPluginOptions): W
// We want this to run after the vite build plugins (https://vitejs.dev/guide/api-plugin.html#plugin-ordering)
enforce: 'post',
moduleParsed: function (mod) {
- if (isUserCode(mod.id)) {
- mod.importedIds
- .concat(mod.dynamicallyImportedIds)
- .filter((name) => isUserCode(name))
- .forEach((depIdUnsafe) => {
- const depId = normalize(depIdUnsafe);
- if (statsMap.has(depId)) {
- const m = statsMap.get(depId);
- if (m) {
- m.reasons = (m.reasons ?? [])
- .concat(createReasons([mod.id]))
- .filter((r) => r.moduleName !== depId);
- statsMap.set(depId, m);
- }
- } else {
- statsMap.set(depId, createStatsMapModule(depId, [mod.id]));
- }
- });
+ if (!isUserCode(mod.id)) {
+ return;
}
+ mod.importedIds
+ .concat(mod.dynamicallyImportedIds)
+ .filter((name) => isUserCode(name))
+ .forEach((depIdUnsafe) => {
+ const depId = normalize(depIdUnsafe);
+ if (!statsMap.has(depId)) {
+ statsMap.set(depId, createStatsMapModule(depId, [mod.id]));
+ return;
+ }
+ const m = statsMap.get(depId);
+ if (!m) {
+ return;
+ }
+ m.reasons = (m.reasons ?? [])
+ .concat(createReasons([mod.id]))
+ .filter((r) => r.moduleName !== depId);
+ statsMap.set(depId, m);
+ });
},
storybookGetStats() {
diff --git a/code/builders/builder-vite/src/virtual-file-names.ts b/code/builders/builder-vite/src/virtual-file-names.ts
index 0da0c5517dec..c38cb7322db6 100644
--- a/code/builders/builder-vite/src/virtual-file-names.ts
+++ b/code/builders/builder-vite/src/virtual-file-names.ts
@@ -1,4 +1,14 @@
-export const virtualFileId = '/virtual:/@storybook/builder-vite/vite-app.js';
-export const virtualStoriesFile = '/virtual:/@storybook/builder-vite/storybook-stories.js';
-export const virtualPreviewFile = '/virtual:/@storybook/builder-vite/preview-entry.js';
-export const virtualAddonSetupFile = '/virtual:/@storybook/builder-vite/setup-addons.js';
+export const SB_VIRTUAL_FILES = {
+ VIRTUAL_APP_FILE: '/virtual:/@storybook/builder-vite/vite-app.js',
+ VIRTUAL_STORIES_FILE: '/virtual:/@storybook/builder-vite/storybook-stories.js',
+ VIRTUAL_PREVIEW_FILE: '/virtual:/@storybook/builder-vite/preview-entry.js',
+ VIRTUAL_ADDON_SETUP_FILE: '/virtual:/@storybook/builder-vite/setup-addons.js',
+};
+
+export function getResolvedVirtualModuleId(virtualModuleId: string) {
+ return `\0${virtualModuleId}`;
+}
+
+export function getOriginalVirtualModuleId(resolvedVirtualModuleId: string) {
+ return resolvedVirtualModuleId.slice(1);
+}
diff --git a/code/builders/builder-webpack5/package.json b/code/builders/builder-webpack5/package.json
index c0cb7ac2cff4..e6f8716ffc4f 100644
--- a/code/builders/builder-webpack5/package.json
+++ b/code/builders/builder-webpack5/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/builder-webpack5",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook framework-agnostic API",
"keywords": [
"storybook"
@@ -41,7 +41,7 @@
"node": "./dist/loaders/export-order-loader.js",
"require": "./dist/loaders/export-order-loader.js"
},
- "./templates/virtualModuleModernEntry.js.handlebars": "./templates/virtualModuleModernEntry.js.handlebars",
+ "./templates/virtualModuleModernEntry.js": "./templates/virtualModuleModernEntry.js",
"./templates/preview.ejs": "./templates/preview.ejs",
"./templates/virtualModuleEntry.template.js": "./templates/virtualModuleEntry.template.js",
"./templates/virtualModuleStory.template.js": "./templates/virtualModuleStory.template.js",
@@ -74,7 +74,6 @@
"es-module-lexer": "^1.5.0",
"express": "^4.19.2",
"fork-ts-checker-webpack-plugin": "^8.0.0",
- "fs-extra": "^11.1.0",
"html-webpack-plugin": "^5.5.0",
"magic-string": "^0.30.5",
"path-browserify": "^1.0.1",
diff --git a/code/builders/builder-webpack5/src/index.ts b/code/builders/builder-webpack5/src/index.ts
index f9a83f8efaf2..ffc11812562b 100644
--- a/code/builders/builder-webpack5/src/index.ts
+++ b/code/builders/builder-webpack5/src/index.ts
@@ -1,3 +1,4 @@
+import { cp } from 'node:fs/promises';
import { join, parse } from 'node:path';
import { PREVIEW_BUILDER_PROGRESS } from 'storybook/internal/core-events';
@@ -12,7 +13,6 @@ import type { Builder, Options } from 'storybook/internal/types';
import { checkWebpackVersion } from '@storybook/core-webpack';
import express from 'express';
-import fs from 'fs-extra';
import prettyTime from 'pretty-hrtime';
import { corePath } from 'storybook/core-path';
import type { Configuration, Stats, StatsOptions } from 'webpack';
@@ -292,7 +292,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime,
const previewDirOrigin = previewResolvedDir;
const previewDirTarget = join(options.outputDir || '', `sb-preview`);
- const previewFiles = fs.copy(previewDirOrigin, previewDirTarget, {
+ const previewFiles = cp(previewDirOrigin, previewDirTarget, {
filter: (src) => {
const { ext } = parse(src);
if (ext) {
@@ -300,6 +300,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime,
}
return true;
},
+ recursive: true,
});
const [webpackCompilationOutput] = await Promise.all([webpackCompilation, previewFiles]);
diff --git a/code/builders/builder-webpack5/src/preview/virtual-module-mapping.ts b/code/builders/builder-webpack5/src/preview/virtual-module-mapping.ts
index 2b78d926f12e..ec55e40c5291 100644
--- a/code/builders/builder-webpack5/src/preview/virtual-module-mapping.ts
+++ b/code/builders/builder-webpack5/src/preview/virtual-module-mapping.ts
@@ -2,7 +2,6 @@ import { join, resolve } from 'node:path';
import {
getBuilderOptions,
- handlebars,
loadPreviewOrConfigFile,
normalizeStories,
readTemplate,
@@ -51,18 +50,28 @@ export const getVirtualModules = async (options: Options) => {
const needPipelinedImport = !!builderOptions.lazyCompilation && !isProd;
virtualModules[storiesPath] = toImportFn(stories, { needPipelinedImport });
const configEntryPath = resolve(join(workingDir, 'storybook-config-entry.js'));
- virtualModules[configEntryPath] = handlebars(
+ virtualModules[configEntryPath] = (
await readTemplate(
- require.resolve(
- '@storybook/builder-webpack5/templates/virtualModuleModernEntry.js.handlebars'
- )
- ),
- {
- storiesFilename,
- previewAnnotations,
- }
+ require.resolve('@storybook/builder-webpack5/templates/virtualModuleModernEntry.js')
+ )
+ )
+ .replaceAll(`'{{storiesFilename}}'`, `'./${storiesFilename}'`)
+ .replaceAll(
+ `'{{previewAnnotations}}'`,
+ previewAnnotations
+ .filter(Boolean)
+ .map((entry) => `'${entry}'`)
+ .join(',')
+ )
+ .replaceAll(
+ `'{{previewAnnotations_requires}}'`,
+ previewAnnotations
+ .filter(Boolean)
+ .map((entry) => `require('${entry}')`)
+ .join(',')
+ )
// We need to double escape `\` for webpack. We may have some in windows paths
- ).replace(/\\/g, '\\\\');
+ .replace(/\\/g, '\\\\');
entries.push(configEntryPath);
return {
diff --git a/code/builders/builder-webpack5/templates/virtualModuleModernEntry.js b/code/builders/builder-webpack5/templates/virtualModuleModernEntry.js
new file mode 100644
index 000000000000..4ae082c43b2e
--- /dev/null
+++ b/code/builders/builder-webpack5/templates/virtualModuleModernEntry.js
@@ -0,0 +1,33 @@
+import { createBrowserChannel } from 'storybook/internal/channels';
+import { PreviewWeb, addons, composeConfigs } from 'storybook/internal/preview-api';
+
+import { global } from '@storybook/global';
+
+import { importFn } from '{{storiesFilename}}';
+
+const getProjectAnnotations = () => composeConfigs(['{{previewAnnotations_requires}}']);
+
+const channel = createBrowserChannel({ page: 'preview' });
+addons.setChannel(channel);
+
+if (global.CONFIG_TYPE === 'DEVELOPMENT') {
+ window.__STORYBOOK_SERVER_CHANNEL__ = channel;
+}
+
+const preview = new PreviewWeb(importFn, getProjectAnnotations);
+
+window.__STORYBOOK_PREVIEW__ = preview;
+window.__STORYBOOK_STORY_STORE__ = preview.storyStore;
+window.__STORYBOOK_ADDONS_CHANNEL__ = channel;
+
+if (import.meta.webpackHot) {
+ import.meta.webpackHot.accept('{{storiesFilename}}', () => {
+ // importFn has changed so we need to patch the new one in
+ preview.onStoriesChanged({ importFn });
+ });
+
+ import.meta.webpackHot.accept(['{{previewAnnotations}}'], () => {
+ // getProjectAnnotations has changed so we need to patch the new one in
+ preview.onGetProjectAnnotationsChanged({ getProjectAnnotations });
+ });
+}
diff --git a/code/builders/builder-webpack5/templates/virtualModuleModernEntry.js.handlebars b/code/builders/builder-webpack5/templates/virtualModuleModernEntry.js.handlebars
deleted file mode 100644
index 14fa7585ec3b..000000000000
--- a/code/builders/builder-webpack5/templates/virtualModuleModernEntry.js.handlebars
+++ /dev/null
@@ -1,34 +0,0 @@
-import { global } from '@storybook/global';
-
-import { ClientApi, PreviewWeb, addons, composeConfigs } from 'storybook/internal/preview-api';
-import { createBrowserChannel } from 'storybook/internal/channels';
-
-import { importFn } from './{{storiesFilename}}';
-
-const getProjectAnnotations = () =>
-composeConfigs([{{#each previewAnnotations}}require('{{this}}'),{{/each}}]);
-
-const channel = createBrowserChannel({ page: 'preview' });
-addons.setChannel(channel);
-
-if (global.CONFIG_TYPE === 'DEVELOPMENT'){
-window.__STORYBOOK_SERVER_CHANNEL__ = channel;
-}
-
-const preview = new PreviewWeb(importFn, getProjectAnnotations);
-
-window.__STORYBOOK_PREVIEW__ = preview;
-window.__STORYBOOK_STORY_STORE__ = preview.storyStore;
-window.__STORYBOOK_ADDONS_CHANNEL__ = channel;
-
-if (import.meta.webpackHot) {
-import.meta.webpackHot.accept('./{{storiesFilename}}', () => {
-// importFn has changed so we need to patch the new one in
-preview.onStoriesChanged({ importFn });
-});
-
-import.meta.webpackHot.accept([{{#each previewAnnotations}}'{{this}}',{{/each}}], () => {
-// getProjectAnnotations has changed so we need to patch the new one in
-preview.onGetProjectAnnotationsChanged({ getProjectAnnotations });
-});
-}
\ No newline at end of file
diff --git a/code/core/package.json b/code/core/package.json
index b6497ac7809a..9a8dff954fdb 100644
--- a/code/core/package.json
+++ b/code/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/core",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook framework-agnostic API",
"keywords": [
"storybook"
@@ -279,10 +279,12 @@
"dependencies": {
"@storybook/csf": "^0.1.11",
"@types/express": "^4.17.21",
+ "better-opn": "^3.0.2",
"browser-assert": "^1.2.1",
- "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0",
+ "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0",
"esbuild-register": "^3.5.0",
"express": "^4.19.2",
+ "jsdoc-type-pratt-parser": "^4.0.0",
"process": "^0.11.10",
"recast": "^0.23.5",
"semver": "^7.6.2",
@@ -320,16 +322,13 @@
"@types/diff": "^5.0.9",
"@types/ejs": "^3.1.1",
"@types/find-cache-dir": "^5.0.0",
- "@types/fs-extra": "^11.0.1",
"@types/js-yaml": "^4.0.5",
- "@types/lodash": "^4.14.167",
"@types/node": "^22.0.0",
"@types/npmlog": "^7.0.0",
"@types/picomatch": "^2.3.0",
"@types/prettier": "^3.0.0",
"@types/pretty-hrtime": "^1.0.0",
"@types/prompts": "^2.0.9",
- "@types/qs": "^6",
"@types/react-syntax-highlighter": "11.0.5",
"@types/react-transition-group": "^4",
"@types/semver": "^7.5.8",
@@ -340,12 +339,10 @@
"ansi-to-html": "^0.7.2",
"assert": "^2.1.0",
"babel-plugin-react-docgen": "4.2.1",
- "better-opn": "^3.0.2",
"boxen": "^7.1.1",
"browser-dtector": "^3.4.0",
"camelcase": "^8.0.0",
"chai": "^4.4.1",
- "chalk": "^5.3.0",
"cli-table3": "^0.6.1",
"commander": "^12.1.0",
"comment-parser": "^1.4.1",
@@ -361,6 +358,7 @@
"diff": "^5.2.0",
"downshift": "^9.0.4",
"ejs": "^3.1.10",
+ "es-toolkit": "^1.21.0",
"esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0",
"esbuild-plugin-alias": "^0.2.1",
"execa": "^8.0.1",
@@ -371,29 +369,27 @@
"find-cache-dir": "^5.0.0",
"find-up": "^7.0.0",
"flush-promises": "^1.0.2",
- "fs-extra": "^11.1.0",
"fuse.js": "^3.6.1",
"get-npm-tarball-url": "^2.0.3",
"glob": "^10.0.0",
"globby": "^14.0.1",
- "handlebars": "^4.7.7",
+ "jiti": "^1.21.6",
"js-yaml": "^4.1.0",
- "jsdoc-type-pratt-parser": "^4.0.0",
"lazy-universal-dotenv": "^4.0.0",
"leven": "^4.0.0",
- "lodash": "^4.17.21",
"markdown-to-jsx": "^7.4.5",
"memfs": "^4.11.1",
"memoizerific": "^1.11.3",
"nanoid": "^4.0.2",
"npmlog": "^7.0.0",
"open": "^8.4.0",
+ "picocolors": "^1.1.0",
"picomatch": "^2.3.0",
+ "picoquery": "^1.4.0",
"polished": "^4.2.2",
"prettier": "^3.2.5",
"pretty-hrtime": "^1.0.3",
"prompts": "^2.4.0",
- "qs": "^6.10.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.5",
@@ -421,6 +417,14 @@
"use-resize-observer": "^9.1.0",
"watchpack": "^2.2.0"
},
+ "peerDependencies": {
+ "prettier": "^2 || ^3"
+ },
+ "peerDependenciesMeta": {
+ "prettier": {
+ "optional": true
+ }
+ },
"publishConfig": {
"access": "public"
},
diff --git a/code/core/scripts/entries.ts b/code/core/scripts/entries.ts
index afe8acddd84d..e3b78e483990 100644
--- a/code/core/scripts/entries.ts
+++ b/code/core/scripts/entries.ts
@@ -28,7 +28,13 @@ export const getEntries = (cwd: string) => {
define('src/preview-api/index.ts', ['browser', 'node'], true),
define('src/manager-api/index.ts', ['browser', 'node'], true, ['react']),
define('src/router/index.ts', ['browser', 'node'], true, ['react']),
- define('src/components/index.ts', ['browser', 'node'], true, ['react', 'react-dom']),
+ define(
+ 'src/components/index.ts',
+ ['browser', 'node'],
+ true,
+ ['react', 'react-dom'],
+ ['prettier'] // the syntax highlighter uses prettier/standalone to format the code
+ ),
define('src/theming/index.ts', ['browser', 'node'], true, ['react']),
define('src/theming/create.ts', ['browser', 'node'], true, ['react']),
define('src/docs-tools/index.ts', ['browser', 'node'], true),
diff --git a/code/core/scripts/helpers/dependencies.ts b/code/core/scripts/helpers/dependencies.ts
index 82b22a022ad4..89e67fc554f3 100644
--- a/code/core/scripts/helpers/dependencies.ts
+++ b/code/core/scripts/helpers/dependencies.ts
@@ -1,7 +1,6 @@
+import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
-import { readJson } from 'fs-extra';
-
export async function flattenDependencies(
list: string[],
output: string[] = [],
@@ -18,7 +17,9 @@ export async function flattenDependencies(
console.log(dep + ' not found');
return;
}
- const { dependencies = {}, peerDependencies = {} } = await readJson(path);
+ const { dependencies = {}, peerDependencies = {} } = JSON.parse(
+ await readFile(path, { encoding: 'utf8' })
+ );
const all: string[] = [
...new Set([...Object.keys(dependencies), ...Object.keys(peerDependencies)]),
]
diff --git a/code/core/scripts/helpers/generatePackageJsonFile.ts b/code/core/scripts/helpers/generatePackageJsonFile.ts
index 9ecf2f7679e5..271f775cba23 100644
--- a/code/core/scripts/helpers/generatePackageJsonFile.ts
+++ b/code/core/scripts/helpers/generatePackageJsonFile.ts
@@ -1,7 +1,6 @@
-import { writeFile } from 'node:fs/promises';
+import { readFile, writeFile } from 'node:fs/promises';
import { join, relative } from 'node:path';
-import { readJSON } from 'fs-extra';
import slash from 'slash';
import { sortPackageJson } from '../../../../scripts/prepare/tools';
@@ -11,7 +10,7 @@ const cwd = process.cwd();
export async function generatePackageJsonFile(entries: ReturnType) {
const location = join(cwd, 'package.json');
- const pkgJson = await readJSON(location);
+ const pkgJson = JSON.parse(await readFile(location, { encoding: 'utf8' }));
/**
* Re-create the `exports` field in `code/core/package.json` This way we only need to update the
diff --git a/code/core/scripts/helpers/generateTypesFiles.ts b/code/core/scripts/helpers/generateTypesFiles.ts
index a657c6fbd8a7..fd7a7eff789d 100644
--- a/code/core/scripts/helpers/generateTypesFiles.ts
+++ b/code/core/scripts/helpers/generateTypesFiles.ts
@@ -1,7 +1,7 @@
import { join, relative } from 'node:path';
import { spawn } from '../../../../scripts/prepare/tools';
-import { chalk, limit, process } from '../../../../scripts/prepare/tools';
+import { limit, picocolors, process } from '../../../../scripts/prepare/tools';
import type { getEntries } from '../entries';
export async function generateTypesFiles(
@@ -69,7 +69,7 @@ export async function generateTypesFiles(
console.log(index, fileName);
process.exit(dtsProcess.exitCode || 1);
} else {
- console.log('Generated types for', chalk.cyan(relative(cwd, dtsEntries[index])));
+ console.log('Generated types for', picocolors.cyan(relative(cwd, dtsEntries[index])));
}
});
})
diff --git a/code/core/scripts/helpers/generateTypesMapperFiles.ts b/code/core/scripts/helpers/generateTypesMapperFiles.ts
index 2f79f80643d8..5283321c84c0 100644
--- a/code/core/scripts/helpers/generateTypesMapperFiles.ts
+++ b/code/core/scripts/helpers/generateTypesMapperFiles.ts
@@ -1,7 +1,6 @@
-import { writeFile } from 'node:fs/promises';
-import { join, relative } from 'node:path';
-
-import { ensureFile } from 'fs-extra';
+import { existsSync } from 'node:fs';
+import { mkdir, writeFile } from 'node:fs/promises';
+import { dirname, join, relative } from 'node:path';
import { dedent } from '../../../../scripts/prepare/tools';
import type { getEntries } from '../entries';
@@ -34,7 +33,10 @@ export async function generateTypesMapperFiles(entries: ReturnType {
const location = filePath.replace('src', 'dist').replace(/\.tsx?/, '.d.ts');
- await ensureFile(location);
+ if (!existsSync(location)) {
+ const directory = dirname(location);
+ await mkdir(directory, { recursive: true });
+ }
await writeFile(location, await generateTypesMapperContent(filePath));
})
);
diff --git a/code/core/scripts/prep.ts b/code/core/scripts/prep.ts
index 49c2bd00cfa0..f8726e79df48 100644
--- a/code/core/scripts/prep.ts
+++ b/code/core/scripts/prep.ts
@@ -1,18 +1,18 @@
/* eslint-disable local-rules/no-uncategorized-errors */
-import { watch } from 'node:fs';
+import { existsSync, mkdirSync, watch } from 'node:fs';
import { mkdir, rm, writeFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
-import { ensureDir } from 'fs-extra';
+import type { Metafile } from 'esbuild';
import {
- chalk,
dedent,
esbuild,
globalExternals,
measure,
merge,
nodeInternals,
+ picocolors,
prettyTime,
process,
} from '../../../scripts/prepare/tools';
@@ -77,12 +77,15 @@ async function run() {
types,
]);
- console.log('Files generated in', chalk.yellow(prettyTime(filesTime)));
- console.log('Package.json generated in', chalk.yellow(prettyTime(packageJsonTime)));
- console.log(isWatch ? 'Watcher started in' : 'Bundled in', chalk.yellow(prettyTime(distTime)));
+ console.log('Files generated in', picocolors.yellow(prettyTime(filesTime)));
+ console.log('Package.json generated in', picocolors.yellow(prettyTime(packageJsonTime)));
+ console.log(
+ isWatch ? 'Watcher started in' : 'Bundled in',
+ picocolors.yellow(prettyTime(distTime))
+ );
console.log(
isOptimized ? 'Generated types in' : 'Generated type mappers in',
- chalk.yellow(prettyTime(typesTime))
+ picocolors.yellow(prettyTime(typesTime))
);
async function generateDistFiles() {
@@ -193,6 +196,14 @@ async function run() {
'@storybook/core': join(cwd, 'src'),
react: dirname(require.resolve('react/package.json')),
'react-dom': dirname(require.resolve('react-dom/package.json')),
+ 'react-dom/client': join(
+ dirname(require.resolve('react-dom/package.json')),
+ 'client'
+ ),
+ },
+ define: {
+ // This should set react in prod mode for the manager
+ 'process.env.NODE_ENV': JSON.stringify('production'),
},
external: [],
})
@@ -305,28 +316,61 @@ async function run() {
// show a log message when a file is compiled
watch(join(cwd, 'dist'), { recursive: true }, (event, filename) => {
- console.log(`compiled ${chalk.cyan(filename)}`);
+ console.log(`compiled ${picocolors.cyan(filename)}`);
});
} else {
- await Promise.all(
- compile.map(async (context, index) => {
- const out = await context.rebuild();
+ // repo root/bench/esbuild-metafiles/core
+ const metafilesDir = join(__dirname, '..', '..', 'bench', 'esbuild-metafiles', 'core');
+ if (existsSync(metafilesDir)) {
+ await rm(metafilesDir, { recursive: true });
+ }
+ await mkdir(metafilesDir, { recursive: true });
+
+ const outputs = await Promise.all(
+ compile.map(async (context) => {
+ const output = await context.rebuild();
await context.dispose();
- if (out.metafile) {
- const { outputs } = out.metafile;
- const keys = Object.keys(outputs);
- const format = keys.every((key) => key.endsWith('.js')) ? 'esm' : 'cjs';
- const outName =
- keys.length === 1 ? dirname(keys[0]).replace('dist/', '') : `meta-${format}-${index}`;
-
- await ensureDir('report');
- await writeFile(`report/${outName}.json`, JSON.stringify(out.metafile, null, 2));
- await writeFile(
- `report/${outName}.txt`,
- await esbuild.analyzeMetafile(out.metafile, { color: false, verbose: false })
- );
- }
+ return output;
+ })
+ );
+
+ const metafileByModule: Record = {};
+
+ for (const currentOutput of outputs) {
+ if (!currentOutput.metafile) {
+ continue;
+ }
+
+ const keys = Object.keys(currentOutput.metafile.outputs);
+ const moduleName = keys.length === 1 ? dirname(keys[0]).replace('dist/', '') : 'core';
+
+ const existingMetafile = metafileByModule[moduleName];
+
+ if (existingMetafile) {
+ existingMetafile.inputs = {
+ ...existingMetafile.inputs,
+ ...currentOutput.metafile.inputs,
+ };
+ existingMetafile.outputs = {
+ ...existingMetafile.outputs,
+ ...currentOutput.metafile.outputs,
+ };
+ } else {
+ metafileByModule[moduleName] = currentOutput.metafile;
+ }
+ }
+
+ await Promise.all(
+ Object.entries(metafileByModule).map(async ([moduleName, metafile]) => {
+ await writeFile(
+ join(metafilesDir, `${moduleName}.json`),
+ JSON.stringify(metafile, null, 2)
+ );
+ await writeFile(
+ join(metafilesDir, `${moduleName}.txt`),
+ await esbuild.analyzeMetafile(metafile, { color: false, verbose: false })
+ );
})
);
}
diff --git a/code/core/src/builder-manager/index.ts b/code/core/src/builder-manager/index.ts
index 1e246b5d1cb1..b068caf206c7 100644
--- a/code/core/src/builder-manager/index.ts
+++ b/code/core/src/builder-manager/index.ts
@@ -1,3 +1,4 @@
+import { cp, rm, writeFile } from 'node:fs/promises';
import { dirname, join, parse } from 'node:path';
import { stringifyProcessEnvs } from '@storybook/core/common';
@@ -9,7 +10,6 @@ import { globalExternals } from '@fal-works/esbuild-plugin-global-externals';
import { pnpPlugin } from '@yarnpkg/esbuild-plugin-pnp';
import aliasPlugin from 'esbuild-plugin-alias';
import express from 'express';
-import fs from 'fs-extra';
import type {
BuilderBuildResult,
@@ -149,7 +149,7 @@ const starter: StarterFunction = async function* starterGeneratorFn({
// make sure we clear output directory of addons dir before starting
// this could cause caching issues where addons are loaded when they shouldn't
const addonsDir = config.outdir;
- await fs.remove(addonsDir);
+ await rm(addonsDir, { recursive: true, force: true });
yield;
@@ -256,7 +256,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime,
yield;
- const managerFiles = fs.copy(coreDirOrigin, coreDirTarget, {
+ const managerFiles = cp(coreDirOrigin, coreDirTarget, {
filter: (src) => {
const { ext } = parse(src);
if (ext) {
@@ -264,6 +264,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime,
}
return true;
},
+ recursive: true,
});
const { cssFiles, jsFiles } = await readOrderedFiles(addonsDir, compilation?.outputFiles);
@@ -288,11 +289,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime,
globals
);
- await Promise.all([
- //
- fs.writeFile(join(options.outputDir, 'index.html'), html),
- managerFiles,
- ]);
+ await Promise.all([writeFile(join(options.outputDir, 'index.html'), html), managerFiles]);
logger.trace({ message: '=> Manager built', time: process.hrtime(startTime) });
diff --git a/code/core/src/builder-manager/utils/files.ts b/code/core/src/builder-manager/utils/files.ts
index 94ec31677c4a..352d558dd981 100644
--- a/code/core/src/builder-manager/utils/files.ts
+++ b/code/core/src/builder-manager/utils/files.ts
@@ -1,7 +1,8 @@
-import { join, normalize } from 'node:path';
+import { existsSync } from 'node:fs';
+import { mkdir, writeFile } from 'node:fs/promises';
+import { dirname, join, normalize } from 'node:path';
import type { OutputFile } from 'esbuild';
-import fs from 'fs-extra';
import slash from 'slash';
import type { Compilation } from '../types';
@@ -15,8 +16,11 @@ export async function readOrderedFiles(
// convert deeply nested paths to a single level, also remove special characters
const { location, url } = sanitizePath(file, addonsDir);
- await fs.ensureFile(location);
- await fs.writeFile(location, file.contents);
+ if (!existsSync(location)) {
+ const directory = dirname(location);
+ await mkdir(directory, { recursive: true });
+ }
+ await writeFile(location, file.contents);
return url;
}) || []
);
diff --git a/code/core/src/builder-manager/utils/managerEntries.ts b/code/core/src/builder-manager/utils/managerEntries.ts
index d808596f52e4..787116d05c0e 100644
--- a/code/core/src/builder-manager/utils/managerEntries.ts
+++ b/code/core/src/builder-manager/utils/managerEntries.ts
@@ -1,8 +1,9 @@
-import { join, parse, relative, sep } from 'node:path';
+import { existsSync } from 'node:fs';
+import { mkdir, writeFile } from 'node:fs/promises';
+import { dirname, join, parse, relative, sep } from 'node:path';
import { resolvePathInStorybookCache } from '@storybook/core/common';
-import fs from 'fs-extra';
import slash from 'slash';
const sanitizeBase = (path: string) => {
@@ -55,8 +56,11 @@ export async function wrapManagerEntries(entrypoints: string[], uniqueId?: strin
sanitizeFinal(join(`${sanitizeBase(base)}-${i}`, `${sanitizeBase(name)}-bundle.js`))
);
- await fs.ensureFile(location);
- await fs.writeFile(location, `import '${slash(entry)}';`);
+ if (!existsSync(location)) {
+ const directory = dirname(location);
+ await mkdir(directory, { recursive: true });
+ }
+ await writeFile(location, `import '${slash(entry)}';`);
return location;
})
diff --git a/code/core/src/builder-manager/utils/template.ts b/code/core/src/builder-manager/utils/template.ts
index b264a3d5e02e..1ca99e07e2e6 100644
--- a/code/core/src/builder-manager/utils/template.ts
+++ b/code/core/src/builder-manager/utils/template.ts
@@ -1,9 +1,9 @@
+import { readFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import type { DocsOptions, Options, Ref, TagsOptions } from '@storybook/core/types';
import { render } from 'ejs';
-import fs from 'fs-extra';
export const getTemplatePath = async (template: string) => {
return join(dirname(require.resolve('@storybook/core/package.json')), 'assets/server', template);
@@ -12,7 +12,7 @@ export const getTemplatePath = async (template: string) => {
export const readTemplate = async (template: string) => {
const path = await getTemplatePath(template);
- return fs.readFile(path, 'utf8');
+ return readFile(path, { encoding: 'utf8' });
};
export async function getManagerMainTemplate() {
diff --git a/code/core/src/cli/bin/index.ts b/code/core/src/cli/bin/index.ts
index 20cc55c1d809..060879635d61 100644
--- a/code/core/src/cli/bin/index.ts
+++ b/code/core/src/cli/bin/index.ts
@@ -3,10 +3,10 @@ import { addToGlobalContext } from '@storybook/core/telemetry';
import { logger } from '@storybook/core/node-logger';
-import chalk from 'chalk';
import { program } from 'commander';
import { findPackageSync } from 'fd-package-json';
import leven from 'leven';
+import picocolors from 'picocolors';
import invariant from 'tiny-invariant';
import { build } from '../build';
@@ -70,7 +70,7 @@ command('dev')
)
.action(async (options) => {
logger.setLevel(options.loglevel);
- consoleLogger.log(chalk.bold(`${pkg.name} v${pkg.version}`) + chalk.reset('\n'));
+ consoleLogger.log(picocolors.bold(`${pkg.name} v${pkg.version}`) + picocolors.reset('\n'));
// The key is the field created in `options` variable for
// each command line argument. Value is the env variable.
@@ -110,7 +110,7 @@ command('build')
.action(async (options) => {
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
logger.setLevel(options.loglevel);
- consoleLogger.log(chalk.bold(`${pkg.name} v${pkg.version}\n`));
+ consoleLogger.log(picocolors.bold(`${pkg.name} v${pkg.version}\n`));
// The key is the field created in `options` variable for
// each command line argument. Value is the env variable.
diff --git a/code/core/src/cli/detect.test.ts b/code/core/src/cli/detect.test.ts
index f12ece4314fa..95c1b126dca3 100644
--- a/code/core/src/cli/detect.test.ts
+++ b/code/core/src/cli/detect.test.ts
@@ -26,10 +26,6 @@ vi.mock('fs', () => ({
default: vi.fn(),
}));
-vi.mock('fs-extra', () => ({
- pathExistsSync: vi.fn(() => true),
-}));
-
vi.mock('@storybook/core/node-logger');
const MOCK_FRAMEWORK_FILES: {
diff --git a/code/core/src/cli/eslintPlugin.ts b/code/core/src/cli/eslintPlugin.ts
index 4b8d47b293a2..24903afc35cd 100644
--- a/code/core/src/cli/eslintPlugin.ts
+++ b/code/core/src/cli/eslintPlugin.ts
@@ -1,13 +1,13 @@
import { existsSync } from 'node:fs';
+import { readFile, writeFile } from 'node:fs/promises';
import type { JsPackageManager } from '@storybook/core/common';
import { paddedLog } from '@storybook/core/common';
import { readConfig, writeConfig } from '@storybook/core/csf-tools';
-import chalk from 'chalk';
import detectIndent from 'detect-indent';
-import { readFile, readJson, writeJson } from 'fs-extra';
+import picocolors from 'picocolors';
import prompts from 'prompts';
import { dedent } from 'ts-dedent';
@@ -72,13 +72,15 @@ export async function configureEslintPlugin(
if (eslintFile) {
paddedLog(`Configuring Storybook ESLint plugin at ${eslintFile}`);
if (eslintFile.endsWith('json')) {
- const eslintConfig = (await readJson(eslintFile)) as { extends?: string[] };
+ const eslintConfig = JSON.parse(await readFile(eslintFile, { encoding: 'utf8' })) as {
+ extends?: string[];
+ };
const existingExtends = normalizeExtends(eslintConfig.extends).filter(Boolean);
eslintConfig.extends = [...existingExtends, 'plugin:storybook/recommended'] as string[];
- const eslintFileContents = await readFile(eslintFile, 'utf8');
+ const eslintFileContents = await readFile(eslintFile, { encoding: 'utf8' });
const spaces = detectIndent(eslintFileContents).amount || 2;
- await writeJson(eslintFile, eslintConfig, { spaces });
+ await writeFile(eslintFile, JSON.stringify(eslintConfig, undefined, spaces));
} else {
const eslint = await readConfig(eslintFile);
const existingExtends = normalizeExtends(eslint.getFieldValue(['extends'])).filter(Boolean);
@@ -106,7 +108,7 @@ export const suggestESLintPlugin = async (): Promise => {
type: 'confirm',
name: 'shouldInstall',
message: dedent`
- We have detected that you're using ESLint. Storybook provides a plugin that gives the best experience with Storybook and helps follow best practices: ${chalk.yellow(
+ We have detected that you're using ESLint. Storybook provides a plugin that gives the best experience with Storybook and helps follow best practices: ${picocolors.yellow(
'https://github.com/storybookjs/eslint-plugin-storybook#readme'
)}
diff --git a/code/core/src/cli/helpers.test.ts b/code/core/src/cli/helpers.test.ts
index cff797a8f505..4615f0cfdd84 100644
--- a/code/core/src/cli/helpers.test.ts
+++ b/code/core/src/cli/helpers.test.ts
@@ -1,8 +1,10 @@
+import fs from 'node:fs';
+import fsp from 'node:fs/promises';
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { JsPackageManager } from '@storybook/core/common';
-import fse from 'fs-extra';
import { sep } from 'path';
import { IS_WINDOWS } from '../../../vitest.helpers';
@@ -13,21 +15,18 @@ import { SupportedLanguage } from './project_types';
const normalizePath = (path: string) => (IS_WINDOWS ? path.replace(/\//g, sep) : path);
const fsMocks = vi.hoisted(() => ({
+ cpSync: vi.fn(() => ({})),
existsSync: vi.fn(),
}));
-const fseMocks = vi.hoisted(() => ({
- copySync: vi.fn(() => ({})),
- copy: vi.fn(() => ({})),
- ensureDir: vi.fn(() => {}),
- existsSync: vi.fn(),
- pathExists: vi.fn(),
+const fspMocks = vi.hoisted(() => ({
+ cp: vi.fn(() => ({})),
readFile: vi.fn(() => ''),
writeFile: vi.fn(),
}));
-vi.mock('fs', async (importOriginal) => {
- const actual = await importOriginal();
+vi.mock('node:fs', async (importOriginal) => {
+ const actual = await importOriginal();
return {
...actual,
...fsMocks,
@@ -42,14 +41,14 @@ vi.mock('./dirs', () => ({
normalizePath(`@storybook/${renderer}`),
}));
-vi.mock('fs-extra', async (importOriginal) => {
- const actual = await importOriginal();
+vi.mock('node:fs/promises', async (importOriginal) => {
+ const actual = await importOriginal();
return {
...actual,
- ...fseMocks,
+ ...fspMocks,
default: {
...actual,
- ...fseMocks,
+ ...fspMocks,
},
};
});
@@ -83,7 +82,7 @@ describe('Helpers', () => {
helpers.copyTemplate('');
- expect(fse.copySync).toHaveBeenCalledWith(
+ expect(fs.cpSync).toHaveBeenCalledWith(
expect.stringMatching(csfDirectory),
expect.anything(),
expect.anything()
@@ -115,7 +114,7 @@ describe('Helpers', () => {
const componentsDirectory = exists.map((folder: string) =>
normalizePath(`@storybook/react/template/cli/${folder}`)
);
- fseMocks.pathExists.mockImplementation(
+ fsMocks.existsSync.mockImplementation(
(filePath) =>
componentsDirectory.includes(filePath) ||
filePath === normalizePath('@storybook/react/template/cli')
@@ -127,7 +126,7 @@ describe('Helpers', () => {
commonAssetsDir: normalizePath('create-storybook/rendererAssets/common'),
});
- expect(fse.copy).toHaveBeenNthCalledWith(
+ expect(fsp.cp).toHaveBeenNthCalledWith(
1,
normalizePath('create-storybook/rendererAssets/common'),
'./stories',
@@ -135,17 +134,12 @@ describe('Helpers', () => {
);
const expectedDirectory = normalizePath(`@storybook/react/template/cli${expected}`);
- expect(fse.copy).toHaveBeenNthCalledWith(
- 2,
- expectedDirectory,
- './stories',
- expect.anything()
- );
+ expect(fsp.cp).toHaveBeenNthCalledWith(2, expectedDirectory, './stories', expect.anything());
}
);
it(`should copy to src folder when exists`, async () => {
- vi.mocked(fse.pathExists).mockImplementation((filePath) => {
+ vi.mocked(fs.existsSync).mockImplementation((filePath) => {
return filePath === normalizePath('@storybook/react/template/cli') || filePath === './src';
});
await helpers.copyTemplateFiles({
@@ -153,11 +147,11 @@ describe('Helpers', () => {
language: SupportedLanguage.JAVASCRIPT,
packageManager: packageManagerMock,
});
- expect(fse.copy).toHaveBeenCalledWith(expect.anything(), './src/stories', expect.anything());
+ expect(fsp.cp).toHaveBeenCalledWith(expect.anything(), './src/stories', expect.anything());
});
it(`should copy to root folder when src doesn't exist`, async () => {
- vi.mocked(fse.pathExists).mockImplementation((filePath) => {
+ vi.mocked(fs.existsSync).mockImplementation((filePath) => {
return filePath === normalizePath('@storybook/react/template/cli');
});
await helpers.copyTemplateFiles({
@@ -165,7 +159,7 @@ describe('Helpers', () => {
language: SupportedLanguage.JAVASCRIPT,
packageManager: packageManagerMock,
});
- expect(fse.copy).toHaveBeenCalledWith(expect.anything(), './stories', expect.anything());
+ expect(fsp.cp).toHaveBeenCalledWith(expect.anything(), './stories', expect.anything());
});
it(`should throw an error for unsupported renderer`, async () => {
diff --git a/code/core/src/cli/helpers.ts b/code/core/src/cli/helpers.ts
index 355e142cece1..38cf6151294f 100644
--- a/code/core/src/cli/helpers.ts
+++ b/code/core/src/cli/helpers.ts
@@ -1,4 +1,5 @@
-import { existsSync, readFileSync, writeFileSync } from 'node:fs';
+import { cpSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
+import { cp, readFile, writeFile } from 'node:fs/promises';
import { join, resolve } from 'node:path';
import {
@@ -10,9 +11,8 @@ import {
import { versions as storybookMonorepoPackages } from '@storybook/core/common';
import type { SupportedFrameworks, SupportedRenderers } from '@storybook/core/types';
-import chalk from 'chalk';
import { findUpSync } from 'find-up';
-import { copy, copySync, pathExists, readFile, writeFile } from 'fs-extra';
+import picocolors from 'picocolors';
import { coerce, satisfies } from 'semver';
import stripJsonComments from 'strip-json-comments';
import invariant from 'tiny-invariant';
@@ -34,7 +34,7 @@ export function readFileAsJson(jsonPath: string, allowComments?: boolean) {
try {
return JSON.parse(jsonContent);
} catch (e) {
- logger.error(chalk.red(`Invalid json in file: ${filePath}`));
+ logger.error(picocolors.red(`Invalid json in file: ${filePath}`));
throw e;
}
}
@@ -130,7 +130,7 @@ export function copyTemplate(templateRoot: string, destination = '.') {
throw new Error(`Couldn't find template dir`);
}
- copySync(templateDir, destination, { overwrite: true });
+ cpSync(templateDir, destination, { recursive: true });
}
type CopyTemplateFilesOptions = {
@@ -197,30 +197,30 @@ export async function copyTemplateFiles({
const assetsTS38 = join(assetsDir, languageFolderMapping[SupportedLanguage.TYPESCRIPT_3_8]);
// Ideally use the assets that match the language & version.
- if (await pathExists(assetsLanguage)) {
+ if (existsSync(assetsLanguage)) {
return assetsLanguage;
}
// Use fallback typescript 3.8 assets if new ones aren't available
- if (language === SupportedLanguage.TYPESCRIPT_4_9 && (await pathExists(assetsTS38))) {
+ if (language === SupportedLanguage.TYPESCRIPT_4_9 && existsSync(assetsTS38)) {
return assetsTS38;
}
// Fallback further to TS (for backwards compatibility purposes)
- if (await pathExists(assetsTS)) {
+ if (existsSync(assetsTS)) {
return assetsTS;
}
// Fallback further to JS
- if (await pathExists(assetsJS)) {
+ if (existsSync(assetsJS)) {
return assetsJS;
}
// As a last resort, look for the root of the asset directory
- if (await pathExists(assetsDir)) {
+ if (existsSync(assetsDir)) {
return assetsDir;
}
throw new Error(`Unsupported renderer: ${renderer} (${baseDir})`);
};
const targetPath = async () => {
- if (await pathExists('./src')) {
+ if (existsSync('./src')) {
return './src/stories';
}
return './stories';
@@ -228,11 +228,11 @@ export async function copyTemplateFiles({
const destinationPath = destination ?? (await targetPath());
if (commonAssetsDir) {
- await copy(commonAssetsDir, destinationPath, {
- overwrite: true,
+ await cp(commonAssetsDir, destinationPath, {
+ recursive: true,
});
}
- await copy(await templatePath(), destinationPath, { overwrite: true });
+ await cp(await templatePath(), destinationPath, { recursive: true });
if (commonAssetsDir) {
let rendererType = frameworkToRenderer[renderer] || 'react';
@@ -248,7 +248,7 @@ export async function copyTemplateFiles({
export async function adjustTemplate(templatePath: string, templateData: Record) {
// for now, we're just doing a simple string replace
// in the future we might replace this with a proper templating engine
- let template = await readFile(templatePath, 'utf8');
+ let template = await readFile(templatePath, { encoding: 'utf8' });
Object.keys(templateData).forEach((key) => {
template = template.replaceAll(`{{${key}}}`, `${templateData[key]}`);
diff --git a/code/core/src/common/index.ts b/code/core/src/common/index.ts
index 0e7e92bd6a71..6166e285ab05 100644
--- a/code/core/src/common/index.ts
+++ b/code/core/src/common/index.ts
@@ -18,7 +18,6 @@ export * from './utils/get-storybook-info';
export * from './utils/get-storybook-refs';
export * from './utils/glob-to-regexp';
export * from './utils/HandledError';
-export * from './utils/handlebars';
export * from './utils/interpolate';
export * from './utils/interpret-files';
export * from './utils/interpret-require';
diff --git a/code/core/src/common/js-package-manager/JsPackageManager.test.ts b/code/core/src/common/js-package-manager/JsPackageManager.test.ts
new file mode 100644
index 000000000000..153edd0be596
--- /dev/null
+++ b/code/core/src/common/js-package-manager/JsPackageManager.test.ts
@@ -0,0 +1,54 @@
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+
+import { JsPackageManager } from './JsPackageManager';
+
+vi.mock('../versions', () => ({
+ default: {
+ '@storybook/react': '8.3.0',
+ },
+}));
+
+describe('JsPackageManager', () => {
+ let jsPackageManager: JsPackageManager;
+ let mockLatestVersion: ReturnType;
+ let mockStorybookPackagesVersions: Record;
+
+ beforeEach(() => {
+ mockLatestVersion = vi.fn();
+ mockStorybookPackagesVersions = {
+ '@storybook/react': '8.3.0',
+ };
+
+ // @ts-expect-error Ignore abstract class error
+ jsPackageManager = new JsPackageManager();
+ jsPackageManager.latestVersion = mockLatestVersion;
+
+ vi.clearAllMocks();
+ });
+
+ describe('getVersionedPackages method', () => {
+ it('should return the latest stable release version when current version is the latest stable release', async () => {
+ mockLatestVersion.mockResolvedValue('8.3.0');
+
+ const result = await jsPackageManager.getVersionedPackages(['@storybook/react']);
+
+ expect(result).toEqual(['@storybook/react@^8.3.0']);
+ });
+
+ it('should return the current version when it is not the latest stable release', async () => {
+ mockLatestVersion.mockResolvedValue('8.3.1');
+
+ const result = await jsPackageManager.getVersionedPackages(['@storybook/react']);
+
+ expect(result).toEqual(['@storybook/react@8.3.0']);
+ });
+
+ it('should return the latest stable release version when there is no current version', async () => {
+ mockLatestVersion.mockResolvedValue('2.0.0');
+
+ const result = await jsPackageManager.getVersionedPackages(['@storybook/new-addon@^8.3.0']);
+
+ expect(result).toEqual(['@storybook/new-addon@^2.0.0']);
+ });
+ });
+});
diff --git a/code/core/src/common/js-package-manager/JsPackageManager.ts b/code/core/src/common/js-package-manager/JsPackageManager.ts
index 4b3e00c2f5e7..5b0bdde0fdb6 100644
--- a/code/core/src/common/js-package-manager/JsPackageManager.ts
+++ b/code/core/src/common/js-package-manager/JsPackageManager.ts
@@ -2,9 +2,9 @@ import { existsSync, readFileSync } from 'node:fs';
import { readFile, writeFile } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
-import chalk from 'chalk';
import type { CommonOptions } from 'execa';
import { execaCommand, execaCommandSync } from 'execa';
+import picocolors from 'picocolors';
import { gt, satisfies } from 'semver';
import invariant from 'tiny-invariant';
import { dedent } from 'ts-dedent';
@@ -60,7 +60,7 @@ export abstract class JsPackageManager {
/** Get the INSTALLED version of a package from the package.json file */
async getPackageVersion(packageName: string, basePath = this.cwd): Promise {
const packageJSON = await this.getPackageJSON(packageName, basePath);
- return packageJSON ? packageJSON.version ?? null : null;
+ return packageJSON ? (packageJSON.version ?? null) : null;
}
constructor(options?: JsPackageManagerOptions) {
@@ -337,13 +337,13 @@ export abstract class JsPackageManager {
const k = packageName as keyof typeof storybookPackagesVersions;
const currentVersion = storybookPackagesVersions[k];
- if (currentVersion === latestInRange) {
- return `${packageName}`;
- }
- if (currentVersion) {
- return `${packageName}@${currentVersion}`;
+ const isLatestStableRelease = currentVersion === latestInRange;
+
+ if (isLatestStableRelease || !currentVersion) {
+ return `${packageName}@^${latestInRange}`;
}
- return `${packageName}@^${latestInRange}`;
+
+ return `${packageName}@${currentVersion}`;
})
);
}
@@ -384,11 +384,11 @@ export abstract class JsPackageManager {
latest = await this.latestVersion(packageName, constraint);
} catch (e) {
if (current) {
- logger.warn(`\n ${chalk.yellow(String(e))}`);
+ logger.warn(`\n ${picocolors.yellow(String(e))}`);
return current;
}
- logger.error(`\n ${chalk.red(String(e))}`);
+ logger.error(`\n ${picocolors.red(String(e))}`);
throw new HandledError(e);
}
@@ -418,7 +418,7 @@ export abstract class JsPackageManager {
.find((version) => satisfies(version, constraint));
invariant(
latestVersionSatisfyingTheConstraint != null,
- 'No version satisfying the constraint.'
+ `No version satisfying the constraint: ${packageName}${constraint}`
);
return latestVersionSatisfyingTheConstraint;
}
diff --git a/code/core/src/common/js-package-manager/JsPackageManagerFactory.ts b/code/core/src/common/js-package-manager/JsPackageManagerFactory.ts
index 7477a1e5f2c0..4b3c6184e59f 100644
--- a/code/core/src/common/js-package-manager/JsPackageManagerFactory.ts
+++ b/code/core/src/common/js-package-manager/JsPackageManagerFactory.ts
@@ -125,17 +125,35 @@ export class JsPackageManagerFactory {
}
function hasNPM(cwd?: string) {
- const npmVersionCommand = spawnSync('npm', ['--version'], { cwd, shell: true });
+ const npmVersionCommand = spawnSync('npm', ['--version'], {
+ cwd,
+ shell: true,
+ env: {
+ COREPACK_ENABLE_STRICT: '0',
+ },
+ });
return npmVersionCommand.status === 0;
}
function hasPNPM(cwd?: string) {
- const pnpmVersionCommand = spawnSync('pnpm', ['--version'], { cwd, shell: true });
+ const pnpmVersionCommand = spawnSync('pnpm', ['--version'], {
+ cwd,
+ shell: true,
+ env: {
+ COREPACK_ENABLE_STRICT: '0',
+ },
+ });
return pnpmVersionCommand.status === 0;
}
function getYarnVersion(cwd?: string): 1 | 2 | undefined {
- const yarnVersionCommand = spawnSync('yarn', ['--version'], { cwd, shell: true });
+ const yarnVersionCommand = spawnSync('yarn', ['--version'], {
+ cwd,
+ shell: true,
+ env: {
+ COREPACK_ENABLE_STRICT: '0',
+ },
+ });
if (yarnVersionCommand.status !== 0) {
return undefined;
diff --git a/code/core/src/common/js-package-manager/PNPMProxy.ts b/code/core/src/common/js-package-manager/PNPMProxy.ts
index f8df230d0fe6..3f4073c6f35e 100644
--- a/code/core/src/common/js-package-manager/PNPMProxy.ts
+++ b/code/core/src/common/js-package-manager/PNPMProxy.ts
@@ -4,7 +4,6 @@ import { join } from 'node:path';
import { FindPackageVersionsError } from '@storybook/core/server-errors';
import { findUpSync } from 'find-up';
-import { pathExistsSync } from 'fs-extra';
import { dedent } from 'ts-dedent';
import { createLogStream } from '../utils/cli';
@@ -42,7 +41,7 @@ export class PNPMProxy extends JsPackageManager {
const CWD = process.cwd();
const pnpmWorkspaceYaml = `${CWD}/pnpm-workspace.yaml`;
- return pathExistsSync(pnpmWorkspaceYaml);
+ return existsSync(pnpmWorkspaceYaml);
}
async initPackageJson() {
diff --git a/code/core/src/common/utils/cli.ts b/code/core/src/common/utils/cli.ts
index 00b831133d2a..05f4704360ea 100644
--- a/code/core/src/common/utils/cli.ts
+++ b/code/core/src/common/utils/cli.ts
@@ -1,9 +1,9 @@
-import { realpath } from 'node:fs/promises';
+import type { WriteStream } from 'node:fs';
+import { createWriteStream, mkdirSync } from 'node:fs';
+import { readFile, realpath, rename, rm, writeFile } from 'node:fs/promises';
import os from 'node:os';
import { join } from 'node:path';
-import type { WriteStream } from 'fs-extra';
-import { createWriteStream, mkdirSync, move, readFile, remove, writeFile } from 'fs-extra';
import { type MergeExclusive } from 'type-fest';
import uniqueString from 'unique-string';
@@ -17,7 +17,7 @@ const getPath = async (prefix = '') => join(await tempDir(), prefix + uniqueStri
export async function temporaryDirectory({ prefix = '' } = {}) {
const directory = await getPath(prefix);
- await mkdirSync(directory);
+ mkdirSync(directory);
return directory;
}
@@ -146,12 +146,10 @@ export const createLogStream = async (
return new Promise((resolve, reject) => {
logStream.once('open', () => {
- const moveLogFile = async () => move(temporaryLogPath, finalLogPath, { overwrite: true });
+ const moveLogFile = async () => rename(temporaryLogPath, finalLogPath);
const clearLogFile = async () => writeFile(temporaryLogPath, '');
- const removeLogFile = async () => remove(temporaryLogPath);
- const readLogFile = async () => {
- return readFile(temporaryLogPath, 'utf8');
- };
+ const removeLogFile = async () => rm(temporaryLogPath, { recursive: true, force: true });
+ const readLogFile = async () => readFile(temporaryLogPath, { encoding: 'utf8' });
resolve({ logStream, moveLogFile, clearLogFile, removeLogFile, readLogFile });
});
logStream.once('error', reject);
diff --git a/code/core/src/common/utils/get-storybook-info.ts b/code/core/src/common/utils/get-storybook-info.ts
index af65546949eb..a7e887a87d63 100644
--- a/code/core/src/common/utils/get-storybook-info.ts
+++ b/code/core/src/common/utils/get-storybook-info.ts
@@ -1,10 +1,9 @@
+import { existsSync } from 'node:fs';
import { join } from 'node:path';
import type { SupportedFrameworks } from '@storybook/core/types';
import type { CoreCommon_StorybookInfo, PackageJson } from '@storybook/core/types';
-import { pathExistsSync } from 'fs-extra';
-
import { getStorybookConfiguration } from './get-storybook-configuration';
export const rendererPackages: Record = {
@@ -92,9 +91,7 @@ const validConfigExtensions = ['ts', 'js', 'tsx', 'jsx', 'mjs', 'cjs'];
export const findConfigFile = (prefix: string, configDir: string) => {
const filePrefix = join(configDir, prefix);
- const extension = validConfigExtensions.find((ext: string) =>
- pathExistsSync(`${filePrefix}.${ext}`)
- );
+ const extension = validConfigExtensions.find((ext: string) => existsSync(`${filePrefix}.${ext}`));
return extension ? `${filePrefix}.${extension}` : null;
};
diff --git a/code/core/src/common/utils/get-storybook-refs.ts b/code/core/src/common/utils/get-storybook-refs.ts
index 053ae17abc93..4108ba969412 100644
--- a/code/core/src/common/utils/get-storybook-refs.ts
+++ b/code/core/src/common/utils/get-storybook-refs.ts
@@ -1,3 +1,4 @@
+import { readFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import type { Options, Ref } from '@storybook/core/types';
@@ -5,7 +6,6 @@ import type { Options, Ref } from '@storybook/core/types';
import { logger } from '@storybook/core/node-logger';
import { findUp } from 'find-up';
-import { readJSON } from 'fs-extra';
import resolveFrom from 'resolve-from';
export const getAutoRefs = async (options: Options): Promise> => {
@@ -15,7 +15,8 @@ export const getAutoRefs = async (options: Options): Promise
}
const directory = dirname(location);
- const { dependencies = [], devDependencies = [] } = (await readJSON(location)) || {};
+ const { dependencies = [], devDependencies = [] } =
+ JSON.parse(await readFile(location, { encoding: 'utf8' })) || {};
const deps = Object.keys({ ...dependencies, ...devDependencies });
const list = await Promise.all(
@@ -23,7 +24,8 @@ export const getAutoRefs = async (options: Options): Promise
try {
const l = resolveFrom(directory, join(d, 'package.json'));
- const { storybook, name, version } = (await readJSON(l)) || {};
+ const { storybook, name, version } =
+ JSON.parse(await readFile(l, { encoding: 'utf8' })) || {};
if (storybook?.url) {
return { id: name, ...storybook, version };
diff --git a/code/core/src/common/utils/handlebars.ts b/code/core/src/common/utils/handlebars.ts
deleted file mode 100644
index 56a79a03e963..000000000000
--- a/code/core/src/common/utils/handlebars.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import Handlebars from 'handlebars';
-
-export function handlebars(source: string, data: any) {
- const template = Handlebars.compile(source);
-
- return template(data);
-}
diff --git a/code/core/src/common/utils/log-config.ts b/code/core/src/common/utils/log-config.ts
index 5d50f0285298..aab43e9b29ec 100644
--- a/code/core/src/common/utils/log-config.ts
+++ b/code/core/src/common/utils/log-config.ts
@@ -1,6 +1,6 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
export function logConfig(caption: unknown, config: unknown) {
- console.log(chalk.cyan(caption));
+ console.log(picocolors.cyan(String(caption)));
console.dir(config, { depth: null });
}
diff --git a/code/core/src/common/utils/log.ts b/code/core/src/common/utils/log.ts
index 43119e616b24..3f7e8d51cb72 100644
--- a/code/core/src/common/utils/log.ts
+++ b/code/core/src/common/utils/log.ts
@@ -1,15 +1,15 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
const logger = console;
export const commandLog = (message: string) => {
- process.stdout.write(chalk.cyan(' • ') + message);
+ process.stdout.write(picocolors.cyan(' • ') + message);
// Need `void` to be able to use this function in a then of a Promise
return (errorMessage?: string | void, errorInfo?: string) => {
if (errorMessage) {
- process.stdout.write(`. ${chalk.red('✖')}\n`);
- logger.error(`\n ${chalk.red(errorMessage)}`);
+ process.stdout.write(`. ${picocolors.red('✖')}\n`);
+ logger.error(`\n ${picocolors.red(errorMessage)}`);
if (!errorInfo) {
return;
@@ -17,13 +17,13 @@ export const commandLog = (message: string) => {
const newErrorInfo = errorInfo
.split('\n')
- .map((line) => ` ${chalk.dim(line)}`)
+ .map((line) => ` ${picocolors.dim(line)}`)
.join('\n');
logger.error(`${newErrorInfo}\n`);
return;
}
- process.stdout.write(`. ${chalk.green('✓')}\n`);
+ process.stdout.write(`. ${picocolors.green('✓')}\n`);
};
};
@@ -56,7 +56,7 @@ export function codeLog(codeLines: string[], leftPadAmount?: number) {
.map((line) => {
const rightPadAmount = maxLength - line.length;
let newLine = line + getChars(' ', rightPadAmount);
- newLine = getChars(' ', leftPadAmount || 2) + chalk.inverse(` ${newLine} `);
+ newLine = getChars(' ', leftPadAmount || 2) + picocolors.inverse(` ${newLine} `);
return newLine;
})
.join('\n');
diff --git a/code/core/src/common/utils/notify-telemetry.ts b/code/core/src/common/utils/notify-telemetry.ts
index bb39028dedeb..4857fba0631b 100644
--- a/code/core/src/common/utils/notify-telemetry.ts
+++ b/code/core/src/common/utils/notify-telemetry.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { cache } from './cache';
@@ -19,14 +19,14 @@ export const notifyTelemetry = async () => {
cache.set(TELEMETRY_KEY_NOTIFY_DATE, Date.now().toString());
logger.log(
- `${chalk.magenta.bold(
- 'Attention'
+ `${picocolors.magenta(
+ picocolors.bold('Attention')
)}: Storybook now collects completely anonymous telemetry regarding usage.`
);
logger.log(`This information is used to shape Storybook's roadmap and prioritize features.`);
logger.log(
`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:`
);
- logger.log(chalk.cyan('https://storybook.js.org/telemetry'));
+ logger.log(picocolors.cyan('https://storybook.js.org/telemetry'));
logger.log();
};
diff --git a/code/core/src/common/versions.ts b/code/core/src/common/versions.ts
index ed064cc606f5..bd7cbc52f1c4 100644
--- a/code/core/src/common/versions.ts
+++ b/code/core/src/common/versions.ts
@@ -1,87 +1,87 @@
// auto generated file, do not edit
export default {
- '@storybook/addon-a11y': '8.3.0-beta.4',
- '@storybook/addon-actions': '8.3.0-beta.4',
- '@storybook/addon-backgrounds': '8.3.0-beta.4',
- '@storybook/addon-controls': '8.3.0-beta.4',
- '@storybook/addon-docs': '8.3.0-beta.4',
- '@storybook/addon-essentials': '8.3.0-beta.4',
- '@storybook/addon-mdx-gfm': '8.3.0-beta.4',
- '@storybook/addon-highlight': '8.3.0-beta.4',
- '@storybook/addon-interactions': '8.3.0-beta.4',
- '@storybook/addon-jest': '8.3.0-beta.4',
- '@storybook/addon-links': '8.3.0-beta.4',
- '@storybook/addon-measure': '8.3.0-beta.4',
- '@storybook/addon-onboarding': '8.3.0-beta.4',
- '@storybook/addon-outline': '8.3.0-beta.4',
- '@storybook/addon-storysource': '8.3.0-beta.4',
- '@storybook/experimental-addon-test': '8.3.0-beta.4',
- '@storybook/addon-themes': '8.3.0-beta.4',
- '@storybook/addon-toolbars': '8.3.0-beta.4',
- '@storybook/addon-viewport': '8.3.0-beta.4',
- '@storybook/builder-vite': '8.3.0-beta.4',
- '@storybook/builder-webpack5': '8.3.0-beta.4',
- '@storybook/core': '8.3.0-beta.4',
- '@storybook/builder-manager': '8.3.0-beta.4',
- '@storybook/channels': '8.3.0-beta.4',
- '@storybook/client-logger': '8.3.0-beta.4',
- '@storybook/components': '8.3.0-beta.4',
- '@storybook/core-common': '8.3.0-beta.4',
- '@storybook/core-events': '8.3.0-beta.4',
- '@storybook/core-server': '8.3.0-beta.4',
- '@storybook/csf-tools': '8.3.0-beta.4',
- '@storybook/docs-tools': '8.3.0-beta.4',
- '@storybook/manager': '8.3.0-beta.4',
- '@storybook/manager-api': '8.3.0-beta.4',
- '@storybook/node-logger': '8.3.0-beta.4',
- '@storybook/preview': '8.3.0-beta.4',
- '@storybook/preview-api': '8.3.0-beta.4',
- '@storybook/router': '8.3.0-beta.4',
- '@storybook/telemetry': '8.3.0-beta.4',
- '@storybook/theming': '8.3.0-beta.4',
- '@storybook/types': '8.3.0-beta.4',
- '@storybook/angular': '8.3.0-beta.4',
- '@storybook/ember': '8.3.0-beta.4',
- '@storybook/experimental-nextjs-vite': '8.3.0-beta.4',
- '@storybook/html-vite': '8.3.0-beta.4',
- '@storybook/html-webpack5': '8.3.0-beta.4',
- '@storybook/nextjs': '8.3.0-beta.4',
- '@storybook/preact-vite': '8.3.0-beta.4',
- '@storybook/preact-webpack5': '8.3.0-beta.4',
- '@storybook/react-vite': '8.3.0-beta.4',
- '@storybook/react-webpack5': '8.3.0-beta.4',
- '@storybook/server-webpack5': '8.3.0-beta.4',
- '@storybook/svelte-vite': '8.3.0-beta.4',
- '@storybook/svelte-webpack5': '8.3.0-beta.4',
- '@storybook/sveltekit': '8.3.0-beta.4',
- '@storybook/vue3-vite': '8.3.0-beta.4',
- '@storybook/vue3-webpack5': '8.3.0-beta.4',
- '@storybook/web-components-vite': '8.3.0-beta.4',
- '@storybook/web-components-webpack5': '8.3.0-beta.4',
- '@storybook/blocks': '8.3.0-beta.4',
- storybook: '8.3.0-beta.4',
- sb: '8.3.0-beta.4',
- '@storybook/cli': '8.3.0-beta.4',
- '@storybook/codemod': '8.3.0-beta.4',
- '@storybook/core-webpack': '8.3.0-beta.4',
- 'create-storybook': '8.3.0-beta.4',
- '@storybook/csf-plugin': '8.3.0-beta.4',
- '@storybook/instrumenter': '8.3.0-beta.4',
- '@storybook/react-dom-shim': '8.3.0-beta.4',
- '@storybook/source-loader': '8.3.0-beta.4',
- '@storybook/test': '8.3.0-beta.4',
- '@storybook/preset-create-react-app': '8.3.0-beta.4',
- '@storybook/preset-html-webpack': '8.3.0-beta.4',
- '@storybook/preset-preact-webpack': '8.3.0-beta.4',
- '@storybook/preset-react-webpack': '8.3.0-beta.4',
- '@storybook/preset-server-webpack': '8.3.0-beta.4',
- '@storybook/preset-svelte-webpack': '8.3.0-beta.4',
- '@storybook/preset-vue3-webpack': '8.3.0-beta.4',
- '@storybook/html': '8.3.0-beta.4',
- '@storybook/preact': '8.3.0-beta.4',
- '@storybook/react': '8.3.0-beta.4',
- '@storybook/server': '8.3.0-beta.4',
- '@storybook/svelte': '8.3.0-beta.4',
- '@storybook/vue3': '8.3.0-beta.4',
- '@storybook/web-components': '8.3.0-beta.4',
+ '@storybook/addon-a11y': '8.4.0-alpha.4',
+ '@storybook/addon-actions': '8.4.0-alpha.4',
+ '@storybook/addon-backgrounds': '8.4.0-alpha.4',
+ '@storybook/addon-controls': '8.4.0-alpha.4',
+ '@storybook/addon-docs': '8.4.0-alpha.4',
+ '@storybook/addon-essentials': '8.4.0-alpha.4',
+ '@storybook/addon-mdx-gfm': '8.4.0-alpha.4',
+ '@storybook/addon-highlight': '8.4.0-alpha.4',
+ '@storybook/addon-interactions': '8.4.0-alpha.4',
+ '@storybook/addon-jest': '8.4.0-alpha.4',
+ '@storybook/addon-links': '8.4.0-alpha.4',
+ '@storybook/addon-measure': '8.4.0-alpha.4',
+ '@storybook/addon-onboarding': '8.4.0-alpha.4',
+ '@storybook/addon-outline': '8.4.0-alpha.4',
+ '@storybook/addon-storysource': '8.4.0-alpha.4',
+ '@storybook/experimental-addon-test': '8.4.0-alpha.4',
+ '@storybook/addon-themes': '8.4.0-alpha.4',
+ '@storybook/addon-toolbars': '8.4.0-alpha.4',
+ '@storybook/addon-viewport': '8.4.0-alpha.4',
+ '@storybook/builder-vite': '8.4.0-alpha.4',
+ '@storybook/builder-webpack5': '8.4.0-alpha.4',
+ '@storybook/core': '8.4.0-alpha.4',
+ '@storybook/builder-manager': '8.4.0-alpha.4',
+ '@storybook/channels': '8.4.0-alpha.4',
+ '@storybook/client-logger': '8.4.0-alpha.4',
+ '@storybook/components': '8.4.0-alpha.4',
+ '@storybook/core-common': '8.4.0-alpha.4',
+ '@storybook/core-events': '8.4.0-alpha.4',
+ '@storybook/core-server': '8.4.0-alpha.4',
+ '@storybook/csf-tools': '8.4.0-alpha.4',
+ '@storybook/docs-tools': '8.4.0-alpha.4',
+ '@storybook/manager': '8.4.0-alpha.4',
+ '@storybook/manager-api': '8.4.0-alpha.4',
+ '@storybook/node-logger': '8.4.0-alpha.4',
+ '@storybook/preview': '8.4.0-alpha.4',
+ '@storybook/preview-api': '8.4.0-alpha.4',
+ '@storybook/router': '8.4.0-alpha.4',
+ '@storybook/telemetry': '8.4.0-alpha.4',
+ '@storybook/theming': '8.4.0-alpha.4',
+ '@storybook/types': '8.4.0-alpha.4',
+ '@storybook/angular': '8.4.0-alpha.4',
+ '@storybook/ember': '8.4.0-alpha.4',
+ '@storybook/experimental-nextjs-vite': '8.4.0-alpha.4',
+ '@storybook/html-vite': '8.4.0-alpha.4',
+ '@storybook/html-webpack5': '8.4.0-alpha.4',
+ '@storybook/nextjs': '8.4.0-alpha.4',
+ '@storybook/preact-vite': '8.4.0-alpha.4',
+ '@storybook/preact-webpack5': '8.4.0-alpha.4',
+ '@storybook/react-vite': '8.4.0-alpha.4',
+ '@storybook/react-webpack5': '8.4.0-alpha.4',
+ '@storybook/server-webpack5': '8.4.0-alpha.4',
+ '@storybook/svelte-vite': '8.4.0-alpha.4',
+ '@storybook/svelte-webpack5': '8.4.0-alpha.4',
+ '@storybook/sveltekit': '8.4.0-alpha.4',
+ '@storybook/vue3-vite': '8.4.0-alpha.4',
+ '@storybook/vue3-webpack5': '8.4.0-alpha.4',
+ '@storybook/web-components-vite': '8.4.0-alpha.4',
+ '@storybook/web-components-webpack5': '8.4.0-alpha.4',
+ '@storybook/blocks': '8.4.0-alpha.4',
+ storybook: '8.4.0-alpha.4',
+ sb: '8.4.0-alpha.4',
+ '@storybook/cli': '8.4.0-alpha.4',
+ '@storybook/codemod': '8.4.0-alpha.4',
+ '@storybook/core-webpack': '8.4.0-alpha.4',
+ 'create-storybook': '8.4.0-alpha.4',
+ '@storybook/csf-plugin': '8.4.0-alpha.4',
+ '@storybook/instrumenter': '8.4.0-alpha.4',
+ '@storybook/react-dom-shim': '8.4.0-alpha.4',
+ '@storybook/source-loader': '8.4.0-alpha.4',
+ '@storybook/test': '8.4.0-alpha.4',
+ '@storybook/preset-create-react-app': '8.4.0-alpha.4',
+ '@storybook/preset-html-webpack': '8.4.0-alpha.4',
+ '@storybook/preset-preact-webpack': '8.4.0-alpha.4',
+ '@storybook/preset-react-webpack': '8.4.0-alpha.4',
+ '@storybook/preset-server-webpack': '8.4.0-alpha.4',
+ '@storybook/preset-svelte-webpack': '8.4.0-alpha.4',
+ '@storybook/preset-vue3-webpack': '8.4.0-alpha.4',
+ '@storybook/html': '8.4.0-alpha.4',
+ '@storybook/preact': '8.4.0-alpha.4',
+ '@storybook/react': '8.4.0-alpha.4',
+ '@storybook/server': '8.4.0-alpha.4',
+ '@storybook/svelte': '8.4.0-alpha.4',
+ '@storybook/vue3': '8.4.0-alpha.4',
+ '@storybook/web-components': '8.4.0-alpha.4',
};
diff --git a/code/core/src/components/components/tooltip/ListItem.tsx b/code/core/src/components/components/tooltip/ListItem.tsx
index 068ab88d59c2..8d50a05273de 100644
--- a/code/core/src/components/components/tooltip/ListItem.tsx
+++ b/code/core/src/components/components/tooltip/ListItem.tsx
@@ -169,7 +169,7 @@ const getItemProps = memoize(100)((onClick, href, LinkWrapper) => ({
export type LinkWrapperType = (props: any) => ReactNode;
-export interface ListItemProps extends Omit, 'href' | 'title'> {
+export interface ListItemProps extends Omit, 'title'> {
loading?: boolean;
title?: ReactNode;
center?: ReactNode;
diff --git a/code/core/src/components/components/tooltip/TooltipLinkList.tsx b/code/core/src/components/components/tooltip/TooltipLinkList.tsx
index 51540335d034..f1467babec3a 100644
--- a/code/core/src/components/components/tooltip/TooltipLinkList.tsx
+++ b/code/core/src/components/components/tooltip/TooltipLinkList.tsx
@@ -22,7 +22,7 @@ export interface Link extends Omit {
id: string;
onClick?: (
event: SyntheticEvent,
- item: Pick
+ item: Pick
) => void;
}
@@ -31,11 +31,11 @@ interface ItemProps extends Link {
}
const Item = ({ id, onClick, ...rest }: ItemProps) => {
- const { active, disabled, title } = rest;
+ const { active, disabled, title, href } = rest;
const handleClick = useCallback(
- (event: SyntheticEvent) => onClick?.(event, { id, active, disabled, title }),
- [onClick, id, active, disabled, title]
+ (event: SyntheticEvent) => onClick?.(event, { id, active, disabled, title, href }),
+ [onClick, id, active, disabled, title, href]
);
return ;
diff --git a/code/core/src/core-server/__for-testing__/main.ts b/code/core/src/core-server/__for-testing__/main.ts
index d930ed1504a0..586e91025120 100644
--- a/code/core/src/core-server/__for-testing__/main.ts
+++ b/code/core/src/core-server/__for-testing__/main.ts
@@ -20,7 +20,6 @@ const config: StorybookConfig = {
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-storysource',
- '@storybook/addon-links',
'@storybook/addon-jest',
'@storybook/addon-a11y',
],
diff --git a/code/core/src/core-server/build-dev.ts b/code/core/src/core-server/build-dev.ts
index 5c87a7d9ccd8..c8b336a22e09 100644
--- a/code/core/src/core-server/build-dev.ts
+++ b/code/core/src/core-server/build-dev.ts
@@ -1,3 +1,4 @@
+import { readFile } from 'node:fs/promises';
import { join, relative, resolve } from 'node:path';
import {
@@ -18,7 +19,6 @@ import { global } from '@storybook/global';
import { deprecate } from '@storybook/core/node-logger';
import { MissingBuilderError, NoStatsForViteDevError } from '@storybook/core/server-errors';
-import { readFile } from 'fs-extra';
import prompts from 'prompts';
import invariant from 'tiny-invariant';
import { dedent } from 'ts-dedent';
@@ -156,7 +156,7 @@ export async function buildDevStandalone(
if (/\.c[jt]s$/.test(mainJsPath)) {
deprecate(deprecationMessage);
}
- const mainJsContent = await readFile(mainJsPath, 'utf-8');
+ const mainJsContent = await readFile(mainJsPath, { encoding: 'utf8' });
// Regex that matches any CommonJS-specific syntax, stolen from Vite: https://github.com/vitejs/vite/blob/91a18c2f7da796ff8217417a4bf189ddda719895/packages/vite/src/node/ssr/ssrExternal.ts#L87
const CJS_CONTENT_REGEX =
/\bmodule\.exports\b|\bexports[.[]|\brequire\s*\(|\bObject\.(?:defineProperty|defineProperties|assign)\s*\(\s*exports\b/;
diff --git a/code/core/src/core-server/build-static.ts b/code/core/src/core-server/build-static.ts
index 248b2746ea8c..8d8d5f9cce60 100644
--- a/code/core/src/core-server/build-static.ts
+++ b/code/core/src/core-server/build-static.ts
@@ -1,3 +1,6 @@
+import { existsSync } from 'node:fs';
+import { cp, mkdir, readdir } from 'node:fs/promises';
+import { rm } from 'node:fs/promises';
import { dirname, join, relative, resolve } from 'node:path';
import {
@@ -13,8 +16,7 @@ import { global } from '@storybook/global';
import { logger } from '@storybook/core/node-logger';
-import chalk from 'chalk';
-import { copy, emptyDir, ensureDir } from 'fs-extra';
+import picocolors from 'picocolors';
import { StoryIndexGenerator } from './utils/StoryIndexGenerator';
import { buildOrThrow } from './utils/build-or-throw';
@@ -39,12 +41,28 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption
options.outputDir = resolve(options.outputDir);
options.configDir = resolve(options.configDir);
- logger.info(`=> Cleaning outputDir: ${chalk.cyan(relative(process.cwd(), options.outputDir))}`);
+ logger.info(
+ `=> Cleaning outputDir: ${picocolors.cyan(relative(process.cwd(), options.outputDir))}`
+ );
if (options.outputDir === '/') {
throw new Error("Won't remove directory '/'. Check your outputDir!");
}
- await emptyDir(options.outputDir);
- await ensureDir(options.outputDir);
+
+ try {
+ const outputDirFiles = await readdir(options.outputDir);
+ for (const file of outputDirFiles) {
+ await rm(file, { recursive: true, force: true });
+ }
+ } catch {
+ await mkdir(options.outputDir, { recursive: true });
+ }
+
+ if (!existsSync(options.outputDir)) {
+ await mkdir(options.outputDir, { recursive: true });
+ } else if ((await readdir(options.outputDir)).length > 0) {
+ await rm(options.outputDir, { recursive: true, force: true });
+ await mkdir(options.outputDir, { recursive: true });
+ }
const config = await loadMainConfig(options);
const { framework } = config;
@@ -127,7 +145,7 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption
dirname(require.resolve('@storybook/core/package.json')),
'assets/browser'
);
- effects.push(copy(coreServerPublicDir, options.outputDir));
+ effects.push(cp(coreServerPublicDir, options.outputDir, { recursive: true }));
let initializedStoryIndexGenerator: Promise =
Promise.resolve(undefined);
diff --git a/code/core/src/core-server/presets/common-preset.ts b/code/core/src/core-server/presets/common-preset.ts
index bca9a1c9cfe6..10f8be05614f 100644
--- a/code/core/src/core-server/presets/common-preset.ts
+++ b/code/core/src/core-server/presets/common-preset.ts
@@ -1,3 +1,5 @@
+import { existsSync } from 'node:fs';
+import { readFile } from 'node:fs/promises';
import { dirname, isAbsolute, join } from 'node:path';
import type { Channel } from '@storybook/core/channels';
@@ -21,7 +23,6 @@ import type {
import { readCsf } from '@storybook/core/csf-tools';
import { logger } from '@storybook/core/node-logger';
-import { pathExists, readFile } from 'fs-extra';
import { dedent } from 'ts-dedent';
import { initCreateNewStoryChannel } from '../server-channel/create-new-story-channel';
@@ -75,14 +76,14 @@ export const favicon = async (
if (targetEndpoint === '/') {
const url = 'favicon.svg';
const path = join(staticPath, url);
- if (await pathExists(path)) {
+ if (existsSync(path)) {
results.push(path);
}
}
if (targetEndpoint === '/') {
const url = 'favicon.ico';
const path = join(staticPath, url);
- if (await pathExists(path)) {
+ if (existsSync(path)) {
results.push(path);
}
}
@@ -256,8 +257,8 @@ export const docs: PresetProperty<'docs'> = (docsOptions, { docs: docsMode }: CL
export const managerHead = async (_: any, options: Options) => {
const location = join(options.configDir, 'manager-head.html');
- if (await pathExists(location)) {
- const contents = readFile(location, 'utf-8');
+ if (existsSync(location)) {
+ const contents = readFile(location, { encoding: 'utf8' });
const interpolations = options.presets.apply>('env');
return interpolate(await contents, await interpolations);
diff --git a/code/core/src/core-server/presets/favicon.test.ts b/code/core/src/core-server/presets/favicon.test.ts
index 7af2bfbfb34d..4e6c72cc0618 100644
--- a/code/core/src/core-server/presets/favicon.test.ts
+++ b/code/core/src/core-server/presets/favicon.test.ts
@@ -1,11 +1,10 @@
+import * as fs from 'node:fs';
import { dirname, join } from 'node:path';
import { expect, it, vi } from 'vitest';
import { logger } from '@storybook/core/node-logger';
-import * as fs from 'fs-extra';
-
import * as m from './common-preset';
const defaultFavicon = join(
@@ -30,17 +29,6 @@ const createOptions = (locations: string[]): Parameters[1] =>
},
});
-vi.mock('fs-extra', () => {
- return {
- pathExists: vi.fn((p: string) => {
- return false;
- }),
- existsSync: vi.fn((p: string) => {
- return false;
- }),
- };
-});
-
vi.mock('@storybook/core/node-logger', () => {
return {
logger: {
@@ -49,7 +37,13 @@ vi.mock('@storybook/core/node-logger', () => {
};
});
-const pathExists = vi.mocked(fs.pathExists);
+vi.mock('node:fs', async (importOriginal) => ({
+ ...(await importOriginal()),
+ existsSync: vi.fn((p: string) => {
+ return false;
+ }),
+}));
+const existsSyncMock = vi.mocked(fs.existsSync);
it('with no staticDirs favicon should return default', async () => {
const options = createOptions([]);
@@ -59,7 +53,7 @@ it('with no staticDirs favicon should return default', async () => {
it('with staticDirs containing a single favicon.ico should return the found favicon', async () => {
const location = 'static';
- pathExists.mockImplementation((p: string) => {
+ existsSyncMock.mockImplementation((p: string | Buffer | URL) => {
if (p === createPath(location)) {
return true;
}
@@ -75,7 +69,7 @@ it('with staticDirs containing a single favicon.ico should return the found favi
it('with staticDirs containing a single favicon.svg should return the found favicon', async () => {
const location = 'static';
- pathExists.mockImplementation((p: string) => {
+ existsSyncMock.mockImplementation((p: string | Buffer | URL) => {
if (p === createPath(location)) {
return true;
}
@@ -91,7 +85,7 @@ it('with staticDirs containing a single favicon.svg should return the found favi
it('with staticDirs containing a multiple favicons should return the first favicon and warn', async () => {
const location = 'static';
- pathExists.mockImplementation((p: string) => {
+ existsSyncMock.mockImplementation((p: string | Buffer | URL) => {
if (p === createPath(location)) {
return true;
}
@@ -113,7 +107,7 @@ it('with staticDirs containing a multiple favicons should return the first favic
it('with multiple staticDirs containing a multiple favicons should return the first favicon and warn', async () => {
const locationA = 'static-a';
const locationB = 'static-b';
- pathExists.mockImplementation((p: string) => {
+ existsSyncMock.mockImplementation((p: string | Buffer | URL) => {
if (p === createPath(locationA)) {
return true;
}
diff --git a/code/core/src/core-server/utils/StoryIndexGenerator.ts b/code/core/src/core-server/utils/StoryIndexGenerator.ts
index 0c5338debf88..aed65a46bf6b 100644
--- a/code/core/src/core-server/utils/StoryIndexGenerator.ts
+++ b/code/core/src/core-server/utils/StoryIndexGenerator.ts
@@ -1,4 +1,6 @@
/* eslint-disable no-underscore-dangle */
+import { existsSync } from 'node:fs';
+import { readFile } from 'node:fs/promises';
import { dirname, extname, join, normalize, relative, resolve, sep } from 'node:path';
import { commonGlobOptions, normalizeStoryPath } from '@storybook/core/common';
@@ -21,9 +23,8 @@ import { getStorySortParameter, loadConfig } from '@storybook/core/csf-tools';
import { logger, once } from '@storybook/core/node-logger';
import { sortStoriesV7, userOrAutoTitleFromSpecifier } from '@storybook/core/preview-api';
-import chalk from 'chalk';
import { findUp } from 'find-up';
-import fs from 'fs-extra';
+import picocolors from 'picocolors';
import slash from 'slash';
import invariant from 'tiny-invariant';
import { dedent } from 'ts-dedent';
@@ -139,7 +140,7 @@ export class StoryIndexGenerator {
if (files.length === 0) {
once.warn(
- `No story files found for the specified pattern: ${chalk.blue(
+ `No story files found for the specified pattern: ${picocolors.blue(
join(specifier.directory, specifier.files)
)}`
);
@@ -324,7 +325,7 @@ export class StoryIndexGenerator {
const absoluteComponentPath = resolve(dirname(absolutePath), rawPath);
const existing = ['', '.js', '.ts', '.jsx', '.tsx', '.mjs', '.mts']
.map((ext) => `${absoluteComponentPath}${ext}`)
- .find((candidate) => fs.existsSync(candidate));
+ .find((candidate) => existsSync(candidate));
if (existing) {
const relativePath = relative(this.options.workingDir, existing);
return slash(normalizeStoryPath(relativePath));
@@ -432,7 +433,7 @@ export class StoryIndexGenerator {
const normalizedPath = normalizeStoryPath(relativePath);
const importPath = slash(normalizedPath);
- const content = await fs.readFile(absolutePath, 'utf8');
+ const content = await readFile(absolutePath, { encoding: 'utf8' });
const { analyze } = await import('@storybook/docs-mdx');
const result = await analyze(content);
@@ -526,7 +527,7 @@ export class StoryIndexGenerator {
} catch (err) {
if (err && (err as { source: any }).source?.match(/mdast-util-mdx-jsx/g)) {
logger.warn(
- `💡 This seems to be an MDX2 syntax error. Please refer to the MDX section in the following resource for assistance on how to fix this: ${chalk.yellow(
+ `💡 This seems to be an MDX2 syntax error. Please refer to the MDX section in the following resource for assistance on how to fix this: ${picocolors.yellow(
'https://storybook.js.org/migration-guides/7.0'
)}`
);
@@ -753,9 +754,9 @@ export class StoryIndexGenerator {
async getPreviewCode() {
const previewFile = ['js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs', 'mts']
.map((ext) => join(this.options.configDir, `preview.${ext}`))
- .find((fname) => fs.existsSync(fname));
+ .find((fname) => existsSync(fname));
- return previewFile && (await fs.readFile(previewFile, 'utf-8')).toString();
+ return previewFile && (await readFile(previewFile, { encoding: 'utf8' })).toString();
}
getProjectTags(previewCode?: string) {
diff --git a/code/core/src/core-server/utils/__tests__/server-statics.test.ts b/code/core/src/core-server/utils/__tests__/server-statics.test.ts
index ca532a8b04f9..0dfaea67f5df 100644
--- a/code/core/src/core-server/utils/__tests__/server-statics.test.ts
+++ b/code/core/src/core-server/utils/__tests__/server-statics.test.ts
@@ -1,19 +1,17 @@
+import fs from 'node:fs';
import { resolve } from 'node:path';
import { beforeEach, describe, expect, it, vi } from 'vitest';
-import fs from 'fs-extra';
-
import { onlyWindows, skipWindows } from '../../../../../vitest.helpers';
import { parseStaticDir } from '../server-statics';
-vi.mock('fs-extra');
-const pathExistsMock = vi.mocked(fs.pathExists);
+vi.mock('node:fs');
+const existsSyncMock = vi.mocked(fs.existsSync);
describe('parseStaticDir', () => {
beforeEach(() => {
- // @ts-expect-error for some reason vitest does not match the return type with one of the overloads from pathExists
- pathExistsMock.mockResolvedValue(true);
+ existsSyncMock.mockReturnValue(true);
});
it('returns the static dir/path and default target', async () => {
@@ -57,8 +55,7 @@ describe('parseStaticDir', () => {
});
it('checks that the path exists', async () => {
- // @ts-expect-error for some reason vitest does not match the return type with one of the overloads from pathExists
- pathExistsMock.mockResolvedValue(false);
+ existsSyncMock.mockReturnValueOnce(false);
await expect(parseStaticDir('nonexistent')).rejects.toThrow(resolve('nonexistent'));
});
diff --git a/code/core/src/core-server/utils/copy-all-static-files.ts b/code/core/src/core-server/utils/copy-all-static-files.ts
index bc9a515731ef..ba5ccac883c8 100644
--- a/code/core/src/core-server/utils/copy-all-static-files.ts
+++ b/code/core/src/core-server/utils/copy-all-static-files.ts
@@ -1,11 +1,11 @@
+import { cp } from 'node:fs/promises';
import { join, relative } from 'node:path';
import { getDirectoryFromWorkingDir } from '@storybook/core/common';
import { logger } from '@storybook/core/node-logger';
-import chalk from 'chalk';
-import fs from 'fs-extra';
+import picocolors from 'picocolors';
import { parseStaticDir } from './server-statics';
@@ -19,17 +19,18 @@ export async function copyAllStaticFiles(staticDirs: any[] | undefined, outputDi
// we copy prebuild static files from node_modules/@storybook/manager & preview
if (!staticDir.includes('node_modules')) {
- const from = chalk.cyan(print(staticDir));
- const to = chalk.cyan(print(targetDir));
+ const from = picocolors.cyan(print(staticDir));
+ const to = picocolors.cyan(print(targetDir));
logger.info(`=> Copying static files: ${from} => ${to}`);
}
// Storybook's own files should not be overwritten, so we skip such files if we find them
const skipPaths = ['index.html', 'iframe.html'].map((f) => join(targetPath, f));
- await fs.copy(staticPath, targetPath, {
+ await cp(staticPath, targetPath, {
dereference: true,
preserveTimestamps: true,
filter: (_, dest) => !skipPaths.includes(dest),
+ recursive: true,
});
} catch (e) {
if (e instanceof Error) {
@@ -65,13 +66,14 @@ export async function copyAllStaticFilesRelativeToMain(
const skipPaths = ['index.html', 'iframe.html'].map((f) => join(targetPath, f));
if (!from.includes('node_modules')) {
logger.info(
- `=> Copying static files: ${chalk.cyan(print(from))} at ${chalk.cyan(print(targetPath))}`
+ `=> Copying static files: ${picocolors.cyan(print(from))} at ${picocolors.cyan(print(targetPath))}`
);
}
- await fs.copy(from, targetPath, {
+ await cp(from, targetPath, {
dereference: true,
preserveTimestamps: true,
filter: (_, dest) => !skipPaths.includes(dest),
+ recursive: true,
});
}, Promise.resolve());
}
diff --git a/code/core/src/core-server/utils/get-component-variable-name.ts b/code/core/src/core-server/utils/get-component-variable-name.ts
index bc29ff9e51d6..34f72352afc7 100644
--- a/code/core/src/core-server/utils/get-component-variable-name.ts
+++ b/code/core/src/core-server/utils/get-component-variable-name.ts
@@ -6,6 +6,7 @@
*/
export const getComponentVariableName = async (name: string) => {
const camelCase = await import('camelcase');
+
const camelCased = camelCase.default(name.replace(/^[^a-zA-Z_$]*/, ''), { pascalCase: true });
const sanitized = camelCased.replace(/[^a-zA-Z_$]+/, '');
return sanitized;
diff --git a/code/core/src/core-server/utils/metadata.ts b/code/core/src/core-server/utils/metadata.ts
index cc047d841768..a617b3e1f52c 100644
--- a/code/core/src/core-server/utils/metadata.ts
+++ b/code/core/src/core-server/utils/metadata.ts
@@ -1,12 +1,13 @@
+import { writeFile } from 'node:fs/promises';
+
import { getStorybookMetadata } from '@storybook/core/telemetry';
import type { Request, Response, Router } from 'express';
-import { writeJSON } from 'fs-extra';
export async function extractStorybookMetadata(outputFile: string, configDir: string) {
const storybookMetadata = await getStorybookMetadata(configDir);
- await writeJSON(outputFile, storybookMetadata);
+ await writeFile(outputFile, JSON.stringify(storybookMetadata));
}
export function useStorybookMetadata(router: Router, configDir?: string) {
diff --git a/code/core/src/core-server/utils/output-startup-information.ts b/code/core/src/core-server/utils/output-startup-information.ts
index cfe60bef6cc9..411e3e80be46 100644
--- a/code/core/src/core-server/utils/output-startup-information.ts
+++ b/code/core/src/core-server/utils/output-startup-information.ts
@@ -3,8 +3,8 @@ import type { VersionCheck } from '@storybook/core/types';
import { colors } from '@storybook/core/node-logger';
import boxen from 'boxen';
-import chalk from 'chalk';
import Table from 'cli-table3';
+import picocolors from 'picocolors';
import prettyTime from 'pretty-hrtime';
import { dedent } from 'ts-dedent';
@@ -50,13 +50,13 @@ export function outputStartupInformation(options: {
});
serveMessage.push(
- ['Local:', chalk.cyan(address)],
- ['On your network:', chalk.cyan(networkAddress)]
+ ['Local:', picocolors.cyan(address)],
+ ['On your network:', picocolors.cyan(networkAddress)]
);
const timeStatement = [
- managerTotalTime && `${chalk.underline(prettyTime(managerTotalTime))} for manager`,
- previewTotalTime && `${chalk.underline(prettyTime(previewTotalTime))} for preview`,
+ managerTotalTime && `${picocolors.underline(prettyTime(managerTotalTime))} for manager`,
+ previewTotalTime && `${picocolors.underline(prettyTime(previewTotalTime))} for preview`,
]
.filter(Boolean)
.join(' and ');
@@ -64,8 +64,10 @@ export function outputStartupInformation(options: {
console.log(
boxen(
dedent`
- ${colors.green(`Storybook ${chalk.bold(version)} for ${chalk.bold(name)} started`)}
- ${chalk.gray(timeStatement)}
+ ${colors.green(
+ `Storybook ${picocolors.bold(version)} for ${picocolors.bold(name)} started`
+ )}
+ ${picocolors.gray(timeStatement)}
${serveMessage.toString()}${updateMessage ? `\n\n${updateMessage}` : ''}
`,
diff --git a/code/core/src/core-server/utils/output-stats.ts b/code/core/src/core-server/utils/output-stats.ts
index c3a5b9f8aad9..24c736147f1e 100644
--- a/code/core/src/core-server/utils/output-stats.ts
+++ b/code/core/src/core-server/utils/output-stats.ts
@@ -1,3 +1,4 @@
+import { createWriteStream } from 'node:fs';
import { join } from 'node:path';
import type { Stats } from '@storybook/core/types';
@@ -5,17 +6,16 @@ import type { Stats } from '@storybook/core/types';
import { logger } from '@storybook/core/node-logger';
import { stringifyStream } from '@discoveryjs/json-ext';
-import chalk from 'chalk';
-import fs from 'fs-extra';
+import picocolors from 'picocolors';
export async function outputStats(directory: string, previewStats?: any, managerStats?: any) {
if (previewStats) {
const filePath = await writeStats(directory, 'preview', previewStats as Stats);
- logger.info(`=> preview stats written to ${chalk.cyan(filePath)}`);
+ logger.info(`=> preview stats written to ${picocolors.cyan(filePath)}`);
}
if (managerStats) {
const filePath = await writeStats(directory, 'manager', managerStats as Stats);
- logger.info(`=> manager stats written to ${chalk.cyan(filePath)}`);
+ logger.info(`=> manager stats written to ${picocolors.cyan(filePath)}`);
}
}
@@ -25,7 +25,7 @@ export const writeStats = async (directory: string, name: string, stats: Stats)
await new Promise((resolve, reject) => {
stringifyStream(data, null, 2)
.on('error', reject)
- .pipe(fs.createWriteStream(filePath))
+ .pipe(createWriteStream(filePath))
.on('error', reject)
.on('finish', resolve);
});
diff --git a/code/core/src/core-server/utils/server-init.ts b/code/core/src/core-server/utils/server-init.ts
index b63d663e6857..60710cb19cfb 100644
--- a/code/core/src/core-server/utils/server-init.ts
+++ b/code/core/src/core-server/utils/server-init.ts
@@ -1,7 +1,8 @@
+import { readFile } from 'node:fs/promises';
+
import { logger } from '@storybook/core/node-logger';
import type { Express } from 'express';
-import { readFile } from 'fs-extra';
import http from 'http';
import https from 'https';
@@ -29,9 +30,9 @@ export async function getServer(
}
const sslOptions = {
- ca: await Promise.all((options.sslCa || []).map((ca) => readFile(ca, 'utf-8'))),
- cert: await readFile(options.sslCert, 'utf-8'),
- key: await readFile(options.sslKey, 'utf-8'),
+ ca: await Promise.all((options.sslCa || []).map((ca) => readFile(ca, { encoding: 'utf8' }))),
+ cert: await readFile(options.sslCert, { encoding: 'utf8' }),
+ key: await readFile(options.sslKey, { encoding: 'utf8' }),
};
return https.createServer(sslOptions, app);
diff --git a/code/core/src/core-server/utils/server-statics.ts b/code/core/src/core-server/utils/server-statics.ts
index b10a573e7d90..b6bc406923e8 100644
--- a/code/core/src/core-server/utils/server-statics.ts
+++ b/code/core/src/core-server/utils/server-statics.ts
@@ -1,3 +1,4 @@
+import { existsSync } from 'node:fs';
import { basename, isAbsolute, posix, resolve, sep, win32 } from 'node:path';
import { getDirectoryFromWorkingDir } from '@storybook/core/common';
@@ -5,10 +6,9 @@ import type { Options } from '@storybook/core/types';
import { logger } from '@storybook/core/node-logger';
-import chalk from 'chalk';
import type { Router } from 'express';
import express from 'express';
-import { pathExists } from 'fs-extra';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
export async function useStatics(router: Router, options: Options) {
@@ -36,7 +36,7 @@ export async function useStatics(router: Router, options: Options) {
// Don't log for the internal static dir
if (!targetEndpoint.startsWith('/sb-')) {
logger.info(
- `=> Serving static files from ${chalk.cyan(staticDir)} at ${chalk.cyan(targetEndpoint)}`
+ `=> Serving static files from ${picocolors.cyan(staticDir)} at ${picocolors.cyan(targetEndpoint)}`
);
}
@@ -69,10 +69,10 @@ export const parseStaticDir = async (arg: string) => {
const targetDir = target.replace(/^\/?/, './');
const targetEndpoint = targetDir.substring(1);
- if (!(await pathExists(staticPath))) {
+ if (!existsSync(staticPath)) {
throw new Error(
dedent`
- Failed to load static files, no such directory: ${chalk.cyan(staticPath)}
+ Failed to load static files, no such directory: ${picocolors.cyan(staticPath)}
Make sure this directory exists.
`
);
diff --git a/code/core/src/core-server/utils/stories-json.test.ts b/code/core/src/core-server/utils/stories-json.test.ts
index e9f5e73a7378..3faa5da2fe19 100644
--- a/code/core/src/core-server/utils/stories-json.test.ts
+++ b/code/core/src/core-server/utils/stories-json.test.ts
@@ -6,8 +6,8 @@ import { normalizeStoriesEntry } from '@storybook/core/common';
import { STORY_INDEX_INVALIDATED } from '@storybook/core/core-events';
+import { debounce } from 'es-toolkit/compat';
import type { Request, Response, Router } from 'express';
-import debounce from 'lodash/debounce.js';
import Watchpack from 'watchpack';
import { csfIndexer } from '../presets/common-preset';
@@ -17,7 +17,7 @@ import type { ServerChannel } from './get-server-channel';
import { DEBOUNCE, useStoriesJson } from './stories-json';
vi.mock('watchpack');
-vi.mock('lodash/debounce');
+vi.mock('es-toolkit/compat');
vi.mock('@storybook/core/node-logger');
const workingDir = join(__dirname, '__mockdata__');
@@ -471,8 +471,7 @@ describe('useStoriesJson', () => {
it('debounces invalidation events', async () => {
vi.mocked(debounce).mockImplementation(
- // @ts-expect-error it doesn't think default exists
- (await vi.importActual('lodash/debounce.js')).default
+ (await vi.importActual('es-toolkit/compat')).debounce
);
const mockServerChannel = { emit: vi.fn() } as any as ServerChannel;
diff --git a/code/core/src/core-server/utils/stories-json.ts b/code/core/src/core-server/utils/stories-json.ts
index 8956a4ed7ab1..3426aa689de1 100644
--- a/code/core/src/core-server/utils/stories-json.ts
+++ b/code/core/src/core-server/utils/stories-json.ts
@@ -1,12 +1,12 @@
+import { writeFile } from 'node:fs/promises';
import { basename } from 'node:path';
import type { NormalizedStoriesSpecifier, StoryIndex } from '@storybook/core/types';
import { STORY_INDEX_INVALIDATED } from '@storybook/core/core-events';
+import { debounce } from 'es-toolkit/compat';
import type { Request, Response, Router } from 'express';
-import { writeJSON } from 'fs-extra';
-import debounce from 'lodash/debounce.js';
import type { StoryIndexGenerator } from './StoryIndexGenerator';
import type { ServerChannel } from './get-server-channel';
@@ -22,7 +22,7 @@ export async function extractStoriesJson(
) {
const generator = await initializedStoryIndexGenerator;
const storyIndex = await generator.getIndex();
- await writeJSON(outputFile, transform ? transform(storyIndex) : storyIndex);
+ await writeFile(outputFile, JSON.stringify(transform ? transform(storyIndex) : storyIndex));
}
export function useStoriesJson({
diff --git a/code/core/src/core-server/utils/update-check.ts b/code/core/src/core-server/utils/update-check.ts
index 4d1fe03c685c..efdd6ba95510 100644
--- a/code/core/src/core-server/utils/update-check.ts
+++ b/code/core/src/core-server/utils/update-check.ts
@@ -3,7 +3,7 @@ import type { VersionCheck } from '@storybook/core/types';
import { colors } from '@storybook/core/node-logger';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import semver from 'semver';
import { dedent } from 'ts-dedent';
@@ -44,13 +44,13 @@ export function createUpdateMessage(updateInfo: VersionCheck, version: string):
updateInfo.success && semver.lt(version, updateInfo.data.latest.version)
? dedent`
${colors.orange(
- `A new version (${chalk.bold(updateInfo.data.latest.version)}) is available!`
+ `A new version (${picocolors.bold(updateInfo.data.latest.version)}) is available!`
)}
- ${chalk.gray('Upgrade now:')} ${colors.green(upgradeCommand)}
+ ${picocolors.gray('Upgrade now:')} ${colors.green(upgradeCommand)}
- ${chalk.gray('Read full changelog:')} ${chalk.gray.underline(
- 'https://github.com/storybookjs/storybook/blob/main/CHANGELOG.md'
+ ${picocolors.gray('Read full changelog:')} ${picocolors.gray(
+ picocolors.underline('https://github.com/storybookjs/storybook/blob/main/CHANGELOG.md')
)}
`
: '';
diff --git a/code/core/src/core-server/utils/warnWhenUsingArgTypesRegex.ts b/code/core/src/core-server/utils/warnWhenUsingArgTypesRegex.ts
index dd543bff54bc..ea28dbfc9c00 100644
--- a/code/core/src/core-server/utils/warnWhenUsingArgTypesRegex.ts
+++ b/code/core/src/core-server/utils/warnWhenUsingArgTypesRegex.ts
@@ -1,17 +1,20 @@
+import { readFile } from 'node:fs/promises';
+
import { type BabelFile, core } from '@storybook/core/babel';
import type { StorybookConfig } from '@storybook/core/types';
import { babelParse } from '@storybook/core/csf-tools';
-import chalk from 'chalk';
-import { readFile } from 'fs-extra';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
export async function warnWhenUsingArgTypesRegex(
previewConfigPath: string | undefined,
config: StorybookConfig
) {
- const previewContent = previewConfigPath ? await readFile(previewConfigPath, 'utf8') : '';
+ const previewContent = previewConfigPath
+ ? await readFile(previewConfigPath, { encoding: 'utf8' })
+ : '';
const hasVisualTestAddon =
config?.addons?.some((it) =>
@@ -31,17 +34,17 @@ export async function warnWhenUsingArgTypesRegex(
Identifier: (path) => {
if (path.node.name === 'argTypesRegex') {
const message = dedent`
- ${chalk.bold('Attention')}: We've detected that you're using ${chalk.cyan(
+ ${picocolors.bold('Attention')}: We've detected that you're using ${picocolors.cyan(
'actions.argTypesRegex'
)} together with the visual test addon:
${path.buildCodeFrameError(previewConfigPath).message}
- We recommend removing the ${chalk.cyan(
+ We recommend removing the ${picocolors.cyan(
'argTypesRegex'
- )} and assigning explicit action with the ${chalk.cyan(
+ )} and assigning explicit action with the ${picocolors.cyan(
'fn'
- )} function from ${chalk.cyan('@storybook/test')} instead:
+ )} function from ${picocolors.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.
diff --git a/code/core/src/core-server/utils/whats-new.ts b/code/core/src/core-server/utils/whats-new.ts
index 1091d77ab74f..cb523f78318e 100644
--- a/code/core/src/core-server/utils/whats-new.ts
+++ b/code/core/src/core-server/utils/whats-new.ts
@@ -1,3 +1,5 @@
+import { writeFile } from 'node:fs/promises';
+
import type { Channel } from '@storybook/core/channels';
import { findConfigFile } from '@storybook/core/common';
import { telemetry } from '@storybook/core/telemetry';
@@ -14,7 +16,6 @@ import {
import { printConfig, readConfig } from '@storybook/core/csf-tools';
import { logger } from '@storybook/core/node-logger';
-import fs from 'fs-extra';
import invariant from 'tiny-invariant';
import { sendTelemetryError } from '../withTelemetry';
@@ -93,7 +94,7 @@ export function initializeWhatsNew(
invariant(mainPath, `unable to find storybook main file in ${options.configDir}`);
const main = await readConfig(mainPath);
main.setFieldValue(['core', 'disableWhatsNewNotifications'], disableWhatsNewNotifications);
- await fs.writeFile(mainPath, printConfig(main).code);
+ await writeFile(mainPath, printConfig(main).code);
if (isTelemetryEnabled) {
await telemetry('core-config', { disableWhatsNewNotifications });
}
diff --git a/code/core/src/csf-tools/CsfFile.test.ts b/code/core/src/csf-tools/CsfFile.test.ts
index 18d441e666b8..d7194edcdf2e 100644
--- a/code/core/src/csf-tools/CsfFile.test.ts
+++ b/code/core/src/csf-tools/CsfFile.test.ts
@@ -81,6 +81,7 @@ describe('CsfFile', () => {
stories:
- id: foo-bar--a
name: A
+ localName: A
parameters:
__id: foo-bar--a
__stats:
@@ -94,6 +95,7 @@ describe('CsfFile', () => {
moduleMock: false
- id: foo-bar--b
name: B
+ localName: B
parameters:
__id: foo-bar--b
__stats:
@@ -790,6 +792,7 @@ describe('CsfFile', () => {
stories:
- id: foo-bar--a
name: A
+ localName: default
__stats:
play: false
render: false
@@ -801,6 +804,7 @@ describe('CsfFile', () => {
moduleMock: false
- id: foo-bar--b
name: B
+ localName: B
__stats:
play: false
render: false
@@ -878,6 +882,7 @@ describe('CsfFile', () => {
stories:
- id: foo-bar--a
name: A
+ localName: A
parameters:
__id: foo-bar--a
__stats:
diff --git a/code/core/src/csf-tools/CsfFile.ts b/code/core/src/csf-tools/CsfFile.ts
index d9859da5cecb..d1cafb31bb64 100644
--- a/code/core/src/csf-tools/CsfFile.ts
+++ b/code/core/src/csf-tools/CsfFile.ts
@@ -190,6 +190,7 @@ export interface StaticMeta
export interface StaticStory extends Pick {
id: string;
+ localName?: string;
__stats: IndexInputStats;
}
@@ -488,6 +489,7 @@ export class CsfFile {
node.specifiers.forEach((specifier) => {
if (t.isExportSpecifier(specifier) && t.isIdentifier(specifier.exported)) {
const { name: exportName } = specifier.exported;
+ const { name: localName } = specifier.local;
const decl = t.isProgram(parent)
? findVarInitialization(specifier.local.name, parent)
: specifier.local;
@@ -515,6 +517,7 @@ export class CsfFile {
self._stories[exportName] = {
id: 'FIXME',
name: exportName,
+ localName,
parameters: {},
__stats: {},
};
diff --git a/code/core/src/csf-tools/vitest-plugin/transformer.test.ts b/code/core/src/csf-tools/vitest-plugin/transformer.test.ts
index 72a1d05ab403..5b030ac19c73 100644
--- a/code/core/src/csf-tools/vitest-plugin/transformer.test.ts
+++ b/code/core/src/csf-tools/vitest-plugin/transformer.test.ts
@@ -213,6 +213,55 @@ describe('transformer', () => {
`);
});
+ describe("use the story's name as test title", () => {
+ it('should support CSF v3 via name property', async () => {
+ const code = `
+ export default { component: Button }
+ export const Primary = { name: "custom name" };`;
+ const result = await transform({ code });
+
+ expect(result.code).toMatchInlineSnapshot(`
+ import { test as _test, expect as _expect } from "vitest";
+ import { testStory as _testStory } from "@storybook/experimental-addon-test/internal/test-utils";
+ const _meta = {
+ component: Button,
+ title: "automatic/calculated/title"
+ };
+ export default _meta;
+ export const Primary = {
+ name: "custom name"
+ };
+ const _isRunningFromThisFile = import.meta.url.includes(globalThis.__vitest_worker__.filepath ?? _expect.getState().testPath);
+ if (_isRunningFromThisFile) {
+ _test("custom name", _testStory("Primary", Primary, _meta, []));
+ }
+ `);
+ });
+
+ it('should support CSF v1/v2 via storyName property', async () => {
+ const code = `
+ export default { component: Button }
+ export const Story = () => {}
+ Story.storyName = 'custom name';`;
+ const result = await transform({ code: code });
+ expect(result.code).toMatchInlineSnapshot(`
+ import { test as _test, expect as _expect } from "vitest";
+ import { testStory as _testStory } from "@storybook/experimental-addon-test/internal/test-utils";
+ const _meta = {
+ component: Button,
+ title: "automatic/calculated/title"
+ };
+ export default _meta;
+ export const Story = () => {};
+ Story.storyName = 'custom name';
+ const _isRunningFromThisFile = import.meta.url.includes(globalThis.__vitest_worker__.filepath ?? _expect.getState().testPath);
+ if (_isRunningFromThisFile) {
+ _test("custom name", _testStory("Story", Story, _meta, []));
+ }
+ `);
+ });
+ });
+
it('should add test statement to const declared exported stories', async () => {
const code = `
export default {};
@@ -247,6 +296,40 @@ describe('transformer', () => {
`);
});
+ it('should add test statement to const declared renamed exported stories', async () => {
+ const code = `
+ export default {};
+ const Primary = {
+ args: {
+ label: 'Primary Button',
+ },
+ };
+
+ export { Primary as PrimaryStory };
+ `;
+
+ const result = await transform({ code });
+
+ expect(result.code).toMatchInlineSnapshot(`
+ import { test as _test, expect as _expect } from "vitest";
+ import { testStory as _testStory } from "@storybook/experimental-addon-test/internal/test-utils";
+ const _meta = {
+ title: "automatic/calculated/title"
+ };
+ export default _meta;
+ const Primary = {
+ args: {
+ label: 'Primary Button'
+ }
+ };
+ export { Primary as PrimaryStory };
+ const _isRunningFromThisFile = import.meta.url.includes(globalThis.__vitest_worker__.filepath ?? _expect.getState().testPath);
+ if (_isRunningFromThisFile) {
+ _test("PrimaryStory", _testStory("PrimaryStory", Primary, _meta, []));
+ }
+ `);
+ });
+
it('should add tests for multiple stories', async () => {
const code = `
export default {};
diff --git a/code/core/src/csf-tools/vitest-plugin/transformer.ts b/code/core/src/csf-tools/vitest-plugin/transformer.ts
index 51ea1169f6c3..677235975cb1 100644
--- a/code/core/src/csf-tools/vitest-plugin/transformer.ts
+++ b/code/core/src/csf-tools/vitest-plugin/transformer.ts
@@ -201,19 +201,23 @@ export async function vitestTransform({
ast.program.body.push(isRunningFromThisFileDeclaration);
const getTestStatementForStory = ({
+ localName,
exportName,
+ testTitle,
node,
}: {
+ localName: string;
exportName: string;
+ testTitle: string;
node: t.Node;
}): t.ExpressionStatement => {
// Create the _test expression directly using the exportName identifier
const testStoryCall = t.expressionStatement(
t.callExpression(vitestTestId, [
- t.stringLiteral(exportName),
+ t.stringLiteral(testTitle),
t.callExpression(testStoryId, [
t.stringLiteral(exportName),
- t.identifier(exportName),
+ t.identifier(localName),
t.identifier(metaExportName),
skipTagsId,
]),
@@ -239,10 +243,10 @@ export async function vitestTransform({
return;
}
- return getTestStatementForStory({
- exportName,
- node,
- });
+ const localName = parsed._stories[exportName].localName ?? exportName;
+ // use the story's name as the test title for vitest, and fallback to exportName
+ const testTitle = parsed._stories[exportName].name ?? exportName;
+ return getTestStatementForStory({ testTitle, localName, exportName, node });
})
.filter((st) => !!st) as t.ExpressionStatement[];
diff --git a/code/core/src/docs-tools/argTypes/convert/convert.test.ts b/code/core/src/docs-tools/argTypes/convert/convert.test.ts
index 25d1b04f01aa..11ca35291953 100644
--- a/code/core/src/docs-tools/argTypes/convert/convert.test.ts
+++ b/code/core/src/docs-tools/argTypes/convert/convert.test.ts
@@ -4,7 +4,7 @@ import { describe, expect, it } from 'vitest';
import { transformSync } from '@storybook/core/babel';
-import mapValues from 'lodash/mapValues.js';
+import { mapValues } from 'es-toolkit';
import requireFromString from 'require-from-string';
import { normalizeNewlines } from '../utils';
diff --git a/code/core/src/docs-tools/argTypes/convert/proptypes/convert.ts b/code/core/src/docs-tools/argTypes/convert/proptypes/convert.ts
index 710ddfa449d5..c3426ecfd3e2 100644
--- a/code/core/src/docs-tools/argTypes/convert/proptypes/convert.ts
+++ b/code/core/src/docs-tools/argTypes/convert/proptypes/convert.ts
@@ -1,6 +1,6 @@
import type { SBType } from '@storybook/core/types';
-import mapValues from 'lodash/mapValues.js';
+import { mapValues } from 'es-toolkit';
import { parseLiteral } from '../utils';
import type { PTType } from './types';
diff --git a/code/core/src/manager-api/lib/merge.ts b/code/core/src/manager-api/lib/merge.ts
index ec4c474942b0..f00195c304b2 100644
--- a/code/core/src/manager-api/lib/merge.ts
+++ b/code/core/src/manager-api/lib/merge.ts
@@ -1,10 +1,13 @@
import { logger } from '@storybook/core/client-logger';
-import isEqual from 'lodash/isEqual.js';
-import mergeWith from 'lodash/mergeWith.js';
+import { isEqual, mergeWith, omitBy, pick } from 'es-toolkit';
-export default (a: TObj, b: Partial) =>
- mergeWith({}, a, b, (objValue: TObj, srcValue: Partial) => {
+export default (a: TObj, ...b: Partial[]): TObj => {
+ // start with empty object
+ let target = {};
+
+ // merge object a unto target
+ target = mergeWith({}, a, (objValue: TObj, srcValue: Partial) => {
if (Array.isArray(srcValue) && Array.isArray(objValue)) {
srcValue.forEach((s) => {
const existing = objValue.find((o) => o === s || isEqual(o, s));
@@ -19,5 +22,52 @@ export default (a: TObj, b: Partial) =>
logger.log(['the types mismatch, picking', objValue]);
return objValue;
}
- return undefined;
});
+
+ for (const obj of b) {
+ // merge object b unto target
+ target = mergeWith(target, obj, (objValue: TObj, srcValue: Partial) => {
+ if (Array.isArray(srcValue) && Array.isArray(objValue)) {
+ srcValue.forEach((s) => {
+ const existing = objValue.find((o) => o === s || isEqual(o, s));
+ if (!existing) {
+ objValue.push(s);
+ }
+ });
+
+ return objValue;
+ }
+ if (Array.isArray(objValue)) {
+ logger.log(['the types mismatch, picking', objValue]);
+ return objValue;
+ }
+ });
+ }
+
+ return target as TObj;
+};
+
+export const noArrayMerge = (a: TObj, ...b: Partial[]): TObj => {
+ // start with empty object
+ let target = {};
+
+ // merge object a unto target
+ target = mergeWith({}, a, (objValue: TObj, srcValue: Partial) => {
+ // Treat arrays as scalars:
+ if (Array.isArray(srcValue)) {
+ return srcValue;
+ }
+ });
+
+ for (const obj of b) {
+ // merge object b unto target
+ target = mergeWith(target, obj, (objValue: TObj, srcValue: Partial) => {
+ // Treat arrays as scalars:
+ if (Array.isArray(srcValue)) {
+ return srcValue;
+ }
+ });
+ }
+
+ return target as TObj;
+};
diff --git a/code/core/src/manager-api/lib/stories.ts b/code/core/src/manager-api/lib/stories.ts
index a4ff209c3249..556acba0076d 100644
--- a/code/core/src/manager-api/lib/stories.ts
+++ b/code/core/src/manager-api/lib/stories.ts
@@ -19,8 +19,7 @@ import type {
} from '@storybook/core/types';
import { sanitize } from '@storybook/csf';
-import countBy from 'lodash/countBy.js';
-import mapValues from 'lodash/mapValues.js';
+import { countBy, mapValues } from 'es-toolkit';
import memoize from 'memoizerific';
import { dedent } from 'ts-dedent';
@@ -41,7 +40,7 @@ export const denormalizeStoryParameters = ({
kindParameters[storyData.kind],
storyData.parameters as unknown as Parameters
),
- }));
+ })) as SetStoriesStoryData;
};
export const transformSetStoriesStoryDataToStoriesHash = (
@@ -112,7 +111,7 @@ export const transformStoryIndexV2toV3 = (index: StoryIndexV2): StoryIndexV3 =>
};
export const transformStoryIndexV3toV4 = (index: StoryIndexV3): API_PreparedStoryIndex => {
- const countByTitle = countBy(Object.values(index.stories), 'title');
+ const countByTitle = countBy(Object.values(index.stories), (item) => item.title);
return {
v: 4,
entries: Object.values(index.stories).reduce(
diff --git a/code/core/src/manager-api/modules/layout.ts b/code/core/src/manager-api/modules/layout.ts
index 642f78fc5813..468d51af1b8a 100644
--- a/code/core/src/manager-api/modules/layout.ts
+++ b/code/core/src/manager-api/modules/layout.ts
@@ -5,8 +5,7 @@ import { global } from '@storybook/global';
import { SET_CONFIG } from '@storybook/core/core-events';
-import { dequal as deepEqual } from 'dequal';
-import pick from 'lodash/pick.js';
+import { isEqual as deepEqual, pick, toMerged } from 'es-toolkit';
import merge from '../lib/merge';
import type { ModuleFn } from '../lib/types';
@@ -319,14 +318,13 @@ export const init: ModuleFn = ({ store, provider, singleStory
return {
...defaultLayoutState,
layout: {
- ...defaultLayoutState.layout,
- ...pick(options, Object.keys(defaultLayoutState.layout)),
+ ...toMerged(
+ defaultLayoutState.layout,
+ pick(options, Object.keys(defaultLayoutState.layout))
+ ),
...(singleStory && { navSize: 0 }),
},
- ui: {
- ...defaultLayoutState.ui,
- ...pick(options, Object.keys(defaultLayoutState.ui)),
- },
+ ui: toMerged(defaultLayoutState.ui, pick(options, Object.keys(defaultLayoutState.ui))),
selectedPanel: selectedPanel || defaultLayoutState.selectedPanel,
theme: theme || defaultLayoutState.theme,
};
@@ -351,7 +349,7 @@ export const init: ModuleFn = ({ store, provider, singleStory
const updatedLayout = {
...layout,
- ...options.layout,
+ ...(options.layout || {}),
...pick(options, Object.keys(layout)),
...(singleStory && { navSize: 0 }),
};
@@ -359,7 +357,7 @@ export const init: ModuleFn = ({ store, provider, singleStory
const updatedUi = {
...ui,
...options.ui,
- ...pick(options, Object.keys(ui)),
+ ...toMerged(options.ui || {}, pick(options, Object.keys(ui))),
};
const updatedTheme = {
@@ -388,7 +386,7 @@ export const init: ModuleFn = ({ store, provider, singleStory
},
};
- const persisted = pick(store.getState(), 'layout', 'selectedPanel');
+ const persisted = pick(store.getState(), ['layout', 'selectedPanel']);
provider.channel?.on(SET_CONFIG, () => {
api.setOptions(merge(api.getInitialOptions(), persisted));
diff --git a/code/core/src/manager-api/modules/notifications.ts b/code/core/src/manager-api/modules/notifications.ts
index cda1025001ae..c1410cfbeb78 100644
--- a/code/core/src/manager-api/modules/notifications.ts
+++ b/code/core/src/manager-api/modules/notifications.ts
@@ -1,6 +1,6 @@
import type { API_Notification } from '@storybook/core/types';
-import partition from 'lodash/partition.js';
+import { partition } from 'es-toolkit';
import type { ModuleFn } from '../lib/types';
diff --git a/code/core/src/manager-api/root.tsx b/code/core/src/manager-api/root.tsx
index bf992f1907a8..1c150b47f4b3 100644
--- a/code/core/src/manager-api/root.tsx
+++ b/code/core/src/manager-api/root.tsx
@@ -41,11 +41,12 @@ import {
STORY_CHANGED,
} from '@storybook/core/core-events';
-import mergeWith from 'lodash/mergeWith.js';
+import { mergeWith } from 'es-toolkit';
import { createContext } from './context';
import getInitialState from './initial-state';
import { types } from './lib/addons';
+import { noArrayMerge } from './lib/merge';
import type { ModuleFn } from './lib/types';
import * as addons from './modules/addons';
import * as channel from './modules/channel';
@@ -129,14 +130,7 @@ export type ManagerProviderProps = RouterData &
// This is duplicated from @storybook/preview-api for the reasons mentioned in lib-addons/types.js
export const combineParameters = (...parameterSets: Parameters[]) =>
- mergeWith({}, ...parameterSets, (objValue: any, srcValue: any) => {
- // Treat arrays as scalars:
- if (Array.isArray(srcValue)) {
- return srcValue;
- }
-
- return undefined;
- });
+ noArrayMerge({}, ...parameterSets);
class ManagerProvider extends Component {
api: API = {} as API;
diff --git a/code/core/src/manager-api/tests/url.test.js b/code/core/src/manager-api/tests/url.test.js
index a3612650530c..15e1c288863a 100644
--- a/code/core/src/manager-api/tests/url.test.js
+++ b/code/core/src/manager-api/tests/url.test.js
@@ -19,7 +19,7 @@ describe('initial state', () => {
describe('config query parameters', () => {
it('handles full parameter', () => {
const navigate = vi.fn();
- const location = { search: new URLSearchParams({ full: '1' }).toString() };
+ const location = { search: '?' + new URLSearchParams({ full: '1' }).toString() };
const {
state: { layout },
@@ -34,7 +34,7 @@ describe('initial state', () => {
it('handles nav parameter', () => {
const navigate = vi.fn();
- const location = { search: new URLSearchParams({ nav: '0' }).toString() };
+ const location = { search: '?' + new URLSearchParams({ nav: '0' }).toString() };
const {
state: { layout },
@@ -45,7 +45,7 @@ describe('initial state', () => {
it('handles shortcuts parameter', () => {
const navigate = vi.fn();
- const location = { search: new URLSearchParams({ shortcuts: '0' }).toString() };
+ const location = { search: '?' + new URLSearchParams({ shortcuts: '0' }).toString() };
const {
state: { ui },
@@ -56,7 +56,7 @@ describe('initial state', () => {
it('handles panel parameter, bottom', () => {
const navigate = vi.fn();
- const location = { search: new URLSearchParams({ panel: 'bottom' }).toString() };
+ const location = { search: '?' + new URLSearchParams({ panel: 'bottom' }).toString() };
const {
state: { layout },
@@ -67,7 +67,7 @@ describe('initial state', () => {
it('handles panel parameter, right', () => {
const navigate = vi.fn();
- const location = { search: new URLSearchParams({ panel: 'right' }).toString() };
+ const location = { search: '?' + new URLSearchParams({ panel: 'right' }).toString() };
const {
state: { layout },
@@ -78,7 +78,7 @@ describe('initial state', () => {
it('handles panel parameter, 0', () => {
const navigate = vi.fn();
- const location = { search: new URLSearchParams({ panel: '0' }).toString() };
+ const location = { search: '?' + new URLSearchParams({ panel: '0' }).toString() };
const {
state: { layout },
diff --git a/code/core/src/manager-api/version.ts b/code/core/src/manager-api/version.ts
index 1c66037e4d34..a9c6904a4e73 100644
--- a/code/core/src/manager-api/version.ts
+++ b/code/core/src/manager-api/version.ts
@@ -1 +1 @@
-export const version = '8.3.0-beta.4';
+export const version = '8.4.0-alpha.4';
diff --git a/code/core/src/manager/components/mobile/navigation/MobileNavigation.stories.tsx b/code/core/src/manager/components/mobile/navigation/MobileNavigation.stories.tsx
index e445d73dea23..37e18b66a1fe 100644
--- a/code/core/src/manager/components/mobile/navigation/MobileNavigation.stories.tsx
+++ b/code/core/src/manager/components/mobile/navigation/MobileNavigation.stories.tsx
@@ -5,7 +5,7 @@ import { within } from '@storybook/test';
import { ManagerContext } from '@storybook/core/manager-api';
-import { startCase } from 'lodash';
+import { startCase } from 'es-toolkit';
import { LayoutProvider, useLayout } from '../../layout/LayoutProvider';
import { MobileNavigation } from './MobileNavigation';
diff --git a/code/core/src/manager/components/preview/Preview.tsx b/code/core/src/manager/components/preview/Preview.tsx
index 8a459276f0e7..ed73a059951f 100644
--- a/code/core/src/manager/components/preview/Preview.tsx
+++ b/code/core/src/manager/components/preview/Preview.tsx
@@ -202,7 +202,7 @@ export function filterTabs(panels: Addon_BaseType[], parameters?: Record ({
index,
...(typeof tabs[key] === 'string' ? { title: tabs[key] } : tabs[key]),
diff --git a/code/core/src/manager/components/preview/Toolbar.tsx b/code/core/src/manager/components/preview/Toolbar.tsx
index 2f81f309435d..5dd460e80e69 100644
--- a/code/core/src/manager/components/preview/Toolbar.tsx
+++ b/code/core/src/manager/components/preview/Toolbar.tsx
@@ -188,7 +188,10 @@ function toolbarItemHasBeenExcluded(item: Partial, entry: LeafEn
const toolbarItemsFromStoryParameters = 'toolbar' in parameters ? parameters.toolbar : undefined;
const { toolbar: toolbarItemsFromAddonsConfig } = addons.getConfig();
- const toolbarItems = merge(toolbarItemsFromAddonsConfig, toolbarItemsFromStoryParameters);
+ const toolbarItems = merge(
+ toolbarItemsFromAddonsConfig || {},
+ toolbarItemsFromStoryParameters || {}
+ );
// @ts-expect-error (non strict)
return toolbarItems ? !!toolbarItems[item?.id]?.hidden : false;
diff --git a/code/core/src/manager/components/preview/utils/stringifyQueryParams.tsx b/code/core/src/manager/components/preview/utils/stringifyQueryParams.tsx
index 4175503808bb..dea058659eac 100644
--- a/code/core/src/manager/components/preview/utils/stringifyQueryParams.tsx
+++ b/code/core/src/manager/components/preview/utils/stringifyQueryParams.tsx
@@ -1,4 +1,10 @@
-import qs from 'qs';
+import { stringify } from 'picoquery';
-export const stringifyQueryParams = (queryParams: Record) =>
- qs.stringify(queryParams, { addQueryPrefix: true, encode: false }).replace(/^\?/, '&');
+export const stringifyQueryParams = (queryParams: Record) => {
+ const result = stringify(queryParams);
+ if (result === '') {
+ return '';
+ }
+
+ return `&${result}`;
+};
diff --git a/code/core/src/manager/components/sidebar/Brand.tsx b/code/core/src/manager/components/sidebar/Brand.tsx
index 995b924948ec..f4ca666e0579 100644
--- a/code/core/src/manager/components/sidebar/Brand.tsx
+++ b/code/core/src/manager/components/sidebar/Brand.tsx
@@ -12,7 +12,7 @@ export const StorybookLogoStyled = styled(StorybookLogo)(({ theme }) => ({
export const Img = styled.img({
display: 'block',
- maxWidth: '150px',
+ maxWidth: '150px !important',
maxHeight: '100px',
});
diff --git a/code/core/src/manager/components/sidebar/useExpanded.ts b/code/core/src/manager/components/sidebar/useExpanded.ts
index 5f9a479b3308..e216c2b2e0cc 100644
--- a/code/core/src/manager/components/sidebar/useExpanded.ts
+++ b/code/core/src/manager/components/sidebar/useExpanded.ts
@@ -7,7 +7,7 @@ import { STORIES_COLLAPSE_ALL, STORIES_EXPAND_ALL } from '@storybook/core/core-e
import type { StoriesHash } from '@storybook/core/manager-api';
import { useStorybookApi } from '@storybook/core/manager-api';
-import throttle from 'lodash/throttle.js';
+import { throttle } from 'es-toolkit';
import { matchesKeyCode, matchesModifiers } from '../../keybinding';
import { getAncestorIds, getDescendantIds, isAncestor, scrollIntoView } from '../../utils/tree';
diff --git a/code/core/src/manager/components/sidebar/useLastViewed.ts b/code/core/src/manager/components/sidebar/useLastViewed.ts
index da53f6645ba3..48aa8e10cf12 100644
--- a/code/core/src/manager/components/sidebar/useLastViewed.ts
+++ b/code/core/src/manager/components/sidebar/useLastViewed.ts
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo, useRef } from 'react';
-import debounce from 'lodash/debounce.js';
+import { debounce } from 'es-toolkit/compat';
import store from 'store2';
import type { Selection, StoryRef } from './types';
diff --git a/code/core/src/manager/globals/exports.ts b/code/core/src/manager/globals/exports.ts
index 2596f0f01602..ff8a7182e815 100644
--- a/code/core/src/manager/globals/exports.ts
+++ b/code/core/src/manager/globals/exports.ts
@@ -618,7 +618,6 @@ export default {
'getMatch',
'parsePath',
'queryFromLocation',
- 'queryFromString',
'stringifyQuery',
'useNavigate',
],
@@ -635,7 +634,6 @@ export default {
'getMatch',
'parsePath',
'queryFromLocation',
- 'queryFromString',
'stringifyQuery',
'useNavigate',
],
@@ -652,7 +650,6 @@ export default {
'getMatch',
'parsePath',
'queryFromLocation',
- 'queryFromString',
'stringifyQuery',
'useNavigate',
],
diff --git a/code/core/src/node-logger/index.ts b/code/core/src/node-logger/index.ts
index 1a8abc242d25..7235ceff3751 100644
--- a/code/core/src/node-logger/index.ts
+++ b/code/core/src/node-logger/index.ts
@@ -1,5 +1,4 @@
///
-import chalk from 'chalk';
import npmLog from 'npmlog';
import prettyTime from 'pretty-hrtime';
@@ -7,14 +6,34 @@ import prettyTime from 'pretty-hrtime';
// there are issues with the build: https://github.com/storybookjs/storybook/issues/14621
npmLog.stream = process.stdout;
+function hex(hexColor: string) {
+ // Ensure the hex color is 6 characters long and starts with '#'
+ if (!/^#?[0-9A-Fa-f]{6}$/.test(hexColor)) {
+ throw new Error('Invalid hex color. It must be a 6-character hex code.');
+ }
+
+ // Remove the leading '#' if it exists
+ if (hexColor.startsWith('#')) {
+ hexColor = hexColor.slice(1);
+ }
+
+ // Convert hex to RGB
+ const r = parseInt(hexColor.slice(0, 2), 16);
+ const g = parseInt(hexColor.slice(2, 4), 16);
+ const b = parseInt(hexColor.slice(4, 6), 16);
+
+ // Return the ANSI escape sequence for the given RGB color
+ return (text: string) => `\x1b[38;2;${r};${g};${b}m${text}\x1b[39m`;
+}
+
export const colors = {
- pink: chalk.hex('F1618C'),
- purple: chalk.hex('B57EE5'),
- orange: chalk.hex('F3AD38'),
- green: chalk.hex('A2E05E'),
- blue: chalk.hex('6DABF5'),
- red: chalk.hex('F16161'),
- gray: chalk.gray,
+ pink: hex('#F1618C'),
+ purple: hex('#B57EE5'),
+ orange: hex('#F3AD38'),
+ green: hex('#A2E05E'),
+ blue: hex('#6DABF5'),
+ red: hex('#F16161'),
+ gray: hex('#B8C2CC'),
};
export const logger = {
@@ -40,7 +59,7 @@ export const logger = {
console.log(
msg
- .replace(message.toString(), chalk.red(message.toString()))
+ .replace(message.toString(), colors.red(message.toString()))
.replaceAll(process.cwd(), '.')
);
}
diff --git a/code/core/src/preview-api/modules/preview-web/PreviewWeb.test.ts b/code/core/src/preview-api/modules/preview-web/PreviewWeb.test.ts
index 7994173c2d07..57e3b0357a44 100644
--- a/code/core/src/preview-api/modules/preview-web/PreviewWeb.test.ts
+++ b/code/core/src/preview-api/modules/preview-web/PreviewWeb.test.ts
@@ -31,7 +31,7 @@ import {
UPDATE_STORY_ARGS,
} from '@storybook/core/core-events';
-import merge from 'lodash/merge.js';
+import { merge, toMerged } from 'es-toolkit';
import { addons } from '../addons';
import type { StoryStore } from '../store';
@@ -308,7 +308,7 @@ describe('PreviewWeb', () => {
expect(mockChannel.emit).toHaveBeenCalledWith(STORY_MISSING, 'component-one--missing');
mockChannel.emit.mockClear();
- const newComponentOneExports = merge({}, componentOneExports, {
+ const newComponentOneExports = toMerged(componentOneExports, {
d: { args: { foo: 'd' }, play: vi.fn() },
});
const newImportFn = vi.fn(async (path) => {
@@ -362,7 +362,7 @@ describe('PreviewWeb', () => {
});
await waitForSetCurrentStory();
- const newComponentOneExports = merge({}, componentOneExports, {
+ const newComponentOneExports = toMerged(componentOneExports, {
d: { args: { foo: 'd' }, play: vi.fn() },
});
const newImportFn = vi.fn(async (path) => {
@@ -2927,7 +2927,7 @@ describe('PreviewWeb', () => {
});
describe('when the current story changes', () => {
- const newComponentOneExports = merge({}, componentOneExports, {
+ const newComponentOneExports = toMerged(componentOneExports, {
a: { args: { foo: 'edited' } },
});
const newImportFn = vi.fn(async (path) => {
@@ -3282,7 +3282,7 @@ describe('PreviewWeb', () => {
afterEach(() => {
vi.useRealTimers();
});
- const newComponentOneExports = merge({}, componentOneExports, {
+ const newComponentOneExports = toMerged(componentOneExports, {
a: { args: { bar: 'edited' }, argTypes: { bar: { type: { name: 'string' } } } },
});
const newImportFn = vi.fn(async (path) => {
diff --git a/code/core/src/preview-api/modules/preview-web/SelectionStore.ts b/code/core/src/preview-api/modules/preview-web/SelectionStore.ts
index 439fb7a219b1..7d56f5bf586c 100644
--- a/code/core/src/preview-api/modules/preview-web/SelectionStore.ts
+++ b/code/core/src/preview-api/modules/preview-web/SelectionStore.ts
@@ -21,5 +21,5 @@ export interface SelectionStore {
setSelection(selection: Selection): void;
- setQueryParams(queryParams: qs.ParsedQs): void;
+ setQueryParams(queryParams: Record): void;
}
diff --git a/code/core/src/preview-api/modules/preview-web/UrlStore.test.ts b/code/core/src/preview-api/modules/preview-web/UrlStore.test.ts
index 09c6e8925743..3ee48adc2222 100644
--- a/code/core/src/preview-api/modules/preview-web/UrlStore.test.ts
+++ b/code/core/src/preview-api/modules/preview-web/UrlStore.test.ts
@@ -40,7 +40,7 @@ describe('UrlStore', () => {
);
});
it('should replace legacy parameters but preserve others', () => {
- document.location.search = 'foo=bar&selectedStory=selStory&selectedKind=selKind';
+ document.location.search = '?foo=bar&selectedStory=selStory&selectedKind=selKind';
setPath({ storyId: 'story--id', viewMode: 'story' });
expect(history.replaceState).toHaveBeenCalledWith(
{},
@@ -49,7 +49,7 @@ describe('UrlStore', () => {
);
});
it('should ignore + keep hashes', () => {
- document.location.search = 'foo=bar&selectedStory=selStory&selectedKind=selKind';
+ document.location.search = '?foo=bar&selectedStory=selStory&selectedKind=selKind';
document.location.hash = '#foobar';
setPath({ storyId: 'story--id', viewMode: 'story' });
expect(history.replaceState).toHaveBeenCalledWith(
diff --git a/code/core/src/preview-api/modules/preview-web/UrlStore.ts b/code/core/src/preview-api/modules/preview-web/UrlStore.ts
index 8bcdb5260acb..c4a68c17decb 100644
--- a/code/core/src/preview-api/modules/preview-web/UrlStore.ts
+++ b/code/core/src/preview-api/modules/preview-web/UrlStore.ts
@@ -1,7 +1,7 @@
import type { ViewMode } from '@storybook/core/types';
import { global } from '@storybook/global';
-import qs from 'qs';
+import { parse, stringify } from 'picoquery';
import type { Selection, SelectionSpecifier, SelectionStore } from './SelectionStore';
import { parseArgsParam } from './parseArgsParam';
@@ -21,20 +21,16 @@ const getQueryString = ({
extraParams,
}: {
selection?: Selection;
- extraParams?: qs.ParsedQs;
+ extraParams?: Record;
}) => {
- const search = typeof document !== 'undefined' ? document.location.search : '';
- const { path, selectedKind, selectedStory, ...rest } = qs.parse(search, {
- ignoreQueryPrefix: true,
+ const search = document?.location.search.slice(1);
+ const { path, selectedKind, selectedStory, ...rest } = parse(search);
+ const queryStr = stringify({
+ ...rest,
+ ...extraParams,
+ ...(selection && { id: selection.storyId, viewMode: selection.viewMode }),
});
- return qs.stringify(
- {
- ...rest,
- ...extraParams,
- ...(selection && { id: selection.storyId, viewMode: selection.viewMode }),
- },
- { encode: false, addQueryPrefix: true }
- );
+ return `?${queryStr}`;
};
export const setPath = (selection?: Selection) => {
@@ -48,10 +44,10 @@ export const setPath = (selection?: Selection) => {
};
type ValueOf = T[keyof T];
-const isObject = (val: Record) =>
+const isObject = (val: Record): val is object =>
val != null && typeof val === 'object' && Array.isArray(val) === false;
-const getFirstString = (v: ValueOf): string | void => {
+const getFirstString = (v: ValueOf>): string | void => {
if (v === undefined) {
return undefined;
}
@@ -61,15 +57,18 @@ const getFirstString = (v: ValueOf): string | void => {
if (Array.isArray(v)) {
return getFirstString(v[0]);
}
- if (isObject(v)) {
- return getFirstString(Object.values(v).filter(Boolean) as string[]);
+ if (isObject(v as Record)) {
+ return getFirstString(
+ Object.values(v as Record).filter(Boolean) as string[]
+ );
}
return undefined;
};
export const getSelectionSpecifierFromPath: () => SelectionSpecifier | null = () => {
if (typeof document !== 'undefined') {
- const query = qs.parse(document.location.search, { ignoreQueryPrefix: true });
+ const queryStr = document.location.search.slice(1);
+ const query = parse(queryStr);
const args = typeof query.args === 'string' ? parseArgsParam(query.args) : undefined;
const globals = typeof query.globals === 'string' ? parseArgsParam(query.globals) : undefined;
@@ -103,7 +102,7 @@ export class UrlStore implements SelectionStore {
setPath(this.selection);
}
- setQueryParams(queryParams: qs.ParsedQs) {
+ setQueryParams(queryParams: Record) {
const query = getQueryString({ extraParams: queryParams });
const { hash = '' } = document.location;
history.replaceState({}, '', `${document.location.pathname}${query}${hash}`);
diff --git a/code/core/src/preview-api/modules/preview-web/WebView.ts b/code/core/src/preview-api/modules/preview-web/WebView.ts
index 3b7ff22646cc..90b0d23dce7b 100644
--- a/code/core/src/preview-api/modules/preview-web/WebView.ts
+++ b/code/core/src/preview-api/modules/preview-web/WebView.ts
@@ -4,7 +4,7 @@ import { global } from '@storybook/global';
import { logger } from '@storybook/core/client-logger';
import AnsiToHtml from 'ansi-to-html';
-import qs from 'qs';
+import { parse } from 'picoquery';
import { dedent } from 'ts-dedent';
import type { View } from './View';
@@ -50,9 +50,7 @@ export class WebView implements View {
// Special code for testing situations
if (typeof document !== 'undefined') {
// eslint-disable-next-line @typescript-eslint/naming-convention
- const { __SPECIAL_TEST_PARAMETER__ } = qs.parse(document.location.search, {
- ignoreQueryPrefix: true,
- });
+ const { __SPECIAL_TEST_PARAMETER__ } = parse(document.location.search.slice(1));
switch (__SPECIAL_TEST_PARAMETER__) {
case 'preparing-story': {
this.showPreparingStory();
diff --git a/code/core/src/preview-api/modules/preview-web/parseArgsParam.test.ts b/code/core/src/preview-api/modules/preview-web/parseArgsParam.test.ts
index 28879b3464df..7b8da7118702 100644
--- a/code/core/src/preview-api/modules/preview-web/parseArgsParam.test.ts
+++ b/code/core/src/preview-api/modules/preview-web/parseArgsParam.test.ts
@@ -127,7 +127,7 @@ describe('parseArgsParam', () => {
});
it('parses single object in array', () => {
- const args = parseArgsParam('arr[].one:A;arr[].two:B');
+ const args = parseArgsParam('arr[0].one:A;arr[0].two:B');
expect(args).toStrictEqual({ arr: [{ one: 'A', two: 'B' }] });
});
@@ -135,14 +135,10 @@ describe('parseArgsParam', () => {
expect(parseArgsParam('arr[0].key:A;arr[1].key:B')).toStrictEqual({
arr: [{ key: 'A' }, { key: 'B' }],
});
- expect(parseArgsParam('arr[0][key]:A;arr[1][key]:B')).toStrictEqual({
- arr: [{ key: 'A' }, { key: 'B' }],
- });
});
it('parses nested object in array', () => {
- expect(parseArgsParam('arr[].foo.bar:val')).toStrictEqual({ arr: [{ foo: { bar: 'val' } }] });
- expect(parseArgsParam('arr[][foo][bar]:val')).toStrictEqual({ arr: [{ foo: { bar: 'val' } }] });
+ expect(parseArgsParam('arr[0].foo.bar:val')).toStrictEqual({ arr: [{ foo: { bar: 'val' } }] });
});
describe('key sanitization', () => {
@@ -164,8 +160,6 @@ describe('parseArgsParam', () => {
expect(parseArgsParam('a/b:val')).toStrictEqual({});
expect(parseArgsParam('a\\b:val')).toStrictEqual({});
expect(parseArgsParam('a|b:val')).toStrictEqual({});
- expect(parseArgsParam('a[b:val')).toStrictEqual({});
- expect(parseArgsParam('a]b:val')).toStrictEqual({});
expect(parseArgsParam('a{b:val')).toStrictEqual({});
expect(parseArgsParam('a}b:val')).toStrictEqual({});
expect(parseArgsParam('a?b:val')).toStrictEqual({});
@@ -185,14 +179,10 @@ describe('parseArgsParam', () => {
it('also applies to nested object keys', () => {
expect(parseArgsParam('obj.a!b:val')).toStrictEqual({});
- expect(parseArgsParam('obj[a!b]:val')).toStrictEqual({});
- expect(parseArgsParam('arr[][a!b]:val')).toStrictEqual({});
- expect(parseArgsParam('arr[0][a!b]:val')).toStrictEqual({});
});
it('completely omits an arg when a (deeply) nested key is invalid', () => {
expect(parseArgsParam('obj.foo.a!b:val;obj.foo.bar:val;obj.baz:val')).toStrictEqual({});
- expect(parseArgsParam('obj.foo[][a!b]:val;obj.foo.bar:val;obj.baz:val')).toStrictEqual({});
expect(parseArgsParam('obj.foo.a!b:val;key:val')).toStrictEqual({ key: 'val' });
});
});
@@ -247,10 +237,6 @@ describe('parseArgsParam', () => {
it('also applies to nested object and array values', () => {
expect(parseArgsParam('obj.key:a!b')).toStrictEqual({});
- expect(parseArgsParam('obj[key]:a!b')).toStrictEqual({});
- expect(parseArgsParam('arr[][key]:a!b')).toStrictEqual({});
- expect(parseArgsParam('arr[0][key]:a!b')).toStrictEqual({});
- expect(parseArgsParam('arr[]:a!b')).toStrictEqual({});
expect(parseArgsParam('arr[0]:a!b')).toStrictEqual({});
});
@@ -258,7 +244,6 @@ describe('parseArgsParam', () => {
expect(parseArgsParam('obj.key:a!b;obj.foo:val;obj.bar.baz:val')).toStrictEqual({});
expect(parseArgsParam('obj.arr[]:a!b;obj.foo:val;obj.bar.baz:val')).toStrictEqual({});
expect(parseArgsParam('obj.arr[0]:val;obj.arr[1]:a!b;obj.foo:val')).toStrictEqual({});
- expect(parseArgsParam('obj.arr[][one]:a!b;obj.arr[][two]:val')).toStrictEqual({});
expect(parseArgsParam('arr[]:val;arr[]:a!b;key:val')).toStrictEqual({ key: 'val' });
expect(parseArgsParam('arr[0]:val;arr[1]:a!1;key:val')).toStrictEqual({ key: 'val' });
expect(parseArgsParam('arr[0]:val;arr[2]:a!1;key:val')).toStrictEqual({ key: 'val' });
diff --git a/code/core/src/preview-api/modules/preview-web/parseArgsParam.ts b/code/core/src/preview-api/modules/preview-web/parseArgsParam.ts
index 6cdffe7eae10..9045530a3c19 100644
--- a/code/core/src/preview-api/modules/preview-web/parseArgsParam.ts
+++ b/code/core/src/preview-api/modules/preview-web/parseArgsParam.ts
@@ -2,8 +2,8 @@ import type { Args } from '@storybook/core/types';
import { once } from '@storybook/core/client-logger';
-import isPlainObject from 'lodash/isPlainObject.js';
-import qs from 'qs';
+import { isPlainObject } from 'es-toolkit';
+import { type Options, parse } from 'picoquery';
import { dedent } from 'ts-dedent';
// Keep this in sync with validateArgs in router/src/utils.ts
@@ -53,17 +53,14 @@ const validateArgs = (key = '', value: unknown): boolean => {
return false;
};
-const QS_OPTIONS = {
+const QUERY_OPTIONS: Partial = {
delimiter: ';', // we're parsing a single query param
- allowDots: true, // objects are encoded using dot notation
- allowSparse: true, // arrays will be merged on top of their initial value
- decoder(
- str: string,
- defaultDecoder: (str: string, decoder?: any, charset?: string) => string,
- charset: string,
- type: 'key' | 'value'
- ) {
- if (type === 'value' && str.startsWith('!')) {
+ nesting: true,
+ arrayRepeat: true,
+ arrayRepeatSyntax: 'bracket',
+ nestingSyntax: 'js', // objects are encoded using dot notation
+ valueDeserializer(str: string) {
+ if (str.startsWith('!')) {
if (str === '!undefined') {
return undefined;
}
@@ -81,7 +78,7 @@ const QS_OPTIONS = {
}
if (str.startsWith('!date(') && str.endsWith(')')) {
- return new Date(str.slice(6, -1));
+ return new Date(str.replaceAll(' ', '+').slice(6, -1));
}
if (str.startsWith('!hex(') && str.endsWith(')')) {
@@ -90,28 +87,28 @@ const QS_OPTIONS = {
const color = str.slice(1).match(COLOR_REGEXP);
if (color) {
- if (str.startsWith('!rgba')) {
+ if (str.startsWith('!rgba') || str.startsWith('!RGBA')) {
return `${color[1]}(${color[2]}, ${color[3]}, ${color[4]}, ${color[5]})`;
}
- if (str.startsWith('!hsla')) {
+ if (str.startsWith('!hsla') || str.startsWith('!HSLA')) {
return `${color[1]}(${color[2]}, ${color[3]}%, ${color[4]}%, ${color[5]})`;
}
- return str.startsWith('!rgb')
+ return str.startsWith('!rgb') || str.startsWith('!RGB')
? `${color[1]}(${color[2]}, ${color[3]}, ${color[4]})`
: `${color[1]}(${color[2]}, ${color[3]}%, ${color[4]}%)`;
}
}
- if (type === 'value' && NUMBER_REGEXP.test(str)) {
+ if (NUMBER_REGEXP.test(str)) {
return Number(str);
}
- return defaultDecoder(str, defaultDecoder, charset);
+ return str;
},
};
export const parseArgsParam = (argsString: string): Args => {
const parts = argsString.split(';').map((part) => part.replace('=', '~').replace(':', '='));
- return Object.entries(qs.parse(parts.join(';'), QS_OPTIONS)).reduce((acc, [key, value]) => {
+ return Object.entries(parse(parts.join(';'), QUERY_OPTIONS)).reduce((acc, [key, value]) => {
if (validateArgs(key, value)) {
return Object.assign(acc, { [key]: value });
}
diff --git a/code/core/src/preview-api/modules/store/StoryStore.ts b/code/core/src/preview-api/modules/store/StoryStore.ts
index 510373177fa9..cd7dc40a6ed1 100644
--- a/code/core/src/preview-api/modules/store/StoryStore.ts
+++ b/code/core/src/preview-api/modules/store/StoryStore.ts
@@ -31,8 +31,7 @@ import {
MissingStoryFromCsfFileError,
} from '@storybook/core/preview-errors';
-import mapValues from 'lodash/mapValues.js';
-import pick from 'lodash/pick.js';
+import { mapValues, omitBy, pick, toMerged } from 'es-toolkit';
import memoize from 'memoizerific';
import { HooksContext } from '../addons';
@@ -47,6 +46,13 @@ import {
processCSFFile,
} from './csf';
+export function picky, K extends keyof T>(
+ obj: T,
+ keys: K[]
+): Partial> {
+ return omitBy(pick(obj, keys), (v) => v === undefined);
+}
+
// TODO -- what are reasonable values for these?
const CSF_CACHE_SIZE = 1000;
const STORY_CACHE_SIZE = 10000;
@@ -354,7 +360,7 @@ export class StoryStore {
const stories: Record = mapValues(value.stories, (story) => {
const { importPath } = this.storyIndex.entries[story.id];
return {
- ...pick(story, ['id', 'name', 'title']),
+ ...picky(story, ['id', 'name', 'title']),
importPath,
// These 3 fields were going to be dropped in v7, but instead we will keep them for the
// 7.x cycle so that v7 Storybooks can be composed successfully in v6 Storybook.
@@ -362,10 +368,10 @@ export class StoryStore {
kind: story.title,
story: story.name,
parameters: {
- ...pick(story.parameters, allowedParameters),
+ ...picky(story.parameters, allowedParameters),
fileName: importPath,
},
- };
+ } as V3CompatIndexEntry;
});
return {
diff --git a/code/core/src/preview-api/modules/store/args.ts b/code/core/src/preview-api/modules/store/args.ts
index e34df974c42c..ea678dd4d2df 100644
--- a/code/core/src/preview-api/modules/store/args.ts
+++ b/code/core/src/preview-api/modules/store/args.ts
@@ -9,8 +9,7 @@ import type {
import { once } from '@storybook/core/client-logger';
-import { dequal as deepEqual } from 'dequal';
-import isPlainObject from 'lodash/isPlainObject.js';
+import { isEqual as deepEqual, isPlainObject } from 'es-toolkit';
import { dedent } from 'ts-dedent';
const INCOMPATIBLE = Symbol('incompatible');
diff --git a/code/core/src/preview-api/modules/store/csf/normalizeInputTypes.ts b/code/core/src/preview-api/modules/store/csf/normalizeInputTypes.ts
index d0bdaabdfe1b..f4685dc83090 100644
--- a/code/core/src/preview-api/modules/store/csf/normalizeInputTypes.ts
+++ b/code/core/src/preview-api/modules/store/csf/normalizeInputTypes.ts
@@ -7,7 +7,7 @@ import type {
StrictInputType,
} from '@storybook/core/types';
-import mapValues from 'lodash/mapValues.js';
+import { mapValues } from 'es-toolkit';
const normalizeType = (type: InputType['type']): StrictInputType['type'] => {
return typeof type === 'string' ? { name: type } : type;
diff --git a/code/core/src/preview-api/modules/store/csf/portable-stories.ts b/code/core/src/preview-api/modules/store/csf/portable-stories.ts
index 1525b6e3e6d8..2ae9143a1a5d 100644
--- a/code/core/src/preview-api/modules/store/csf/portable-stories.ts
+++ b/code/core/src/preview-api/modules/store/csf/portable-stories.ts
@@ -76,7 +76,17 @@ export function setProjectAnnotations(
const annotations = Array.isArray(projectAnnotations) ? projectAnnotations : [projectAnnotations];
globalThis.globalProjectAnnotations = composeConfigs(annotations.map(extractAnnotation));
- return globalThis.globalProjectAnnotations;
+ /*
+ We must return the composition of default and global annotations here
+ To ensure that the user has the full project annotations, eg. when running
+
+ const projectAnnotations = setProjectAnnotations(...);
+ beforeAll(projectAnnotations.beforeAll)
+ */
+ return composeConfigs([
+ globalThis.defaultProjectAnnotations ?? {},
+ globalThis.globalProjectAnnotations ?? {},
+ ]);
}
const cleanups: CleanupCallback[] = [];
@@ -116,7 +126,7 @@ export function composeStory 0
? defaultConfig
- : globalThis.defaultProjectAnnotations ?? {},
+ : (globalThis.defaultProjectAnnotations ?? {}),
globalThis.globalProjectAnnotations ?? {},
projectAnnotations ?? {},
])
diff --git a/code/core/src/preview-api/modules/store/filterArgTypes.ts b/code/core/src/preview-api/modules/store/filterArgTypes.ts
index 274489a67cf4..16c2dd3a6c51 100644
--- a/code/core/src/preview-api/modules/store/filterArgTypes.ts
+++ b/code/core/src/preview-api/modules/store/filterArgTypes.ts
@@ -1,6 +1,6 @@
import type { StrictArgTypes } from '@storybook/core/types';
-import pickBy from 'lodash/pickBy.js';
+import { pickBy } from 'es-toolkit';
export type PropDescriptor = string[] | RegExp;
@@ -18,8 +18,8 @@ export const filterArgTypes = (
return (
argTypes &&
pickBy(argTypes, (argType, key) => {
- const name = argType.name || key;
- return (!include || matches(name, include)) && (!exclude || !matches(name, exclude));
+ const name = argType.name || key.toString();
+ return !!(!include || matches(name, include)) && (!exclude || !matches(name, exclude));
})
);
};
diff --git a/code/core/src/preview-api/modules/store/inferArgTypes.ts b/code/core/src/preview-api/modules/store/inferArgTypes.ts
index ea8b45b75609..007b4971a173 100644
--- a/code/core/src/preview-api/modules/store/inferArgTypes.ts
+++ b/code/core/src/preview-api/modules/store/inferArgTypes.ts
@@ -2,7 +2,7 @@ import type { ArgTypesEnhancer, Renderer, SBType } from '@storybook/core/types';
import { logger } from '@storybook/core/client-logger';
-import mapValues from 'lodash/mapValues.js';
+import { mapValues } from 'es-toolkit';
import { dedent } from 'ts-dedent';
import { combineParameters } from './parameters';
diff --git a/code/core/src/preview-api/modules/store/inferControls.ts b/code/core/src/preview-api/modules/store/inferControls.ts
index b4408d85921f..92447845cc92 100644
--- a/code/core/src/preview-api/modules/store/inferControls.ts
+++ b/code/core/src/preview-api/modules/store/inferControls.ts
@@ -7,7 +7,7 @@ import type {
import { logger } from '@storybook/core/client-logger';
-import mapValues from 'lodash/mapValues.js';
+import { mapValues } from 'es-toolkit';
import { filterArgTypes } from './filterArgTypes';
import { combineParameters } from './parameters';
@@ -77,7 +77,7 @@ export const inferControls: ArgTypesEnhancer = (context) => {
const filteredArgTypes = filterArgTypes(argTypes, include, exclude);
const withControls = mapValues(filteredArgTypes, (argType, name) => {
- return argType?.type && inferControl(argType, name, matchers);
+ return argType?.type && inferControl(argType, name.toString(), matchers);
});
return combineParameters(withControls, filteredArgTypes);
diff --git a/code/core/src/preview-api/modules/store/parameters.ts b/code/core/src/preview-api/modules/store/parameters.ts
index 43dee876d71b..3b6077614801 100644
--- a/code/core/src/preview-api/modules/store/parameters.ts
+++ b/code/core/src/preview-api/modules/store/parameters.ts
@@ -1,7 +1,7 @@
// Utilities for handling parameters
import type { Parameters } from '@storybook/core/types';
-import isPlainObject from 'lodash/isPlainObject.js';
+import { isPlainObject } from 'es-toolkit';
/**
* Safely combine parameters recursively. Only copy objects when needed. Algorithm = always
diff --git a/code/core/src/router/router.tsx b/code/core/src/router/router.tsx
index 1866984ca3ea..d2b9d3f6e8b1 100644
--- a/code/core/src/router/router.tsx
+++ b/code/core/src/router/router.tsx
@@ -6,7 +6,7 @@ import { global } from '@storybook/global';
import * as R from 'react-router-dom';
import type { LinkProps, NavigateOptions, RenderData } from './types';
-import { getMatch, parsePath, queryFromString } from './utils';
+import { getMatch, parsePath, queryFromLocation } from './utils';
const { document } = global;
@@ -76,7 +76,7 @@ Link.displayName = 'QueryLink';
*/
export const Location = ({ children }: LocationProps) => {
const location = R.useLocation();
- const { path, singleStory } = queryFromString(location.search);
+ const { path, singleStory } = queryFromLocation(location);
const { viewMode, storyId, refId } = parsePath(path);
return (
diff --git a/code/core/src/router/utils.ts b/code/core/src/router/utils.ts
index 3859668c175e..e0772f45a31c 100644
--- a/code/core/src/router/utils.ts
+++ b/code/core/src/router/utils.ts
@@ -1,10 +1,9 @@
import { once } from '@storybook/core/client-logger';
-import { dequal as deepEqual } from 'dequal';
-import isPlainObject from 'lodash/isPlainObject.js';
+import { isEqual as deepEqual, isPlainObject } from 'es-toolkit';
import memoize from 'memoizerific';
-import type { IStringifyOptions } from 'qs';
-import qs from 'qs';
+import type { Options as QueryOptions } from 'picoquery';
+import { parse, stringify } from 'picoquery';
import { dedent } from 'ts-dedent';
export interface StoryData {
@@ -120,6 +119,9 @@ const validateArgs = (key = '', value: unknown): boolean => {
return false;
};
+// Note this isn't a picoquery serializer because pq will turn any object
+// into a nested key internally. So we need to deal witth things like `Date`
+// up front.
const encodeSpecialValues = (value: unknown): any => {
if (value === undefined) {
return '!undefined';
@@ -143,9 +145,14 @@ const encodeSpecialValues = (value: unknown): any => {
return `!${value}`;
}
+ if (value instanceof Date) {
+ return `!date(${value.toISOString()})`;
+ }
+
if (Array.isArray(value)) {
return value.map(encodeSpecialValues);
}
+
if (isPlainObject(value)) {
return Object.entries(value as Record).reduce(
(acc, [key, val]) => Object.assign(acc, { [key]: encodeSpecialValues(val) }),
@@ -155,13 +162,27 @@ const encodeSpecialValues = (value: unknown): any => {
return value;
};
-const QS_OPTIONS: IStringifyOptions = {
- encode: false, // we handle URL encoding ourselves
- delimiter: ';', // we don't actually create multiple query params
- allowDots: true, // encode objects using dot notation: obj.key=val
- format: 'RFC1738', // encode spaces using the + sign
- serializeDate: (date: Date) => `!date(${date.toISOString()})`,
+// Replaces some url-encoded characters with their decoded equivalents.
+// The URI RFC specifies these should be encoded, but all browsers will
+// tolerate them being decoded, so we opt to go with it for cleaner looking
+// URIs.
+const decodeKnownQueryChar = (chr: string) => {
+ switch (chr) {
+ case '%20':
+ return '+';
+ case '%5B':
+ return '[';
+ case '%5D':
+ return ']';
+ case '%2C':
+ return ',';
+ case '%3A':
+ return ':';
+ }
+ return chr;
};
+const knownQueryChar = /%[0-9A-F]{2}/g;
+
export const buildArgsParam = (initialArgs: Args | undefined, args: Args): string => {
const update = deepDiff(initialArgs, args);
@@ -181,9 +202,12 @@ export const buildArgsParam = (initialArgs: Args | undefined, args: Args): strin
return acc;
}, {} as Args);
- return qs
- .stringify(encodeSpecialValues(object), QS_OPTIONS)
- .replace(/ /g, '+')
+ return stringify(encodeSpecialValues(object), {
+ delimiter: ';', // we don't actually create multiple query params
+ nesting: true,
+ nestingSyntax: 'js', // encode objects using dot notation: obj.key=val
+ })
+ .replace(knownQueryChar, decodeKnownQueryChar)
.split(';')
.map((part: string) => part.replace('=', ':'))
.join(';');
@@ -193,12 +217,16 @@ interface Query {
[key: string]: any;
}
-export const queryFromString = memoize(1000)(
- (s?: string): Query => (s !== undefined ? qs.parse(s, { ignoreQueryPrefix: true }) : {})
-);
-export const queryFromLocation = (location: Partial) => queryFromString(location.search);
-export const stringifyQuery = (query: Query) =>
- qs.stringify(query, { addQueryPrefix: true, encode: false });
+const queryFromString = memoize(1000)((s?: string): Query => (s !== undefined ? parse(s) : {}));
+
+export const queryFromLocation = (location: Partial) => {
+ return queryFromString(location.search ? location.search.slice(1) : '');
+};
+
+export const stringifyQuery = (query: Query) => {
+ const queryStr = stringify(query);
+ return queryStr ? '?' + queryStr : '';
+};
type Match = { path: string };
diff --git a/code/core/src/server-errors.ts b/code/core/src/server-errors.ts
index 7160087cdfb6..5df2a3df9667 100644
--- a/code/core/src/server-errors.ts
+++ b/code/core/src/server-errors.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import { StorybookError } from './storybook-error';
@@ -349,21 +349,21 @@ export class MainFileESMOnlyImportError extends StorybookError {
];
if (data.line) {
message.push(
- chalk.white(
- `In your ${chalk.yellow(data.location)} file, line ${chalk.bold.cyan(
- data.num
+ picocolors.white(
+ `In your ${picocolors.yellow(data.location)} file, line ${picocolors.bold(
+ picocolors.cyan(data.num)
)} threw an error:`
),
- chalk.grey(data.line)
+ picocolors.gray(data.line)
);
}
message.push(
'',
- chalk.white(
- `Convert the static import to a dynamic import ${chalk.underline('where they are used')}.`
+ picocolors.white(
+ `Convert the static import to a dynamic import ${picocolors.underline('where they are used')}.`
),
- chalk.white(`Example:`) + ' ' + chalk.gray(`await import();`),
+ picocolors.white(`Example:`) + ' ' + picocolors.gray(`await import();`),
''
);
@@ -398,7 +398,7 @@ export class MainFileMissingError extends StorybookError {
code: 6,
documentation,
message: dedent`
- No configuration files have been found in your configDir: ${chalk.yellow(data.location)}.
+ No configuration files have been found in your configDir: ${picocolors.yellow(data.location)}.
Storybook needs a "main.js" file, please add it.
${helperMessage}`,
@@ -408,7 +408,7 @@ export class MainFileMissingError extends StorybookError {
export class MainFileEvaluationError extends StorybookError {
constructor(public data: { location: string; error: Error }) {
- const errorText = chalk.white(
+ const errorText = picocolors.white(
(data.error.stack || data.error.message).replaceAll(process.cwd(), '')
);
@@ -416,7 +416,7 @@ export class MainFileEvaluationError extends StorybookError {
category: Category.CORE_SERVER,
code: 7,
message: dedent`
- Storybook couldn't evaluate your ${chalk.yellow(data.location)} file.
+ Storybook couldn't evaluate your ${picocolors.yellow(data.location)} file.
Original error:
${errorText}`,
diff --git a/code/core/src/telemetry/get-monorepo-type.test.ts b/code/core/src/telemetry/get-monorepo-type.test.ts
index 530da9f4abc9..3f8ecfca723a 100644
--- a/code/core/src/telemetry/get-monorepo-type.test.ts
+++ b/code/core/src/telemetry/get-monorepo-type.test.ts
@@ -1,13 +1,12 @@
/* eslint-disable no-underscore-dangle */
+import * as fs from 'node:fs';
import { join } from 'node:path';
import { describe, expect, it, vi } from 'vitest';
-import * as fsExtra from 'fs-extra';
-
import { getMonorepoType, monorepoConfigs } from './get-monorepo-type';
-vi.mock('fs-extra', async () => import('../../../__mocks__/fs-extra'));
+vi.mock('node:fs', async () => import('../../../__mocks__/fs'));
vi.mock('@storybook/core/common', async (importOriginal) => {
return {
@@ -25,7 +24,7 @@ const checkMonorepoType = ({ monorepoConfigFile, isYarnWorkspace = false }: any)
mockFiles[join('root', monorepoConfigFile)] = '{}';
}
- vi.mocked(fsExtra as any).__setMockFiles(mockFiles);
+ vi.mocked(fs as any).__setMockFiles(mockFiles);
return getMonorepoType();
};
diff --git a/code/core/src/telemetry/get-monorepo-type.ts b/code/core/src/telemetry/get-monorepo-type.ts
index 41dcc0087044..143241b0a214 100644
--- a/code/core/src/telemetry/get-monorepo-type.ts
+++ b/code/core/src/telemetry/get-monorepo-type.ts
@@ -1,10 +1,9 @@
+import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import { getProjectRoot } from '@storybook/core/common';
import type { PackageJson } from '@storybook/core/types';
-import { existsSync, readJsonSync } from 'fs-extra';
-
export const monorepoConfigs = {
Nx: 'nx.json',
Turborepo: 'turbo.json',
@@ -36,7 +35,9 @@ export const getMonorepoType = (): MonorepoType => {
return undefined;
}
- const packageJson = readJsonSync(join(projectRootPath, 'package.json')) as PackageJson;
+ const packageJson = JSON.parse(
+ readFileSync(join(projectRootPath, 'package.json'), { encoding: 'utf8' })
+ ) as PackageJson;
if (packageJson?.workspaces) {
return 'Workspaces';
diff --git a/code/core/src/telemetry/notify.ts b/code/core/src/telemetry/notify.ts
index 171d48ae671f..a6cf3e5b8b51 100644
--- a/code/core/src/telemetry/notify.ts
+++ b/code/core/src/telemetry/notify.ts
@@ -1,6 +1,6 @@
import { cache } from '@storybook/core/common';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
const TELEMETRY_KEY_NOTIFY_DATE = 'telemetry-notification-date';
@@ -20,14 +20,14 @@ export const notify = async () => {
logger.log();
logger.log(
- `${chalk.magenta.bold(
- 'attention'
+ `${picocolors.magenta(
+ picocolors.bold('attention')
)} => Storybook now collects completely anonymous telemetry regarding usage.`
);
logger.log(`This information is used to shape Storybook's roadmap and prioritize features.`);
logger.log(
`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:`
);
- logger.log(chalk.cyan('https://storybook.js.org/telemetry'));
+ logger.log(picocolors.cyan('https://storybook.js.org/telemetry'));
logger.log();
};
diff --git a/code/core/src/telemetry/package-json.ts b/code/core/src/telemetry/package-json.ts
index b88ebc237ddc..4104425fc2d0 100644
--- a/code/core/src/telemetry/package-json.ts
+++ b/code/core/src/telemetry/package-json.ts
@@ -1,7 +1,6 @@
+import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
-import { readJson } from 'fs-extra';
-
import type { Dependency } from './types';
export const getActualPackageVersions = async (packages: Record>) => {
@@ -25,6 +24,6 @@ export const getActualPackageJson = async (packageName: string) => {
const resolvedPackageJson = require.resolve(join(packageName, 'package.json'), {
paths: [process.cwd()],
});
- const packageJson = await readJson(resolvedPackageJson);
+ const packageJson = JSON.parse(await readFile(resolvedPackageJson, { encoding: 'utf8' }));
return packageJson;
};
diff --git a/code/core/template/stories/args.stories.ts b/code/core/template/stories/args.stories.ts
index 4f3b766b5a30..4c83a98ca0ae 100644
--- a/code/core/template/stories/args.stories.ts
+++ b/code/core/template/stories/args.stories.ts
@@ -8,7 +8,13 @@ import {
UPDATE_STORY_ARGS,
} from '@storybook/core/core-events';
-import pick from 'lodash/pick';
+function pick(obj, keys) {
+ const result = {};
+ for (const key of keys) {
+ result[key] = obj[key];
+ }
+ return result;
+}
export default {
component: globalThis.Components.Pre,
diff --git a/code/core/template/stories/preview.ts b/code/core/template/stories/preview.ts
index 4cd4c64abff5..bba2716864bc 100644
--- a/code/core/template/stories/preview.ts
+++ b/code/core/template/stories/preview.ts
@@ -30,14 +30,14 @@ export const parameters = {
export const loaders = [async () => ({ projectValue: 2 })];
-export const decorators = [
- (storyFn: PartialStoryFn, context: StoryContext) => {
- if (context.parameters.useProjectDecorator) {
- return storyFn({ args: { ...context.args, text: `project ${context.args.text}` } });
- }
- return storyFn();
- },
-];
+const testProjectDecorator = (storyFn: PartialStoryFn, context: StoryContext) => {
+ if (context.parameters.useProjectDecorator) {
+ return storyFn({ args: { ...context.args, text: `project ${context.args.text}` } });
+ }
+ return storyFn();
+};
+
+export const decorators = [testProjectDecorator];
export const initialGlobals = {
foo: 'fooValue',
diff --git a/code/deprecated/builder-manager/package.json b/code/deprecated/builder-manager/package.json
index 7b388a902566..2241d408f94a 100644
--- a/code/deprecated/builder-manager/package.json
+++ b/code/deprecated/builder-manager/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/builder-manager",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook manager builder",
"keywords": [
"storybook"
diff --git a/code/deprecated/channels/package.json b/code/deprecated/channels/package.json
index d00f6021aed4..989eb9d09902 100644
--- a/code/deprecated/channels/package.json
+++ b/code/deprecated/channels/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/channels",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "",
"keywords": [
"storybook"
diff --git a/code/deprecated/client-logger/package.json b/code/deprecated/client-logger/package.json
index 57c2cf4316d6..defd774b7e98 100644
--- a/code/deprecated/client-logger/package.json
+++ b/code/deprecated/client-logger/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/client-logger",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "",
"keywords": [
"storybook"
diff --git a/code/deprecated/components/package.json b/code/deprecated/components/package.json
index f97b5c2aef14..af12bf460e83 100644
--- a/code/deprecated/components/package.json
+++ b/code/deprecated/components/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/components",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Core Storybook Components",
"keywords": [
"storybook"
diff --git a/code/deprecated/core-common/package.json b/code/deprecated/core-common/package.json
index 89b42c158fe5..cdb957400eee 100644
--- a/code/deprecated/core-common/package.json
+++ b/code/deprecated/core-common/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/core-common",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook framework-agnostic API",
"keywords": [
"storybook"
diff --git a/code/deprecated/core-events/package.json b/code/deprecated/core-events/package.json
index 0a21e7d9e8c4..60df609a13f0 100644
--- a/code/deprecated/core-events/package.json
+++ b/code/deprecated/core-events/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/core-events",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Event names used in storybook core",
"keywords": [
"storybook"
diff --git a/code/deprecated/core-server/package.json b/code/deprecated/core-server/package.json
index 2a09a96d174d..945fee8bde17 100644
--- a/code/deprecated/core-server/package.json
+++ b/code/deprecated/core-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/core-server",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook framework-agnostic API",
"keywords": [
"storybook"
diff --git a/code/deprecated/csf-tools/package.json b/code/deprecated/csf-tools/package.json
index 2703425d8857..fa07f41a9c4f 100644
--- a/code/deprecated/csf-tools/package.json
+++ b/code/deprecated/csf-tools/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/csf-tools",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Parse and manipulate CSF and Storybook config files",
"keywords": [
"storybook"
diff --git a/code/deprecated/docs-tools/package.json b/code/deprecated/docs-tools/package.json
index d952c1789ce4..71a266ec2f14 100644
--- a/code/deprecated/docs-tools/package.json
+++ b/code/deprecated/docs-tools/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/docs-tools",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Shared utility functions for frameworks to implement docs",
"keywords": [
"storybook"
diff --git a/code/deprecated/manager-api/package.json b/code/deprecated/manager-api/package.json
index 65449140e3d5..afbbd9c167a2 100644
--- a/code/deprecated/manager-api/package.json
+++ b/code/deprecated/manager-api/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/manager-api",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Core Storybook Manager API & Context",
"keywords": [
"storybook"
diff --git a/code/deprecated/manager/package.json b/code/deprecated/manager/package.json
index 30ec9ee1459d..f8010ef1954b 100644
--- a/code/deprecated/manager/package.json
+++ b/code/deprecated/manager/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/manager",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Core Storybook UI",
"keywords": [
"storybook"
diff --git a/code/deprecated/node-logger/package.json b/code/deprecated/node-logger/package.json
index d26f3d0fb207..c3728db1b91d 100644
--- a/code/deprecated/node-logger/package.json
+++ b/code/deprecated/node-logger/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/node-logger",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "",
"keywords": [
"storybook"
diff --git a/code/deprecated/preview-api/package.json b/code/deprecated/preview-api/package.json
index 1bcedb15c9ba..9e3d575c3ff4 100644
--- a/code/deprecated/preview-api/package.json
+++ b/code/deprecated/preview-api/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/preview-api",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "",
"keywords": [
"storybook"
diff --git a/code/deprecated/preview/package.json b/code/deprecated/preview/package.json
index 5e293120aa4d..ec568708d0a7 100644
--- a/code/deprecated/preview/package.json
+++ b/code/deprecated/preview/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/preview",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "",
"keywords": [
"storybook"
diff --git a/code/deprecated/router/package.json b/code/deprecated/router/package.json
index 83b20c6f6b31..4100a86b3bfe 100644
--- a/code/deprecated/router/package.json
+++ b/code/deprecated/router/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/router",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Core Storybook Router",
"keywords": [
"storybook"
diff --git a/code/deprecated/telemetry/package.json b/code/deprecated/telemetry/package.json
index 909e98511df3..abdc96c17583 100644
--- a/code/deprecated/telemetry/package.json
+++ b/code/deprecated/telemetry/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/telemetry",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Telemetry logging for crash reports and usage statistics",
"keywords": [
"storybook"
diff --git a/code/deprecated/theming/package.json b/code/deprecated/theming/package.json
index 803249838852..6b48bea4e4b5 100644
--- a/code/deprecated/theming/package.json
+++ b/code/deprecated/theming/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/theming",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Core Storybook Components",
"keywords": [
"storybook"
diff --git a/code/deprecated/types/package.json b/code/deprecated/types/package.json
index da38c9b0cea1..6681ae17c6d6 100644
--- a/code/deprecated/types/package.json
+++ b/code/deprecated/types/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/types",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Core Storybook TS Types",
"keywords": [
"storybook"
diff --git a/code/e2e-tests/addon-docs.spec.ts b/code/e2e-tests/addon-docs.spec.ts
index c7486bc55923..bcf6e6fea9fc 100644
--- a/code/e2e-tests/addon-docs.spec.ts
+++ b/code/e2e-tests/addon-docs.spec.ts
@@ -196,11 +196,7 @@ test.describe('addon-docs', () => {
// Arrange - Setup expectations
let expectedReactVersionRange = /^18/;
- if (
- templateName.includes('preact') ||
- templateName.includes('react-webpack/17') ||
- templateName.includes('react-vite/17')
- ) {
+ if (templateName.includes('react-webpack/17') || templateName.includes('react-vite/17')) {
expectedReactVersionRange = /^17/;
} else if (templateName.includes('react16')) {
expectedReactVersionRange = /^16/;
diff --git a/code/e2e-tests/util.ts b/code/e2e-tests/util.ts
index 00122ef595d2..2137386c6675 100644
--- a/code/e2e-tests/util.ts
+++ b/code/e2e-tests/util.ts
@@ -49,7 +49,7 @@ export class SbPage {
await this.page.waitForURL((url) =>
url.search.includes(
- `path=/${viewMode ?? name === 'docs' ? 'docs' : 'story'}/${titleId}--${storyId}`
+ `path=/${(viewMode ?? name === 'docs') ? 'docs' : 'story'}/${titleId}--${storyId}`
)
);
diff --git a/code/frameworks/angular/package.json b/code/frameworks/angular/package.json
index 1f299fad3865..80fd82ef952a 100644
--- a/code/frameworks/angular/package.json
+++ b/code/frameworks/angular/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/angular",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Angular: Develop Angular components in isolation with hot reloading.",
"keywords": [
"storybook",
diff --git a/code/frameworks/angular/src/client/docs/sourceDecorator.ts b/code/frameworks/angular/src/client/docs/sourceDecorator.ts
index cd3335c8efa2..cbd3f838b340 100644
--- a/code/frameworks/angular/src/client/docs/sourceDecorator.ts
+++ b/code/frameworks/angular/src/client/docs/sourceDecorator.ts
@@ -1,6 +1,6 @@
import { SNIPPET_RENDERED, SourceType } from 'storybook/internal/docs-tools';
import { addons, useEffect } from 'storybook/internal/preview-api';
-import { PartialStoryFn } from 'storybook/internal/types';
+import { ArgsStoryFn, PartialStoryFn } from 'storybook/internal/types';
import { computesTemplateSourceFromComponent } from '../../renderer';
import { AngularRenderer, StoryContext } from '../types';
@@ -32,9 +32,11 @@ export const sourceDecorator = (
return story;
}
const channel = addons.getChannel();
- const { props, template, userDefinedTemplate } = story;
-
+ const { props, userDefinedTemplate } = story;
const { component, argTypes, parameters } = context;
+ const template: string = parameters.docs?.source?.excludeDecorators
+ ? (context.originalStoryFn as ArgsStoryFn)(context.args, context).template
+ : story.template;
let toEmit: string;
diff --git a/code/frameworks/angular/src/server/angular-cli-webpack.js b/code/frameworks/angular/src/server/angular-cli-webpack.js
index 92ed6225d19f..6be86712e5bc 100644
--- a/code/frameworks/angular/src/server/angular-cli-webpack.js
+++ b/code/frameworks/angular/src/server/angular-cli-webpack.js
@@ -68,7 +68,7 @@ exports.getWebpackConfig = async (baseConfig, { builderOptions, builderContext }
outputPath:
typeof builderOptions.outputPath === 'string'
? builderOptions.outputPath
- : builderOptions.outputPath?.base ?? 'noop-out',
+ : (builderOptions.outputPath?.base ?? 'noop-out'),
// Fixed options
optimization: false,
diff --git a/code/frameworks/ember/package.json b/code/frameworks/ember/package.json
index 0ccf6e3e8511..754307bf9578 100644
--- a/code/frameworks/ember/package.json
+++ b/code/frameworks/ember/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/ember",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Ember: Develop Ember Component in isolation with Hot Reloading.",
"homepage": "https://github.com/storybookjs/storybook/tree/next/code/frameworks/ember",
"bugs": {
diff --git a/code/frameworks/experimental-nextjs-vite/package.json b/code/frameworks/experimental-nextjs-vite/package.json
index 18c1ad61eb65..0dfc086ed678 100644
--- a/code/frameworks/experimental-nextjs-vite/package.json
+++ b/code/frameworks/experimental-nextjs-vite/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/experimental-nextjs-vite",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Next.js and Vite",
"keywords": [
"storybook",
@@ -99,7 +99,7 @@
"@storybook/react": "workspace:*",
"@storybook/test": "workspace:*",
"styled-jsx": "5.1.6",
- "vite-plugin-storybook-nextjs": "^1.0.10"
+ "vite-plugin-storybook-nextjs": "^1.0.11"
},
"devDependencies": {
"@types/node": "^18.0.0",
diff --git a/code/frameworks/html-vite/package.json b/code/frameworks/html-vite/package.json
index c96bb225cbb7..cd823b5bee3e 100644
--- a/code/frameworks/html-vite/package.json
+++ b/code/frameworks/html-vite/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/html-vite",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for HTML and Vite: Develop HTML in isolation with Hot Reloading.",
"keywords": [
"storybook"
diff --git a/code/frameworks/html-webpack5/package.json b/code/frameworks/html-webpack5/package.json
index d942efb70359..3d67cc8595ac 100644
--- a/code/frameworks/html-webpack5/package.json
+++ b/code/frameworks/html-webpack5/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/html-webpack5",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.",
"keywords": [
"storybook"
diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json
index 415e57c96964..e11c4b5beb2a 100644
--- a/code/frameworks/nextjs/package.json
+++ b/code/frameworks/nextjs/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/nextjs",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Next.js",
"keywords": [
"storybook",
@@ -148,7 +148,6 @@
"babel-loader": "^9.1.3",
"css-loader": "^6.7.3",
"find-up": "^5.0.0",
- "fs-extra": "^11.1.0",
"image-size": "^1.0.0",
"loader-utils": "^3.2.1",
"node-polyfill-webpack-plugin": "^2.0.1",
@@ -157,7 +156,7 @@
"postcss-loader": "^8.1.1",
"react-refresh": "^0.14.0",
"resolve-url-loader": "^5.0.0",
- "sass-loader": "^12.4.0",
+ "sass-loader": "^13.2.0",
"semver": "^7.3.5",
"style-loader": "^3.3.1",
"styled-jsx": "^5.1.6",
diff --git a/code/frameworks/nextjs/src/config/webpack.ts b/code/frameworks/nextjs/src/config/webpack.ts
index 3860207e124e..a0ea2d47bded 100644
--- a/code/frameworks/nextjs/src/config/webpack.ts
+++ b/code/frameworks/nextjs/src/config/webpack.ts
@@ -2,7 +2,7 @@ import type { NextConfig } from 'next';
import type { Configuration as WebpackConfig } from 'webpack';
import { DefinePlugin } from 'webpack';
-import { addScopedAlias, resolveNextConfig } from '../utils';
+import { addScopedAlias, resolveNextConfig, setAlias } from '../utils';
const tryResolve = (path: string) => {
try {
@@ -22,12 +22,32 @@ export const configureConfig = async ({
const nextConfig = await resolveNextConfig({ nextConfigPath });
addScopedAlias(baseConfig, 'next/config');
+
+ // @ts-expect-error We know that alias is an object
+ if (baseConfig.resolve?.alias?.['react-dom']) {
+ // Removing the alias to react-dom to avoid conflicts with the alias we are setting
+ // because the react-dom alias is an exact match and we need to alias separate parts of react-dom
+ // in different places
+ // @ts-expect-error We know that alias is an object
+ delete baseConfig.resolve.alias?.['react-dom'];
+ }
+
if (tryResolve('next/dist/compiled/react')) {
addScopedAlias(baseConfig, 'react', 'next/dist/compiled/react');
}
+ if (tryResolve('next/dist/compiled/react-dom/cjs/react-dom-test-utils.production.js')) {
+ setAlias(
+ baseConfig,
+ 'react-dom/test-utils',
+ 'next/dist/compiled/react-dom/cjs/react-dom-test-utils.production.js'
+ );
+ }
if (tryResolve('next/dist/compiled/react-dom')) {
- addScopedAlias(baseConfig, 'react-dom', 'next/dist/compiled/react-dom');
+ setAlias(baseConfig, 'react-dom$', 'next/dist/compiled/react-dom');
+ setAlias(baseConfig, 'react-dom/client', 'next/dist/compiled/react-dom/client');
+ setAlias(baseConfig, 'react-dom/server', 'next/dist/compiled/react-dom/server');
}
+
setupRuntimeConfig(baseConfig, nextConfig);
return nextConfig;
diff --git a/code/frameworks/nextjs/src/utils.ts b/code/frameworks/nextjs/src/utils.ts
index 9c8abc6c88c8..198917513166 100644
--- a/code/frameworks/nextjs/src/utils.ts
+++ b/code/frameworks/nextjs/src/utils.ts
@@ -27,23 +27,27 @@ export const resolveNextConfig = async ({
return loadConfig(PHASE_DEVELOPMENT_SERVER, dir, undefined);
};
-// This is to help the addon in development
-// Without it, webpack resolves packages in its node_modules instead of the example's node_modules
-export const addScopedAlias = (baseConfig: WebpackConfig, name: string, alias?: string): void => {
+export function setAlias(baseConfig: WebpackConfig, name: string, alias: string) {
baseConfig.resolve ??= {};
baseConfig.resolve.alias ??= {};
const aliasConfig = baseConfig.resolve.alias;
- const scopedAlias = scopedResolve(`${alias ?? name}`);
-
if (Array.isArray(aliasConfig)) {
aliasConfig.push({
name,
- alias: scopedAlias,
+ alias,
});
} else {
- aliasConfig[name] = scopedAlias;
+ aliasConfig[name] = alias;
}
+}
+
+// This is to help the addon in development
+// Without it, webpack resolves packages in its node_modules instead of the example's node_modules
+export const addScopedAlias = (baseConfig: WebpackConfig, name: string, alias?: string): void => {
+ const scopedAlias = scopedResolve(`${alias ?? name}`);
+
+ setAlias(baseConfig, name, scopedAlias);
};
/**
@@ -64,7 +68,7 @@ export const scopedResolve = (id: string): string => {
let scopedModulePath;
try {
- // TODO: Remove in next major release (SB 8.0) and use the statement in the catch block per default instead
+ // TODO: Remove in next major release (SB 9.0) and use the statement in the catch block per default instead
scopedModulePath = require.resolve(id, { paths: [resolve()] });
} catch (e) {
scopedModulePath = require.resolve(id);
diff --git a/code/frameworks/preact-vite/package.json b/code/frameworks/preact-vite/package.json
index 7901cc54ddb1..c76fddce92a5 100644
--- a/code/frameworks/preact-vite/package.json
+++ b/code/frameworks/preact-vite/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/preact-vite",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Preact and Vite: Develop Preact components in isolation with Hot Reloading.",
"keywords": [
"storybook"
diff --git a/code/frameworks/preact-webpack5/package.json b/code/frameworks/preact-webpack5/package.json
index 5924abce7021..95f7226d83e9 100644
--- a/code/frameworks/preact-webpack5/package.json
+++ b/code/frameworks/preact-webpack5/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/preact-webpack5",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Preact: Develop Preact Component in isolation.",
"keywords": [
"storybook"
diff --git a/code/frameworks/react-vite/package.json b/code/frameworks/react-vite/package.json
index ffcab019deb2..235ed5c9224c 100644
--- a/code/frameworks/react-vite/package.json
+++ b/code/frameworks/react-vite/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/react-vite",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for React and Vite: Develop React components in isolation with Hot Reloading.",
"keywords": [
"storybook"
@@ -47,7 +47,7 @@
"prep": "jiti ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
- "@joshwooding/vite-plugin-react-docgen-typescript": "0.3.1",
+ "@joshwooding/vite-plugin-react-docgen-typescript": "0.3.0",
"@rollup/pluginutils": "^5.0.2",
"@storybook/builder-vite": "workspace:*",
"@storybook/react": "workspace:*",
diff --git a/code/frameworks/react-webpack5/package.json b/code/frameworks/react-webpack5/package.json
index 8fa2d42ff357..927e1ea8d4b2 100644
--- a/code/frameworks/react-webpack5/package.json
+++ b/code/frameworks/react-webpack5/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/react-webpack5",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for React: Develop React Component in isolation with Hot Reloading.",
"keywords": [
"storybook"
diff --git a/code/frameworks/server-webpack5/package.json b/code/frameworks/server-webpack5/package.json
index b77941e0f4e7..73a6ad914d9d 100644
--- a/code/frameworks/server-webpack5/package.json
+++ b/code/frameworks/server-webpack5/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/server-webpack5",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Server: View HTML snippets from a server in isolation with Hot Reloading.",
"keywords": [
"storybook"
diff --git a/code/frameworks/svelte-vite/package.json b/code/frameworks/svelte-vite/package.json
index f2b87e3bc268..e7e036eba116 100644
--- a/code/frameworks/svelte-vite/package.json
+++ b/code/frameworks/svelte-vite/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/svelte-vite",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Svelte and Vite: Develop Svelte components in isolation with Hot Reloading.",
"keywords": [
"storybook"
diff --git a/code/frameworks/svelte-webpack5/package.json b/code/frameworks/svelte-webpack5/package.json
index f368810a808b..1ced4ec74704 100644
--- a/code/frameworks/svelte-webpack5/package.json
+++ b/code/frameworks/svelte-webpack5/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/svelte-webpack5",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Svelte: Develop Svelte Component in isolation with Hot Reloading.",
"keywords": [
"storybook"
diff --git a/code/frameworks/sveltekit/package.json b/code/frameworks/sveltekit/package.json
index f76c5e909f4e..5b669a08ff31 100644
--- a/code/frameworks/sveltekit/package.json
+++ b/code/frameworks/sveltekit/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/sveltekit",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for SvelteKit",
"keywords": [
"storybook",
diff --git a/code/frameworks/sveltekit/src/preview.ts b/code/frameworks/sveltekit/src/preview.ts
index f93c06862c29..6eb8a816dd12 100644
--- a/code/frameworks/sveltekit/src/preview.ts
+++ b/code/frameworks/sveltekit/src/preview.ts
@@ -15,125 +15,125 @@ const normalizeHrefConfig = (hrefConfig: HrefConfig): NormalizedHrefConfig => {
return hrefConfig;
};
-export const decorators: Decorator[] = [
- (Story, ctx) => {
- const svelteKitParameters: SvelteKitParameters = ctx.parameters?.sveltekit_experimental ?? {};
- setPage(svelteKitParameters?.stores?.page);
- setNavigating(svelteKitParameters?.stores?.navigating);
- setUpdated(svelteKitParameters?.stores?.updated);
- setAfterNavigateArgument(svelteKitParameters?.navigation?.afterNavigate);
+const svelteKitMocksDecorator: Decorator = (Story, ctx) => {
+ const svelteKitParameters: SvelteKitParameters = ctx.parameters?.sveltekit_experimental ?? {};
+ setPage(svelteKitParameters?.stores?.page);
+ setNavigating(svelteKitParameters?.stores?.navigating);
+ setUpdated(svelteKitParameters?.stores?.updated);
+ setAfterNavigateArgument(svelteKitParameters?.navigation?.afterNavigate);
- onMount(() => {
- const globalClickListener = (e: MouseEvent) => {
- // we add a global click event listener and we check if there's a link in the composedPath
- const path = e.composedPath();
- const element = path.findLast((el) => el instanceof HTMLElement && el.tagName === 'A');
- if (element && element instanceof HTMLAnchorElement) {
- // if the element is an a-tag we get the href of the element
- // and compare it to the hrefs-parameter set by the user
- const to = element.getAttribute('href');
- if (!to) {
- return;
- }
- e.preventDefault();
- const defaultActionCallback = () => action('navigate')(to, e);
- if (!svelteKitParameters.hrefs) {
- defaultActionCallback();
- return;
- }
-
- let callDefaultCallback = true;
- // we loop over every href set by the user and check if the href matches
- // if it does we call the callback provided by the user and disable the default callback
- Object.entries(svelteKitParameters.hrefs).forEach(([href, hrefConfig]) => {
- const { callback, asRegex } = normalizeHrefConfig(hrefConfig);
- const isMatch = asRegex ? new RegExp(href).test(to) : to === href;
- if (isMatch) {
- callDefaultCallback = false;
- callback?.(to, e);
- }
- });
- if (callDefaultCallback) {
- defaultActionCallback();
- }
+ onMount(() => {
+ const globalClickListener = (e: MouseEvent) => {
+ // we add a global click event listener and we check if there's a link in the composedPath
+ const path = e.composedPath();
+ const element = path.findLast((el) => el instanceof HTMLElement && el.tagName === 'A');
+ if (element && element instanceof HTMLAnchorElement) {
+ // if the element is an a-tag we get the href of the element
+ // and compare it to the hrefs-parameter set by the user
+ const to = element.getAttribute('href');
+ if (!to) {
+ return;
+ }
+ e.preventDefault();
+ const defaultActionCallback = () => action('navigate')(to, e);
+ if (!svelteKitParameters.hrefs) {
+ defaultActionCallback();
+ return;
}
- };
-
- /**
- * Function that create and add listeners for the event that are emitted by the mocked
- * functions. The event name is based on the function name
- *
- * Eg. storybook:goto, storybook:invalidateAll
- *
- * @param baseModule The base module where the function lives (navigation|forms)
- * @param functions The list of functions in that module that emit events
- * @param {boolean} [defaultToAction] The list of functions in that module that emit events
- * @returns A function to remove all the listener added
- */
- function createListeners(
- baseModule: keyof SvelteKitParameters,
- functions: string[],
- defaultToAction?: boolean
- ) {
- // the array of every added listener, we can use this in the return function
- // to clean them
- const toRemove: Array<{
- eventType: string;
- listener: (event: { detail: any[] }) => void;
- }> = [];
- functions.forEach((func) => {
- // we loop over every function and check if the user actually passed
- // a function in sveltekit_experimental[baseModule][func] eg. sveltekit_experimental.navigation.goto
- const hasFunction =
- (svelteKitParameters as any)[baseModule]?.[func] &&
- (svelteKitParameters as any)[baseModule][func] instanceof Function;
- // if we default to an action we still add the listener (this will be the case for goto, invalidate, invalidateAll)
- if (hasFunction || defaultToAction) {
- // we create the listener that will just get the detail array from the custom element
- // and call the user provided function spreading this args in...this will basically call
- // the function that the user provide with the same arguments the function is invoked to
- // eg. if it calls goto("/my-route") inside the component the function sveltekit_experimental.navigation.goto
- // it provided to storybook will be called with "/my-route"
- const listener = ({ detail = [] as any[] }) => {
- const args = Array.isArray(detail) ? detail : [];
- // if it has a function in the parameters we call that function
- // otherwise we invoke the action
- const fnToCall = hasFunction
- ? (svelteKitParameters as any)[baseModule][func]
- : action(func);
- fnToCall(...args);
- };
- const eventType = `storybook:${func}`;
- toRemove.push({ eventType, listener });
- // add the listener to window
- (window.addEventListener as any)(eventType, listener);
+ let callDefaultCallback = true;
+ // we loop over every href set by the user and check if the href matches
+ // if it does we call the callback provided by the user and disable the default callback
+ Object.entries(svelteKitParameters.hrefs).forEach(([href, hrefConfig]) => {
+ const { callback, asRegex } = normalizeHrefConfig(hrefConfig);
+ const isMatch = asRegex ? new RegExp(href).test(to) : to === href;
+ if (isMatch) {
+ callDefaultCallback = false;
+ callback?.(to, e);
}
});
- return () => {
- // loop over every listener added and remove them
- toRemove.forEach(({ eventType, listener }) => {
- // @ts-expect-error apparently you can't remove a custom listener to the window with TS
- window.removeEventListener(eventType, listener);
- });
- };
+ if (callDefaultCallback) {
+ defaultActionCallback();
+ }
}
+ };
- const removeNavigationListeners = createListeners(
- 'navigation',
- ['goto', 'invalidate', 'invalidateAll', 'pushState', 'replaceState'],
- true
- );
- const removeFormsListeners = createListeners('forms', ['enhance']);
- window.addEventListener('click', globalClickListener);
+ /**
+ * Function that create and add listeners for the event that are emitted by the mocked
+ * functions. The event name is based on the function name
+ *
+ * Eg. storybook:goto, storybook:invalidateAll
+ *
+ * @param baseModule The base module where the function lives (navigation|forms)
+ * @param functions The list of functions in that module that emit events
+ * @param {boolean} [defaultToAction] The list of functions in that module that emit events
+ * @returns A function to remove all the listener added
+ */
+ function createListeners(
+ baseModule: keyof SvelteKitParameters,
+ functions: string[],
+ defaultToAction?: boolean
+ ) {
+ // the array of every added listener, we can use this in the return function
+ // to clean them
+ const toRemove: Array<{
+ eventType: string;
+ listener: (event: { detail: any[] }) => void;
+ }> = [];
+ functions.forEach((func) => {
+ // we loop over every function and check if the user actually passed
+ // a function in sveltekit_experimental[baseModule][func] eg. sveltekit_experimental.navigation.goto
+ const hasFunction =
+ (svelteKitParameters as any)[baseModule]?.[func] &&
+ (svelteKitParameters as any)[baseModule][func] instanceof Function;
+ // if we default to an action we still add the listener (this will be the case for goto, invalidate, invalidateAll)
+ if (hasFunction || defaultToAction) {
+ // we create the listener that will just get the detail array from the custom element
+ // and call the user provided function spreading this args in...this will basically call
+ // the function that the user provide with the same arguments the function is invoked to
+ // eg. if it calls goto("/my-route") inside the component the function sveltekit_experimental.navigation.goto
+ // it provided to storybook will be called with "/my-route"
+ const listener = ({ detail = [] as any[] }) => {
+ const args = Array.isArray(detail) ? detail : [];
+ // if it has a function in the parameters we call that function
+ // otherwise we invoke the action
+ const fnToCall = hasFunction
+ ? (svelteKitParameters as any)[baseModule][func]
+ : action(func);
+ fnToCall(...args);
+ };
+ const eventType = `storybook:${func}`;
+ toRemove.push({ eventType, listener });
+ // add the listener to window
+ (window.addEventListener as any)(eventType, listener);
+ }
+ });
return () => {
- window.removeEventListener('click', globalClickListener);
- removeNavigationListeners();
- removeFormsListeners();
+ // loop over every listener added and remove them
+ toRemove.forEach(({ eventType, listener }) => {
+ // @ts-expect-error apparently you can't remove a custom listener to the window with TS
+ window.removeEventListener(eventType, listener);
+ });
};
- });
+ }
+
+ const removeNavigationListeners = createListeners(
+ 'navigation',
+ ['goto', 'invalidate', 'invalidateAll', 'pushState', 'replaceState'],
+ true
+ );
+ const removeFormsListeners = createListeners('forms', ['enhance']);
+ window.addEventListener('click', globalClickListener);
+
+ return () => {
+ window.removeEventListener('click', globalClickListener);
+ removeNavigationListeners();
+ removeFormsListeners();
+ };
+ });
+
+ return Story();
+};
- return Story();
- },
-];
+export const decorators: Decorator[] = [svelteKitMocksDecorator];
diff --git a/code/frameworks/vue3-vite/package.json b/code/frameworks/vue3-vite/package.json
index 081720b77961..4583e8580935 100644
--- a/code/frameworks/vue3-vite/package.json
+++ b/code/frameworks/vue3-vite/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/vue3-vite",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Vue3 and Vite: Develop Vue3 components in isolation with Hot Reloading.",
"keywords": [
"storybook"
diff --git a/code/frameworks/vue3-vite/src/plugins/vue-component-meta.ts b/code/frameworks/vue3-vite/src/plugins/vue-component-meta.ts
index bb06a958a771..aea3d269a1e3 100644
--- a/code/frameworks/vue3-vite/src/plugins/vue-component-meta.ts
+++ b/code/frameworks/vue3-vite/src/plugins/vue-component-meta.ts
@@ -26,7 +26,7 @@ export async function vueComponentMeta(tsconfigPath = 'tsconfig.json'): Promise<
// exclude stories, virtual modules and storybook internals
const exclude =
- /\.stories\.(ts|tsx|js|jsx)$|^\/virtual:|^\/sb-preview\/|\.storybook\/.*\.(ts|js)$/;
+ /\.stories\.(ts|tsx|js|jsx)$|^\0\/virtual:|^\/virtual:|^\/sb-preview\/|\.storybook\/.*\.(ts|js)$/;
const include = /\.(vue|ts|js|tsx|jsx)$/;
const filter = createFilter(include, exclude);
@@ -287,5 +287,11 @@ function removeNestedSchemas(schema: PropertyMetaSchema) {
if (typeof schema !== 'object') {
return;
}
+ if (schema.kind === 'enum') {
+ // for enum types, we do not want to remove the schemas because otherwise the controls will be missing
+ // instead we remove the nested schemas for the enum entries to prevent out of memory errors for types like "HTMLElement | MouseEvent"
+ schema.schema?.forEach((enumSchema) => removeNestedSchemas(enumSchema));
+ return;
+ }
delete schema.schema;
}
diff --git a/code/frameworks/vue3-vite/src/preset.ts b/code/frameworks/vue3-vite/src/preset.ts
index 574ab0a04ad3..8bbaf394d442 100644
--- a/code/frameworks/vue3-vite/src/preset.ts
+++ b/code/frameworks/vue3-vite/src/preset.ts
@@ -22,7 +22,7 @@ export const viteFinal: StorybookConfig['viteFinal'] = async (config, options) =
const framework = await options.presets.apply('framework');
const frameworkOptions: FrameworkOptions =
- typeof framework === 'string' ? {} : framework.options ?? {};
+ typeof framework === 'string' ? {} : (framework.options ?? {});
const docgen = resolveDocgenOptions(frameworkOptions.docgen);
diff --git a/code/frameworks/vue3-webpack5/package.json b/code/frameworks/vue3-webpack5/package.json
index 965b4020a46f..fc3c1593f73f 100644
--- a/code/frameworks/vue3-webpack5/package.json
+++ b/code/frameworks/vue3-webpack5/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/vue3-webpack5",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Vue 3: Develop Vue 3 Components in isolation with Hot Reloading.",
"keywords": [
"storybook"
diff --git a/code/frameworks/web-components-vite/package.json b/code/frameworks/web-components-vite/package.json
index 64ded4415b44..045c9adc59b9 100644
--- a/code/frameworks/web-components-vite/package.json
+++ b/code/frameworks/web-components-vite/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/web-components-vite",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for web-components and Vite: Develop Web Components in isolation with Hot Reloading.",
"keywords": [
"storybook"
diff --git a/code/frameworks/web-components-webpack5/package.json b/code/frameworks/web-components-webpack5/package.json
index b3fe2ca851a9..64f70871f847 100644
--- a/code/frameworks/web-components-webpack5/package.json
+++ b/code/frameworks/web-components-webpack5/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/web-components-webpack5",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for web-components: View web components snippets in isolation with Hot Reloading.",
"keywords": [
"lit",
diff --git a/code/lib/blocks/package.json b/code/lib/blocks/package.json
index c6f1f96d7fae..8232f41a52c3 100644
--- a/code/lib/blocks/package.json
+++ b/code/lib/blocks/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/blocks",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook Doc Blocks",
"keywords": [
"storybook"
@@ -46,10 +46,8 @@
"@storybook/csf": "^0.1.11",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.10",
- "@types/lodash": "^4.14.167",
"color-convert": "^2.0.1",
"dequal": "^2.0.2",
- "lodash": "^4.17.21",
"markdown-to-jsx": "^7.4.5",
"memoizerific": "^1.11.3",
"polished": "^4.2.2",
@@ -63,6 +61,7 @@
"@storybook/react": "workspace:*",
"@storybook/test": "workspace:*",
"@types/color-convert": "^2.0.0",
+ "es-toolkit": "^1.21.0",
"tocbot": "^4.20.1"
},
"peerDependencies": {
diff --git a/code/lib/blocks/src/components/ArgsTable/ArgValue.tsx b/code/lib/blocks/src/components/ArgsTable/ArgValue.tsx
index 0df1accdada6..3b97b757e490 100644
--- a/code/lib/blocks/src/components/ArgsTable/ArgValue.tsx
+++ b/code/lib/blocks/src/components/ArgsTable/ArgValue.tsx
@@ -6,7 +6,7 @@ import { styled } from 'storybook/internal/theming';
import { ChevronSmallDownIcon, ChevronSmallUpIcon } from '@storybook/icons';
-import uniq from 'lodash/uniq.js';
+import { uniq } from 'es-toolkit/compat';
import memoize from 'memoizerific';
import type { PropSummaryValue } from './types';
diff --git a/code/lib/blocks/src/components/ArgsTable/ArgsTable.tsx b/code/lib/blocks/src/components/ArgsTable/ArgsTable.tsx
index e0dab71a171c..5a1c71b36a2a 100644
--- a/code/lib/blocks/src/components/ArgsTable/ArgsTable.tsx
+++ b/code/lib/blocks/src/components/ArgsTable/ArgsTable.tsx
@@ -8,7 +8,7 @@ import { styled } from 'storybook/internal/theming';
import { includeConditionalArg } from '@storybook/csf';
import { DocumentIcon, UndoIcon } from '@storybook/icons';
-import pickBy from 'lodash/pickBy.js';
+import { pickBy } from 'es-toolkit/compat';
import { transparentize } from 'polished';
import { EmptyBlock } from '..';
@@ -346,7 +346,7 @@ export const ArgsTable: FC = (props) => {
const { rows, args, globals } = 'rows' in props && props;
const groups = groupRows(
pickBy(
- rows,
+ rows || {},
(row) => !row?.table?.disable && safeIncludeConditionalArg(row, args || {}, globals || {})
),
sort
diff --git a/code/lib/blocks/src/controls/Color.stories.tsx b/code/lib/blocks/src/controls/Color.stories.tsx
index f12a0f05f80b..8b77cff62f0c 100644
--- a/code/lib/blocks/src/controls/Color.stories.tsx
+++ b/code/lib/blocks/src/controls/Color.stories.tsx
@@ -40,9 +40,9 @@ export const WithPresetColors: Story = {
{ color: '#ff4785', title: 'Coral' },
{ color: '#1EA7FD', title: 'Ocean' },
{ color: 'rgb(252, 82, 31)', title: 'Orange' },
- { color: 'RGBA(255, 174, 0, 0.5)', title: 'Gold' },
+ { color: 'rgba(255, 174, 0, 0.5)', title: 'Gold' },
{ color: 'hsl(101, 52%, 49%)', title: 'Green' },
- { color: 'HSLA(179,65%,53%,0.5)', title: 'Seafoam' },
+ { color: 'hsla(179,65%,53%,0.5)', title: 'Seafoam' },
{ color: '#6F2CAC', title: 'Purple' },
{ color: '#2A0481', title: 'Ultraviolet' },
{ color: 'black' },
@@ -58,7 +58,7 @@ export const WithPresetColors: Story = {
'#fe4a49',
'#FED766',
'rgba(0, 159, 183, 1)',
- 'HSLA(240,11%,91%,0.5)',
+ 'hsla(240,11%,91%,0.5)',
'slategray',
],
},
diff --git a/code/lib/blocks/src/controls/Color.tsx b/code/lib/blocks/src/controls/Color.tsx
index 2feafd97d148..82b95aa95f22 100644
--- a/code/lib/blocks/src/controls/Color.tsx
+++ b/code/lib/blocks/src/controls/Color.tsx
@@ -7,7 +7,7 @@ import { styled } from 'storybook/internal/theming';
import { MarkupIcon } from '@storybook/icons';
import convert from 'color-convert';
-import debounce from 'lodash/debounce.js';
+import { debounce } from 'es-toolkit/compat';
import { HexColorPicker, HslaStringColorPicker, RgbaStringColorPicker } from 'react-colorful';
import { getControlId } from './helpers';
diff --git a/code/lib/blocks/src/controls/Object.tsx b/code/lib/blocks/src/controls/Object.tsx
index 50b5b9891d5e..382dca01a57b 100644
--- a/code/lib/blocks/src/controls/Object.tsx
+++ b/code/lib/blocks/src/controls/Object.tsx
@@ -7,7 +7,7 @@ import { type Theme, styled, useTheme } from 'storybook/internal/theming';
import { global } from '@storybook/global';
import { AddIcon, EyeCloseIcon, EyeIcon, SubtractIcon } from '@storybook/icons';
-import cloneDeep from 'lodash/cloneDeep.js';
+import { cloneDeep } from 'es-toolkit/compat';
import { getControlId, getControlSetterButtonId } from './helpers';
import { JsonTree } from './react-editable-json-tree';
diff --git a/code/lib/cli-sb/package.json b/code/lib/cli-sb/package.json
index 20382e2fec72..0493bd1cfe04 100644
--- a/code/lib/cli-sb/package.json
+++ b/code/lib/cli-sb/package.json
@@ -1,6 +1,6 @@
{
"name": "sb",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook CLI",
"keywords": [
"storybook"
diff --git a/code/lib/cli-sb/project.json b/code/lib/cli-sb/project.json
index efaba5c541b2..25f5e9e0ebb9 100644
--- a/code/lib/cli-sb/project.json
+++ b/code/lib/cli-sb/project.json
@@ -1,5 +1,5 @@
{
- "name": "cli-sb",
+ "name": "sb",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {}
diff --git a/code/lib/cli-storybook/package.json b/code/lib/cli-storybook/package.json
index d37d1aaf529e..02be45508db0 100644
--- a/code/lib/cli-storybook/package.json
+++ b/code/lib/cli-storybook/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/cli",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook CLI",
"keywords": [
"storybook"
@@ -44,14 +44,12 @@
"@babel/types": "^7.24.0",
"@storybook/codemod": "workspace:*",
"@types/semver": "^7.3.4",
- "chalk": "^4.1.0",
"commander": "^12.1.0",
"create-storybook": "workspace:*",
"cross-spawn": "^7.0.3",
"envinfo": "^7.7.3",
"fd-package-json": "^1.2.0",
"find-up": "^5.0.0",
- "fs-extra": "^11.1.0",
"giget": "^1.0.0",
"glob": "^10.0.0",
"globby": "^14.0.1",
@@ -67,6 +65,7 @@
"@types/cross-spawn": "^6.0.2",
"@types/prompts": "^2.0.9",
"boxen": "^7.1.1",
+ "picocolors": "^1.1.0",
"slash": "^5.0.0",
"strip-ansi": "^7.1.0",
"typescript": "^5.3.2"
diff --git a/code/lib/cli-storybook/project.json b/code/lib/cli-storybook/project.json
index 229b547bb854..82cebd8431b3 100644
--- a/code/lib/cli-storybook/project.json
+++ b/code/lib/cli-storybook/project.json
@@ -1,5 +1,5 @@
{
- "name": "cli-storybook",
+ "name": "cli",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {
diff --git a/code/lib/cli-storybook/src/autoblock/block-dependencies-versions.ts b/code/lib/cli-storybook/src/autoblock/block-dependencies-versions.ts
index 49ebdbb54318..1bc9bdc7fca6 100644
--- a/code/lib/cli-storybook/src/autoblock/block-dependencies-versions.ts
+++ b/code/lib/cli-storybook/src/autoblock/block-dependencies-versions.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { lt } from 'semver';
import { dedent } from 'ts-dedent';
@@ -52,7 +52,7 @@ export const blocker = createBlocker({
return dedent`
Support for react-script < 5.0.0 has been removed.
Please see the migration guide for more information:
- ${chalk.yellow(
+ ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#create-react-app-dropped-cra4-support'
)}
@@ -62,7 +62,7 @@ export const blocker = createBlocker({
return dedent`
Support for Vue 2 has been removed.
Please see the migration guide for more information:
- ${chalk.yellow('https://v3-migration.vuejs.org/')}
+ ${picocolors.yellow('https://v3-migration.vuejs.org/')}
Please upgrade to the latest version of Vue.
`;
@@ -70,7 +70,7 @@ export const blocker = createBlocker({
return dedent`
Support for Angular < 15 has been removed.
Please see the migration guide for more information:
- ${chalk.yellow('https://angular.io/guide/update-to-version-15')}
+ ${picocolors.yellow('https://angular.io/guide/update-to-version-15')}
Please upgrade to the latest version of Angular.
`;
@@ -78,7 +78,7 @@ export const blocker = createBlocker({
return dedent`
Support for Next.js < 13.5 has been removed.
Please see the migration guide for more information:
- ${chalk.yellow(
+ ${picocolors.yellow(
'https://nextjs.org/docs/pages/building-your-application/upgrading/version-13'
)}
diff --git a/code/lib/cli-storybook/src/autoblock/block-node-version.ts b/code/lib/cli-storybook/src/autoblock/block-node-version.ts
index 6c4340c38dad..850b4b460d3a 100644
--- a/code/lib/cli-storybook/src/autoblock/block-node-version.ts
+++ b/code/lib/cli-storybook/src/autoblock/block-node-version.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { lt } from 'semver';
import { dedent } from 'ts-dedent';
@@ -18,7 +18,7 @@ export const blocker = createBlocker({
We've detected you're using Node.js v${data.nodeVersion}.
Storybook needs Node.js 18 or higher.
- ${chalk.yellow('https://nodejs.org/en/download')}
+ ${picocolors.yellow('https://nodejs.org/en/download')}
`;
},
});
diff --git a/code/lib/cli-storybook/src/autoblock/block-storystorev6.ts b/code/lib/cli-storybook/src/autoblock/block-storystorev6.ts
index 7904c39ae33f..9d28b8d15a08 100644
--- a/code/lib/cli-storybook/src/autoblock/block-storystorev6.ts
+++ b/code/lib/cli-storybook/src/autoblock/block-storystorev6.ts
@@ -1,6 +1,6 @@
import type { StorybookConfigRaw } from 'storybook/internal/types';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import { createBlocker } from './types';
@@ -22,7 +22,7 @@ export const blocker = createBlocker({
StoryStoreV7 feature must be removed from your Storybook configuration.
This feature was removed in Storybook 8.0.0.
Please see the migration guide for more information:
- ${chalk.yellow(
+ ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#storystorev6-and-storiesof-is-deprecated'
)}
@@ -30,7 +30,7 @@ export const blocker = createBlocker({
export default = {
features: {
- ${chalk.cyan(`storyStoreV7: false`)}, <--- ${chalk.bold('remove this line')}
+ ${picocolors.cyan(`storyStoreV7: false`)}, <--- ${picocolors.bold('remove this line')}
},
};
diff --git a/code/lib/cli-storybook/src/autoblock/index.ts b/code/lib/cli-storybook/src/autoblock/index.ts
index a09d1b60477f..5e61a8042582 100644
--- a/code/lib/cli-storybook/src/autoblock/index.ts
+++ b/code/lib/cli-storybook/src/autoblock/index.ts
@@ -1,7 +1,7 @@
import { logger } from 'storybook/internal/node-logger';
import boxen from 'boxen';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import type { AutoblockOptions, Blocker } from './types';
@@ -49,7 +49,9 @@ export const autoblock = async (
if (faults.length > 0) {
const messages = {
welcome: `Storybook has found potential blockers in your project that need to be resolved before upgrading:`,
- reminder: chalk.yellow('Fix the above issues and try running the upgrade command again.'),
+ reminder: picocolors.yellow(
+ 'Fix the above issues and try running the upgrade command again.'
+ ),
};
const borderColor = '#FC521F';
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/__test__/main-config-without-wrappers.js b/code/lib/cli-storybook/src/automigrate/fixes/__test__/main-config-without-wrappers.js
index 0979ef60ff60..e88bf7243994 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/__test__/main-config-without-wrappers.js
+++ b/code/lib/cli-storybook/src/automigrate/fixes/__test__/main-config-without-wrappers.js
@@ -1,10 +1,6 @@
const config = {
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
- addons: [
- '@storybook/addon-links',
- '@storybook/addon-essentials',
- '@storybook/addon-interactions',
- ],
+ addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/angular',
options: {},
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/addon-postcss.ts b/code/lib/cli-storybook/src/automigrate/fixes/addon-postcss.ts
index 130ac71d38d2..d8bd135da2f9 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/addon-postcss.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/addon-postcss.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import { getAddonNames } from '../helpers/mainConfigFile';
@@ -28,13 +28,13 @@ export const addonPostCSS: Fix = {
prompt() {
return dedent`
- ${chalk.bold(
+ ${picocolors.bold(
'Attention'
)}: We've detected that you're using the following package which is incompatible with Storybook 8 and beyond:
- - ${chalk.cyan(`@storybook/addon-postcss`)}
+ - ${picocolors.cyan(`@storybook/addon-postcss`)}
- Please migrate to ${chalk.cyan(
+ Please migrate to ${picocolors.cyan(
`@storybook/addon-styling-webpack`
)} once you're done upgrading.
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/addons-api.ts b/code/lib/cli-storybook/src/automigrate/fixes/addons-api.ts
index f014b331c064..b0236fcf82a7 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/addons-api.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/addons-api.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import type { Fix } from '../types';
@@ -27,18 +27,18 @@ export const addonsAPI: Fix = {
prompt() {
return dedent`
- ${chalk.bold(
+ ${picocolors.bold(
'Attention'
)}: We've detected that you're using the following package which is removed in Storybook 8 and beyond:
- - ${chalk.cyan(`@storybook/addons`)}
+ - ${picocolors.cyan(`@storybook/addons`)}
- This package has been deprecated and replaced with ${chalk.cyan(
+ This package has been deprecated and replaced with ${picocolors.cyan(
`@storybook/preview-api`
- )} and ${chalk.cyan(`@storybook/manager-api`)}.
+ )} and ${picocolors.cyan(`@storybook/manager-api`)}.
You can find more information about the new addons API in the migration guide:
- ${chalk.yellow(
+ ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#new-addons-api'
)}
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/angular-builders-multiproject.ts b/code/lib/cli-storybook/src/automigrate/fixes/angular-builders-multiproject.ts
index 182ab521942b..dad252bcf42d 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/angular-builders-multiproject.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/angular-builders-multiproject.ts
@@ -1,6 +1,6 @@
import { AngularJSON, isNxProject } from 'storybook/internal/cli';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import semver from 'semver';
import { dedent } from 'ts-dedent';
@@ -47,13 +47,13 @@ export const angularBuildersMultiproject: Fix:storybook')} to start Storybook.
+ )} and execute ${picocolors.yellow('ng run :storybook')} to start Storybook.
- ❌ Your Angular workspace uses multiple projects defined in the ${chalk.yellow(
+ ❌ Your Angular workspace uses multiple projects defined in the ${picocolors.yellow(
'angular.json'
- )} file and we were not able to detect a root project. Therefore we are not able to automigrate to use Angular Storybook builder. Instead, please visit ${chalk.yellow(
+ )} file and we were not able to detect a root project. Therefore we are not able to automigrate to use Angular Storybook builder. Instead, please visit ${picocolors.yellow(
'https://github.com/storybookjs/storybook/tree/next/code/frameworks/angular'
)} to do the migration manually.
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/angular-builders.ts b/code/lib/cli-storybook/src/automigrate/fixes/angular-builders.ts
index 80eccbc1f046..30798fe53335 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/angular-builders.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/angular-builders.ts
@@ -2,7 +2,7 @@ import { AngularJSON, isNxProject } from 'storybook/internal/cli';
import type { JsPackageManager } from 'storybook/internal/common';
import type { StorybookConfig } from 'storybook/internal/types';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import prompts from 'prompts';
import { dedent } from 'ts-dedent';
@@ -60,7 +60,7 @@ export const angularBuilders: Fix = {
Also feel free to remove the Compodoc script from your package.json file if you don't use it apart from Storybook anymore. Storybook uses Compodoc internally and you don't have to call in separately anymore.
- Read more about the Angular builder here: ${chalk.yellow(
+ Read more about the Angular builder here: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/tree/next/code/frameworks/angular#how-do-i-migrate-to-an-angular-storybook-builder'
)}
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/autodocs-tags.ts b/code/lib/cli-storybook/src/automigrate/fixes/autodocs-tags.ts
index ebfbf45213a6..30d5c9a5ed8e 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/autodocs-tags.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/autodocs-tags.ts
@@ -1,7 +1,7 @@
import { readConfig, writeConfig } from 'storybook/internal/csf-tools';
import type { DocsOptions } from 'storybook/internal/types';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import { updateMainConfig } from '../helpers/mainConfigFile';
@@ -30,13 +30,13 @@ export const autodocsTags: Fix = {
if (autodocs === true && !previewConfigPath) {
throw Error(dedent`
- ❌ Failed to remove the deprecated ${chalk.cyan('docs.autodocs')} setting from ${chalk.cyan(
- mainConfigPath
- )}.
+ ❌ Failed to remove the deprecated ${picocolors.cyan(
+ 'docs.autodocs'
+ )} setting from ${picocolors.cyan(mainConfigPath)}.
- There is no preview config file in which to add the ${chalk.cyan('autodocs')} tag.
+ There is no preview config file in which to add the ${picocolors.cyan('autodocs')} tag.
- Please perform the migration by hand: ${chalk.yellow(MIGRATION)}
+ Please perform the migration by hand: ${picocolors.yellow(MIGRATION)}
`);
return null;
}
@@ -52,21 +52,23 @@ export const autodocsTags: Fix = {
falseMessage = dedent`
- There is no ${chalk.cyan('docs.autodocs = false')} equivalent.
- You'll need to check your stories to ensure none are tagged with ${chalk.cyan('autodocs')}.
+ There is no ${picocolors.cyan('docs.autodocs = false')} equivalent.
+ You'll need to check your stories to ensure none are tagged with ${picocolors.cyan(
+ 'autodocs'
+ )}.
`;
} else if (autodocs === true) {
- trueMessage = ` and update ${chalk.cyan(previewConfigPath)}`;
+ trueMessage = ` and update ${picocolors.cyan(previewConfigPath)}`;
}
return dedent`
- The ${chalk.cyan('docs.autodocs')} setting in ${chalk.cyan(
+ The ${picocolors.cyan('docs.autodocs')} setting in ${picocolors.cyan(
mainConfigPath
)} is deprecated.${falseMessage}
- Learn more: ${chalk.yellow(MIGRATION)}
+ Learn more: ${picocolors.yellow(MIGRATION)}
- Remove ${chalk.cyan('docs.autodocs')}${trueMessage}?
+ Remove ${picocolors.cyan('docs.autodocs')}${trueMessage}?
`;
},
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/autodocs-true.ts b/code/lib/cli-storybook/src/automigrate/fixes/autodocs-true.ts
index 5f3dc66a899d..9ed1aa6b06ed 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/autodocs-true.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/autodocs-true.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import { updateMainConfig } from '../helpers/mainConfigFile';
@@ -42,7 +42,9 @@ export const autodocsTrue: Fix = {
},
prompt({ value }) {
- const autodocsFormatted = chalk.cyan(`docs: { autodocs: ${JSON.stringify(value ?? true)} }`);
+ const autodocsFormatted = picocolors.cyan(
+ `docs: { autodocs: ${JSON.stringify(value ?? true)} }`
+ );
const tagWarning = dedent`
NOTE: if you're upgrading from an older 7.0-beta using the 'docsPage' tag,
please update your story files to use the 'autodocs' tag instead.
@@ -59,7 +61,7 @@ export const autodocsTrue: Fix = {
${autodocsFormatted}
${value === 'tag' ? tagWarning : ''}
- More info: ${chalk.yellow(
+ More info: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#autodocs-changes'
)}
`;
@@ -72,7 +74,7 @@ export const autodocsTrue: Fix = {
${autodocsFormatted}
- More info: ${chalk.yellow(
+ More info: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#autodocs-changes'
)}
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/builder-vite.ts b/code/lib/cli-storybook/src/automigrate/fixes/builder-vite.ts
index 0f3e7714edee..258d033d8649 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/builder-vite.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/builder-vite.ts
@@ -2,7 +2,7 @@ import { getStorybookVersionSpecifier } from 'storybook/internal/cli';
import { writeConfig } from 'storybook/internal/csf-tools';
import type { PackageJson } from 'storybook/internal/types';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import { updateMainConfig } from '../helpers/mainConfigFile';
@@ -42,18 +42,18 @@ export const builderVite: Fix = {
},
prompt({ builder }) {
- const builderFormatted = chalk.cyan(JSON.stringify(builder, null, 2));
+ const builderFormatted = picocolors.cyan(JSON.stringify(builder, null, 2));
return dedent`
We've detected you're using the community vite builder: ${builderFormatted}
- 'storybook-builder-vite' is deprecated and now located at ${chalk.cyan(
+ 'storybook-builder-vite' is deprecated and now located at ${picocolors.cyan(
'@storybook/builder-vite'
)}.
We can upgrade your project to use the new builder automatically.
- More info: ${chalk.yellow(
+ More info: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#vite-builder-renamed'
)}
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/cra5.ts b/code/lib/cli-storybook/src/automigrate/fixes/cra5.ts
index 968b24cb4985..26904460664a 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/cra5.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/cra5.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import semver from 'semver';
import { dedent } from 'ts-dedent';
@@ -36,17 +36,17 @@ export const cra5: Fix = {
},
prompt({ craVersion }) {
- const craFormatted = chalk.cyan(`Create React App (CRA) ${craVersion}`);
+ const craFormatted = picocolors.cyan(`Create React App (CRA) ${craVersion}`);
return dedent`
We've detected you are running ${craFormatted} which is powered by webpack5.
Your Storybook's main.js files specifies webpack4, which is incompatible.
- In order to work with your version of CRA, we need to install Storybook's ${chalk.cyan(
+ In order to work with your version of CRA, we need to install Storybook's ${picocolors.cyan(
'@storybook/builder-webpack5'
)}.
- More info: ${chalk.yellow(
+ More info: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#cra5-upgrade'
)}
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/eslint-plugin.test.ts b/code/lib/cli-storybook/src/automigrate/fixes/eslint-plugin.test.ts
index 2120abd7098f..5c8bee41c2f0 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/eslint-plugin.test.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/eslint-plugin.test.ts
@@ -1,17 +1,17 @@
/* eslint-disable no-underscore-dangle */
import * as fs from 'node:fs';
+import * as fsp from 'node:fs/promises';
import { describe, expect, it, vi } from 'vitest';
import type { PackageJson } from 'storybook/internal/common';
-import * as fsExtra from 'fs-extra';
import { dedent } from 'ts-dedent';
import { makePackageManager } from '../helpers/testing-helpers';
import { eslintPlugin } from './eslint-plugin';
-vi.mock('fs-extra', async () => import('../../../../../__mocks__/fs-extra'));
+vi.mock('node:fs/promises', async () => import('../../../../../__mocks__/fs/promises'));
vi.mock('fs');
const checkEslint = async ({
@@ -23,7 +23,7 @@ const checkEslint = async ({
hasEslint?: boolean;
eslintExtension?: string;
}) => {
- vi.mocked(fsExtra as any).__setMockFiles({
+ vi.mocked(fsp as any).__setMockFiles({
[`.eslintrc.${eslintExtension}`]: !hasEslint
? null
: dedent(`
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/eslint-plugin.ts b/code/lib/cli-storybook/src/automigrate/fixes/eslint-plugin.ts
index 719ee2e046dd..a0d21efbc70b 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/eslint-plugin.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/eslint-plugin.ts
@@ -5,7 +5,7 @@ import {
findEslintFile,
} from 'storybook/internal/cli';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import type { Fix } from '../types';
@@ -58,7 +58,9 @@ export const eslintPlugin: Fix = {
In order to have the best experience with Storybook and follow best practices, we advise you to install eslint-plugin-storybook.
- More info: ${chalk.yellow('https://github.com/storybookjs/eslint-plugin-storybook#readme')}
+ More info: ${picocolors.yellow(
+ 'https://github.com/storybookjs/eslint-plugin-storybook#readme'
+ )}
`;
},
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/initial-globals.test.ts b/code/lib/cli-storybook/src/automigrate/fixes/initial-globals.test.ts
index e74d5ce810da..b78a80fe79b3 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/initial-globals.test.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/initial-globals.test.ts
@@ -1,17 +1,16 @@
/* eslint-disable no-underscore-dangle */
+import * as fsp from 'node:fs/promises';
import { join } from 'node:path';
import { expect, it, vi } from 'vitest';
-import * as fsExtra from 'fs-extra';
-
import { initialGlobals } from './initial-globals';
-vi.mock('fs-extra', async () => import('../../../../../__mocks__/fs-extra'));
+vi.mock('node:fs/promises', async () => import('../../../../../__mocks__/fs/promises'));
const previewConfigPath = join('.storybook', 'preview.js');
const check = async (previewContents: string) => {
- vi.mocked(fsExtra as any).__setMockFiles({
+ vi.mocked(fsp as any).__setMockFiles({
[previewConfigPath]: previewContents,
});
return initialGlobals.check({
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/initial-globals.ts b/code/lib/cli-storybook/src/automigrate/fixes/initial-globals.ts
index d4a96033a8c5..f452be36067a 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/initial-globals.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/initial-globals.ts
@@ -1,9 +1,10 @@
+import { readFile, writeFile } from 'node:fs/promises';
+
import type { ConfigFile } from 'storybook/internal/csf-tools';
import { formatConfig, loadConfig } from 'storybook/internal/csf-tools';
import type { Expression } from '@babel/types';
-import chalk from 'chalk';
-import { readFile, writeFile } from 'fs-extra';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import type { Fix } from '../types';
@@ -38,12 +39,14 @@ export const initialGlobals: Fix = {
prompt({ previewConfigPath }) {
return dedent`
- The ${chalk.cyan('globals')} setting in ${chalk.cyan(previewConfigPath)} is deprecated
- and has been renamed to ${chalk.cyan('initialGlobals')}.
+ The ${picocolors.cyan('globals')} setting in ${picocolors.cyan(
+ previewConfigPath
+ )} is deprecated
+ and has been renamed to ${picocolors.cyan('initialGlobals')}.
- Learn more: ${chalk.yellow(MIGRATION)}
+ Learn more: ${picocolors.yellow(MIGRATION)}
- Rename ${chalk.cyan('globals')} to ${chalk.cyan('initalGlobals')}?
+ Rename ${picocolors.cyan('globals')} to ${picocolors.cyan('initalGlobals')}?
`;
},
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/mdx-1-to-3.ts b/code/lib/cli-storybook/src/automigrate/fixes/mdx-1-to-3.ts
index 221edd9298e0..e53ee7cde234 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/mdx-1-to-3.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/mdx-1-to-3.ts
@@ -1,7 +1,7 @@
+import { readFile, writeFile } from 'node:fs/promises';
import { basename } from 'node:path';
-import chalk from 'chalk';
-import fse from 'fs-extra';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import type { Fix } from '../types';
@@ -59,7 +59,7 @@ export const mdx1to3: Fix = {
prompt({ storiesMdxFiles }) {
return dedent`
- We've found ${chalk.yellow(storiesMdxFiles.length)} '.stories.mdx' files in your project.
+ We've found ${picocolors.yellow(storiesMdxFiles.length)} '.stories.mdx' files in your project.
Storybook has upgraded to MDX3 (https://mdxjs.com/blog/v3/). MDX3 itself doesn't contain disruptive breaking changes, whereas the transition from MDX1 to MDX2 was a significant change.
We can try to automatically upgrade your MDX files to MDX3 format using some common patterns.
@@ -67,21 +67,21 @@ export const mdx1to3: Fix = {
After this install completes, and before you start Storybook, we strongly recommend reading the MDX2 section
of the 7.0 migration guide. It contains useful tools for detecting and fixing any remaining issues.
- ${chalk.cyan('https://storybook.js.org/migration-guides/7.0')}
+ ${picocolors.cyan('https://storybook.js.org/migration-guides/7.0')}
`;
},
async run({ result: { storiesMdxFiles }, dryRun }) {
await Promise.all([
...storiesMdxFiles.map(async (fname) => {
- const contents = await fse.readFile(fname, 'utf-8');
+ const contents = await readFile(fname, { encoding: 'utf8' });
const updated = fixMdxComments(fixMdxStyleTags(contents));
if (updated === contents) {
logger.info(`🆗 Unmodified ${basename(fname)}`);
} else {
logger.info(`✅ Modified ${basename(fname)}`);
if (!dryRun) {
- await fse.writeFile(fname, updated);
+ await writeFile(fname, updated);
}
}
}),
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/mdx-to-csf.ts b/code/lib/cli-storybook/src/automigrate/fixes/mdx-to-csf.ts
index 373ec72f8c35..55e36258c9cd 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/mdx-to-csf.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/mdx-to-csf.ts
@@ -2,8 +2,8 @@ import type { StoriesEntry } from 'storybook/internal/types';
import { runCodemod } from '@storybook/codemod';
-import chalk from 'chalk';
import { glob } from 'glob';
+import picocolors from 'picocolors';
import { prompt } from 'prompts';
import { dedent } from 'ts-dedent';
@@ -43,9 +43,9 @@ export const mdxToCSF: Fix = {
if (!existingStoriesEntries) {
throw new Error(dedent`
- ❌ Unable to determine Storybook stories globs in ${chalk.blue(
- mainConfig
- )}, skipping ${chalk.cyan(this.id)} fix.
+ ❌ Unable to determine Storybook stories globs in ${picocolors.blue(
+ JSON.stringify(mainConfig, null, 2)
+ )}, skipping ${picocolors.cyan(this.id)} fix.
In Storybook 7, we have deprecated defining stories in MDX files, and consequently have changed the suffix to simply .mdx.
@@ -53,7 +53,7 @@ export const mdxToCSF: Fix = {
We were unable to automatically migrate your 'stories' config to include any .mdx file instead of just .stories.mdx.
We suggest you make this change manually.
- To learn more about this change, see: ${chalk.yellow(
+ To learn more about this change, see: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#mdx-docs-files'
)}
`);
@@ -97,7 +97,7 @@ export const mdxToCSF: Fix = {
.join('\n');
return dedent`
We've detected your project has one or more globs in your 'stories' config that matches .stories.mdx files:
- ${chalk.cyan(prettyExistingStoriesEntries)}
+ ${picocolors.cyan(prettyExistingStoriesEntries)}
In Storybook 7, we have deprecated defining stories in MDX files, and consequently have changed the suffix to simply .mdx. Since Storybook 8, we have removed the support of story definition in MDX files entirely. Therefore '.stories.mdx' files aren't supported anymore.
@@ -105,11 +105,11 @@ export const mdxToCSF: Fix = {
We can automatically migrate your 'stories' config to include any .mdx file instead of just .stories.mdx.
That would result in the following 'stories' config:
- ${chalk.cyan(prettyNextStoriesEntries)}
+ ${picocolors.cyan(prettyNextStoriesEntries)}
Additionally, we will run the 'mdx-to-csf' codemod for you, which tries to transform '*.stories.mdx' files to '*.stories.js' and '*.mdx' files.
- To learn more about this change, see: ${chalk.yellow(
+ To learn more about this change, see: ${picocolors.yellow(
'https://storybook.js.org/docs/migration-guide#storiesmdx-to-mdxcsf'
)}
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/missing-storybook-dependencies.ts b/code/lib/cli-storybook/src/automigrate/fixes/missing-storybook-dependencies.ts
index ad912867f6f2..9b5892b51a32 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/missing-storybook-dependencies.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/missing-storybook-dependencies.ts
@@ -4,7 +4,7 @@ import { getStorybookVersionSpecifier } from 'storybook/internal/cli';
import type { InstallationMetadata, JsPackageManager } from '@storybook/core/common';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import type { Fix } from '../types';
@@ -112,7 +112,7 @@ export const missingStorybookDependencies: Fix
- `- ${chalk.cyan(pkg)}: (${files.length} ${files.length === 1 ? 'file' : 'files'})`
+ `- ${picocolors.cyan(pkg)}: (${files.length} ${files.length === 1 ? 'file' : 'files'})`
)
.sort()
.join('\n')}
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/new-frameworks.ts b/code/lib/cli-storybook/src/automigrate/fixes/new-frameworks.ts
index 4c891e172099..2de095f742c6 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/new-frameworks.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/new-frameworks.ts
@@ -2,7 +2,7 @@ import { getStorybookVersionSpecifier } from 'storybook/internal/cli';
import { frameworkPackages, rendererPackages } from 'storybook/internal/common';
import type { Preset } from 'storybook/internal/types';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import semver from 'semver';
import invariant from 'tiny-invariant';
import { dedent } from 'ts-dedent';
@@ -211,13 +211,13 @@ export const newFrameworks: Fix = {
if (viteVersion && semver.lt(viteVersion, '3.0.0')) {
throw new Error(dedent`
- ❌ Your project should be upgraded to use the framework package ${chalk.bold(
+ ❌ Your project should be upgraded to use the framework package ${picocolors.bold(
newFrameworkPackage
- )}, but we detected that you are using Vite ${chalk.bold(
+ )}, but we detected that you are using Vite ${picocolors.bold(
viteVersion
- )}, which is unsupported since ${chalk.bold(
+ )}, which is unsupported since ${picocolors.bold(
'Storybook 7.0'
- )}. Please upgrade Vite to ${chalk.bold('3.0.0 or higher')} and rerun this migration.
+ )}. Please upgrade Vite to ${picocolors.bold('3.0.0 or higher')} and rerun this migration.
`);
}
@@ -263,41 +263,47 @@ export const newFrameworks: Fix = {
if (dependenciesToRemove.length > 0) {
migrationSteps += `- Remove the following dependencies:
- ${dependenciesToRemove.map((dep) => `- * ${chalk.cyan(dep)}`).join('\n')}\n`;
+ ${dependenciesToRemove.map((dep) => `- * ${picocolors.cyan(dep)}`).join('\n')}\n`;
}
if (dependenciesToAdd.length > 0) {
migrationSteps += `- Add the following dependencies:
- ${dependenciesToAdd.map((dep) => `- * ${chalk.cyan(dep)}`).join('\n')}\n`;
+ ${dependenciesToAdd.map((dep) => `- * ${picocolors.cyan(dep)}`).join('\n')}\n`;
}
if (!hasFrameworkInMainConfig) {
- migrationSteps += `- Update or specify the ${chalk.yellow('framework')} field in ${chalk.blue(
- mainConfigPath
- )} with the value of "${chalk.cyan(frameworkPackage)}".\n`;
+ migrationSteps += `- Update or specify the ${picocolors.yellow(
+ 'framework'
+ )} field in ${picocolors.blue(mainConfigPath)} with the value of "${picocolors.cyan(
+ frameworkPackage
+ )}".\n`;
}
if (Object.keys(rendererOptions).length > 0) {
- migrationSteps += `- Move the ${chalk.yellow(`${renderer}Options`)} field in ${chalk.blue(
- mainConfigPath
- )} to ${chalk.yellow('framework.options')}, and remove that field entirely.
- More info: ${chalk.yellow(
+ migrationSteps += `- Move the ${picocolors.yellow(
+ `${renderer}Options`
+ )} field in ${picocolors.blue(mainConfigPath)} to ${picocolors.yellow(
+ 'framework.options'
+ )}, and remove that field entirely.
+ More info: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#frameworkoptions-renamed'
)}\n`;
}
if (addonsToRemove.length > 0) {
- migrationSteps += `- Remove the following addons from your ${chalk.blue(
+ migrationSteps += `- Remove the following addons from your ${picocolors.blue(
mainConfigPath
)}, as the new framework also supports features provided by them:
- ${addonsToRemove.map((dep) => `- * ${chalk.cyan(dep)}`).join('\n')}
+ ${addonsToRemove.map((dep) => `- * ${picocolors.cyan(dep)}`).join('\n')}
`;
}
if (Object.keys(addonOptions).length > 0) {
- migrationSteps += `- Move the addon options "${chalk.yellow(
+ migrationSteps += `- Move the addon options "${picocolors.yellow(
Object.keys(addonOptions).join(', ')
- )}" in ${chalk.blue(mainConfigPath)} to the ${chalk.yellow('framework.options')} field.\n`;
+ )}" in ${picocolors.blue(mainConfigPath)} to the ${picocolors.yellow(
+ 'framework.options'
+ )} field.\n`;
}
if (builderConfig) {
@@ -305,14 +311,16 @@ export const newFrameworks: Fix = {
typeof builderConfig === 'object' &&
Object.keys(builderConfig.options || {}).length > 0
) {
- migrationSteps += `- Move the ${chalk.yellow('core.builder.options')} field in ${chalk.blue(
- mainConfigPath
- )} to ${chalk.yellow('framework.options.builder')}\n`;
+ migrationSteps += `- Move the ${picocolors.yellow(
+ 'core.builder.options'
+ )} field in ${picocolors.blue(mainConfigPath)} to ${picocolors.yellow(
+ 'framework.options.builder'
+ )}\n`;
}
- migrationSteps += `- Remove the ${chalk.yellow('core.builder')} field in ${chalk.blue(
- mainConfigPath
- )}.\n`;
+ migrationSteps += `- Remove the ${picocolors.yellow(
+ 'core.builder'
+ )} field in ${picocolors.blue(mainConfigPath)}.\n`;
}
if (
@@ -320,47 +328,49 @@ export const newFrameworks: Fix = {
dependenciesToRemove.includes('@storybook/manager-webpack4')
) {
disclaimer = dedent`\n\n
- ${chalk.underline(chalk.bold(chalk.cyan('Webpack 4 users')))}
+ ${picocolors.underline(picocolors.bold(picocolors.cyan('Webpack 4 users')))}
Unless you're using Storybook's Vite builder, this automigration will install a Webpack 5 based framework.
Given you were using Storybook's Webpack 4 builder (default in 6.x, discontinued in 7.0), this could be a breaking change -- especially if your project has a custom webpack configuration.
- To learn more about migrating from Webpack4, see: ${chalk.yellow(
+ To learn more about migrating from Webpack4, see: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#webpack4-support-discontinued'
)}`;
}
if (metaFramework === 'nextjs') {
if (dependenciesToRemove.includes('storybook-addon-next-router')) {
- migrationSteps += `- Migrate the usage of the ${chalk.cyan(
+ migrationSteps += `- Migrate the usage of the ${picocolors.cyan(
'storybook-addon-next-router'
- )} addon to use the APIs from the ${chalk.magenta(
+ )} addon to use the APIs from the ${picocolors.magenta(
'@storybook/nextjs'
)} framework package instead. Follow the instructions below.`;
}
if (frameworkPackage === '@storybook/react-vite') {
disclaimer = dedent`\n\n
- ${chalk.bold('Important')}: We've detected you are using Storybook in a Next.js project.
+ ${picocolors.bold(
+ 'Important'
+ )}: We've detected you are using Storybook in a Next.js project.
- This migration is set to update your project to use the ${chalk.magenta(
+ This migration is set to update your project to use the ${picocolors.magenta(
'@storybook/react-vite'
- )} framework, but Storybook provides a framework package specifically for Next.js projects: ${chalk.magenta(
+ )} framework, but Storybook provides a framework package specifically for Next.js projects: ${picocolors.magenta(
'@storybook/nextjs'
)}.
This package provides a better, out of the box experience for Next.js users, however it is only compatible with the Webpack 5 builder, so we can't automigrate for you, as you are using the Vite builder. If you switch this project to use Webpack 5 and rerun this migration, we can update your project.
- If you are interested in using this package, see: ${chalk.yellow(
+ If you are interested in using this package, see: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/README.md'
)}
`;
} else if (frameworkPackage === '@storybook/nextjs') {
disclaimer = dedent`\n\n
- The ${chalk.magenta(
+ The ${picocolors.magenta(
'@storybook/nextjs'
- )} package provides great user experience for Next.js users, and we highly recommend you to read more about it at ${chalk.yellow(
+ )} package provides great user experience for Next.js users, and we highly recommend you to read more about it at ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/README.md'
)}
`;
@@ -370,31 +380,33 @@ export const newFrameworks: Fix = {
if (metaFramework === 'sveltekit') {
if (frameworkPackage === '@storybook/svelte-webpack5') {
disclaimer = dedent`\n\n
- ${chalk.bold('Important')}: We've detected you are using Storybook in a SvelteKit project.
+ ${picocolors.bold(
+ 'Important'
+ )}: We've detected you are using Storybook in a SvelteKit project.
- This migration is set to update your project to use the ${chalk.magenta(
+ This migration is set to update your project to use the ${picocolors.magenta(
'@storybook/svelte-webpack5'
- )} framework, but Storybook provides a framework package specifically for SvelteKit projects: ${chalk.magenta(
+ )} framework, but Storybook provides a framework package specifically for SvelteKit projects: ${picocolors.magenta(
'@storybook/sveltekit'
)}.
This package provides a better experience for SvelteKit users, however it is only compatible with the Vite builder, so we can't automigrate for you, as you are using the Webpack builder.
- If you are interested in using this package, see: ${chalk.yellow(
+ If you are interested in using this package, see: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/code/frameworks/sveltekit/README.md'
)}
`;
} else {
- migrationSteps += `- Remove the ${chalk.yellow(
+ migrationSteps += `- Remove the ${picocolors.yellow(
`${renderer}Options`
- )} field from ${chalk.blue(mainConfigPath)}.
- More info: ${chalk.yellow(
+ )} field from ${picocolors.blue(mainConfigPath)}.
+ More info: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#vite-builder-uses-vite-config-automatically'
)}\n`;
disclaimer = dedent`\n\n
- The ${chalk.magenta(
+ The ${picocolors.magenta(
'@storybook/sveltekit'
- )} package provides great user experience for SvelteKit users, and we highly recommend you to read more about it at ${chalk.yellow(
+ )} package provides great user experience for SvelteKit users, and we highly recommend you to read more about it at ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/code/frameworks/sveltekit/README.md'
)}
`;
@@ -406,14 +418,14 @@ export const newFrameworks: Fix = {
Storybook 7 introduced the concept of frameworks, which abstracts configuration for renderers (e.g. React, Vue), builders (e.g. Webpack, Vite) and defaults to make integrations easier.
- Your project should be updated to use Storybook's framework: ${chalk.magenta(
+ Your project should be updated to use Storybook's framework: ${picocolors.magenta(
frameworkPackage
)}. We can attempt to do this for you automatically.
Here are the steps this migration will do to migrate your project:
${migrationSteps}
- To learn more about the new framework format, see: ${chalk.yellow(
+ To learn more about the new framework format, see: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#new-framework-api'
)}${disclaimer}
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/react-docgen.ts b/code/lib/cli-storybook/src/automigrate/fixes/react-docgen.ts
index bec42b612a46..86b67df2509f 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/react-docgen.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/react-docgen.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import { getRendererName, updateMainConfig } from '../helpers/mainConfigFile';
@@ -47,25 +47,25 @@ export const reactDocgen: Fix = {
`;
} else {
return dedent`
- Since Storybook 8.0, ${chalk.cyan(
+ Since Storybook 8.0, ${picocolors.cyan(
'react-docgen'
- )} is now the default for generating component controls, replacing ${chalk.cyan(
+ )} is now the default for generating component controls, replacing ${picocolors.cyan(
'react-docgen-typescript'
)}.
This offers better performance and suits most cases.
However, for complex TypeScript types or specific type features, the generated controls might not be as precise.
For more on this change, check the migration guide:
- ${chalk.yellow(
+ ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#react-docgen-component-analysis-by-default'
)}
For known "react-docgen" limitations, see:
- ${chalk.yellow('https://github.com/storybookjs/storybook/issues/26606')}
+ ${picocolors.yellow('https://github.com/storybookjs/storybook/issues/26606')}
- Press Y to revert to ${chalk.cyan('react-docgen-typescript')}, press N to use ${chalk.cyan(
- 'react-docgen'
- )}
+ Press Y to revert to ${picocolors.cyan(
+ 'react-docgen-typescript'
+ )}, press N to use ${picocolors.cyan('react-docgen')}
`;
}
},
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/remove-argtypes-regex.ts b/code/lib/cli-storybook/src/automigrate/fixes/remove-argtypes-regex.ts
index ec84504127c8..3ae9de96b8db 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/remove-argtypes-regex.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/remove-argtypes-regex.ts
@@ -4,7 +4,7 @@ import { babelParse } from 'storybook/internal/csf-tools';
import * as babel from '@babel/core';
import type { BabelFile, NodePath } from '@babel/core';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import type { Fix } from '../types';
@@ -40,7 +40,7 @@ export const removeArgtypesRegex: Fix<{ argTypesRegex: NodePath; previewConfigPa
},
prompt({ argTypesRegex, previewConfigPath }) {
return dedent`
- ${chalk.bold('Attention')}: We've detected that you're using argTypesRegex:
+ ${picocolors.bold('Attention')}: We've detected that you're using argTypesRegex:
${argTypesRegex.buildCodeFrameError(`${previewConfigPath}`).message}
@@ -53,12 +53,12 @@ export const removeArgtypesRegex: Fix<{ argTypesRegex: NodePath; previewConfigPa
(fn) function to mock your component's methods instead.
Use the following command to check for implied mocked actions in your play functions:
- ${chalk.cyan(
+ ${picocolors.cyan(
'npx storybook migrate find-implicit-spies --glob="**/*.stories.@(js|jsx|ts|tsx)"'
)}
Then, refer to our docs to migrate your play functions to Storybook 8:
- ${chalk.yellow(
+ ${picocolors.yellow(
'https://storybook.js.org/docs/8.0/essentials/actions#via-storybooktest-fn-spy-function'
)}
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/remove-global-client-apis.test.ts b/code/lib/cli-storybook/src/automigrate/fixes/remove-global-client-apis.test.ts
index 6ae48ad95018..3e1af96c38f6 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/remove-global-client-apis.test.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/remove-global-client-apis.test.ts
@@ -1,19 +1,18 @@
/* eslint-disable no-underscore-dangle */
+import * as fsp from 'node:fs/promises';
import { join } from 'node:path';
import { describe, expect, it, vi } from 'vitest';
import type { JsPackageManager } from 'storybook/internal/common';
-import * as fsExtra from 'fs-extra';
-
import { RemovedAPIs, removedGlobalClientAPIs as migration } from './remove-global-client-apis';
-vi.mock('fs-extra', async () => import('../../../../../__mocks__/fs-extra'));
+vi.mock('node:fs/promises', async () => import('../../../../../__mocks__/fs/promises'));
const check = async ({ contents, previewConfigPath }: any) => {
if (contents) {
- vi.mocked(fsExtra as any).__setMockFiles({
+ vi.mocked(fsp as any).__setMockFiles({
[join('.storybook', 'preview.js')]: contents,
});
}
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/remove-global-client-apis.ts b/code/lib/cli-storybook/src/automigrate/fixes/remove-global-client-apis.ts
index 232ac4fea188..36213c09e953 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/remove-global-client-apis.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/remove-global-client-apis.ts
@@ -1,5 +1,6 @@
-import chalk from 'chalk';
-import { readFile } from 'fs-extra';
+import { readFile } from 'node:fs/promises';
+
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import type { Fix } from '../types';
@@ -26,7 +27,7 @@ export const removedGlobalClientAPIs: Fix = {
async check({ previewConfigPath }) {
if (previewConfigPath) {
- const contents = await readFile(previewConfigPath, 'utf8');
+ const contents = await readFile(previewConfigPath, { encoding: 'utf8' });
const usedAPIs = Object.values(RemovedAPIs).reduce((acc, item) => {
if (contents.includes(item)) {
@@ -47,16 +48,18 @@ export const removedGlobalClientAPIs: Fix = {
},
prompt({ usedAPIs, previewPath }) {
return dedent`
- ${chalk.bold(
- chalk.red('Attention')
+ ${picocolors.bold(
+ picocolors.red('Attention')
)}: We could not automatically make this change. You'll need to do it manually.
- The following APIs (used in "${chalk.yellow(previewPath)}") have been removed from Storybook:
+ The following APIs (used in "${picocolors.yellow(
+ previewPath
+ )}") have been removed from Storybook:
- ${usedAPIs.map((api) => `- ${chalk.cyan(api)}`).join('\n')}
+ ${usedAPIs.map((api) => `- ${picocolors.cyan(api)}`).join('\n')}
Please see the migration guide for more information:
- ${chalk.yellow(
+ ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#removed-global-client-apis'
)}
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/remove-jest-testing-library.ts b/code/lib/cli-storybook/src/automigrate/fixes/remove-jest-testing-library.ts
index 928ace2519bf..b7de9df0afc6 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/remove-jest-testing-library.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/remove-jest-testing-library.ts
@@ -2,7 +2,7 @@ import { getStorybookVersionSpecifier } from 'storybook/internal/cli';
import { runCodemod } from '@storybook/codemod';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import prompts from 'prompts';
import { dedent } from 'ts-dedent';
@@ -24,13 +24,13 @@ export const removeJestTestingLibrary: Fix<{ incompatiblePackages: string[] }> =
},
prompt({ incompatiblePackages }) {
return dedent`
- ${chalk.bold(
+ ${picocolors.bold(
'Attention'
)}: We've detected that you're using the following packages which are known to be incompatible since Storybook 8:
- ${incompatiblePackages.map((name) => `- ${chalk.cyan(`${name}`)}`).join('\n')}
+ ${incompatiblePackages.map((name) => `- ${picocolors.cyan(`${name}`)}`).join('\n')}
- We will uninstall them for you and install ${chalk.cyan('@storybook/test')} instead.
+ We will uninstall them for you and install ${picocolors.cyan('@storybook/test')} instead.
Also, we can help you migrate your stories to use the new package.
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/sb-binary.ts b/code/lib/cli-storybook/src/automigrate/fixes/sb-binary.ts
index 36dcdc32cc11..e680f93da772 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/sb-binary.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/sb-binary.ts
@@ -1,7 +1,7 @@
import { getStorybookVersionSpecifier } from 'storybook/internal/cli';
import type { PackageJsonWithDepsAndDevDeps } from 'storybook/internal/common';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import type { Fix } from '../types';
@@ -50,10 +50,10 @@ export const sbBinary: Fix = {
},
prompt({ storybookVersion, hasSbBinary, hasStorybookBinary }) {
- const sbFormatted = chalk.cyan(`Storybook ${storybookVersion}`);
+ const sbFormatted = picocolors.cyan(`Storybook ${storybookVersion}`);
const storybookBinaryMessage = !hasStorybookBinary
- ? `We've detected you are using ${sbFormatted} without Storybook's ${chalk.magenta(
+ ? `We've detected you are using ${sbFormatted} without Storybook's ${picocolors.magenta(
'storybook'
)} binary. Starting in Storybook 7.0, it has to be installed.`
: '';
@@ -66,7 +66,7 @@ export const sbBinary: Fix = {
${storybookBinaryMessage}
${extraMessage}
- More info: ${chalk.yellow(
+ More info: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#start-storybook--build-storybook-binaries-removed'
)}
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/sb-scripts.ts b/code/lib/cli-storybook/src/automigrate/fixes/sb-scripts.ts
index af80c94c394e..d3c34e8ef8b8 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/sb-scripts.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/sb-scripts.ts
@@ -1,7 +1,7 @@
import type { PackageJsonWithDepsAndDevDeps } from 'storybook/internal/common';
import type { PackageJson } from 'storybook/internal/types';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import semver from 'semver';
import { dedent } from 'ts-dedent';
@@ -101,16 +101,16 @@ export const sbScripts: Fix = {
},
prompt({ storybookVersion, storybookScripts }) {
- const sbFormatted = chalk.cyan(`Storybook ${storybookVersion}`);
+ const sbFormatted = picocolors.cyan(`Storybook ${storybookVersion}`);
const newScriptsMessage = Object.keys(storybookScripts).reduce((acc: string[], scriptKey) => {
acc.push(
[
- chalk.bold(scriptKey),
+ picocolors.bold(scriptKey),
'from:',
- chalk.cyan(storybookScripts[scriptKey].before),
+ picocolors.cyan(storybookScripts[scriptKey].before),
'to:',
- chalk.cyan(storybookScripts[scriptKey].after),
+ picocolors.cyan(storybookScripts[scriptKey].after),
].join('\n')
);
return acc;
@@ -118,16 +118,16 @@ export const sbScripts: Fix = {
return dedent`
We've detected you are using ${sbFormatted} with scripts from previous versions of Storybook.
- Starting in Storybook 7, the ${chalk.yellow('start-storybook')} and ${chalk.yellow(
+ Starting in Storybook 7, the ${picocolors.yellow('start-storybook')} and ${picocolors.yellow(
'build-storybook'
- )} binaries have changed to ${chalk.magenta('storybook dev')} and ${chalk.magenta(
+ )} binaries have changed to ${picocolors.magenta('storybook dev')} and ${picocolors.magenta(
'storybook build'
)} respectively.
In order to work with ${sbFormatted}, your storybook scripts have to be adjusted to use the binary. We can adjust them for you:
${newScriptsMessage.join('\n\n')}
- In case this migration did not cover all of your scripts, or you'd like more info: ${chalk.yellow(
+ In case this migration did not cover all of your scripts, or you'd like more info: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#start-storybook--build-storybook-binaries-removed'
)}
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/storyshots-migration.ts b/code/lib/cli-storybook/src/automigrate/fixes/storyshots-migration.ts
index 7024a4763c6f..b6836584edd3 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/storyshots-migration.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/storyshots-migration.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import type { Fix } from '../types';
@@ -21,12 +21,12 @@ export const storyshotsMigration: Fix = {
},
prompt() {
return dedent`
- ${chalk.bold(
+ ${picocolors.bold(
'Attention'
)}: Storyshots is now officially deprecated, is no longer being maintained, and was removed in Storybook 8.
We recommend following the migration guide we've prepared to help you during this transition period:
- ${chalk.yellow('https://storybook.js.org/docs/writing-tests/storyshots-migration-guide')}
+ ${picocolors.yellow('https://storybook.js.org/docs/writing-tests/storyshots-migration-guide')}
`;
},
};
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/upgrade-storybook-related-dependencies.ts b/code/lib/cli-storybook/src/automigrate/fixes/upgrade-storybook-related-dependencies.ts
index 0df85cb43635..40200ab9ac41 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/upgrade-storybook-related-dependencies.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/upgrade-storybook-related-dependencies.ts
@@ -1,7 +1,7 @@
import type { JsPackageManager } from 'storybook/internal/common';
import { isCorePackage } from 'storybook/internal/common';
-import { cyan, yellow } from 'chalk';
+import { cyan, yellow } from 'picocolors';
import { gt } from 'semver';
import { dedent } from 'ts-dedent';
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/vta.ts b/code/lib/cli-storybook/src/automigrate/fixes/vta.ts
index f2c174bd9d01..5232412e2fa1 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/vta.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/vta.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import { getAddonNames, updateMainConfig } from '../helpers/mainConfigFile';
@@ -31,7 +31,7 @@ export const vta: Fix = {
return dedent`
New to Storybook 8: Storybook's Visual Tests addon helps you catch unintentional changes/bugs in your stories. The addon is powered by Chromatic, a cloud-based testing tool developed by Storybook's core team.
- Learn more: ${chalk.yellow('https://storybook.js.org/docs/writing-tests/visual-testing')}
+ Learn more: ${picocolors.yellow('https://storybook.js.org/docs/writing-tests/visual-testing')}
Install Visual Tests addon in your project?
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/vue3.ts b/code/lib/cli-storybook/src/automigrate/fixes/vue3.ts
index aa04378d0622..1ce497a8cee7 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/vue3.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/vue3.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import semver from 'semver';
import { dedent } from 'ts-dedent';
@@ -35,17 +35,17 @@ export const vue3: Fix = {
},
prompt({ vueVersion, storybookVersion }) {
- const vueFormatted = chalk.cyan(`Vue ${vueVersion}`);
- const sbFormatted = chalk.cyan(`Storybook ${storybookVersion}`);
+ const vueFormatted = picocolors.cyan(`Vue ${vueVersion}`);
+ const sbFormatted = picocolors.cyan(`Storybook ${storybookVersion}`);
return dedent`
We've detected you are running ${vueFormatted} with Storybook.
${sbFormatted} runs webpack4 by default, which is incompatible.
- In order to work with your version of Vue, we need to install Storybook's ${chalk.cyan(
+ In order to work with your version of Vue, we need to install Storybook's ${picocolors.cyan(
'webpack5 builder'
)}.
- More info: ${chalk.yellow(
+ More info: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#vue3-upgrade'
)}
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/webpack5-compiler-setup.test.ts b/code/lib/cli-storybook/src/automigrate/fixes/webpack5-compiler-setup.test.ts
index 5cf952a3a5c4..ccba633cc488 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/webpack5-compiler-setup.test.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/webpack5-compiler-setup.test.ts
@@ -36,8 +36,8 @@ vi.mock('prompts', () => {
};
});
-// mock chalk yellow and cyan
-vi.mock('chalk', () => {
+// mock picocolors yellow and cyan
+vi.mock('picocolors', () => {
return {
default: {
yellow: (str: string) => str,
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli-storybook/src/automigrate/fixes/webpack5-compiler-setup.ts
index 2462e02ff159..32ff8d93a36f 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/webpack5-compiler-setup.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/webpack5-compiler-setup.ts
@@ -8,7 +8,7 @@ import {
import { frameworkPackages } from 'storybook/internal/common';
import type { SupportedFrameworks } from 'storybook/internal/types';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import prompts from 'prompts';
import { dedent } from 'ts-dedent';
@@ -115,23 +115,23 @@ export const webpack5CompilerSetup = {
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.`);
+ The ${picocolors.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:\n
- - If you have a ${chalk.yellow('.babelrc')} (or ${chalk.yellow(
+ - If you have a ${picocolors.yellow('.babelrc')} (or ${picocolors.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(
+ - If you have a ${picocolors.yellow('.babelrc')} (or ${picocolors.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,
+ ${picocolors.yellow(
+ 'experimental.forceSwcTransforms = true'
+ )} in your ${picocolors.yellow('next.config.js')} file,
Storybook will use SWC as the compiler.
- - If you don't have a ${chalk.yellow('.babelrc')} (or ${chalk.yellow(
+ - If you don't have a ${picocolors.yellow('.babelrc')} (or ${picocolors.yellow(
'babel.config.js'
)}) file in your project, Storybook will use SWC as the compiler.
`);
@@ -142,7 +142,7 @@ export const webpack5CompilerSetup = {
- 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(
+ You can find more information here: ${picocolors.yellow(
'https://storybook.js.org/docs/8.0/builders/webpack#compiler-support'
)}
`);
@@ -152,7 +152,7 @@ export const webpack5CompilerSetup = {
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(
+ You can find more information here: ${picocolors.yellow(
'https://storybook.js.org/docs/8.0/builders/webpack#compiler-support'
)}
`);
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/webpack5.ts b/code/lib/cli-storybook/src/automigrate/fixes/webpack5.ts
index ff27a1479590..da33295d49f9 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/webpack5.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/webpack5.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import semver from 'semver';
import { dedent } from 'ts-dedent';
@@ -43,17 +43,17 @@ export const webpack5 = {
},
prompt({ webpackVersion }) {
- const webpackFormatted = chalk.cyan(`webpack ${webpackVersion}`);
+ const webpackFormatted = picocolors.cyan(`webpack ${webpackVersion}`);
return dedent`
We've detected you're running ${webpackFormatted}.
Your Storybook's main.js files specifies webpack4, which is incompatible.
- To run Storybook in webpack5-mode, we can install Storybook's ${chalk.cyan(
+ To run Storybook in webpack5-mode, we can install Storybook's ${picocolors.cyan(
'@storybook/builder-webpack5'
)} for you.
- More info: ${chalk.yellow(
+ More info: ${picocolors.yellow(
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#webpack-5-manager-build'
)}
`;
diff --git a/code/lib/cli-storybook/src/automigrate/fixes/wrap-require.ts b/code/lib/cli-storybook/src/automigrate/fixes/wrap-require.ts
index 723a2874031a..94ba2e2ade8d 100644
--- a/code/lib/cli-storybook/src/automigrate/fixes/wrap-require.ts
+++ b/code/lib/cli-storybook/src/automigrate/fixes/wrap-require.ts
@@ -1,7 +1,7 @@
import { detectPnp } from 'storybook/internal/cli';
import { readConfig } from 'storybook/internal/csf-tools';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import { updateMainConfig } from '../helpers/mainConfigFile';
@@ -50,7 +50,7 @@ export const wrapRequire: Fix = {
},
prompt({ storybookVersion, isStorybookInMonorepo }) {
- const sbFormatted = chalk.cyan(`Storybook ${storybookVersion}`);
+ const sbFormatted = picocolors.cyan(`Storybook ${storybookVersion}`);
return dedent`We have detected that you're using ${sbFormatted} in a ${
isStorybookInMonorepo ? 'monorepo' : 'PnP'
diff --git a/code/lib/cli-storybook/src/automigrate/helpers/checkWebpack5Builder.ts b/code/lib/cli-storybook/src/automigrate/helpers/checkWebpack5Builder.ts
index cc6a977454dc..6e27a27f513c 100644
--- a/code/lib/cli-storybook/src/automigrate/helpers/checkWebpack5Builder.ts
+++ b/code/lib/cli-storybook/src/automigrate/helpers/checkWebpack5Builder.ts
@@ -1,6 +1,6 @@
import type { StorybookConfigRaw } from 'storybook/internal/types';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import semver from 'semver';
import { dedent } from 'ts-dedent';
@@ -22,11 +22,11 @@ export const checkWebpack5Builder = async ({
To upgrade to the latest stable release, run this from your project directory:
- ${chalk.cyan('npx storybook@latest upgrade')}
+ ${picocolors.cyan('npx storybook@latest upgrade')}
To upgrade to the latest pre-release, run this from your project directory:
- ${chalk.cyan('npx storybook@next upgrade')}
+ ${picocolors.cyan('npx storybook@next upgrade')}
`.trim()
);
return null;
diff --git a/code/lib/cli-storybook/src/automigrate/helpers/cleanLog.ts b/code/lib/cli-storybook/src/automigrate/helpers/cleanLog.ts
index 7cb8c864197a..2f8a7b7ed856 100644
--- a/code/lib/cli-storybook/src/automigrate/helpers/cleanLog.ts
+++ b/code/lib/cli-storybook/src/automigrate/helpers/cleanLog.ts
@@ -13,7 +13,7 @@ export const ansiRegex = ({ onlyFirst = false } = {}) => {
export const cleanLog = (str: string) =>
str
- // remove chalk ANSI colors
+ // remove picocolors ANSI colors
.replace(ansiRegex(), '')
// fix boxen output
.replace(/╮│/g, '╮\n│')
diff --git a/code/lib/cli-storybook/src/automigrate/helpers/getMigrationSummary.ts b/code/lib/cli-storybook/src/automigrate/helpers/getMigrationSummary.ts
index 3aaaf7542cd6..67e07c9a5b29 100644
--- a/code/lib/cli-storybook/src/automigrate/helpers/getMigrationSummary.ts
+++ b/code/lib/cli-storybook/src/automigrate/helpers/getMigrationSummary.ts
@@ -1,7 +1,7 @@
import { type InstallationMetadata } from 'storybook/internal/common';
import boxen from 'boxen';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import type { FixSummary } from '../types';
@@ -17,34 +17,36 @@ function getGlossaryMessages(
) {
const messages = [];
if (fixSummary.succeeded.length > 0) {
- messages.push(chalk.bold('Successful migrations:'));
- messages.push(fixSummary.succeeded.map((m) => chalk.green(m)).join(', '));
+ messages.push(picocolors.bold('Successful migrations:'));
+ messages.push(fixSummary.succeeded.map((m) => picocolors.green(m)).join(', '));
}
if (Object.keys(fixSummary.failed).length > 0) {
- messages.push(chalk.bold('Failed migrations:'));
+ messages.push(picocolors.bold('Failed migrations:'));
messages.push(
Object.entries(fixSummary.failed)
.map(([id, error]) => {
- return `${chalk.redBright(id)}:\n${error}`;
+ return `${picocolors.bold(picocolors.red(id))}:\n${error}`;
})
.join('\n')
);
- messages.push(`You can find the full logs in ${chalk.cyan(logFile)}`);
+ messages.push(`You can find the full logs in ${picocolors.cyan(logFile)}`);
}
if (fixSummary.manual.length > 0) {
- messages.push(chalk.bold('Manual migrations:'));
+ messages.push(picocolors.bold('Manual migrations:'));
messages.push(
fixSummary.manual
- .map((m) => (fixResults[m] === FixStatus.MANUAL_SUCCEEDED ? chalk.green(m) : chalk.blue(m)))
+ .map((m) =>
+ fixResults[m] === FixStatus.MANUAL_SUCCEEDED ? picocolors.green(m) : picocolors.blue(m)
+ )
.join(', ')
);
}
if (fixSummary.skipped.length > 0) {
- messages.push(chalk.bold('Skipped migrations:'));
- messages.push(fixSummary.skipped.map((m) => chalk.cyan(m)).join(', '));
+ messages.push(picocolors.bold('Skipped migrations:'));
+ messages.push(fixSummary.skipped.map((m) => picocolors.cyan(m)).join(', '));
}
return messages;
@@ -64,16 +66,16 @@ export function getMigrationSummary({
const messages = [];
messages.push(getGlossaryMessages(fixSummary, fixResults, logFile).join(messageDivider));
- messages.push(dedent`If you'd like to run the migrations again, you can do so by running '${chalk.cyan(
+ messages.push(dedent`If you'd like to run the migrations again, you can do so by running '${picocolors.cyan(
'npx storybook automigrate'
)}'
The automigrations try to migrate common patterns in your project, but might not contain everything needed to migrate to the latest version of Storybook.
- Please check the changelog and migration guide for manual migrations and more information: ${chalk.yellow(
+ Please check the changelog and migration guide for manual migrations and more information: ${picocolors.yellow(
'https://storybook.js.org/docs/8.0/migration-guide'
)}
- And reach out on Discord if you need help: ${chalk.yellow('https://discord.gg/storybook')}
+ And reach out on Discord if you need help: ${picocolors.yellow('https://discord.gg/storybook')}
`);
const hasNoFixes = Object.values(fixResults).every((r) => r === FixStatus.UNNECESSARY);
diff --git a/code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.ts b/code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.ts
index bf9daf36c7ae..57aa4bf7ce07 100644
--- a/code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.ts
+++ b/code/lib/cli-storybook/src/automigrate/helpers/mainConfigFile.ts
@@ -15,7 +15,7 @@ import type { ConfigFile } from 'storybook/internal/csf-tools';
import { readConfig, writeConfig as writeConfigFile } from 'storybook/internal/csf-tools';
import type { StorybookConfig, StorybookConfigRaw } from 'storybook/internal/types';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
const logger = console;
@@ -94,7 +94,9 @@ export const getBuilderPackageName = (mainConfig?: StorybookConfigRaw) => {
export const getFrameworkOptions = (
mainConfig?: StorybookConfigRaw
): Record | null => {
- return typeof mainConfig?.framework === 'string' ? null : mainConfig?.framework?.options ?? null;
+ return typeof mainConfig?.framework === 'string'
+ ? null
+ : (mainConfig?.framework?.options ?? null);
};
/**
@@ -144,7 +146,7 @@ export const getStorybookData = async ({
mainConfig = (await loadMainConfig({ configDir, noCache: true })) as StorybookConfigRaw;
} catch (err) {
throw new Error(
- dedent`Unable to find or evaluate ${chalk.blue(mainConfigPath)}: ${String(err)}`
+ dedent`Unable to find or evaluate ${picocolors.blue(mainConfigPath)}: ${String(err)}`
);
}
@@ -187,13 +189,13 @@ export const updateMainConfig = async (
}
} catch (e) {
logger.info(
- `❌ The migration failed to update your ${chalk.blue(
+ `❌ The migration failed to update your ${picocolors.blue(
mainConfigPath
)} on your behalf because of the following error:
${e}\n`
);
logger.info(
- `⚠️ Storybook automigrations are based on AST parsing and it's possible that your ${chalk.blue(
+ `⚠️ Storybook automigrations are based on AST parsing and it's possible that your ${picocolors.blue(
mainConfigPath
)} file contains a non-standard format (e.g. your export is not an object) or that there was an error when parsing dynamic values (e.g. "require" calls, or usage of environment variables). When your main config is non-standard, automigrations are unfortunately not possible. Please follow the instructions given previously and follow the documentation to make the updates manually.`
);
diff --git a/code/lib/cli-storybook/src/automigrate/helpers/new-frameworks-utils.ts b/code/lib/cli-storybook/src/automigrate/helpers/new-frameworks-utils.ts
index a4ab6ee34a4f..280bb2cf5034 100644
--- a/code/lib/cli-storybook/src/automigrate/helpers/new-frameworks-utils.ts
+++ b/code/lib/cli-storybook/src/automigrate/helpers/new-frameworks-utils.ts
@@ -77,14 +77,14 @@ export const detectBuilderInfo = async ({
const builderPackageName = getBuilderPackageName(mainConfig);
const frameworkPackageName = getFrameworkPackageName(mainConfig) as string;
- let builderOptions = typeof builder !== 'string' ? builder?.options ?? {} : {};
+ let builderOptions = typeof builder !== 'string' ? (builder?.options ?? {}) : {};
if (builderPackageName) {
builderOrFrameworkName = builderPackageName;
} else if (framework) {
if (Object.keys(frameworkPackages).includes(frameworkPackageName)) {
builderOrFrameworkName = frameworkPackageName;
- builderOptions = typeof framework === 'object' ? framework.options?.builder ?? {} : {};
+ builderOptions = typeof framework === 'object' ? (framework.options?.builder ?? {}) : {};
}
}
diff --git a/code/lib/cli-storybook/src/automigrate/index.ts b/code/lib/cli-storybook/src/automigrate/index.ts
index 690c3d1b15f7..9d98d97d7013 100644
--- a/code/lib/cli-storybook/src/automigrate/index.ts
+++ b/code/lib/cli-storybook/src/automigrate/index.ts
@@ -1,3 +1,5 @@
+import { createWriteStream } from 'node:fs';
+import { rename, rm } from 'node:fs/promises';
import { join } from 'node:path';
import {
@@ -9,8 +11,7 @@ import {
} from 'storybook/internal/common';
import boxen from 'boxen';
-import chalk from 'chalk';
-import { createWriteStream, move, remove } from 'fs-extra';
+import picocolors from 'picocolors';
import prompts from 'prompts';
import semver from 'semver';
import invariant from 'tiny-invariant';
@@ -60,7 +61,7 @@ const cleanup = () => {
const logAvailableMigrations = () => {
const availableFixes = allFixes
- .map((f) => chalk.yellow(f.id))
+ .map((f) => picocolors.yellow(f.id))
.map((x) => `- ${x}`)
.join('\n');
@@ -153,7 +154,7 @@ export const automigrate = async ({
const fixes: Fix[] = fixId ? selectedFixes.filter((f) => f.id === fixId) : selectedFixes;
if (fixId && fixes.length === 0) {
- logger.info(`📭 No migrations found for ${chalk.magenta(fixId)}.`);
+ logger.info(`📭 No migrations found for ${picocolors.magenta(fixId)}.`);
logAvailableMigrations();
return null;
}
@@ -182,9 +183,9 @@ export const automigrate = async ({
// if migration failed, display a log file in the users cwd
if (hasFailures) {
- await move(TEMP_LOG_FILE_PATH, join(process.cwd(), LOG_FILE_NAME), { overwrite: true });
+ await rename(TEMP_LOG_FILE_PATH, join(process.cwd(), LOG_FILE_NAME));
} else {
- await remove(TEMP_LOG_FILE_PATH);
+ await rm(TEMP_LOG_FILE_PATH, { recursive: true, force: true });
}
if (!hideMigrationSummary) {
@@ -264,7 +265,7 @@ export async function runFixes({
});
}
} catch (error) {
- logger.info(`⚠️ failed to check fix ${chalk.bold(f.id)}`);
+ logger.info(`⚠️ failed to check fix ${picocolors.bold(f.id)}`);
if (error instanceof Error) {
logger.error(`\n${error.stack}`);
fixSummary.failed[f.id] = error.message;
@@ -274,9 +275,9 @@ export async function runFixes({
if (result) {
const promptType: Prompt =
- typeof f.promptType === 'function' ? await f.promptType(result) : f.promptType ?? 'auto';
+ typeof f.promptType === 'function' ? await f.promptType(result) : (f.promptType ?? 'auto');
- logger.info(`\n🔎 found a '${chalk.cyan(f.id)}' migration:`);
+ logger.info(`\n🔎 found a '${picocolors.cyan(f.id)}' migration:`);
const message = f.prompt(result);
const getTitle = () => {
@@ -341,7 +342,9 @@ export async function runFixes({
{
type: 'confirm',
name: 'fix',
- message: `Do you want to run the '${chalk.cyan(f.id)}' migration on your project?`,
+ message: `Do you want to run the '${picocolors.cyan(
+ f.id
+ )}' migration on your project?`,
initial: f.promptDefaultValue ?? true,
},
{
@@ -382,7 +385,7 @@ export async function runFixes({
mainConfigPath,
skipInstall,
});
- logger.info(`✅ ran ${chalk.cyan(f.id)} migration`);
+ logger.info(`✅ ran ${picocolors.cyan(f.id)} migration`);
fixResults[f.id] = FixStatus.SUCCEEDED;
fixSummary.succeeded.push(f.id);
@@ -391,7 +394,7 @@ export async function runFixes({
fixSummary.failed[f.id] =
error instanceof Error ? error.message : 'Failed to run migration';
- logger.info(`❌ error when running ${chalk.cyan(f.id)} migration`);
+ logger.info(`❌ error when running ${picocolors.cyan(f.id)} migration`);
logger.info(error);
logger.info();
}
diff --git a/code/lib/cli-storybook/src/bin/index.ts b/code/lib/cli-storybook/src/bin/index.ts
index 34f70b540a0b..33105a269d68 100644
--- a/code/lib/cli-storybook/src/bin/index.ts
+++ b/code/lib/cli-storybook/src/bin/index.ts
@@ -7,11 +7,11 @@ import { withTelemetry } from 'storybook/internal/core-server';
import { logger } from 'storybook/internal/node-logger';
import { addToGlobalContext, telemetry } from 'storybook/internal/telemetry';
-import chalk from 'chalk';
import { program } from 'commander';
import envinfo from 'envinfo';
import { findPackageSync } from 'fd-package-json';
import leven from 'leven';
+import picocolors from 'picocolors';
import invariant from 'tiny-invariant';
import { add } from '../add';
@@ -81,7 +81,7 @@ command('upgrade')
command('info')
.description('Prints debugging information about the local environment')
.action(async () => {
- consoleLogger.log(chalk.bold('\nStorybook Environment Info:'));
+ consoleLogger.log(picocolors.bold('\nStorybook Environment Info:'));
const pkgManager = await JsPackageManagerFactory.getPackageManager();
const activePackageManager = pkgManager.type.replace(/\d/, ''); // 'yarn1' -> 'yarn'
const output = await envinfo.run({
@@ -95,7 +95,7 @@ command('info')
consoleLogger.log(
output.replace(
activePackageManagerLine,
- chalk.bold(`${activePackageManagerLine} <----- active`)
+ picocolors.bold(`${activePackageManagerLine} <----- active`)
)
);
});
diff --git a/code/lib/cli-storybook/src/doctor/getDuplicatedDepsWarnings.ts b/code/lib/cli-storybook/src/doctor/getDuplicatedDepsWarnings.ts
index 871bb13e4805..617713f5adfc 100644
--- a/code/lib/cli-storybook/src/doctor/getDuplicatedDepsWarnings.ts
+++ b/code/lib/cli-storybook/src/doctor/getDuplicatedDepsWarnings.ts
@@ -1,7 +1,7 @@
import { frameworkPackages, rendererPackages } from 'storybook/internal/common';
import type { InstallationMetadata } from 'storybook/internal/common';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import { hasMultipleVersions } from './hasMultipleVersions';
@@ -67,9 +67,11 @@ export function getDuplicatedDepsWarnings(
const hasMultipleMajorVersions = hasMultipleVersions(packageVersions);
if (disallowList.includes(dep) && hasMultipleMajorVersions) {
- acc.critical.push(`${chalk.redBright(dep)}:\n${packageVersions.join(', ')}`);
+ acc.critical.push(
+ `${picocolors.bold(picocolors.red(dep))}:\n${packageVersions.join(', ')}`
+ );
} else {
- acc.trivial.push(`${chalk.hex('#ff9800')(dep)}:\n${packageVersions.join(', ')}`);
+ acc.trivial.push(`${picocolors.yellow(dep)}:\n${packageVersions.join(', ')}`);
}
return acc;
@@ -83,7 +85,7 @@ export function getDuplicatedDepsWarnings(
if (critical.length > 0) {
messages.push(
- `${chalk.bold(
+ `${picocolors.bold(
'Critical:'
)} The following dependencies are duplicated and WILL cause unexpected behavior:`
);
@@ -92,7 +94,7 @@ export function getDuplicatedDepsWarnings(
if (trivial.length > 0) {
messages.push(
- `${chalk.bold(
+ `${picocolors.bold(
'Attention:'
)} The following dependencies are duplicated which might cause unexpected behavior:`
);
@@ -101,14 +103,14 @@ export function getDuplicatedDepsWarnings(
messages.push(
'\n',
- `Please try de-duplicating these dependencies by running ${chalk.cyan(
+ `Please try de-duplicating these dependencies by running ${picocolors.cyan(
`${installationMetadata.dedupeCommand}`
)}`
);
messages.push(
'\n',
- `You can find more information for a given dependency by running ${chalk.cyan(
+ `You can find more information for a given dependency by running ${picocolors.cyan(
`${installationMetadata.infoCommand} `
)}`
);
diff --git a/code/lib/cli-storybook/src/doctor/getIncompatibleStorybookPackages.test.ts b/code/lib/cli-storybook/src/doctor/getIncompatibleStorybookPackages.test.ts
index b804567c9a7a..9ed4ff7b34b0 100644
--- a/code/lib/cli-storybook/src/doctor/getIncompatibleStorybookPackages.test.ts
+++ b/code/lib/cli-storybook/src/doctor/getIncompatibleStorybookPackages.test.ts
@@ -9,7 +9,7 @@ import {
getIncompatibleStorybookPackages,
} from './getIncompatibleStorybookPackages';
-vi.mock('chalk', () => {
+vi.mock('picocolors', () => {
return {
default: {
yellow: (str: string) => str,
diff --git a/code/lib/cli-storybook/src/doctor/getIncompatibleStorybookPackages.ts b/code/lib/cli-storybook/src/doctor/getIncompatibleStorybookPackages.ts
index 0c65c5902701..bc2af0904884 100644
--- a/code/lib/cli-storybook/src/doctor/getIncompatibleStorybookPackages.ts
+++ b/code/lib/cli-storybook/src/doctor/getIncompatibleStorybookPackages.ts
@@ -5,7 +5,7 @@ import {
versions as storybookCorePackages,
} from 'storybook/internal/common';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import semver from 'semver';
export type AnalysedPackage = {
@@ -107,15 +107,15 @@ export const getIncompatiblePackagesSummary = (
if (incompatiblePackages.length > 0) {
summaryMessage.push(
- `The following packages are incompatible with Storybook ${chalk.bold(
+ `The following packages are incompatible with Storybook ${picocolors.bold(
currentStorybookVersion
)} as they depend on different major versions of Storybook packages:`
);
incompatiblePackages.forEach(
({ packageName: addonName, packageVersion: addonVersion, homepage, availableUpdate }) => {
- const packageDescription = `${chalk.cyan(addonName)}@${chalk.cyan(addonVersion)}`;
+ const packageDescription = `${picocolors.cyan(addonName)}@${picocolors.cyan(addonVersion)}`;
const updateMessage = availableUpdate ? ` (${availableUpdate} available!)` : '';
- const packageRepo = homepage ? `\n Repo: ${chalk.yellow(homepage)}` : '';
+ const packageRepo = homepage ? `\n Repo: ${picocolors.yellow(homepage)}` : '';
summaryMessage.push(`- ${packageDescription}${updateMessage}${packageRepo}`);
}
@@ -125,7 +125,7 @@ export const getIncompatiblePackagesSummary = (
'\n',
'Please consider updating your packages or contacting the maintainers for compatibility details.',
'For more on Storybook 8 compatibility, see the linked GitHub issue:',
- chalk.yellow('https://github.com/storybookjs/storybook/issues/26031')
+ picocolors.yellow('https://github.com/storybookjs/storybook/issues/26031')
);
}
diff --git a/code/lib/cli-storybook/src/doctor/getMismatchingVersionsWarning.ts b/code/lib/cli-storybook/src/doctor/getMismatchingVersionsWarning.ts
index 00cd470c1b28..da07203e6d26 100644
--- a/code/lib/cli-storybook/src/doctor/getMismatchingVersionsWarning.ts
+++ b/code/lib/cli-storybook/src/doctor/getMismatchingVersionsWarning.ts
@@ -1,7 +1,7 @@
import { frameworkPackages, versions as storybookCorePackages } from 'storybook/internal/common';
import type { InstallationMetadata } from 'storybook/internal/common';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import semver from 'semver';
function getPrimaryVersion(name: string | undefined, installationMetadata?: InstallationMetadata) {
@@ -39,7 +39,7 @@ export function getMismatchingVersionsWarnings(
}
messages.push(
- `${chalk.bold(
+ `${picocolors.bold(
'Attention:'
)} There seems to be a mismatch between your Storybook package versions. This can result in a broken Storybook installation.`
);
@@ -55,9 +55,9 @@ export function getMismatchingVersionsWarnings(
}
messages.push(
- `The version of your storybook core packages should align with ${chalk.yellow(
+ `The version of your storybook core packages should align with ${picocolors.yellow(
versionToCompare
- )} (from the ${chalk.cyan(packageToDisplay)} package) or higher.`
+ )} (from the ${picocolors.cyan(packageToDisplay)} package) or higher.`
);
const filteredDependencies = Object.entries(installationMetadata?.dependencies || []).filter(
@@ -78,7 +78,7 @@ export function getMismatchingVersionsWarnings(
filteredDependencies
.map(
([name, dep]) =>
- `${chalk.hex('#ff9800')(name)}: ${dep[0].version} ${
+ `${picocolors.yellow(name)}: ${dep[0].version} ${
allDependencies?.[name] ? packageJsonSuffix : ''
}`
)
@@ -91,11 +91,11 @@ export function getMismatchingVersionsWarnings(
}
messages.push(
- `You can run ${chalk.cyan(
+ `You can run ${picocolors.cyan(
'npx storybook@latest upgrade'
)} to upgrade all of your Storybook packages to the latest version.
- Alternatively you can try manually changing the versions to match in your package.json. We also recommend regenerating your lockfile, or running the following command to possibly deduplicate your Storybook package versions: ${chalk.cyan(
+ Alternatively you can try manually changing the versions to match in your package.json. We also recommend regenerating your lockfile, or running the following command to possibly deduplicate your Storybook package versions: ${picocolors.cyan(
installationMetadata?.dedupeCommand
)}`
);
diff --git a/code/lib/cli-storybook/src/doctor/index.ts b/code/lib/cli-storybook/src/doctor/index.ts
index 08e798288753..1c7a03477c80 100644
--- a/code/lib/cli-storybook/src/doctor/index.ts
+++ b/code/lib/cli-storybook/src/doctor/index.ts
@@ -1,11 +1,12 @@
+import { createWriteStream } from 'node:fs';
+import { rename, rm } from 'node:fs/promises';
import { join } from 'node:path';
import { JsPackageManagerFactory, temporaryFile } from 'storybook/internal/common';
import type { PackageManagerName } from 'storybook/internal/common';
import boxen from 'boxen';
-import chalk from 'chalk';
-import { createWriteStream, move, remove } from 'fs-extra';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import { cleanLog } from '../automigrate/helpers/cleanLog';
@@ -83,7 +84,7 @@ export const doctor = async ({
} catch (err: any) {
if (err.message.includes('No configuration files have been found')) {
logger.info(
- dedent`[Storybook doctor] Could not find or evaluate your Storybook main.js config directory at ${chalk.blue(
+ dedent`[Storybook doctor] Could not find or evaluate your Storybook main.js config directory at ${picocolors.blue(
userSpecifiedConfigDir || '.storybook'
)} so the doctor command cannot proceed. You might be running this command in a monorepo or a non-standard project structure. If that is the case, please rerun this command by specifying the path to your Storybook config directory via the --config-dir option.`
);
@@ -108,10 +109,10 @@ export const doctor = async ({
if (!('storybook' in allDependencies)) {
logDiagnostic(
- `Package ${chalk.cyan('storybook')} not found`,
+ `Package ${picocolors.cyan('storybook')} not found`,
dedent`
- The ${chalk.cyan('storybook')} package was not found in your package.json.
- Installing ${chalk.cyan('storybook')} as a direct dev dependency in your package.json is required.
+ The ${picocolors.cyan('storybook')} package was not found in your package.json.
+ Installing ${picocolors.cyan('storybook')} as a direct dev dependency in your package.json is required.
`
);
}
@@ -150,7 +151,7 @@ export const doctor = async ({
}
}
- const commandMessage = `You can always recheck the health of your project by running:\n${chalk.cyan(
+ const commandMessage = `You can always recheck the health of your project by running:\n${picocolors.cyan(
'npx storybook doctor'
)}`;
logger.info();
@@ -159,13 +160,13 @@ export const doctor = async ({
logger.info(commandMessage);
logger.info();
- logger.info(`Full logs are available in ${chalk.cyan(LOG_FILE_PATH)}`);
+ logger.info(`Full logs are available in ${picocolors.cyan(LOG_FILE_PATH)}`);
- await move(TEMP_LOG_FILE_PATH, join(process.cwd(), LOG_FILE_NAME), { overwrite: true });
+ await rename(TEMP_LOG_FILE_PATH, join(process.cwd(), LOG_FILE_NAME));
} else {
logger.info(`🥳 Your Storybook project looks good!`);
logger.info(commandMessage);
- await remove(TEMP_LOG_FILE_PATH);
+ await rm(TEMP_LOG_FILE_PATH, { recursive: true, force: true });
}
logger.info();
diff --git a/code/lib/cli-storybook/src/link.ts b/code/lib/cli-storybook/src/link.ts
index 369d668d2c2a..5db8d2cbd930 100644
--- a/code/lib/cli-storybook/src/link.ts
+++ b/code/lib/cli-storybook/src/link.ts
@@ -1,10 +1,10 @@
+import { mkdir, readFile } from 'node:fs/promises';
import { basename, extname, join } from 'node:path';
import { logger } from 'storybook/internal/node-logger';
-import chalk from 'chalk';
import { spawn as spawnAsync, sync as spawnSync } from 'cross-spawn';
-import fse from 'fs-extra';
+import picocolors from 'picocolors';
type ExecOptions = Parameters[2];
@@ -48,7 +48,7 @@ export const exec = async (
if (code === 0) {
resolve(undefined);
} else {
- logger.error(chalk.red(`An error occurred while executing: \`${command}\``));
+ logger.error(picocolors.red(`An error occurred while executing: \`${command}\``));
if (errorMessage) {
logger.info(errorMessage);
}
@@ -61,7 +61,7 @@ export const exec = async (
export const link = async ({ target, local, start }: LinkOptions) => {
const storybookDir = process.cwd();
try {
- const packageJson = await fse.readJSON('package.json');
+ const packageJson = JSON.parse(await readFile('package.json', { encoding: 'utf8' }));
if (packageJson.name !== '@storybook/root') {
throw new Error();
}
@@ -75,7 +75,9 @@ export const link = async ({ target, local, start }: LinkOptions) => {
if (!local) {
const reprosDir = join(storybookDir, '../storybook-repros');
logger.info(`Ensuring directory ${reprosDir}`);
- await fse.ensureDir(reprosDir);
+ // Passing `recursive: true` ensures that the method doesn't throw when
+ // the directory already exists.
+ await mkdir(reprosDir, { recursive: true });
logger.info(`Cloning ${target}`);
await exec(`git clone ${target}`, { cwd: reprosDir });
@@ -84,7 +86,9 @@ export const link = async ({ target, local, start }: LinkOptions) => {
reproDir = join(reprosDir, reproName);
}
- const reproPackageJson = await fse.readJSON(join(reproDir, 'package.json'));
+ const reproPackageJson = JSON.parse(
+ await readFile(join(reproDir, 'package.json'), { encoding: 'utf8' })
+ );
const version = spawnSync('yarn', ['--version'], {
cwd: reproDir,
diff --git a/code/lib/cli-storybook/src/sandbox.ts b/code/lib/cli-storybook/src/sandbox.ts
index a6b4fe40f043..3b43a05af5d5 100644
--- a/code/lib/cli-storybook/src/sandbox.ts
+++ b/code/lib/cli-storybook/src/sandbox.ts
@@ -1,3 +1,5 @@
+import { existsSync } from 'node:fs';
+import { readdir } from 'node:fs/promises';
import { isAbsolute, join } from 'node:path';
import type { PackageManagerName } from 'storybook/internal/common';
@@ -5,10 +7,9 @@ import { JsPackageManagerFactory } from 'storybook/internal/common';
import { versions } from 'storybook/internal/common';
import boxen from 'boxen';
-import chalk from 'chalk';
import { initiate } from 'create-storybook';
-import { existsSync, readdir } from 'fs-extra';
import { downloadTemplate } from 'giget';
+import picocolors from 'picocolors';
import prompts from 'prompts';
import { lt, prerelease } from 'semver';
import invariant from 'tiny-invariant';
@@ -56,18 +57,18 @@ export const sandbox = async ({
const branch = isPrerelease ? 'next' : 'main';
const messages = {
- welcome: `Creating a Storybook ${chalk.bold(currentVersion)} sandbox..`,
- notLatest: chalk.red(dedent`
- This version is behind the latest release, which is: ${chalk.bold(latestVersion)}!
+ welcome: `Creating a Storybook ${picocolors.bold(currentVersion)} sandbox..`,
+ notLatest: picocolors.red(dedent`
+ This version is behind the latest release, which is: ${picocolors.bold(latestVersion)}!
You likely ran the init command through npx, which can use a locally cached version, to get the latest please run:
- ${chalk.bold('npx storybook@latest sandbox')}
+ ${picocolors.bold('npx storybook@latest sandbox')}
You may want to CTRL+C to stop, and run with the latest version instead.
`),
- longInitTime: chalk.yellow(
+ longInitTime: picocolors.yellow(
'The creation of the sandbox will take longer, because we will need to run init.'
),
- prerelease: chalk.yellow('This is a pre-release version.'),
+ prerelease: picocolors.yellow('This is a pre-release version.'),
};
logger.log(
@@ -114,12 +115,12 @@ export const sandbox = async ({
dedent`
🔎 You filtered out all templates. 🔍
- After filtering all the templates with "${chalk.yellow(
+ After filtering all the templates with "${picocolors.yellow(
filterValue
)}", we found no results. Please try again with a different filter.
Available templates:
- ${keys.map((key) => chalk.blue`- ${key}`).join('\n')}
+ ${keys.map((key) => picocolors.blue(`- ${key}`)).join('\n')}
`.trim(),
{ borderStyle: 'round', padding: 1, borderColor: '#F1618C' } as any
)
@@ -133,9 +134,9 @@ export const sandbox = async ({
logger.info(
boxen(
dedent`
- 🤗 Welcome to ${chalk.yellow('sb sandbox')}! 🤗
+ 🤗 Welcome to ${picocolors.yellow('sb sandbox')}! 🤗
- Create a ${chalk.green('new project')} to minimally reproduce Storybook issues.
+ Create a ${picocolors.green('new project')} to minimally reproduce Storybook issues.
1. select an environment that most closely matches your project setup.
2. select a location for the reproduction, outside of your project.
@@ -198,7 +199,7 @@ export const sandbox = async ({
logger.info(`🏃 Adding ${selectedConfig.name} into ${templateDestination}`);
- logger.log(`📦 Downloading sandbox template (${chalk.bold(downloadType)})...`);
+ logger.log(`📦 Downloading sandbox template (${picocolors.bold(downloadType)})...`);
try {
// Download the sandbox based on subfolder "after-storybook" and selected branch
const gitPath = `github:storybookjs/sandboxes/${templateId}/${downloadType}#${branch}`;
@@ -206,11 +207,11 @@ export const sandbox = async ({
force: true,
dir: templateDestination,
});
- // throw an error if templateDestination is an empty directory using fs-extra
+ // throw an error if templateDestination is an empty directory
if ((await readdir(templateDestination)).length === 0) {
- const selected = chalk.yellow(templateId);
+ const selected = picocolors.yellow(templateId);
throw new Error(dedent`
- Template downloaded from ${chalk.blue(gitPath)} is empty.
+ Template downloaded from ${picocolors.blue(gitPath)} is empty.
Are you use it exists? Or did you want to set ${selected} to inDevelopment first?
`);
}
@@ -236,23 +237,23 @@ export const sandbox = async ({
}
const initMessage = init
- ? chalk.yellow(dedent`
+ ? picocolors.yellow(dedent`
yarn install
yarn storybook
`)
- : `Recreate your setup, then ${chalk.yellow(`npx storybook@latest init`)}`;
+ : `Recreate your setup, then ${picocolors.yellow(`npx storybook@latest init`)}`;
logger.info(
boxen(
dedent`
🎉 Your Storybook reproduction project is ready to use! 🎉
- ${chalk.yellow(`cd ${selectedDirectory}`)}
+ ${picocolors.yellow(`cd ${selectedDirectory}`)}
${initMessage}
Once you've recreated the problem you're experiencing, please:
- 1. Document any additional steps in ${chalk.cyan('README.md')}
+ 1. Document any additional steps in ${picocolors.cyan('README.md')}
2. Publish the repository to github
3. Link to the repro repository in your issue
diff --git a/code/lib/cli-storybook/src/upgrade.ts b/code/lib/cli-storybook/src/upgrade.ts
index b880838199c4..6657fb0ee691 100644
--- a/code/lib/cli-storybook/src/upgrade.ts
+++ b/code/lib/cli-storybook/src/upgrade.ts
@@ -18,8 +18,8 @@ import {
import { telemetry } from 'storybook/internal/telemetry';
import boxen from 'boxen';
-import chalk from 'chalk';
import { sync as spawnSync } from 'cross-spawn';
+import picocolors from 'picocolors';
import semver, { clean, eq, lt, prerelease } from 'semver';
import { dedent } from 'ts-dedent';
@@ -171,17 +171,19 @@ export const doUpgrade = async ({
const borderColor = isCLIOutdated ? '#FC521F' : '#F1618C';
const messages = {
- welcome: `Upgrading Storybook from version ${chalk.bold(beforeVersion)} to version ${chalk.bold(
- currentCLIVersion
- )}..`,
- notLatest: chalk.red(dedent`
- This version is behind the latest release, which is: ${chalk.bold(latestCLIVersionOnNPM)}!
+ welcome: `Upgrading Storybook from version ${picocolors.bold(
+ beforeVersion
+ )} to version ${picocolors.bold(currentCLIVersion)}..`,
+ notLatest: picocolors.red(dedent`
+ This version is behind the latest release, which is: ${picocolors.bold(
+ latestCLIVersionOnNPM
+ )}!
You likely ran the upgrade command through npx, which can use a locally cached version, to upgrade to the latest version please run:
- ${chalk.bold('npx storybook@latest upgrade')}
+ ${picocolors.bold('npx storybook@latest upgrade')}
You may want to CTRL+C to stop, and run with the latest version instead.
`),
- prerelease: chalk.yellow('This is a pre-release version.'),
+ prerelease: picocolors.yellow('This is a pre-release version.'),
};
logger.plain(
@@ -252,7 +254,7 @@ export const doUpgrade = async ({
const upgradedDependencies = toUpgradedDependencies(packageJson.dependencies);
const upgradedDevDependencies = toUpgradedDependencies(packageJson.devDependencies);
- logger.info(`Updating dependencies in ${chalk.cyan('package.json')}..`);
+ logger.info(`Updating dependencies in ${picocolors.cyan('package.json')}..`);
if (upgradedDependencies.length > 0) {
await packageManager.addDependencies(
{ installAsDevDependencies: false, skipInstall: true, packageJson },
diff --git a/code/lib/cli/package.json b/code/lib/cli/package.json
index bda6d2d79d6f..0393c4a2cce7 100644
--- a/code/lib/cli/package.json
+++ b/code/lib/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "storybook",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook's CLI - install, dev, build, upgrade, and more",
"keywords": [
"cli",
@@ -322,6 +322,14 @@
"devDependencies": {
"typescript": "^5.3.2"
},
+ "peerDependencies": {
+ "prettier": "^2 || ^3"
+ },
+ "peerDependenciesMeta": {
+ "prettier": {
+ "optional": true
+ }
+ },
"publishConfig": {
"access": "public"
},
diff --git a/code/lib/cli/project.json b/code/lib/cli/project.json
index 82cebd8431b3..4f06674e2e03 100644
--- a/code/lib/cli/project.json
+++ b/code/lib/cli/project.json
@@ -1,5 +1,5 @@
{
- "name": "cli",
+ "name": "storybook",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {
diff --git a/code/lib/cli/scripts/update-core-portal.ts b/code/lib/cli/scripts/update-core-portal.ts
index b5896549366d..520bc3587848 100644
--- a/code/lib/cli/scripts/update-core-portal.ts
+++ b/code/lib/cli/scripts/update-core-portal.ts
@@ -1,7 +1,6 @@
+import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
-import { readJSON } from 'fs-extra';
-
import { sortPackageJson } from '../../../../scripts/node_modules/sort-package-json';
import { generateMapperContent, mapCoreExportToSelf, write } from './utils';
@@ -22,8 +21,12 @@ import { generateMapperContent, mapCoreExportToSelf, write } from './utils';
* remove those manually here.
*/
async function run() {
- const selfPackageJson = await readJSON(join(__dirname, '../package.json'));
- const corePackageJson = await readJSON(join(__dirname, '../../../core/package.json'));
+ const selfPackageJson = JSON.parse(
+ await readFile(join(__dirname, '../package.json'), { encoding: 'utf8' })
+ );
+ const corePackageJson = await JSON.parse(
+ await readFile(join(__dirname, '../../../core/package.json'), { encoding: 'utf8' })
+ );
await Promise.all(
Object.entries>(corePackageJson.exports)
diff --git a/code/lib/cli/scripts/utils.ts b/code/lib/cli/scripts/utils.ts
index e453988fad64..1c27080075c5 100644
--- a/code/lib/cli/scripts/utils.ts
+++ b/code/lib/cli/scripts/utils.ts
@@ -1,8 +1,14 @@
-import { ensureFile, writeFile } from 'fs-extra';
+import { existsSync } from 'node:fs';
+import { mkdir, writeFile } from 'node:fs/promises';
+import { dirname } from 'node:path';
+
import { dedent } from 'ts-dedent';
export const write = async (location: string, data: string) => {
- await ensureFile(location);
+ if (!existsSync(location)) {
+ const directory = dirname(location);
+ await mkdir(directory, { recursive: true });
+ }
return writeFile(location, data);
};
diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json
index d23937951533..27cbe8082728 100644
--- a/code/lib/codemod/package.json
+++ b/code/lib/codemod/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/codemod",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "A collection of codemod scripts written with JSCodeshift",
"keywords": [
"storybook"
@@ -61,9 +61,9 @@
"@storybook/csf": "^0.1.11",
"@types/cross-spawn": "^6.0.2",
"cross-spawn": "^7.0.3",
+ "es-toolkit": "^1.21.0",
"globby": "^14.0.1",
"jscodeshift": "^0.15.1",
- "lodash": "^4.17.21",
"prettier": "^3.1.1",
"recast": "^0.23.5",
"tiny-invariant": "^1.3.1"
@@ -71,6 +71,7 @@
"devDependencies": {
"@types/jscodeshift": "^0.11.10",
"ansi-regex": "^6.0.1",
+ "camelcase": "^8.0.0",
"mdast-util-mdx-jsx": "^3.0.0",
"mdast-util-mdxjs-esm": "^2.0.1",
"remark": "^15.0.1",
diff --git a/code/lib/codemod/src/lib/utils.ts b/code/lib/codemod/src/lib/utils.ts
index 529c87efe731..a89002e10787 100644
--- a/code/lib/codemod/src/lib/utils.ts
+++ b/code/lib/codemod/src/lib/utils.ts
@@ -1,5 +1,4 @@
-import camelCase from 'lodash/camelCase.js';
-import upperFirst from 'lodash/upperFirst.js';
+import { camelCase, upperFirst } from 'es-toolkit/compat';
export const sanitizeName = (name: string) => {
let key = upperFirst(camelCase(name));
diff --git a/code/lib/codemod/src/transforms/__tests__/mdx-to-csf.test.ts b/code/lib/codemod/src/transforms/__tests__/mdx-to-csf.test.ts
index 304fb0c40b05..6d41995d3e82 100644
--- a/code/lib/codemod/src/transforms/__tests__/mdx-to-csf.test.ts
+++ b/code/lib/codemod/src/transforms/__tests__/mdx-to-csf.test.ts
@@ -7,7 +7,7 @@ import { dedent } from 'ts-dedent';
import jscodeshift, { nameToValidExport } from '../mdx-to-csf';
expect.addSnapshotSerializer({
- print: (val: any) => (typeof val === 'string' ? val : JSON.stringify(val, null, 2) ?? ''),
+ print: (val: any) => (typeof val === 'string' ? val : (JSON.stringify(val, null, 2) ?? '')),
test: () => true,
});
diff --git a/code/lib/codemod/src/transforms/mdx-to-csf.ts b/code/lib/codemod/src/transforms/mdx-to-csf.ts
index 15a6ae49e7b4..c83f900d0d87 100644
--- a/code/lib/codemod/src/transforms/mdx-to-csf.ts
+++ b/code/lib/codemod/src/transforms/mdx-to-csf.ts
@@ -11,8 +11,8 @@ import {
types as t,
} from '@storybook/core/babel';
+import { camelCase } from 'es-toolkit';
import type { FileInfo } from 'jscodeshift';
-import camelCase from 'lodash/camelCase';
import type { MdxFlowExpression } from 'mdast-util-mdx-expression';
import type {
MdxJsxAttribute,
@@ -410,7 +410,7 @@ function addStoriesImport(
}
export function nameToValidExport(name: string) {
- const [first, ...rest] = Array.from(camelCase(name));
+ const [first, ...rest] = Array.from(camelCase(name).replace(/\s/g, ''));
return `${first.match(/[a-zA-Z_$]/) ? first.toUpperCase() : `$${first}`}${rest.join('')}`;
}
diff --git a/code/lib/core-webpack/package.json b/code/lib/core-webpack/package.json
index 3ff2f28e586e..2a0895f6a69f 100644
--- a/code/lib/core-webpack/package.json
+++ b/code/lib/core-webpack/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/core-webpack",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook framework-agnostic API",
"keywords": [
"storybook"
diff --git a/code/lib/create-storybook/package.json b/code/lib/create-storybook/package.json
index 187bd286a799..d4009087ca3c 100644
--- a/code/lib/create-storybook/package.json
+++ b/code/lib/create-storybook/package.json
@@ -1,6 +1,6 @@
{
"name": "create-storybook",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Initialize Storybook into your project",
"homepage": "https://github.com/storybookjs/storybook/tree/next/code/lib/create-storybook",
"bugs": {
@@ -56,12 +56,10 @@
},
"dependencies": {
"@types/semver": "^7.3.4",
- "chalk": "^4.1.0",
"commander": "^12.1.0",
"execa": "^5.0.0",
"fd-package-json": "^1.2.0",
"find-up": "^5.0.0",
- "fs-extra": "^11.1.0",
"ora": "^5.4.1",
"prettier": "^3.1.1",
"prompts": "^2.4.0",
@@ -74,6 +72,7 @@
"@types/prompts": "^2.0.9",
"@types/util-deprecate": "^1.0.0",
"boxen": "^7.1.1",
+ "picocolors": "^1.1.0",
"typescript": "^5.3.2"
},
"publishConfig": {
diff --git a/code/lib/create-storybook/src/generators/REACT_NATIVE/index.ts b/code/lib/create-storybook/src/generators/REACT_NATIVE/index.ts
index a1fb9be5ad79..cd89b38a16b0 100644
--- a/code/lib/create-storybook/src/generators/REACT_NATIVE/index.ts
+++ b/code/lib/create-storybook/src/generators/REACT_NATIVE/index.ts
@@ -14,15 +14,19 @@ const generator = async (
const reactVersion = packageJson.dependencies.react;
- const controlsPeerDependencies = [
+ const peerDependencies = [
'react-native-safe-area-context',
'@react-native-async-storage/async-storage',
'@react-native-community/datetimepicker',
'@react-native-community/slider',
+ 'react-native-reanimated',
+ 'react-native-gesture-handler',
+ '@gorhom/bottom-sheet',
+ 'react-native-svg',
].filter((dep) => !packageJson.dependencies[dep] && !packageJson.devDependencies[dep]);
const packagesToResolve = [
- ...controlsPeerDependencies,
+ ...peerDependencies,
'@storybook/addon-ondevice-controls',
'@storybook/addon-ondevice-actions',
'@storybook/react-native',
@@ -57,7 +61,8 @@ const generator = async (
await copyTemplateFiles({
packageManager,
renderer: 'react-native',
- language: SupportedLanguage.TYPESCRIPT_3_8,
+ // this value for language is not used since we only ship the ts template. This means we just fallback to @storybook/react-native/template/cli.
+ language: SupportedLanguage.TYPESCRIPT_4_9,
destination: storybookConfigFolder,
});
};
diff --git a/code/lib/create-storybook/src/generators/baseGenerator.ts b/code/lib/create-storybook/src/generators/baseGenerator.ts
index 20318b78a68e..b1aea02b11e1 100644
--- a/code/lib/create-storybook/src/generators/baseGenerator.ts
+++ b/code/lib/create-storybook/src/generators/baseGenerator.ts
@@ -1,3 +1,4 @@
+import { mkdir } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import type { NpmOptions } from 'storybook/internal/cli';
@@ -10,7 +11,6 @@ import type { JsPackageManager } from 'storybook/internal/common';
import { getPackageDetails, versions as packageVersions } from 'storybook/internal/common';
import type { SupportedFrameworks } from 'storybook/internal/types';
-import fse from 'fs-extra';
import ora from 'ora';
import invariant from 'tiny-invariant';
import { dedent } from 'ts-dedent';
@@ -228,11 +228,7 @@ export async function baseGenerator(
})
: extraAddonPackages;
- extraAddonsToInstall.push(
- '@storybook/addon-links',
- '@storybook/addon-essentials',
- '@chromatic-com/storybook@^1'
- );
+ extraAddonsToInstall.push('@storybook/addon-essentials', '@chromatic-com/storybook@^1');
// added to main.js
const addons = [
@@ -328,7 +324,9 @@ export async function baseGenerator(
addDependenciesSpinner.succeed();
}
- await fse.ensureDir(`./${storybookConfigFolder}`);
+ // Passing `recursive: true` ensures that the method doesn't throw when
+ // the directory already exists.
+ await mkdir(`./${storybookConfigFolder}`, { recursive: true });
if (addMainFile) {
const prefixes = shouldApplyRequireWrapperOnPackageNames
diff --git a/code/lib/create-storybook/src/generators/configure.test.ts b/code/lib/create-storybook/src/generators/configure.test.ts
index b1ce1005d230..853e1102a055 100644
--- a/code/lib/create-storybook/src/generators/configure.test.ts
+++ b/code/lib/create-storybook/src/generators/configure.test.ts
@@ -1,17 +1,20 @@
+import type { Stats } from 'node:fs';
+import * as fsp from 'node:fs/promises';
+
import { beforeAll, describe, expect, it, vi } from 'vitest';
import { SupportedLanguage } from 'storybook/internal/cli';
-import fse from 'fs-extra';
import { dedent } from 'ts-dedent';
import { configureMain, configurePreview } from './configure';
-vi.mock('fs-extra');
+vi.mock('node:fs/promises');
describe('configureMain', () => {
beforeAll(() => {
vi.clearAllMocks();
+ vi.mocked(fsp.stat).mockRejectedValue({});
});
it('should generate main.js', async () => {
@@ -25,7 +28,7 @@ describe('configureMain', () => {
},
});
- const { calls } = vi.mocked(fse.writeFile).mock;
+ const { calls } = vi.mocked(fsp.writeFile).mock;
const [mainConfigPath, mainConfigContent] = calls[0];
expect(mainConfigPath).toEqual('./.storybook/main.js');
@@ -54,7 +57,7 @@ describe('configureMain', () => {
},
});
- const { calls } = vi.mocked(fse.writeFile).mock;
+ const { calls } = vi.mocked(fsp.writeFile).mock;
const [mainConfigPath, mainConfigContent] = calls[0];
expect(mainConfigPath).toEqual('./.storybook/main.ts');
@@ -78,7 +81,6 @@ describe('configureMain', () => {
language: SupportedLanguage.JAVASCRIPT,
prefixes: [],
addons: [
- "%%path.dirname(require.resolve(path.join('@storybook/addon-links', 'package.json')))%%",
"%%path.dirname(require.resolve(path.join('@storybook/addon-essentials', 'package.json')))%%",
"%%path.dirname(require.resolve(path.join('@storybook/preset-create-react-app', 'package.json')))%%",
"%%path.dirname(require.resolve(path.join('@storybook/addon-interactions', 'package.json')))%%",
@@ -89,7 +91,7 @@ describe('configureMain', () => {
},
});
- const { calls } = vi.mocked(fse.writeFile).mock;
+ const { calls } = vi.mocked(fsp.writeFile).mock;
const [mainConfigPath, mainConfigContent] = calls[0];
expect(mainConfigPath).toEqual('./.storybook/main.js');
@@ -100,7 +102,6 @@ describe('configureMain', () => {
const config = {
stories: ['../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
- path.dirname(require.resolve(path.join('@storybook/addon-links', 'package.json'))),
path.dirname(require.resolve(path.join('@storybook/addon-essentials', 'package.json'))),
path.dirname(require.resolve(path.join('@storybook/preset-create-react-app', 'package.json'))),
path.dirname(require.resolve(path.join('@storybook/addon-interactions', 'package.json'))),
@@ -123,7 +124,7 @@ describe('configurePreview', () => {
rendererId: 'react',
});
- const { calls } = vi.mocked(fse.writeFile).mock;
+ const { calls } = vi.mocked(fsp.writeFile).mock;
const [previewConfigPath, previewConfigContent] = calls[0];
expect(previewConfigPath).toEqual('./.storybook/preview.js');
@@ -152,7 +153,7 @@ describe('configurePreview', () => {
rendererId: 'react',
});
- const { calls } = vi.mocked(fse.writeFile).mock;
+ const { calls } = vi.mocked(fsp.writeFile).mock;
const [previewConfigPath, previewConfigContent] = calls[0];
expect(previewConfigPath).toEqual('./.storybook/preview.ts');
@@ -176,13 +177,13 @@ describe('configurePreview', () => {
});
it('should not do anything if the framework template already included a preview', async () => {
- vi.mocked(fse.pathExists).mockImplementationOnce(() => Promise.resolve(true));
+ vi.mocked(fsp.stat).mockResolvedValueOnce({} as Stats);
await configurePreview({
language: SupportedLanguage.TYPESCRIPT_4_9,
storybookConfigFolder: '.storybook',
rendererId: 'react',
});
- expect(fse.writeFile).not.toHaveBeenCalled();
+ expect(fsp.writeFile).not.toHaveBeenCalled();
});
it('should add prefix if frameworkParts are passed', async () => {
@@ -199,7 +200,7 @@ describe('configurePreview', () => {
},
});
- const { calls } = vi.mocked(fse.writeFile).mock;
+ const { calls } = vi.mocked(fsp.writeFile).mock;
const [previewConfigPath, previewConfigContent] = calls[0];
expect(previewConfigPath).toEqual('./.storybook/preview.ts');
diff --git a/code/lib/create-storybook/src/generators/configure.ts b/code/lib/create-storybook/src/generators/configure.ts
index 94304f4c2d06..c7002c58c045 100644
--- a/code/lib/create-storybook/src/generators/configure.ts
+++ b/code/lib/create-storybook/src/generators/configure.ts
@@ -1,9 +1,9 @@
+import { stat, writeFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import { SupportedLanguage, externalFrameworks } from 'storybook/internal/cli';
import { logger } from 'storybook/internal/node-logger';
-import fse from 'fs-extra';
import { dedent } from 'ts-dedent';
interface ConfigureMainOptions {
@@ -35,6 +35,12 @@ interface ConfigurePreviewOptions {
rendererId: string;
}
+const pathExists = async (path: string) => {
+ return stat(path)
+ .then(() => true)
+ .catch(() => false);
+};
+
/**
* We need to clean up the paths in case of pnp input:
* `path.dirname(require.resolve(path.join('@storybook/react-webpack5', 'package.json')))` output:
@@ -59,7 +65,7 @@ export async function configureMain({
...custom
}: ConfigureMainOptions) {
const srcPath = resolve(storybookConfigFolder, '../src');
- const prefix = (await fse.pathExists(srcPath)) ? '../src' : '../stories';
+ const prefix = (await pathExists(srcPath)) ? '../src' : '../stories';
const config = {
stories: [`${prefix}/**/*.mdx`, `${prefix}/**/*.stories.@(${extensions.join('|')})`],
addons,
@@ -114,7 +120,7 @@ export async function configureMain({
logger.verbose(`Failed to prettify ${mainPath}`);
}
- await fse.writeFile(mainPath, mainJsContents, { encoding: 'utf8' });
+ await writeFile(mainPath, mainJsContents, { encoding: 'utf8' });
}
export async function configurePreview(options: ConfigurePreviewOptions) {
@@ -134,7 +140,7 @@ export async function configurePreview(options: ConfigurePreviewOptions) {
const previewPath = `./${options.storybookConfigFolder}/preview.${isTypescript ? 'ts' : 'js'}`;
// If the framework template included a preview then we have nothing to do
- if (await fse.pathExists(previewPath)) {
+ if (await pathExists(previewPath)) {
return;
}
@@ -177,5 +183,5 @@ export async function configurePreview(options: ConfigurePreviewOptions) {
logger.verbose(`Failed to prettify ${previewPath}`);
}
- await fse.writeFile(previewPath, preview, { encoding: 'utf8' });
+ await writeFile(previewPath, preview, { encoding: 'utf8' });
}
diff --git a/code/lib/create-storybook/src/initiate.ts b/code/lib/create-storybook/src/initiate.ts
index 787e16288cc2..5d9e8249c2c0 100644
--- a/code/lib/create-storybook/src/initiate.ts
+++ b/code/lib/create-storybook/src/initiate.ts
@@ -17,8 +17,8 @@ import { NxProjectDetectedError } from 'storybook/internal/server-errors';
import { telemetry } from 'storybook/internal/telemetry';
import boxen from 'boxen';
-import chalk from 'chalk';
import findUp from 'find-up';
+import picocolors from 'picocolors';
import prompts from 'prompts';
import { lt, prerelease } from 'semver';
import { dedent } from 'ts-dedent';
@@ -185,7 +185,7 @@ const installStorybook = async (
return await runGenerator();
} catch (err: any) {
if (err?.message !== 'Canceled by the user' && err?.stack) {
- logger.error(`\n ${chalk.red(err.stack)}`);
+ logger.error(`\n ${picocolors.red(err.stack)}`);
}
throw new HandledError(err);
}
@@ -250,15 +250,15 @@ export async function doInitiate(options: CommandOptions): Promise<
const borderColor = isOutdated ? '#FC521F' : '#F1618C';
const messages = {
- welcome: `Adding Storybook version ${chalk.bold(currentVersion)} to your project..`,
- notLatest: chalk.red(dedent`
- This version is behind the latest release, which is: ${chalk.bold(latestVersion)}!
+ welcome: `Adding Storybook version ${picocolors.bold(currentVersion)} to your project..`,
+ notLatest: picocolors.red(dedent`
+ This version is behind the latest release, which is: ${picocolors.bold(latestVersion)}!
You likely ran the init command through npx, which can use a locally cached version, to get the latest please run:
- ${chalk.bold('npx storybook@latest init')}
+ ${picocolors.bold('npx storybook@latest init')}
You may want to CTRL+C to stop, and run with the latest version instead.
`),
- prelease: chalk.yellow('This is a pre-release version.'),
+ prelease: picocolors.yellow('This is a pre-release version.'),
};
logger.log(
@@ -348,22 +348,25 @@ export async function doInitiate(options: CommandOptions): Promise<
if (projectType === ProjectType.REACT_NATIVE) {
logger.log(dedent`
- ${chalk.yellow('NOTE: installation is not 100% automated.')}
+ ${picocolors.yellow('NOTE: installation is not 100% automated.')}
To run Storybook, you will need to:
1. Replace the contents of your app entry with the following
- ${chalk.inverse(' ' + "export {default} from './.storybook';" + ' ')}
+ ${picocolors.inverse(' ' + "export {default} from './.storybook';" + ' ')}
- 2. Enable transformer.unstable_allowRequireContext in your metro config
+ 2. Wrap your metro config with the withStorybook enhancer function like this:
- For a more detailed guide go to:
- ${chalk.cyan('https://github.com/storybookjs/react-native#existing-project')}
+ ${picocolors.inverse(' ' + "const withStorybook = require('@storybook/react-native/metro/withStorybook');" + ' ')}
+ ${picocolors.inverse(' ' + 'module.exports = withStorybook(defaultConfig);' + ' ')}
+
+ For more details go to:
+ ${picocolors.cyan('https://github.com/storybookjs/react-native#getting-started')}
Then to run your Storybook, type:
- ${chalk.inverse(' ' + packageManager.getRunCommand('start') + ' ')}
+ ${picocolors.inverse(' ' + packageManager.getRunCommand('start') + ' ')}
`);
@@ -375,7 +378,7 @@ export async function doInitiate(options: CommandOptions): Promise<
if (foundGitIgnoreFile && foundGitIgnoreFile.includes(rootDirectory)) {
const contents = await readFile(foundGitIgnoreFile, 'utf-8');
if (!contents.includes('*storybook.log')) {
- await appendFile(foundGitIgnoreFile, '\n*storybook.log');
+ await appendFile(foundGitIgnoreFile, '\n*storybook.log\n');
}
}
@@ -388,12 +391,14 @@ export async function doInitiate(options: CommandOptions): Promise<
boxen(
dedent`
Storybook was successfully installed in your project! 🎉
- To run Storybook manually, run ${chalk.yellow(
- chalk.bold(storybookCommand)
+ To run Storybook manually, run ${picocolors.yellow(
+ picocolors.bold(storybookCommand)
)}. CTRL+C to stop.
- Wanna know more about Storybook? Check out ${chalk.cyan('https://storybook.js.org/')}
- Having trouble or want to chat? Join us at ${chalk.cyan('https://discord.gg/storybook/')}
+ Wanna know more about Storybook? Check out ${picocolors.cyan('https://storybook.js.org/')}
+ Having trouble or want to chat? Join us at ${picocolors.cyan(
+ 'https://discord.gg/storybook/'
+ )}
`,
{ borderStyle: 'round', padding: 1, borderColor: '#F1618C' }
)
diff --git a/code/lib/create-storybook/src/scaffold-new-project.ts b/code/lib/create-storybook/src/scaffold-new-project.ts
index f80ff2281f23..f41d9c876010 100644
--- a/code/lib/create-storybook/src/scaffold-new-project.ts
+++ b/code/lib/create-storybook/src/scaffold-new-project.ts
@@ -1,12 +1,14 @@
+import { readdirSync } from 'node:fs';
+import { rm } from 'node:fs/promises';
+
import type { PackageManagerName } from 'storybook/internal/common';
import { logger } from 'storybook/internal/node-logger';
import { GenerateNewProjectOnInitError } from 'storybook/internal/server-errors';
import { telemetry } from 'storybook/internal/telemetry';
import boxen from 'boxen';
-import chalk from 'chalk';
import execa from 'execa';
-import { readdirSync, remove } from 'fs-extra';
+import picocolors from 'picocolors';
import prompts from 'prompts';
import { dedent } from 'ts-dedent';
@@ -100,7 +102,7 @@ const packageManagerToCoercedName = (
const buildProjectDisplayNameForPrint = ({ displayName }: SupportedProject) => {
const { type, builder, language } = displayName;
- return `${chalk.bold.blue(type)} ${builder ? `+ ${builder} ` : ''}(${language})`;
+ return `${picocolors.bold(picocolors.blue(type))} ${builder ? `+ ${builder} ` : ''}(${language})`;
};
/**
@@ -119,14 +121,14 @@ export const scaffoldNewProject = async (
dedent`
Would you like to generate a new project from the following list?
- ${chalk.bold('Note:')}
+ ${picocolors.bold('Note:')}
Storybook supports many more frameworks and bundlers than listed below. If you don't see your
preferred setup, you can still generate a project then rerun this command to add Storybook.
- ${chalk.bold('Press ^C at any time to quit.')}
+ ${picocolors.bold('Press ^C at any time to quit.')}
`,
{
- title: chalk.bold('🔎 Empty directory detected'),
+ title: picocolors.bold('🔎 Empty directory detected'),
padding: 1,
borderStyle: 'double',
borderColor: 'yellow',
@@ -164,7 +166,7 @@ export const scaffoldNewProject = async (
logger.line(1);
logger.plain(
- `Creating a new "${projectDisplayName}" project with ${chalk.bold(packageManagerName)}...`
+ `Creating a new "${projectDisplayName}" project with ${picocolors.bold(packageManagerName)}...`
);
logger.line(1);
@@ -173,7 +175,7 @@ export const scaffoldNewProject = async (
try {
// If target directory has a .cache folder, remove it
// so that it does not block the creation of the new project
- await remove(`${targetDir}/.cache`);
+ await rm(`${targetDir}/.cache`, { recursive: true, force: true });
// Create new project in temp directory
await execa.command(createScript, {
@@ -200,12 +202,14 @@ export const scaffoldNewProject = async (
logger.plain(
boxen(
dedent`
- "${projectDisplayName}" project with ${chalk.bold(packageManagerName)} created successfully!
+ "${projectDisplayName}" project with ${picocolors.bold(
+ packageManagerName
+ )} created successfully!
Continuing with Storybook installation...
`,
{
- title: chalk.bold('✅ Success!'),
+ title: picocolors.bold('✅ Success!'),
padding: 1,
borderStyle: 'double',
borderColor: 'green',
diff --git a/code/lib/csf-plugin/package.json b/code/lib/csf-plugin/package.json
index 721dab51a263..093264381888 100644
--- a/code/lib/csf-plugin/package.json
+++ b/code/lib/csf-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/csf-plugin",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Enrich CSF files via static analysis",
"keywords": [
"storybook"
diff --git a/code/lib/instrumenter/package.json b/code/lib/instrumenter/package.json
index ff16fc3e7b01..50ee8e5e2855 100644
--- a/code/lib/instrumenter/package.json
+++ b/code/lib/instrumenter/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/instrumenter",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "",
"keywords": [
"storybook"
diff --git a/code/lib/instrumenter/project.json b/code/lib/instrumenter/project.json
index 3ecdad88a0dd..dd7cc79ecbe4 100644
--- a/code/lib/instrumenter/project.json
+++ b/code/lib/instrumenter/project.json
@@ -1,4 +1,5 @@
{
+ "name": "instrumenter",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {
diff --git a/code/lib/react-dom-shim/package.json b/code/lib/react-dom-shim/package.json
index caaeed8cb007..a91f19996877 100644
--- a/code/lib/react-dom-shim/package.json
+++ b/code/lib/react-dom-shim/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/react-dom-shim",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "",
"keywords": [
"storybook"
diff --git a/code/lib/react-dom-shim/src/preventActChecks.tsx b/code/lib/react-dom-shim/src/preventActChecks.tsx
deleted file mode 100644
index f35e2fb25dc5..000000000000
--- a/code/lib/react-dom-shim/src/preventActChecks.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-export {};
-
-declare const globalThis: {
- IS_REACT_ACT_ENVIRONMENT?: boolean;
-};
-
-// TODO(9.0): We should actually wrap all those lines in `act`, but that might be a breaking change.
-// We should make that breaking change for SB 9.0
-export function preventActChecks(callback: () => void): void {
- const originalActEnvironment = globalThis.IS_REACT_ACT_ENVIRONMENT;
- globalThis.IS_REACT_ACT_ENVIRONMENT = false;
- try {
- callback();
- } finally {
- globalThis.IS_REACT_ACT_ENVIRONMENT = originalActEnvironment;
- }
-}
diff --git a/code/lib/react-dom-shim/src/react-16.tsx b/code/lib/react-dom-shim/src/react-16.tsx
index a1e7b1e97009..8c7b2c8f5a67 100644
--- a/code/lib/react-dom-shim/src/react-16.tsx
+++ b/code/lib/react-dom-shim/src/react-16.tsx
@@ -2,14 +2,12 @@
import type { ReactElement } from 'react';
import * as ReactDOM from 'react-dom';
-import { preventActChecks } from './preventActChecks';
-
export const renderElement = async (node: ReactElement, el: Element) => {
return new Promise((resolve) => {
- preventActChecks(() => void ReactDOM.render(node, el, () => resolve(null)));
+ ReactDOM.render(node, el, () => resolve(null));
});
};
export const unmountElement = (el: Element) => {
- preventActChecks(() => void ReactDOM.unmountComponentAtNode(el));
+ ReactDOM.unmountComponentAtNode(el);
};
diff --git a/code/lib/react-dom-shim/src/react-18.tsx b/code/lib/react-dom-shim/src/react-18.tsx
index 5eb72b20eb17..f3398fc65ff0 100644
--- a/code/lib/react-dom-shim/src/react-18.tsx
+++ b/code/lib/react-dom-shim/src/react-18.tsx
@@ -1,15 +1,21 @@
/* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
-import type { FC, ReactElement } from 'react';
+import type { ReactElement } from 'react';
import * as React from 'react';
import type { Root as ReactRoot, RootOptions } from 'react-dom/client';
import * as ReactDOM from 'react-dom/client';
-import { preventActChecks } from './preventActChecks';
-
// A map of all rendered React 18 nodes
const nodes = new Map();
-const WithCallback: FC<{ callback: () => void; children: ReactElement }> = ({
+declare const globalThis: {
+ IS_REACT_ACT_ENVIRONMENT: boolean;
+};
+
+function getIsReactActEnvironment() {
+ return globalThis.IS_REACT_ACT_ENVIRONMENT;
+}
+
+const WithCallback: React.FC<{ callback: () => void; children: ReactElement }> = ({
callback,
children,
}) => {
@@ -43,8 +49,13 @@ export const renderElement = async (node: ReactElement, el: Element, rootOptions
// Create Root Element conditionally for new React 18 Root Api
const root = await getReactRoot(el, rootOptions);
+ if (getIsReactActEnvironment()) {
+ root.render(node);
+ return;
+ }
+
const { promise, resolve } = Promise.withResolvers();
- preventActChecks(() => root.render({node}));
+ root.render({node});
return promise;
};
@@ -52,7 +63,7 @@ export const unmountElement = (el: Element, shouldUseNewRootApi?: boolean) => {
const root = nodes.get(el);
if (root) {
- preventActChecks(() => root.unmount());
+ root.unmount();
nodes.delete(el);
}
};
diff --git a/code/lib/source-loader/package.json b/code/lib/source-loader/package.json
index 22e617fc9c6b..b4c4545f6826 100644
--- a/code/lib/source-loader/package.json
+++ b/code/lib/source-loader/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/source-loader",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Source loader",
"keywords": [
"lib",
@@ -45,8 +45,8 @@
},
"dependencies": {
"@storybook/csf": "^0.1.11",
+ "es-toolkit": "^1.21.0",
"estraverse": "^5.2.0",
- "lodash": "^4.17.21",
"prettier": "^3.1.1"
},
"devDependencies": {
diff --git a/code/lib/source-loader/src/abstract-syntax-tree/generate-helpers.js b/code/lib/source-loader/src/abstract-syntax-tree/generate-helpers.js
index da737063db1c..39c667804aef 100644
--- a/code/lib/source-loader/src/abstract-syntax-tree/generate-helpers.js
+++ b/code/lib/source-loader/src/abstract-syntax-tree/generate-helpers.js
@@ -1,6 +1,6 @@
import { sanitize, storyNameFromExport } from '@storybook/csf';
-import mapKeys from 'lodash/mapKeys.js';
+import { mapKeys } from 'es-toolkit/compat';
import { extractSource } from '../extract-source';
import { patchNode } from './parse-helpers';
diff --git a/code/lib/test/package.json b/code/lib/test/package.json
index 6bcc55ef4574..b2a27b41a5ff 100644
--- a/code/lib/test/package.json
+++ b/code/lib/test/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/test",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "",
"keywords": [
"storybook"
diff --git a/code/lib/test/project.json b/code/lib/test/project.json
index 3ecdad88a0dd..1302a230f1e1 100644
--- a/code/lib/test/project.json
+++ b/code/lib/test/project.json
@@ -1,4 +1,5 @@
{
+ "name": "test",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"targets": {
diff --git a/code/nx.json b/code/nx.json
index 12070cbb8d4e..2f726cea4387 100644
--- a/code/nx.json
+++ b/code/nx.json
@@ -52,7 +52,7 @@
"{workspaceRoot}/../scripts/prepare/{bundle,addon-bundle,esm-bundle}.ts"
],
"dependsOn": ["^build"],
- "outputs": ["{projectRoot}/dist"],
+ "outputs": ["{projectRoot}/dist", "{workspaceRoot}/bench/esbuild-metafiles/{projectName}"],
"cache": true
},
"test": {
diff --git a/code/package.json b/code/package.json
index aa4f7f09cf97..dd66a8428ae6 100644
--- a/code/package.json
+++ b/code/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/root",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"private": true,
"description": "Storybook root",
"homepage": "https://storybook.js.org/",
@@ -36,6 +36,7 @@
"generate-sandboxes": "yarn --cwd ../scripts generate-sandboxes",
"github-release": "github-release-from-changelog",
"i": "yarn --cwd .. i",
+ "knip": "VITE_CJS_IGNORE_WARNING=1 ../scripts/node_modules/.bin/knip --config ../scripts/knip.config.ts",
"lint": "yarn lint:js && yarn lint:md",
"lint:ejs": "ejslint **/*.ejs",
"lint:js": "yarn lint:js:cmd . --quiet",
@@ -82,7 +83,7 @@
"@types/babel__traverse@npm:^7.18.0": "patch:@types/babel__traverse@npm%3A7.20.6#~/.yarn/patches/@types-babel__traverse-npm-7.20.6-fac4243243.patch",
"@types/node": "^22.0.0",
"@vitest/expect@npm:2.0.5": "patch:@vitest/expect@npm%3A2.0.5#~/.yarn/patches/@vitest-expect-npm-2.0.5-8933466cce.patch",
- "esbuild": "^0.23.0",
+ "esbuild": "^0.24.0",
"playwright": "1.46.0",
"playwright-core": "1.46.0",
"serialize-javascript": "^3.1.0",
@@ -168,8 +169,6 @@
"@testing-library/user-event": "^14.5.2",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/express": "^4.17.21",
- "@types/fs-extra": "^11.0.1",
- "@types/lodash": "^4.14.167",
"@types/mock-require": "^2.0.3",
"@types/node": "^22.0.0",
"@types/react": "^18.0.37",
@@ -186,7 +185,8 @@
"create-storybook": "workspace:*",
"cross-env": "^7.0.3",
"danger": "^12.3.3",
- "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0",
+ "es-toolkit": "^1.21.0",
+ "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0",
"esbuild-loader": "^4.2.0",
"esbuild-plugin-alias": "^0.2.1",
"eslint": "^8.56.0",
@@ -194,14 +194,12 @@
"eslint-plugin-local-rules": "portal:../scripts/eslint-plugin-local-rules",
"eslint-plugin-playwright": "^1.6.2",
"eslint-plugin-storybook": "^0.8.0",
- "fs-extra": "^11.1.0",
"github-release-from-changelog": "^2.1.1",
"glob": "^10.0.0",
"happy-dom": "^14.12.0",
"http-server": "^14.1.1",
"husky": "^4.3.7",
"lint-staged": "^13.2.2",
- "lodash": "^4.17.21",
"mock-require": "^3.0.3",
"nx": "18.0.6",
"prettier": "^3.1.1",
diff --git a/code/presets/create-react-app/package.json b/code/presets/create-react-app/package.json
index e77c0aad747a..6b54b01500ba 100644
--- a/code/presets/create-react-app/package.json
+++ b/code/presets/create-react-app/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/preset-create-react-app",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Create React App preset",
"keywords": [
"storybook"
diff --git a/code/presets/html-webpack/package.json b/code/presets/html-webpack/package.json
index 40a127610a18..122bb9a5630c 100644
--- a/code/presets/html-webpack/package.json
+++ b/code/presets/html-webpack/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/preset-html-webpack",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.",
"keywords": [
"storybook"
diff --git a/code/presets/preact-webpack/package.json b/code/presets/preact-webpack/package.json
index 638ff695aca0..06382e51dcce 100644
--- a/code/presets/preact-webpack/package.json
+++ b/code/presets/preact-webpack/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/preset-preact-webpack",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Preact: Develop Preact Component in isolation.",
"keywords": [
"storybook"
diff --git a/code/presets/react-webpack/package.json b/code/presets/react-webpack/package.json
index 76244cac1bf7..50dcf06be887 100644
--- a/code/presets/react-webpack/package.json
+++ b/code/presets/react-webpack/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/preset-react-webpack",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for React: Develop React Component in isolation with Hot Reloading",
"keywords": [
"storybook"
@@ -70,7 +70,6 @@
"@types/node": "^22.0.0",
"@types/semver": "^7.3.4",
"find-up": "^5.0.0",
- "fs-extra": "^11.1.0",
"magic-string": "^0.30.5",
"react-docgen": "^7.0.0",
"resolve": "^1.22.8",
diff --git a/code/presets/server-webpack/package.json b/code/presets/server-webpack/package.json
index b58cd2a73bf9..0eab7670d950 100644
--- a/code/presets/server-webpack/package.json
+++ b/code/presets/server-webpack/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/preset-server-webpack",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Server: View HTML snippets from a server in isolation with Hot Reloading.",
"keywords": [
"storybook"
@@ -63,7 +63,6 @@
"yaml-loader": "^0.8.0"
},
"devDependencies": {
- "fs-extra": "^11.1.0",
"typescript": "^5.3.2",
"yaml": "^2.3.1"
},
diff --git a/code/presets/server-webpack/src/lib/compiler/json-to-csf-compiler.test.ts b/code/presets/server-webpack/src/lib/compiler/json-to-csf-compiler.test.ts
index 0c44665bdc25..f0d8237215cb 100644
--- a/code/presets/server-webpack/src/lib/compiler/json-to-csf-compiler.test.ts
+++ b/code/presets/server-webpack/src/lib/compiler/json-to-csf-compiler.test.ts
@@ -1,14 +1,15 @@
+import { readdirSync } from 'node:fs';
+import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { describe, expect, it } from 'vitest';
-import fs from 'fs-extra';
import YAML from 'yaml';
import { compileCsfModule } from '.';
async function generate(filePath: string) {
- const content = await fs.readFile(filePath, 'utf8');
+ const content = await readFile(filePath, { encoding: 'utf8' });
const parsed = filePath.endsWith('.json') ? JSON.parse(content) : YAML.parse(content);
return compileCsfModule(parsed);
}
@@ -18,7 +19,7 @@ async function generate(filePath: string) {
describe(`${fileType}-to-csf-compiler`, () => {
const transformFixturesDir = join(__dirname, '__testfixtures__');
- fs.readdirSync(transformFixturesDir)
+ readdirSync(transformFixturesDir)
.filter((fileName: string) => inputRegExp.test(fileName))
.forEach((fixtureFile: string) => {
it(`${fixtureFile}`, async () => {
diff --git a/code/presets/svelte-webpack/package.json b/code/presets/svelte-webpack/package.json
index 1c5ccf77b50b..12582f9d912e 100644
--- a/code/presets/svelte-webpack/package.json
+++ b/code/presets/svelte-webpack/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/preset-svelte-webpack",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Svelte: Develop Svelte Component in isolation with Hot Reloading.",
"keywords": [
"storybook"
diff --git a/code/presets/vue3-webpack/package.json b/code/presets/vue3-webpack/package.json
index 6a692823e2d4..e2c30599c391 100644
--- a/code/presets/vue3-webpack/package.json
+++ b/code/presets/vue3-webpack/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/preset-vue3-webpack",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook for Vue 3: Develop Vue 3 Components in isolation with Hot Reloading.",
"keywords": [
"storybook"
diff --git a/code/renderers/html/package.json b/code/renderers/html/package.json
index de24babe4b8e..80d498caafd1 100644
--- a/code/renderers/html/package.json
+++ b/code/renderers/html/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/html",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook HTML renderer",
"keywords": [
"storybook"
diff --git a/code/renderers/preact/package.json b/code/renderers/preact/package.json
index cc83c3a83951..3244b054f186 100644
--- a/code/renderers/preact/package.json
+++ b/code/renderers/preact/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/preact",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook Preact renderer",
"keywords": [
"storybook"
diff --git a/code/renderers/react/package.json b/code/renderers/react/package.json
index 10f5c58b4588..c4e9a05207f4 100644
--- a/code/renderers/react/package.json
+++ b/code/renderers/react/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/react",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook React renderer",
"keywords": [
"storybook"
@@ -89,17 +89,21 @@
"@types/semver": "^7.3.4",
"@types/util-deprecate": "^1.0.0",
"babel-plugin-react-docgen": "^4.2.1",
+ "es-toolkit": "^1.21.0",
"expect-type": "^0.15.0",
- "lodash": "^4.17.21",
"require-from-string": "^2.0.2"
},
"peerDependencies": {
+ "@storybook/test": "workspace:*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
"storybook": "workspace:^",
"typescript": ">= 4.2.x"
},
"peerDependenciesMeta": {
+ "@storybook/test": {
+ "optional": true
+ },
"typescript": {
"optional": true
}
diff --git a/code/renderers/react/src/__test__/Button.stories.tsx b/code/renderers/react/src/__test__/Button.stories.tsx
index bde220fdf469..0e6e0d6e8c67 100644
--- a/code/renderers/react/src/__test__/Button.stories.tsx
+++ b/code/renderers/react/src/__test__/Button.stories.tsx
@@ -103,7 +103,6 @@ export const HooksStory: CSF3Story = {
);
},
play: async ({ canvasElement, step }) => {
- console.log('start of play function');
const canvas = within(canvasElement);
await step('Step label', async () => {
const inputEl = canvas.getByTestId('input');
@@ -112,8 +111,8 @@ export const HooksStory: CSF3Story = {
await userEvent.type(inputEl, 'Hello world!');
await expect(inputEl).toHaveValue('Hello world!');
+ await expect(buttonEl).toHaveTextContent('I am clicked');
});
- console.log('end of play function');
},
};
@@ -182,6 +181,12 @@ export const MountInPlayFunction: CSF3Story<{ mockFn: (val: string) => string }>
},
};
+export const MountInPlayFunctionThrow: CSF3Story<{ mockFn: (val: string) => string }> = {
+ play: async () => {
+ throw new Error('Error thrown in play');
+ },
+};
+
export const WithActionArg: CSF3Story<{ someActionArg: HandlerFunction }> = {
args: {
someActionArg: action('some-action-arg'),
diff --git a/code/renderers/react/src/__test__/ComponentWithError.stories.tsx b/code/renderers/react/src/__test__/ComponentWithError.stories.tsx
new file mode 100644
index 000000000000..627055e2d965
--- /dev/null
+++ b/code/renderers/react/src/__test__/ComponentWithError.stories.tsx
@@ -0,0 +1,13 @@
+import type { Meta, StoryObj } from '..';
+import { ComponentWithError } from './ComponentWithError';
+
+const meta = {
+ title: 'Example/ComponentWithError',
+ component: ComponentWithError as any,
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const ThrowsError: Story = {};
diff --git a/code/renderers/react/src/__test__/ComponentWithError.tsx b/code/renderers/react/src/__test__/ComponentWithError.tsx
new file mode 100644
index 000000000000..37f667cb4f2c
--- /dev/null
+++ b/code/renderers/react/src/__test__/ComponentWithError.tsx
@@ -0,0 +1,4 @@
+export function ComponentWithError() {
+ // eslint-disable-next-line local-rules/no-uncategorized-errors
+ throw new Error('Error in render');
+}
diff --git a/code/renderers/react/src/__test__/portable-stories-legacy.test.tsx b/code/renderers/react/src/__test__/portable-stories-legacy.test.tsx
index 3c7321cdfe63..5567b1fd9fbc 100644
--- a/code/renderers/react/src/__test__/portable-stories-legacy.test.tsx
+++ b/code/renderers/react/src/__test__/portable-stories-legacy.test.tsx
@@ -200,7 +200,11 @@ describe('Legacy Portable Stories API', () => {
it.each(testCases)('Renders %s story', async (_storyName, Story) => {
cleanup();
- if (_storyName === 'CSF2StoryWithLocale' || _storyName === 'MountInPlayFunction') {
+ if (
+ _storyName === 'CSF2StoryWithLocale' ||
+ _storyName === 'MountInPlayFunction' ||
+ _storyName === 'MountInPlayFunctionThrow'
+ ) {
return;
}
diff --git a/code/renderers/react/src/__test__/portable-stories.test.tsx b/code/renderers/react/src/__test__/portable-stories.test.tsx
index 90346edff991..85f33c9a714a 100644
--- a/code/renderers/react/src/__test__/portable-stories.test.tsx
+++ b/code/renderers/react/src/__test__/portable-stories.test.tsx
@@ -2,13 +2,14 @@
/* eslint-disable import/namespace */
import { cleanup, render, screen } from '@testing-library/react';
-import { afterEach, describe, expect, it, vi } from 'vitest';
+import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
import React from 'react';
import { addons } from 'storybook/internal/preview-api';
-import type { Meta } from '@storybook/react';
+import type { ProjectAnnotations } from '@storybook/csf';
+import type { Meta, ReactRenderer } from '@storybook/react';
import * as addonActionsPreview from '@storybook/addon-actions/preview';
@@ -16,23 +17,28 @@ import { expectTypeOf } from 'expect-type';
import { composeStories, composeStory, setProjectAnnotations } from '..';
import type { Button } from './Button';
-import * as stories from './Button.stories';
+import * as ButtonStories from './Button.stories';
+import * as ComponentWithErrorStories from './ComponentWithError.stories';
-setProjectAnnotations([]);
+const HooksStory = composeStory(ButtonStories.HooksStory, ButtonStories.default);
+
+const projectAnnotations = setProjectAnnotations([]);
// example with composeStories, returns an object with all stories composed with args/decorators
-const { CSF3Primary, LoaderStory, MountInPlayFunction } = composeStories(stories);
+const { CSF3Primary, LoaderStory, MountInPlayFunction, MountInPlayFunctionThrow } =
+ composeStories(ButtonStories);
+const { ThrowsError } = composeStories(ComponentWithErrorStories);
+
+beforeAll(async () => {
+ await projectAnnotations.beforeAll?.();
+});
afterEach(() => {
cleanup();
});
-declare const globalThis: {
- IS_REACT_ACT_ENVIRONMENT?: boolean;
-};
-
// example with composeStory, returns a single story composed with args/decorators
-const Secondary = composeStory(stories.CSF2Secondary, stories.default);
+const Secondary = composeStory(ButtonStories.CSF2Secondary, ButtonStories.default);
describe('renders', () => {
it('renders primary button', () => {
render(Hello world);
@@ -60,6 +66,10 @@ describe('renders', () => {
expect(buttonElement).not.toBeNull();
});
+ it('should throw error when rendering a component with a render error', async () => {
+ await expect(() => ThrowsError.run()).rejects.toThrowError('Error in render');
+ });
+
it('should render component mounted in play function', async () => {
await MountInPlayFunction.run();
@@ -67,6 +77,10 @@ describe('renders', () => {
expect(screen.getByTestId('loaded-data').textContent).toEqual('loaded data');
});
+ it('should throw an error in play function', () => {
+ expect(() => MountInPlayFunctionThrow.run()).rejects.toThrowError('Error thrown in play');
+ });
+
it('should call and compose loaders data', async () => {
await LoaderStory.load();
const { getByTestId } = render();
@@ -78,10 +92,6 @@ describe('renders', () => {
});
describe('projectAnnotations', () => {
- afterEach(() => {
- cleanup();
- });
-
it('renders with default projectAnnotations', () => {
setProjectAnnotations([
{
@@ -91,7 +101,7 @@ describe('projectAnnotations', () => {
},
},
]);
- const WithEnglishText = composeStory(stories.CSF2StoryWithLocale, stories.default);
+ const WithEnglishText = composeStory(ButtonStories.CSF2StoryWithLocale, ButtonStories.default);
const { getByText } = render();
const buttonElement = getByText('Hello!');
expect(buttonElement).not.toBeNull();
@@ -99,24 +109,31 @@ describe('projectAnnotations', () => {
});
it('renders with custom projectAnnotations via composeStory params', () => {
- const WithPortugueseText = composeStory(stories.CSF2StoryWithLocale, stories.default, {
- initialGlobals: { locale: 'pt' },
- });
+ const WithPortugueseText = composeStory(
+ ButtonStories.CSF2StoryWithLocale,
+ ButtonStories.default,
+ {
+ initialGlobals: { locale: 'pt' },
+ }
+ );
const { getByText } = render();
const buttonElement = getByText('Olá!');
expect(buttonElement).not.toBeNull();
});
it('has action arg from argTypes when addon-actions annotations are added', () => {
- //@ts-expect-error our tsconfig.jsn#moduleResulution is set to 'node', which doesn't support this import
- const Story = composeStory(stories.WithActionArgType, stories.default, addonActionsPreview);
+ const Story = composeStory(
+ ButtonStories.WithActionArgType,
+ ButtonStories.default,
+ addonActionsPreview as ProjectAnnotations
+ );
expect(Story.args.someActionArg).toHaveProperty('isAction', true);
});
});
describe('CSF3', () => {
it('renders with inferred globalRender', () => {
- const Primary = composeStory(stories.CSF3Button, stories.default);
+ const Primary = composeStory(ButtonStories.CSF3Button, ButtonStories.default);
render(Hello world);
const buttonElement = screen.getByText(/Hello world/i);
@@ -124,14 +141,17 @@ describe('CSF3', () => {
});
it('renders with custom render function', () => {
- const Primary = composeStory(stories.CSF3ButtonWithRender, stories.default);
+ const Primary = composeStory(ButtonStories.CSF3ButtonWithRender, ButtonStories.default);
render();
expect(screen.getByTestId('custom-render')).not.toBeNull();
});
it('renders with play function without canvas element', async () => {
- const CSF3InputFieldFilled = composeStory(stories.CSF3InputFieldFilled, stories.default);
+ const CSF3InputFieldFilled = composeStory(
+ ButtonStories.CSF3InputFieldFilled,
+ ButtonStories.default
+ );
await CSF3InputFieldFilled.run();
const input = screen.getByTestId('input') as HTMLInputElement;
@@ -139,7 +159,10 @@ describe('CSF3', () => {
});
it('renders with play function with canvas element', async () => {
- const CSF3InputFieldFilled = composeStory(stories.CSF3InputFieldFilled, stories.default);
+ const CSF3InputFieldFilled = composeStory(
+ ButtonStories.CSF3InputFieldFilled,
+ ButtonStories.default
+ );
const div = document.createElement('div');
document.body.appendChild(div);
@@ -153,21 +176,16 @@ describe('CSF3', () => {
});
it('renders with hooks', async () => {
- // TODO find out why act is not working here
- globalThis.IS_REACT_ACT_ENVIRONMENT = false;
- const HooksStory = composeStory(stories.HooksStory, stories.default);
-
await HooksStory.run();
const input = screen.getByTestId('input') as HTMLInputElement;
expect(input.value).toEqual('Hello world!');
- globalThis.IS_REACT_ACT_ENVIRONMENT = true;
});
});
// common in addons that need to communicate between manager and preview
it('should pass with decorators that need addons channel', () => {
- const PrimaryWithChannels = composeStory(stories.CSF3Primary, stories.default, {
+ const PrimaryWithChannels = composeStory(ButtonStories.CSF3Primary, ButtonStories.default, {
decorators: [
(StoryFn: any) => {
addons.getChannel();
@@ -186,27 +204,24 @@ describe('ComposeStories types', () => {
type ComposeStoriesParam = Parameters[0];
expectTypeOf({
- ...stories,
- default: stories.default as Meta,
+ ...ButtonStories,
+ default: ButtonStories.default as Meta,
}).toMatchTypeOf();
expectTypeOf({
- ...stories,
- default: stories.default satisfies Meta,
+ ...ButtonStories,
+ default: ButtonStories.default satisfies Meta,
}).toMatchTypeOf();
});
});
-// Batch snapshot testing
-const testCases = Object.values(composeStories(stories)).map(
+const testCases = Object.values(composeStories(ButtonStories)).map(
(Story) => [Story.storyName, Story] as [string, typeof Story]
);
it.each(testCases)('Renders %s story', async (_storyName, Story) => {
- if (_storyName === 'CSF2StoryWithLocale') {
+ if (_storyName === 'CSF2StoryWithLocale' || _storyName === 'MountInPlayFunctionThrow') {
return;
}
- globalThis.IS_REACT_ACT_ENVIRONMENT = false;
await Story.run();
- globalThis.IS_REACT_ACT_ENVIRONMENT = true;
expect(document.body).toMatchSnapshot();
});
diff --git a/code/renderers/react/src/act-compat.ts b/code/renderers/react/src/act-compat.ts
new file mode 100644
index 000000000000..3eab722d3bb1
--- /dev/null
+++ b/code/renderers/react/src/act-compat.ts
@@ -0,0 +1,66 @@
+// Copied from
+// https://github.com/testing-library/react-testing-library/blob/3dcd8a9649e25054c0e650d95fca2317b7008576/src/act-compat.js
+import * as React from 'react';
+
+import * as DeprecatedReactTestUtils from 'react-dom/test-utils';
+
+declare const globalThis: {
+ IS_REACT_ACT_ENVIRONMENT: boolean;
+};
+
+const reactAct =
+ // @ts-expect-error act might not be available in some versions of React
+ typeof React.act === 'function' ? React.act : DeprecatedReactTestUtils.act;
+
+export function setReactActEnvironment(isReactActEnvironment: boolean) {
+ globalThis.IS_REACT_ACT_ENVIRONMENT = isReactActEnvironment;
+}
+
+export function getReactActEnvironment() {
+ return globalThis.IS_REACT_ACT_ENVIRONMENT;
+}
+
+function withGlobalActEnvironment(actImplementation: (callback: () => void) => Promise) {
+ return (callback: () => any) => {
+ const previousActEnvironment = getReactActEnvironment();
+ setReactActEnvironment(true);
+ try {
+ // The return value of `act` is always a thenable.
+ let callbackNeedsToBeAwaited = false;
+ const actResult = actImplementation(() => {
+ const result = callback();
+ if (result !== null && typeof result === 'object' && typeof result.then === 'function') {
+ callbackNeedsToBeAwaited = true;
+ }
+ return result;
+ });
+ if (callbackNeedsToBeAwaited) {
+ const thenable: Promise = actResult;
+ return {
+ then: (resolve: (param: any) => void, reject: (param: any) => void) => {
+ thenable.then(
+ (returnValue) => {
+ setReactActEnvironment(previousActEnvironment);
+ resolve(returnValue);
+ },
+ (error) => {
+ setReactActEnvironment(previousActEnvironment);
+ reject(error);
+ }
+ );
+ },
+ };
+ } else {
+ setReactActEnvironment(previousActEnvironment);
+ return actResult;
+ }
+ } catch (error) {
+ // Can't be a `finally {}` block since we don't know if we have to immediately restore IS_REACT_ACT_ENVIRONMENT
+ // or if we have to await the callback first.
+ setReactActEnvironment(previousActEnvironment);
+ throw error;
+ }
+ };
+}
+
+export const act = withGlobalActEnvironment(reactAct);
diff --git a/code/renderers/react/src/docs/lib/defaultValues/createFromRawDefaultProp.ts b/code/renderers/react/src/docs/lib/defaultValues/createFromRawDefaultProp.ts
index fa8ae48e3c89..96b052a07dda 100644
--- a/code/renderers/react/src/docs/lib/defaultValues/createFromRawDefaultProp.ts
+++ b/code/renderers/react/src/docs/lib/defaultValues/createFromRawDefaultProp.ts
@@ -6,9 +6,7 @@ import {
isTooLongForDefaultValueSummary,
} from 'storybook/internal/docs-tools';
-import isFunction from 'lodash/isFunction.js';
-import isPlainObject from 'lodash/isPlainObject.js';
-import isString from 'lodash/isString.js';
+import { isFunction, isPlainObject, isString } from 'es-toolkit/compat';
import reactElementToJSXString from 'react-element-to-jsx-string';
import { ELEMENT_CAPTION, FUNCTION_CAPTION, OBJECT_CAPTION } from '../captions';
diff --git a/code/renderers/react/src/portable-stories.tsx b/code/renderers/react/src/portable-stories.tsx
index 2ea196e85b4b..7b906c9f4bde 100644
--- a/code/renderers/react/src/portable-stories.tsx
+++ b/code/renderers/react/src/portable-stories.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import * as React from 'react';
import {
composeStories as originalComposeStories,
@@ -17,6 +17,7 @@ import type {
StoryAnnotationsOrFn,
} from 'storybook/internal/types';
+import { act, getReactActEnvironment, setReactActEnvironment } from './act-compat';
import * as reactProjectAnnotations from './entry-preview';
import type { Meta } from './public-types';
import type { ReactRenderer } from './types';
@@ -54,9 +55,68 @@ export function setProjectAnnotations(
// This will not be necessary once we have auto preset loading
export const INTERNAL_DEFAULT_PROJECT_ANNOTATIONS: ProjectAnnotations = {
...reactProjectAnnotations,
- renderToCanvas: (renderContext, canvasElement) => {
+ beforeAll: async function reactBeforeAll() {
+ try {
+ // copied from
+ // https://github.com/testing-library/react-testing-library/blob/3dcd8a9649e25054c0e650d95fca2317b7008576/src/pure.js
+ const { configure } = await import('@storybook/test');
+
+ configure({
+ unstable_advanceTimersWrapper: (cb) => {
+ return act(cb);
+ },
+ // For more context about why we need disable act warnings in waitFor:
+ // https://github.com/reactwg/react-18/discussions/102
+ asyncWrapper: async (cb) => {
+ const previousActEnvironment = getReactActEnvironment();
+ setReactActEnvironment(false);
+ try {
+ const result = await cb();
+ // Drain microtask queue.
+ // Otherwise we'll restore the previous act() environment, before we resolve the `waitFor` call.
+ // The caller would have no chance to wrap the in-flight Promises in `act()`
+ await new Promise((resolve) => {
+ setTimeout(() => {
+ resolve();
+ }, 0);
+
+ if (jestFakeTimersAreEnabled()) {
+ // @ts-expect-error global jest
+ jest.advanceTimersByTime(0);
+ }
+ });
+
+ return result;
+ } finally {
+ setReactActEnvironment(previousActEnvironment);
+ }
+ },
+ eventWrapper: (cb) => {
+ let result;
+ act(() => {
+ result = cb();
+ });
+ return result;
+ },
+ });
+ } catch (e) {
+ // no-op
+ // @storybook/test might not be available
+ }
+ },
+ renderToCanvas: async (renderContext, canvasElement) => {
if (renderContext.storyContext.testingLibraryRender == null) {
- return reactProjectAnnotations.renderToCanvas(renderContext, canvasElement);
+ let unmount: () => void;
+
+ await act(async () => {
+ unmount = await reactProjectAnnotations.renderToCanvas(renderContext, canvasElement);
+ });
+
+ return async () => {
+ await act(() => {
+ unmount();
+ });
+ };
}
const {
storyContext: { context, unboundStoryFn: Story, testingLibraryRender: render },
@@ -149,3 +209,19 @@ export function composeStories;
}
+
+/** The function is used to configure jest's fake timers in environments where React's act is enabled */
+function jestFakeTimersAreEnabled() {
+ // @ts-expect-error global jest
+ if (typeof jest !== 'undefined' && jest !== null) {
+ return (
+ // legacy timers
+
+ // eslint-disable-next-line no-underscore-dangle
+ (setTimeout as any)._isMockFunction === true || // modern timers
+ Object.prototype.hasOwnProperty.call(setTimeout, 'clock')
+ );
+ }
+
+ return false;
+}
diff --git a/code/renderers/react/src/renderToCanvas.tsx b/code/renderers/react/src/renderToCanvas.tsx
index f3a4231d078c..3ae6136f9582 100644
--- a/code/renderers/react/src/renderToCanvas.tsx
+++ b/code/renderers/react/src/renderToCanvas.tsx
@@ -5,6 +5,7 @@ import type { RenderContext } from 'storybook/internal/types';
import { global } from '@storybook/global';
+import { getReactActEnvironment } from './act-compat';
import type { ReactRenderer, StoryContext } from './types';
const { FRAMEWORK_OPTIONS } = global;
@@ -57,7 +58,11 @@ export async function renderToCanvas(
const { renderElement, unmountElement } = await import('@storybook/react-dom-shim');
const Story = unboundStoryFn as FC>;
- const content = (
+ const isActEnabled = getReactActEnvironment();
+
+ const content = isActEnabled ? (
+
+ ) : (
diff --git a/code/renderers/react/template/stories/js-argtypes.stories.jsx b/code/renderers/react/template/stories/js-argtypes.stories.jsx
index 0d884f27951f..f111f47f72ed 100644
--- a/code/renderers/react/template/stories/js-argtypes.stories.jsx
+++ b/code/renderers/react/template/stories/js-argtypes.stories.jsx
@@ -5,7 +5,7 @@ import { ThemeProvider, convert, themes } from 'storybook/internal/theming';
import { PureArgsTable as ArgsTable } from '@storybook/blocks';
-import mapValues from 'lodash/mapValues.js';
+import { mapValues } from 'es-toolkit/compat';
import PropTypes from 'prop-types';
// import { component as JsStyledDocgenComponent } from './__testfixtures__/8279-js-styled-docgen/input';
diff --git a/code/renderers/react/template/stories/ts-argtypes.stories.tsx b/code/renderers/react/template/stories/ts-argtypes.stories.tsx
index b3de474d3953..0af26d5bbd85 100644
--- a/code/renderers/react/template/stories/ts-argtypes.stories.tsx
+++ b/code/renderers/react/template/stories/ts-argtypes.stories.tsx
@@ -7,7 +7,7 @@ import type { Args, Parameters, StoryContext } from 'storybook/internal/types';
import { PureArgsTable as ArgsTable } from '@storybook/blocks';
import type { StoryObj } from '@storybook/react';
-import mapValues from 'lodash/mapValues.js';
+import { mapValues } from 'es-toolkit/compat';
import { component as TsImportedTypesComponent } from './docgen-components/8143-ts-imported-types/input';
import { component as TsReactFcGenericsComponent } from './docgen-components/8143-ts-react-fc-generics/input';
diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json
index f699fde6b410..e4b1b6a518e7 100644
--- a/code/renderers/server/package.json
+++ b/code/renderers/server/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/server",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook Server renderer",
"keywords": [
"storybook"
@@ -51,8 +51,6 @@
"@storybook/manager-api": "workspace:^",
"@storybook/preview-api": "workspace:^",
"@storybook/theming": "workspace:^",
- "@types/fs-extra": "^11.0.1",
- "fs-extra": "^11.1.0",
"ts-dedent": "^2.0.0",
"yaml": "^2.3.1"
},
diff --git a/code/renderers/server/src/preset.ts b/code/renderers/server/src/preset.ts
index 018f50623eee..197a303bb15b 100644
--- a/code/renderers/server/src/preset.ts
+++ b/code/renderers/server/src/preset.ts
@@ -1,8 +1,8 @@
+import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import type { ComponentTitle, PresetProperty, StoryName, Tag } from 'storybook/internal/types';
-import fs from 'fs-extra';
import yaml from 'yaml';
type FileContent = {
@@ -18,9 +18,10 @@ export const experimental_indexers: PresetProperty<'experimental_indexers'> = (
{
test: /(stories|story)\.(json|ya?ml)$/,
createIndex: async (fileName) => {
+ const rawFile = await readFile(fileName, { encoding: 'utf8' });
const content: FileContent = fileName.endsWith('.json')
- ? await fs.readJson(fileName, 'utf-8')
- : yaml.parse((await fs.readFile(fileName, 'utf-8')).toString());
+ ? JSON.parse(rawFile)
+ : yaml.parse(rawFile);
return content.stories.map((story) => {
const tags = Array.from(new Set([...(content.tags ?? []), ...(story.tags ?? [])]));
diff --git a/code/renderers/svelte/package.json b/code/renderers/svelte/package.json
index e09bd81b0d32..eae9138107d7 100644
--- a/code/renderers/svelte/package.json
+++ b/code/renderers/svelte/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/svelte",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook Svelte renderer",
"keywords": [
"storybook"
@@ -68,7 +68,6 @@
"@sveltejs/vite-plugin-svelte": "^3.0.2",
"@testing-library/svelte": "patch:@testing-library/svelte@npm%3A4.1.0#~/.yarn/patches/@testing-library-svelte-npm-4.1.0-34b7037bc0.patch",
"expect-type": "^0.15.0",
- "fs-extra": "^11.1.0",
"svelte": "^5.0.0-next.65",
"svelte-check": "^3.6.4",
"typescript": "^5.3.2"
diff --git a/code/renderers/svelte/scripts/copy-unbundled-to-dist.ts b/code/renderers/svelte/scripts/copy-unbundled-to-dist.ts
index a82964679960..0444a5960183 100644
--- a/code/renderers/svelte/scripts/copy-unbundled-to-dist.ts
+++ b/code/renderers/svelte/scripts/copy-unbundled-to-dist.ts
@@ -1,4 +1,5 @@
-import { copy } from 'fs-extra';
+import { cp } from 'node:fs/promises';
+
import { join } from 'path';
const src = join(__dirname, '..', 'src');
@@ -11,7 +12,7 @@ const run = async () => {
console.log('Copying unbundled files to dist...');
await Promise.all(
PATHS_TO_COPY.map((pathToCopy) =>
- copy(join(src, pathToCopy), join(dist, pathToCopy), { overwrite: true })
+ cp(join(src, pathToCopy), join(dist, pathToCopy), { recursive: true, force: true })
)
);
console.log('Done!');
diff --git a/code/renderers/vue3/package.json b/code/renderers/vue3/package.json
index 77faa267b610..107e76249af0 100644
--- a/code/renderers/vue3/package.json
+++ b/code/renderers/vue3/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/vue3",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook Vue 3 renderer",
"keywords": [
"storybook"
@@ -76,7 +76,6 @@
"@testing-library/vue": "^8.0.0",
"@types/prettier": "^3.0.0",
"@vitejs/plugin-vue": "^4.4.0",
- "lodash": "^4.17.21",
"typescript": "^5.3.2",
"vue": "^3.2.47",
"vue-tsc": "latest"
diff --git a/code/renderers/vue3/src/docs/extractArgTypes.ts b/code/renderers/vue3/src/docs/extractArgTypes.ts
index 7e76c731177f..f52f21c15255 100644
--- a/code/renderers/vue3/src/docs/extractArgTypes.ts
+++ b/code/renderers/vue3/src/docs/extractArgTypes.ts
@@ -129,7 +129,7 @@ export const extractFromVueDocgenApi = (
}
}
- const required = 'required' in docgenInfo ? docgenInfo.required ?? false : false;
+ const required = 'required' in docgenInfo ? (docgenInfo.required ?? false) : false;
return {
name: docgenInfo.name,
diff --git a/code/renderers/web-components/package.json b/code/renderers/web-components/package.json
index f2c1b0b34caf..9cd643ec071f 100644
--- a/code/renderers/web-components/package.json
+++ b/code/renderers/web-components/package.json
@@ -1,6 +1,6 @@
{
"name": "@storybook/web-components",
- "version": "8.3.0-beta.4",
+ "version": "8.4.0-alpha.4",
"description": "Storybook web-components renderer",
"keywords": [
"lit",
diff --git a/code/sandbox/angular-cli-15-ts/project.json b/code/sandbox/angular-cli-15-ts/project.json
index b59d1e58d99f..d2d595560d2a 100644
--- a/code/sandbox/angular-cli-15-ts/project.json
+++ b/code/sandbox/angular-cli-15-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"angular"
],
diff --git a/code/sandbox/angular-cli-default-ts/project.json b/code/sandbox/angular-cli-default-ts/project.json
index 76d3d5738544..cb48bbc83e0e 100644
--- a/code/sandbox/angular-cli-default-ts/project.json
+++ b/code/sandbox/angular-cli-default-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"angular"
],
diff --git a/code/sandbox/angular-cli-prerelease/project.json b/code/sandbox/angular-cli-prerelease/project.json
index e07d7448e0eb..4433e7dde157 100644
--- a/code/sandbox/angular-cli-prerelease/project.json
+++ b/code/sandbox/angular-cli-prerelease/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"angular"
],
diff --git a/code/sandbox/bench-react-vite-default-ts-nodocs/project.json b/code/sandbox/bench-react-vite-default-ts-nodocs/project.json
index 3c879e9894c6..e0ed20e23930 100644
--- a/code/sandbox/bench-react-vite-default-ts-nodocs/project.json
+++ b/code/sandbox/bench-react-vite-default-ts-nodocs/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"react-vite"
],
diff --git a/code/sandbox/bench-react-vite-default-ts-test-build/project.json b/code/sandbox/bench-react-vite-default-ts-test-build/project.json
index ce151efd582d..5e44f6ba14ce 100644
--- a/code/sandbox/bench-react-vite-default-ts-test-build/project.json
+++ b/code/sandbox/bench-react-vite-default-ts-test-build/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"react-vite"
],
diff --git a/code/sandbox/bench-react-vite-default-ts/project.json b/code/sandbox/bench-react-vite-default-ts/project.json
index 07766ae7da96..a59cb05cc369 100644
--- a/code/sandbox/bench-react-vite-default-ts/project.json
+++ b/code/sandbox/bench-react-vite-default-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"react-vite"
],
diff --git a/code/sandbox/bench-react-webpack-18-ts-test-build/project.json b/code/sandbox/bench-react-webpack-18-ts-test-build/project.json
index 45744cfd7fc0..647d78878fd3 100644
--- a/code/sandbox/bench-react-webpack-18-ts-test-build/project.json
+++ b/code/sandbox/bench-react-webpack-18-ts-test-build/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"react-webpack5"
],
diff --git a/code/sandbox/bench-react-webpack-18-ts/project.json b/code/sandbox/bench-react-webpack-18-ts/project.json
index 91958a205f40..2825bb2831d2 100644
--- a/code/sandbox/bench-react-webpack-18-ts/project.json
+++ b/code/sandbox/bench-react-webpack-18-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"react-webpack5"
],
diff --git a/code/sandbox/cra-default-js/project.json b/code/sandbox/cra-default-js/project.json
index e3565c634036..a8febc5d3b00 100644
--- a/code/sandbox/cra-default-js/project.json
+++ b/code/sandbox/cra-default-js/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"react-webpack5"
],
diff --git a/code/sandbox/cra-default-ts/project.json b/code/sandbox/cra-default-ts/project.json
index d3ac6861159f..b51b71aa0940 100644
--- a/code/sandbox/cra-default-ts/project.json
+++ b/code/sandbox/cra-default-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"react-webpack5"
],
diff --git a/code/sandbox/ember-3-js/project.json b/code/sandbox/ember-3-js/project.json
index e31e6d1ebbc4..068515be75b2 100644
--- a/code/sandbox/ember-3-js/project.json
+++ b/code/sandbox/ember-3-js/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"ember"
],
diff --git a/code/sandbox/ember-default-js/project.json b/code/sandbox/ember-default-js/project.json
index f63dfb0f2d3b..81f91494ac2a 100644
--- a/code/sandbox/ember-default-js/project.json
+++ b/code/sandbox/ember-default-js/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"ember"
],
diff --git a/code/sandbox/html-vite-default-js/project.json b/code/sandbox/html-vite-default-js/project.json
index f7c873967190..9363e37e0b07 100644
--- a/code/sandbox/html-vite-default-js/project.json
+++ b/code/sandbox/html-vite-default-js/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"html-vite"
],
diff --git a/code/sandbox/html-vite-default-ts/project.json b/code/sandbox/html-vite-default-ts/project.json
index e297ef74eba4..d816dd3fbd29 100644
--- a/code/sandbox/html-vite-default-ts/project.json
+++ b/code/sandbox/html-vite-default-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"html-vite"
],
diff --git a/code/sandbox/html-webpack-default/project.json b/code/sandbox/html-webpack-default/project.json
index 44dfca1ab5b5..466b55828a54 100644
--- a/code/sandbox/html-webpack-default/project.json
+++ b/code/sandbox/html-webpack-default/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"html-webpack5"
],
diff --git a/code/sandbox/internal-react16-webpack/project.json b/code/sandbox/internal-react16-webpack/project.json
index 909c0797144b..a2164412649b 100644
--- a/code/sandbox/internal-react16-webpack/project.json
+++ b/code/sandbox/internal-react16-webpack/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"react-webpack5"
],
diff --git a/code/sandbox/internal-react18-webpack-babel/project.json b/code/sandbox/internal-react18-webpack-babel/project.json
index 6d0d1545bd57..e1585cc8ba0b 100644
--- a/code/sandbox/internal-react18-webpack-babel/project.json
+++ b/code/sandbox/internal-react18-webpack-babel/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"react-webpack5"
],
diff --git a/code/sandbox/internal-server-webpack5/project.json b/code/sandbox/internal-server-webpack5/project.json
index b1e2f7adfaf7..51b572402699 100644
--- a/code/sandbox/internal-server-webpack5/project.json
+++ b/code/sandbox/internal-server-webpack5/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"server-webpack5"
],
diff --git a/code/sandbox/lit-vite-default-js/project.json b/code/sandbox/lit-vite-default-js/project.json
index 659690873d7d..22bf1065c982 100644
--- a/code/sandbox/lit-vite-default-js/project.json
+++ b/code/sandbox/lit-vite-default-js/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"web-components-vite"
],
diff --git a/code/sandbox/lit-vite-default-ts/project.json b/code/sandbox/lit-vite-default-ts/project.json
index 273084b0d96f..978833395968 100644
--- a/code/sandbox/lit-vite-default-ts/project.json
+++ b/code/sandbox/lit-vite-default-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"web-components-vite"
],
diff --git a/code/sandbox/nextjs-13-ts/project.json b/code/sandbox/nextjs-13-ts/project.json
index e06a73e0bc06..d83ef788bf2c 100644
--- a/code/sandbox/nextjs-13-ts/project.json
+++ b/code/sandbox/nextjs-13-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"nextjs"
],
diff --git a/code/sandbox/nextjs-default-js/project.json b/code/sandbox/nextjs-default-js/project.json
index a7c86faa9f0c..540936cbd268 100644
--- a/code/sandbox/nextjs-default-js/project.json
+++ b/code/sandbox/nextjs-default-js/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"nextjs"
],
diff --git a/code/sandbox/nextjs-default-ts/project.json b/code/sandbox/nextjs-default-ts/project.json
index feb21dacb48f..5144fc10b9d4 100644
--- a/code/sandbox/nextjs-default-ts/project.json
+++ b/code/sandbox/nextjs-default-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"nextjs"
],
diff --git a/code/sandbox/nextjs-prerelease/project.json b/code/sandbox/nextjs-prerelease/project.json
index 3c7d06c9f0df..9579c10b3928 100644
--- a/code/sandbox/nextjs-prerelease/project.json
+++ b/code/sandbox/nextjs-prerelease/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"nextjs"
],
diff --git a/code/sandbox/preact-vite-default-js/project.json b/code/sandbox/preact-vite-default-js/project.json
index 524fb53a196a..4eb038e5c735 100644
--- a/code/sandbox/preact-vite-default-js/project.json
+++ b/code/sandbox/preact-vite-default-js/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"preact-vite"
],
diff --git a/code/sandbox/preact-vite-default-ts/project.json b/code/sandbox/preact-vite-default-ts/project.json
index 2fffaf2a2153..2c1cdf04e3a4 100644
--- a/code/sandbox/preact-vite-default-ts/project.json
+++ b/code/sandbox/preact-vite-default-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"preact-vite"
],
diff --git a/code/sandbox/qwik-vite-default-ts/project.json b/code/sandbox/qwik-vite-default-ts/project.json
index b354e5f74615..0a1c65c53499 100644
--- a/code/sandbox/qwik-vite-default-ts/project.json
+++ b/code/sandbox/qwik-vite-default-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks"
],
"targets": {
diff --git a/code/sandbox/react-vite-default-js/project.json b/code/sandbox/react-vite-default-js/project.json
index 7f440a312ce0..cc44d8e4a6fa 100644
--- a/code/sandbox/react-vite-default-js/project.json
+++ b/code/sandbox/react-vite-default-js/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"react-vite"
],
diff --git a/code/sandbox/react-vite-default-ts/project.json b/code/sandbox/react-vite-default-ts/project.json
index bea7aa3642b1..25c9b2442a1b 100644
--- a/code/sandbox/react-vite-default-ts/project.json
+++ b/code/sandbox/react-vite-default-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"react-vite"
],
diff --git a/code/sandbox/react-webpack-17-ts/project.json b/code/sandbox/react-webpack-17-ts/project.json
index a9dd4fe427ac..01209a70efa9 100644
--- a/code/sandbox/react-webpack-17-ts/project.json
+++ b/code/sandbox/react-webpack-17-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"react-webpack5"
],
diff --git a/code/sandbox/react-webpack-18-ts/project.json b/code/sandbox/react-webpack-18-ts/project.json
index d95c1a4c4bff..34cae14e059c 100644
--- a/code/sandbox/react-webpack-18-ts/project.json
+++ b/code/sandbox/react-webpack-18-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"react-webpack5"
],
diff --git a/code/sandbox/solid-vite-default-js/project.json b/code/sandbox/solid-vite-default-js/project.json
index 471ad5fe3aa4..7bba5774a7a1 100644
--- a/code/sandbox/solid-vite-default-js/project.json
+++ b/code/sandbox/solid-vite-default-js/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks"
],
"targets": {
diff --git a/code/sandbox/solid-vite-default-ts/project.json b/code/sandbox/solid-vite-default-ts/project.json
index 3b084c7804f1..50d504a77255 100644
--- a/code/sandbox/solid-vite-default-ts/project.json
+++ b/code/sandbox/solid-vite-default-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks"
],
"targets": {
diff --git a/code/sandbox/svelte-kit-prerelease-ts/project.json b/code/sandbox/svelte-kit-prerelease-ts/project.json
index e38bfd206d3d..e9ec53e855c8 100644
--- a/code/sandbox/svelte-kit-prerelease-ts/project.json
+++ b/code/sandbox/svelte-kit-prerelease-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"sveltekit"
],
diff --git a/code/sandbox/svelte-kit-skeleton-js/project.json b/code/sandbox/svelte-kit-skeleton-js/project.json
index a7bed3a416e6..c0ac8adf0ba2 100644
--- a/code/sandbox/svelte-kit-skeleton-js/project.json
+++ b/code/sandbox/svelte-kit-skeleton-js/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"sveltekit"
],
diff --git a/code/sandbox/svelte-kit-skeleton-ts/project.json b/code/sandbox/svelte-kit-skeleton-ts/project.json
index 55f7fd0e2256..17f7e5cdee4e 100644
--- a/code/sandbox/svelte-kit-skeleton-ts/project.json
+++ b/code/sandbox/svelte-kit-skeleton-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"sveltekit"
],
diff --git a/code/sandbox/svelte-vite-default-js/project.json b/code/sandbox/svelte-vite-default-js/project.json
index 096864499ea4..d93d4e9b6ab1 100644
--- a/code/sandbox/svelte-vite-default-js/project.json
+++ b/code/sandbox/svelte-vite-default-js/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"svelte-vite"
],
diff --git a/code/sandbox/svelte-vite-default-ts/project.json b/code/sandbox/svelte-vite-default-ts/project.json
index 8236af79dcdc..90dadf8177f4 100644
--- a/code/sandbox/svelte-vite-default-ts/project.json
+++ b/code/sandbox/svelte-vite-default-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"svelte-vite"
],
diff --git a/code/sandbox/vue-cli-default-js/project.json b/code/sandbox/vue-cli-default-js/project.json
index dfbbc9051756..fa29b401d74a 100644
--- a/code/sandbox/vue-cli-default-js/project.json
+++ b/code/sandbox/vue-cli-default-js/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"vue3-webpack5"
],
diff --git a/code/sandbox/vue3-vite-default-js/project.json b/code/sandbox/vue3-vite-default-js/project.json
index 8c49705a9ee0..6bbb297d51cd 100644
--- a/code/sandbox/vue3-vite-default-js/project.json
+++ b/code/sandbox/vue3-vite-default-js/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"vue3-vite"
],
diff --git a/code/sandbox/vue3-vite-default-ts/project.json b/code/sandbox/vue3-vite-default-ts/project.json
index 223d11d694c0..ad777a4b5ece 100644
--- a/code/sandbox/vue3-vite-default-ts/project.json
+++ b/code/sandbox/vue3-vite-default-ts/project.json
@@ -3,12 +3,12 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [
- "cli",
+ "storybook",
"core",
- "essentials",
- "interactions",
- "links",
- "onboarding",
+ "addon-essentials",
+ "addon-interactions",
+ "addon-links",
+ "addon-onboarding",
"blocks",
"vue3-vite"
],
diff --git a/code/vitest-setup.ts b/code/vitest-setup.ts
index 8edd64c36314..5eba16740d1d 100644
--- a/code/vitest-setup.ts
+++ b/code/vitest-setup.ts
@@ -7,6 +7,7 @@ const ignoreList = [
(error: any) => error.message.includes('":nth-child" is potentially unsafe'),
(error: any) => error.message.includes('":first-child" is potentially unsafe'),
(error: any) => error.message.match(/Browserslist: .* is outdated. Please run:/),
+ (error: any) => error.message.includes('Consider adding an error boundary'),
(error: any) =>
error.message.includes('react-async-component-lifecycle-hooks') &&
error.stack.includes('addons/knobs/src/components/__tests__/Options.js'),
diff --git a/code/yarn.lock b/code/yarn.lock
index 805729aa2c76..87da7fda8628 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -2661,170 +2661,170 @@ __metadata:
languageName: node
linkType: hard
-"@esbuild/aix-ppc64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/aix-ppc64@npm:0.23.0"
+"@esbuild/aix-ppc64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/aix-ppc64@npm:0.24.0"
conditions: os=aix & cpu=ppc64
languageName: node
linkType: hard
-"@esbuild/android-arm64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/android-arm64@npm:0.23.0"
+"@esbuild/android-arm64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/android-arm64@npm:0.24.0"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/android-arm@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/android-arm@npm:0.23.0"
+"@esbuild/android-arm@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/android-arm@npm:0.24.0"
conditions: os=android & cpu=arm
languageName: node
linkType: hard
-"@esbuild/android-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/android-x64@npm:0.23.0"
+"@esbuild/android-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/android-x64@npm:0.24.0"
conditions: os=android & cpu=x64
languageName: node
linkType: hard
-"@esbuild/darwin-arm64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/darwin-arm64@npm:0.23.0"
+"@esbuild/darwin-arm64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/darwin-arm64@npm:0.24.0"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/darwin-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/darwin-x64@npm:0.23.0"
+"@esbuild/darwin-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/darwin-x64@npm:0.24.0"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
-"@esbuild/freebsd-arm64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/freebsd-arm64@npm:0.23.0"
+"@esbuild/freebsd-arm64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/freebsd-arm64@npm:0.24.0"
conditions: os=freebsd & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/freebsd-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/freebsd-x64@npm:0.23.0"
+"@esbuild/freebsd-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/freebsd-x64@npm:0.24.0"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
-"@esbuild/linux-arm64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-arm64@npm:0.23.0"
+"@esbuild/linux-arm64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-arm64@npm:0.24.0"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/linux-arm@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-arm@npm:0.23.0"
+"@esbuild/linux-arm@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-arm@npm:0.24.0"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
-"@esbuild/linux-ia32@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-ia32@npm:0.23.0"
+"@esbuild/linux-ia32@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-ia32@npm:0.24.0"
conditions: os=linux & cpu=ia32
languageName: node
linkType: hard
-"@esbuild/linux-loong64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-loong64@npm:0.23.0"
+"@esbuild/linux-loong64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-loong64@npm:0.24.0"
conditions: os=linux & cpu=loong64
languageName: node
linkType: hard
-"@esbuild/linux-mips64el@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-mips64el@npm:0.23.0"
+"@esbuild/linux-mips64el@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-mips64el@npm:0.24.0"
conditions: os=linux & cpu=mips64el
languageName: node
linkType: hard
-"@esbuild/linux-ppc64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-ppc64@npm:0.23.0"
+"@esbuild/linux-ppc64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-ppc64@npm:0.24.0"
conditions: os=linux & cpu=ppc64
languageName: node
linkType: hard
-"@esbuild/linux-riscv64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-riscv64@npm:0.23.0"
+"@esbuild/linux-riscv64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-riscv64@npm:0.24.0"
conditions: os=linux & cpu=riscv64
languageName: node
linkType: hard
-"@esbuild/linux-s390x@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-s390x@npm:0.23.0"
+"@esbuild/linux-s390x@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-s390x@npm:0.24.0"
conditions: os=linux & cpu=s390x
languageName: node
linkType: hard
-"@esbuild/linux-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-x64@npm:0.23.0"
+"@esbuild/linux-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-x64@npm:0.24.0"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
-"@esbuild/netbsd-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/netbsd-x64@npm:0.23.0"
+"@esbuild/netbsd-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/netbsd-x64@npm:0.24.0"
conditions: os=netbsd & cpu=x64
languageName: node
linkType: hard
-"@esbuild/openbsd-arm64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/openbsd-arm64@npm:0.23.0"
+"@esbuild/openbsd-arm64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/openbsd-arm64@npm:0.24.0"
conditions: os=openbsd & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/openbsd-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/openbsd-x64@npm:0.23.0"
+"@esbuild/openbsd-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/openbsd-x64@npm:0.24.0"
conditions: os=openbsd & cpu=x64
languageName: node
linkType: hard
-"@esbuild/sunos-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/sunos-x64@npm:0.23.0"
+"@esbuild/sunos-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/sunos-x64@npm:0.24.0"
conditions: os=sunos & cpu=x64
languageName: node
linkType: hard
-"@esbuild/win32-arm64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/win32-arm64@npm:0.23.0"
+"@esbuild/win32-arm64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/win32-arm64@npm:0.24.0"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/win32-ia32@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/win32-ia32@npm:0.23.0"
+"@esbuild/win32-ia32@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/win32-ia32@npm:0.24.0"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
-"@esbuild/win32-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/win32-x64@npm:0.23.0"
+"@esbuild/win32-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/win32-x64@npm:0.24.0"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
@@ -3677,9 +3677,9 @@ __metadata:
languageName: node
linkType: hard
-"@joshwooding/vite-plugin-react-docgen-typescript@npm:0.3.1":
- version: 0.3.1
- resolution: "@joshwooding/vite-plugin-react-docgen-typescript@npm:0.3.1"
+"@joshwooding/vite-plugin-react-docgen-typescript@npm:0.3.0":
+ version: 0.3.0
+ resolution: "@joshwooding/vite-plugin-react-docgen-typescript@npm:0.3.0"
dependencies:
glob: "npm:^7.2.0"
glob-promise: "npm:^4.2.0"
@@ -3691,7 +3691,7 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
- checksum: 10c0/a9c7a03d7d1daf5bd64949255516ba64c88d5600366c8c74dcdb6f37c2a6099daaec02860b7587d2220e61afa47a0b2de17ef70d723c2db02f24e0890edfd9f3
+ checksum: 10c0/31098ad8fcc2440437534599c111d9f2951dd74821e8ba46c521b969bae4c918d830b7bb0484efbad29a51711bb62d3bc623d5a1ed5b1695b5b5594ea9dd4ca0
languageName: node
linkType: hard
@@ -4999,7 +4999,7 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-slot@npm:1.0.2, @radix-ui/react-slot@npm:^1.0.2":
+"@radix-ui/react-slot@npm:1.0.2":
version: 1.0.2
resolution: "@radix-ui/react-slot@npm:1.0.2"
dependencies:
@@ -5015,7 +5015,7 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-slot@npm:1.1.0":
+"@radix-ui/react-slot@npm:1.1.0, @radix-ui/react-slot@npm:^1.0.2":
version: 1.1.0
resolution: "@radix-ui/react-slot@npm:1.1.0"
dependencies:
@@ -5338,7 +5338,6 @@ __metadata:
"@storybook/icons": "npm:^1.2.10"
"@testing-library/react": "npm:^14.0.0"
axe-core: "npm:^4.2.0"
- lodash: "npm:^4.17.21"
react: "npm:^18.2.0"
react-dom: "npm:^18.2.0"
react-resize-detector: "npm:^7.1.2"
@@ -5393,7 +5392,6 @@ __metadata:
"@storybook/global": "npm:^5.0.0"
"@storybook/icons": "npm:^1.2.10"
dequal: "npm:^2.0.2"
- lodash: "npm:^4.17.21"
react: "npm:^18.2.0"
react-dom: "npm:^18.2.0"
ts-dedent: "npm:^2.0.0"
@@ -5437,7 +5435,6 @@ __metadata:
"@storybook/global": "npm:^5.0.0"
"@storybook/react-dom-shim": "workspace:*"
"@types/react": "npm:^16.8.0 || ^17.0.0 || ^18.0.0"
- fs-extra: "npm:^11.1.0"
react: "npm:^18.2.0"
react-dom: "npm:^18.2.0"
rehype-external-links: "npm:^3.0.0"
@@ -5492,6 +5489,7 @@ __metadata:
"@storybook/instrumenter": "workspace:*"
"@storybook/test": "workspace:*"
"@types/node": "npm:^22.0.0"
+ ansi-to-html: "npm:^0.7.2"
formik: "npm:^2.2.9"
polished: "npm:^4.2.2"
react: "npm:^18.2.0"
@@ -5527,7 +5525,6 @@ __metadata:
dependencies:
"@storybook/csf": "npm:^0.1.11"
"@storybook/global": "npm:^5.0.0"
- fs-extra: "npm:^11.1.0"
ts-dedent: "npm:^2.0.0"
typescript: "npm:^5.3.2"
peerDependencies:
@@ -5758,10 +5755,9 @@ __metadata:
"@storybook/react": "workspace:*"
"@storybook/test": "workspace:*"
"@types/color-convert": "npm:^2.0.0"
- "@types/lodash": "npm:^4.14.167"
color-convert: "npm:^2.0.1"
dequal: "npm:^2.0.2"
- lodash: "npm:^4.17.21"
+ es-toolkit: "npm:^1.21.0"
markdown-to-jsx: "npm:^7.4.5"
memoizerific: "npm:^1.11.3"
polished: "npm:^4.2.2"
@@ -5802,7 +5798,6 @@ __metadata:
es-module-lexer: "npm:^1.5.0"
express: "npm:^4.19.2"
find-cache-dir: "npm:^3.0.0"
- fs-extra: "npm:^11.1.0"
glob: "npm:^10.0.0"
knitwork: "npm:^1.1.0"
magic-string: "npm:^0.30.0"
@@ -5845,7 +5840,6 @@ __metadata:
es-module-lexer: "npm:^1.5.0"
express: "npm:^4.19.2"
fork-ts-checker-webpack-plugin: "npm:^8.0.0"
- fs-extra: "npm:^11.1.0"
html-webpack-plugin: "npm:^5.5.0"
magic-string: "npm:^0.30.5"
path-browserify: "npm:^1.0.1"
@@ -5891,19 +5885,18 @@ __metadata:
"@types/prompts": "npm:^2.0.9"
"@types/semver": "npm:^7.3.4"
boxen: "npm:^7.1.1"
- chalk: "npm:^4.1.0"
commander: "npm:^12.1.0"
create-storybook: "workspace:*"
cross-spawn: "npm:^7.0.3"
envinfo: "npm:^7.7.3"
fd-package-json: "npm:^1.2.0"
find-up: "npm:^5.0.0"
- fs-extra: "npm:^11.1.0"
giget: "npm:^1.0.0"
glob: "npm:^10.0.0"
globby: "npm:^14.0.1"
jscodeshift: "npm:^0.15.1"
leven: "npm:^3.1.0"
+ picocolors: "npm:^1.1.0"
prompts: "npm:^2.4.0"
semver: "npm:^7.3.7"
slash: "npm:^5.0.0"
@@ -5937,10 +5930,11 @@ __metadata:
"@types/cross-spawn": "npm:^6.0.2"
"@types/jscodeshift": "npm:^0.11.10"
ansi-regex: "npm:^6.0.1"
+ camelcase: "npm:^8.0.0"
cross-spawn: "npm:^7.0.3"
+ es-toolkit: "npm:^1.21.0"
globby: "npm:^14.0.1"
jscodeshift: "npm:^0.15.1"
- lodash: "npm:^4.17.21"
mdast-util-mdx-jsx: "npm:^3.0.0"
mdast-util-mdxjs-esm: "npm:^2.0.1"
prettier: "npm:^3.1.1"
@@ -6040,16 +6034,13 @@ __metadata:
"@types/ejs": "npm:^3.1.1"
"@types/express": "npm:^4.17.21"
"@types/find-cache-dir": "npm:^5.0.0"
- "@types/fs-extra": "npm:^11.0.1"
"@types/js-yaml": "npm:^4.0.5"
- "@types/lodash": "npm:^4.14.167"
"@types/node": "npm:^22.0.0"
"@types/npmlog": "npm:^7.0.0"
"@types/picomatch": "npm:^2.3.0"
"@types/prettier": "npm:^3.0.0"
"@types/pretty-hrtime": "npm:^1.0.0"
"@types/prompts": "npm:^2.0.9"
- "@types/qs": "npm:^6"
"@types/react-syntax-highlighter": "npm:11.0.5"
"@types/react-transition-group": "npm:^4"
"@types/semver": "npm:^7.5.8"
@@ -6066,7 +6057,6 @@ __metadata:
browser-dtector: "npm:^3.4.0"
camelcase: "npm:^8.0.0"
chai: "npm:^4.4.1"
- chalk: "npm:^5.3.0"
cli-table3: "npm:^0.6.1"
commander: "npm:^12.1.0"
comment-parser: "npm:^1.4.1"
@@ -6082,6 +6072,7 @@ __metadata:
diff: "npm:^5.2.0"
downshift: "npm:^9.0.4"
ejs: "npm:^3.1.10"
+ es-toolkit: "npm:^1.21.0"
esbuild: "npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0"
esbuild-plugin-alias: "npm:^0.2.1"
esbuild-register: "npm:^3.5.0"
@@ -6093,30 +6084,29 @@ __metadata:
find-cache-dir: "npm:^5.0.0"
find-up: "npm:^7.0.0"
flush-promises: "npm:^1.0.2"
- fs-extra: "npm:^11.1.0"
fuse.js: "npm:^3.6.1"
get-npm-tarball-url: "npm:^2.0.3"
glob: "npm:^10.0.0"
globby: "npm:^14.0.1"
- handlebars: "npm:^4.7.7"
+ jiti: "npm:^1.21.6"
js-yaml: "npm:^4.1.0"
jsdoc-type-pratt-parser: "npm:^4.0.0"
lazy-universal-dotenv: "npm:^4.0.0"
leven: "npm:^4.0.0"
- lodash: "npm:^4.17.21"
markdown-to-jsx: "npm:^7.4.5"
memfs: "npm:^4.11.1"
memoizerific: "npm:^1.11.3"
nanoid: "npm:^4.0.2"
npmlog: "npm:^7.0.0"
open: "npm:^8.4.0"
+ picocolors: "npm:^1.1.0"
picomatch: "npm:^2.3.0"
+ picoquery: "npm:^1.4.0"
polished: "npm:^4.2.2"
prettier: "npm:^3.2.5"
pretty-hrtime: "npm:^1.0.3"
process: "npm:^0.11.10"
prompts: "npm:^2.4.0"
- qs: "npm:^6.10.0"
react: "npm:^18.2.0"
react-dom: "npm:^18.2.0"
react-draggable: "npm:^4.4.5"
@@ -6147,6 +6137,11 @@ __metadata:
util: "npm:^0.12.5"
watchpack: "npm:^2.2.0"
ws: "npm:^8.2.3"
+ peerDependencies:
+ prettier: ^2 || ^3
+ peerDependenciesMeta:
+ prettier:
+ optional: true
languageName: unknown
linkType: soft
@@ -6245,6 +6240,7 @@ __metadata:
"@vitest/browser": "npm:^2.0.0"
boxen: "npm:^8.0.1"
find-up: "npm:^7.0.0"
+ picocolors: "npm:^1.1.0"
semver: "npm:^7.6.3"
tinyrainbow: "npm:^1.2.0"
ts-dedent: "npm:^2.2.0"
@@ -6268,7 +6264,7 @@ __metadata:
sharp: "npm:^0.33.3"
styled-jsx: "npm:5.1.6"
typescript: "npm:^5.3.2"
- vite-plugin-storybook-nextjs: "npm:^1.0.10"
+ vite-plugin-storybook-nextjs: "npm:^1.0.11"
peerDependencies:
"@storybook/test": "workspace:*"
next: ^14.1.0
@@ -6436,7 +6432,6 @@ __metadata:
babel-loader: "npm:^9.1.3"
css-loader: "npm:^6.7.3"
find-up: "npm:^5.0.0"
- fs-extra: "npm:^11.1.0"
image-size: "npm:^1.0.0"
loader-utils: "npm:^3.2.1"
next: "npm:^14.1.0"
@@ -6446,7 +6441,7 @@ __metadata:
postcss-loader: "npm:^8.1.1"
react-refresh: "npm:^0.14.0"
resolve-url-loader: "npm:^5.0.0"
- sass-loader: "npm:^12.4.0"
+ sass-loader: "npm:^13.2.0"
semver: "npm:^7.3.5"
sharp: "npm:^0.33.3"
style-loader: "npm:^3.3.1"
@@ -6585,7 +6580,6 @@ __metadata:
"@types/node": "npm:^22.0.0"
"@types/semver": "npm:^7.3.4"
find-up: "npm:^5.0.0"
- fs-extra: "npm:^11.1.0"
magic-string: "npm:^0.30.5"
react-docgen: "npm:^7.0.0"
resolve: "npm:^1.22.8"
@@ -6611,7 +6605,6 @@ __metadata:
"@storybook/global": "npm:^5.0.0"
"@storybook/server": "workspace:*"
"@types/node": "npm:^22.0.0"
- fs-extra: "npm:^11.1.0"
safe-identifier: "npm:^0.4.1"
ts-dedent: "npm:^2.0.0"
typescript: "npm:^5.3.2"
@@ -6710,7 +6703,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/react-vite@workspace:frameworks/react-vite"
dependencies:
- "@joshwooding/vite-plugin-react-docgen-typescript": "npm:0.3.1"
+ "@joshwooding/vite-plugin-react-docgen-typescript": "npm:0.3.0"
"@rollup/pluginutils": "npm:^5.0.2"
"@storybook/builder-vite": "workspace:*"
"@storybook/react": "workspace:*"
@@ -6770,10 +6763,10 @@ __metadata:
acorn-jsx: "npm:^5.3.1"
acorn-walk: "npm:^7.2.0"
babel-plugin-react-docgen: "npm:^4.2.1"
+ es-toolkit: "npm:^1.21.0"
escodegen: "npm:^2.1.0"
expect-type: "npm:^0.15.0"
html-tags: "npm:^3.1.0"
- lodash: "npm:^4.17.21"
prop-types: "npm:^15.7.2"
react-element-to-jsx-string: "npm:^15.0.0"
require-from-string: "npm:^2.0.2"
@@ -6782,11 +6775,14 @@ __metadata:
type-fest: "npm:~2.19"
util-deprecate: "npm:^1.0.2"
peerDependencies:
+ "@storybook/test": "workspace:*"
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
storybook: "workspace:^"
typescript: ">= 4.2.x"
peerDependenciesMeta:
+ "@storybook/test":
+ optional: true
typescript:
optional: true
languageName: unknown
@@ -6875,8 +6871,6 @@ __metadata:
"@testing-library/user-event": "npm:^14.5.2"
"@trivago/prettier-plugin-sort-imports": "npm:^4.3.0"
"@types/express": "npm:^4.17.21"
- "@types/fs-extra": "npm:^11.0.1"
- "@types/lodash": "npm:^4.14.167"
"@types/mock-require": "npm:^2.0.3"
"@types/node": "npm:^22.0.0"
"@types/react": "npm:^18.0.37"
@@ -6893,7 +6887,8 @@ __metadata:
create-storybook: "workspace:*"
cross-env: "npm:^7.0.3"
danger: "npm:^12.3.3"
- esbuild: "npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0"
+ es-toolkit: "npm:^1.21.0"
+ esbuild: "npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0"
esbuild-loader: "npm:^4.2.0"
esbuild-plugin-alias: "npm:^0.2.1"
eslint: "npm:^8.56.0"
@@ -6901,14 +6896,12 @@ __metadata:
eslint-plugin-local-rules: "portal:../scripts/eslint-plugin-local-rules"
eslint-plugin-playwright: "npm:^1.6.2"
eslint-plugin-storybook: "npm:^0.8.0"
- fs-extra: "npm:^11.1.0"
github-release-from-changelog: "npm:^2.1.1"
glob: "npm:^10.0.0"
happy-dom: "npm:^14.12.0"
http-server: "npm:^14.1.1"
husky: "npm:^4.3.7"
lint-staged: "npm:^13.2.2"
- lodash: "npm:^4.17.21"
mock-require: "npm:^3.0.3"
nx: "npm:18.0.6"
prettier: "npm:^3.1.1"
@@ -6983,8 +6976,6 @@ __metadata:
"@storybook/manager-api": "workspace:^"
"@storybook/preview-api": "workspace:^"
"@storybook/theming": "workspace:^"
- "@types/fs-extra": "npm:^11.0.1"
- fs-extra: "npm:^11.1.0"
ts-dedent: "npm:^2.0.0"
typescript: "npm:^5.3.2"
yaml: "npm:^2.3.1"
@@ -6998,8 +6989,8 @@ __metadata:
resolution: "@storybook/source-loader@workspace:lib/source-loader"
dependencies:
"@storybook/csf": "npm:^0.1.11"
+ es-toolkit: "npm:^1.21.0"
estraverse: "npm:^5.2.0"
- lodash: "npm:^4.17.21"
prettier: "npm:^3.1.1"
typescript: "npm:^5.3.2"
peerDependencies:
@@ -7059,7 +7050,6 @@ __metadata:
"@sveltejs/vite-plugin-svelte": "npm:^3.0.2"
"@testing-library/svelte": "patch:@testing-library/svelte@npm%3A4.1.0#~/.yarn/patches/@testing-library-svelte-npm-4.1.0-34b7037bc0.patch"
expect-type: "npm:^0.15.0"
- fs-extra: "npm:^11.1.0"
svelte: "npm:^5.0.0-next.65"
svelte-check: "npm:^3.6.4"
sveltedoc-parser: "npm:^4.2.1"
@@ -7200,7 +7190,6 @@ __metadata:
"@types/prettier": "npm:^3.0.0"
"@vitejs/plugin-vue": "npm:^4.4.0"
"@vue/compiler-core": "npm:^3.0.0"
- lodash: "npm:^4.17.21"
ts-dedent: "npm:^2.0.0"
type-fest: "npm:~2.19"
typescript: "npm:^5.3.2"
@@ -7867,16 +7856,6 @@ __metadata:
languageName: node
linkType: hard
-"@types/fs-extra@npm:^11.0.1":
- version: 11.0.4
- resolution: "@types/fs-extra@npm:11.0.4"
- dependencies:
- "@types/jsonfile": "npm:*"
- "@types/node": "npm:*"
- checksum: 10c0/9e34f9b24ea464f3c0b18c3f8a82aefc36dc524cc720fc2b886e5465abc66486ff4e439ea3fb2c0acebf91f6d3f74e514f9983b1f02d4243706bdbb7511796ad
- languageName: node
- linkType: hard
-
"@types/fs-extra@npm:^5.0.5":
version: 5.1.0
resolution: "@types/fs-extra@npm:5.1.0"
@@ -8030,13 +8009,6 @@ __metadata:
languageName: node
linkType: hard
-"@types/lodash@npm:^4.14.167":
- version: 4.14.202
- resolution: "@types/lodash@npm:4.14.202"
- checksum: 10c0/6064d43c8f454170841bd67c8266cc9069d9e570a72ca63f06bceb484cb4a3ee60c9c1f305c1b9e3a87826049fd41124b8ef265c4dd08b00f6766609c7fe9973
- languageName: node
- linkType: hard
-
"@types/mdast@npm:^3.0.0":
version: 3.0.15
resolution: "@types/mdast@npm:3.0.15"
@@ -8187,7 +8159,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/qs@npm:*, @types/qs@npm:^6":
+"@types/qs@npm:*":
version: 6.9.10
resolution: "@types/qs@npm:6.9.10"
checksum: 10c0/6be12e5f062d1b41eb037d59bf9cb65bc9410cedd5e6da832dfd7c8e2b3f4c91e81c9b90b51811140770e5052c6c4e8361181bd9437ddcd4515dc128b7c00353
@@ -12495,13 +12467,12 @@ __metadata:
"@types/semver": "npm:^7.3.4"
"@types/util-deprecate": "npm:^1.0.0"
boxen: "npm:^7.1.1"
- chalk: "npm:^4.1.0"
commander: "npm:^12.1.0"
execa: "npm:^5.0.0"
fd-package-json: "npm:^1.2.0"
find-up: "npm:^5.0.0"
- fs-extra: "npm:^11.1.0"
ora: "npm:^5.4.1"
+ picocolors: "npm:^1.1.0"
prettier: "npm:^3.1.1"
prompts: "npm:^2.4.0"
semver: "npm:^7.3.7"
@@ -13744,14 +13715,7 @@ __metadata:
languageName: node
linkType: hard
-"emoji-regex@npm:^10.2.1":
- version: 10.3.0
- resolution: "emoji-regex@npm:10.3.0"
- checksum: 10c0/b4838e8dcdceb44cf47f59abe352c25ff4fe7857acaf5fb51097c427f6f75b44d052eb907a7a3b86f86bc4eae3a93f5c2b7460abe79c407307e6212d65c91163
- languageName: node
- linkType: hard
-
-"emoji-regex@npm:^10.3.0":
+"emoji-regex@npm:^10.2.1, emoji-regex@npm:^10.3.0":
version: 10.4.0
resolution: "emoji-regex@npm:10.4.0"
checksum: 10c0/a3fcedfc58bfcce21a05a5f36a529d81e88d602100145fcca3dc6f795e3c8acc4fc18fe773fbf9b6d6e9371205edb3afa2668ec3473fa2aa7fd47d2a9d46482d
@@ -14102,6 +14066,13 @@ __metadata:
languageName: node
linkType: hard
+"es-toolkit@npm:^1.21.0":
+ version: 1.21.0
+ resolution: "es-toolkit@npm:1.21.0"
+ checksum: 10c0/894a63f8ce5b2e5c1be242c8e8eace6364ea1212d01cdf89594d2cc582c5e1574114ad2ee7022ad5206561c4d5170511d83b38853257249860e56178768854ea
+ languageName: node
+ linkType: hard
+
"es6-promise@npm:^3.1.2":
version: 3.3.1
resolution: "es6-promise@npm:3.3.1"
@@ -14150,34 +14121,34 @@ __metadata:
languageName: node
linkType: hard
-"esbuild@npm:^0.23.0":
- version: 0.23.0
- resolution: "esbuild@npm:0.23.0"
- dependencies:
- "@esbuild/aix-ppc64": "npm:0.23.0"
- "@esbuild/android-arm": "npm:0.23.0"
- "@esbuild/android-arm64": "npm:0.23.0"
- "@esbuild/android-x64": "npm:0.23.0"
- "@esbuild/darwin-arm64": "npm:0.23.0"
- "@esbuild/darwin-x64": "npm:0.23.0"
- "@esbuild/freebsd-arm64": "npm:0.23.0"
- "@esbuild/freebsd-x64": "npm:0.23.0"
- "@esbuild/linux-arm": "npm:0.23.0"
- "@esbuild/linux-arm64": "npm:0.23.0"
- "@esbuild/linux-ia32": "npm:0.23.0"
- "@esbuild/linux-loong64": "npm:0.23.0"
- "@esbuild/linux-mips64el": "npm:0.23.0"
- "@esbuild/linux-ppc64": "npm:0.23.0"
- "@esbuild/linux-riscv64": "npm:0.23.0"
- "@esbuild/linux-s390x": "npm:0.23.0"
- "@esbuild/linux-x64": "npm:0.23.0"
- "@esbuild/netbsd-x64": "npm:0.23.0"
- "@esbuild/openbsd-arm64": "npm:0.23.0"
- "@esbuild/openbsd-x64": "npm:0.23.0"
- "@esbuild/sunos-x64": "npm:0.23.0"
- "@esbuild/win32-arm64": "npm:0.23.0"
- "@esbuild/win32-ia32": "npm:0.23.0"
- "@esbuild/win32-x64": "npm:0.23.0"
+"esbuild@npm:^0.24.0":
+ version: 0.24.0
+ resolution: "esbuild@npm:0.24.0"
+ dependencies:
+ "@esbuild/aix-ppc64": "npm:0.24.0"
+ "@esbuild/android-arm": "npm:0.24.0"
+ "@esbuild/android-arm64": "npm:0.24.0"
+ "@esbuild/android-x64": "npm:0.24.0"
+ "@esbuild/darwin-arm64": "npm:0.24.0"
+ "@esbuild/darwin-x64": "npm:0.24.0"
+ "@esbuild/freebsd-arm64": "npm:0.24.0"
+ "@esbuild/freebsd-x64": "npm:0.24.0"
+ "@esbuild/linux-arm": "npm:0.24.0"
+ "@esbuild/linux-arm64": "npm:0.24.0"
+ "@esbuild/linux-ia32": "npm:0.24.0"
+ "@esbuild/linux-loong64": "npm:0.24.0"
+ "@esbuild/linux-mips64el": "npm:0.24.0"
+ "@esbuild/linux-ppc64": "npm:0.24.0"
+ "@esbuild/linux-riscv64": "npm:0.24.0"
+ "@esbuild/linux-s390x": "npm:0.24.0"
+ "@esbuild/linux-x64": "npm:0.24.0"
+ "@esbuild/netbsd-x64": "npm:0.24.0"
+ "@esbuild/openbsd-arm64": "npm:0.24.0"
+ "@esbuild/openbsd-x64": "npm:0.24.0"
+ "@esbuild/sunos-x64": "npm:0.24.0"
+ "@esbuild/win32-arm64": "npm:0.24.0"
+ "@esbuild/win32-ia32": "npm:0.24.0"
+ "@esbuild/win32-x64": "npm:0.24.0"
dependenciesMeta:
"@esbuild/aix-ppc64":
optional: true
@@ -14229,7 +14200,7 @@ __metadata:
optional: true
bin:
esbuild: bin/esbuild
- checksum: 10c0/08c148c067795165798c0467ce02d2d1ecedc096989bded5f0d795c61a1fcbec6c14d0a3c9f4ad6185cc29ec52087acaa335ed6d98be6ad57f7fa4264626bde0
+ checksum: 10c0/9f1aadd8d64f3bff422ae78387e66e51a5e09de6935a6f987b6e4e189ed00fdc2d1bc03d2e33633b094008529c8b6e06c7ad1a9782fb09fec223bf95998c0683
languageName: node
linkType: hard
@@ -15132,6 +15103,13 @@ __metadata:
languageName: node
linkType: hard
+"fast-decode-uri-component@npm:^1.0.1":
+ version: 1.0.1
+ resolution: "fast-decode-uri-component@npm:1.0.1"
+ checksum: 10c0/039d50c2e99d64f999c3f2126c23fbf75a04a4117e218a149ca0b1d2aeb8c834b7b19d643b9d35d4eabce357189a6a94085f78cf48869e6e26cc59b036284bc3
+ languageName: node
+ linkType: hard
+
"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3":
version: 3.1.3
resolution: "fast-deep-equal@npm:3.1.3"
@@ -16500,24 +16478,6 @@ __metadata:
languageName: node
linkType: hard
-"handlebars@npm:^4.7.7":
- version: 4.7.8
- resolution: "handlebars@npm:4.7.8"
- dependencies:
- minimist: "npm:^1.2.5"
- neo-async: "npm:^2.6.2"
- source-map: "npm:^0.6.1"
- uglify-js: "npm:^3.1.4"
- wordwrap: "npm:^1.0.0"
- dependenciesMeta:
- uglify-js:
- optional: true
- bin:
- handlebars: bin/handlebars
- checksum: 10c0/7aff423ea38a14bb379316f3857fe0df3c5d66119270944247f155ba1f08e07a92b340c58edaa00cfe985c21508870ee5183e0634dcb53dd405f35c93ef7f10d
- languageName: node
- linkType: hard
-
"happy-dom@npm:^14.12.0":
version: 14.12.0
resolution: "happy-dom@npm:14.12.0"
@@ -18355,12 +18315,12 @@ __metadata:
languageName: node
linkType: hard
-"jiti@npm:^1.20.0":
- version: 1.21.0
- resolution: "jiti@npm:1.21.0"
+"jiti@npm:^1.20.0, jiti@npm:^1.21.6":
+ version: 1.21.6
+ resolution: "jiti@npm:1.21.6"
bin:
jiti: bin/jiti.js
- checksum: 10c0/7f361219fe6c7a5e440d5f1dba4ab763a5538d2df8708cdc22561cf25ea3e44b837687931fca7cdd8cdd9f567300e90be989dd1321650045012d8f9ed6aab07f
+ checksum: 10c0/05b9ed58cd30d0c3ccd3c98209339e74f50abd9a17e716f65db46b6a35812103f6bde6e134be7124d01745586bca8cc5dae1d0d952267c3ebe55171949c32e56
languageName: node
linkType: hard
@@ -20918,7 +20878,7 @@ __metadata:
languageName: node
linkType: hard
-"minimist@npm:^1.0.0, minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6, minimist@npm:^1.2.8":
+"minimist@npm:^1.0.0, minimist@npm:^1.2.0, minimist@npm:^1.2.6, minimist@npm:^1.2.8":
version: 1.2.8
resolution: "minimist@npm:1.2.8"
checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6
@@ -22678,10 +22638,10 @@ __metadata:
languageName: node
linkType: hard
-"picocolors@npm:^1.0.0, picocolors@npm:^1.0.1":
- version: 1.0.1
- resolution: "picocolors@npm:1.0.1"
- checksum: 10c0/c63cdad2bf812ef0d66c8db29583802355d4ca67b9285d846f390cc15c2f6ccb94e8cb7eb6a6e97fc5990a6d3ad4ae42d86c84d3146e667c739a4234ed50d400
+"picocolors@npm:^1.0.0, picocolors@npm:^1.0.1, picocolors@npm:^1.1.0":
+ version: 1.1.0
+ resolution: "picocolors@npm:1.1.0"
+ checksum: 10c0/86946f6032148801ef09c051c6fb13b5cf942eaf147e30ea79edb91dd32d700934edebe782a1078ff859fb2b816792e97ef4dab03d7f0b804f6b01a0df35e023
languageName: node
linkType: hard
@@ -22699,6 +22659,15 @@ __metadata:
languageName: node
linkType: hard
+"picoquery@npm:^1.4.0":
+ version: 1.4.0
+ resolution: "picoquery@npm:1.4.0"
+ dependencies:
+ fast-decode-uri-component: "npm:^1.0.1"
+ checksum: 10c0/8c2bdb85eabb51ce1534d0a70ac288dac8d241a32e5d1ee960ee1ed7c5f522752db23cdc63318ac69ce892bdbcc465f4e2d25a5b970b7a29ffa36455cc2d022c
+ languageName: node
+ linkType: hard
+
"pidtree@npm:0.6.0":
version: 0.6.0
resolution: "pidtree@npm:0.6.0"
@@ -23169,16 +23138,7 @@ __metadata:
languageName: node
linkType: hard
-"prettier@npm:*, prettier@npm:^3.1.1":
- version: 3.2.5
- resolution: "prettier@npm:3.2.5"
- bin:
- prettier: bin/prettier.cjs
- checksum: 10c0/ea327f37a7d46f2324a34ad35292af2ad4c4c3c3355da07313339d7e554320f66f65f91e856add8530157a733c6c4a897dc41b577056be5c24c40f739f5ee8c6
- languageName: node
- linkType: hard
-
-"prettier@npm:^3.2.5":
+"prettier@npm:*, prettier@npm:^3.1.1, prettier@npm:^3.2.5":
version: 3.3.3
resolution: "prettier@npm:3.3.3"
bin:
@@ -23606,7 +23566,7 @@ __metadata:
languageName: node
linkType: hard
-"qs@npm:^6.10.0, qs@npm:^6.11.1, qs@npm:^6.11.2, qs@npm:^6.4.0":
+"qs@npm:^6.11.1, qs@npm:^6.11.2, qs@npm:^6.4.0":
version: 6.12.3
resolution: "qs@npm:6.12.3"
dependencies:
@@ -25310,15 +25270,14 @@ __metadata:
languageName: node
linkType: hard
-"sass-loader@npm:^12.4.0":
- version: 12.6.0
- resolution: "sass-loader@npm:12.6.0"
+"sass-loader@npm:^13.2.0":
+ version: 13.3.3
+ resolution: "sass-loader@npm:13.3.3"
dependencies:
- klona: "npm:^2.0.4"
neo-async: "npm:^2.6.2"
peerDependencies:
fibers: ">= 3.1.0"
- node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
+ node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
sass: ^1.3.0
sass-embedded: "*"
webpack: ^5.0.0
@@ -25331,7 +25290,7 @@ __metadata:
optional: true
sass-embedded:
optional: true
- checksum: 10c0/e1ef655f3898cc4c45f02b3c627f8baf998139993a9a79c524153a80814282bfe20d8d8d703b8cf1d05457c1930940b65e2156d11285ed0861f9a1016f993e53
+ checksum: 10c0/5e955a4ffce35ee0a46fce677ce51eaa69587fb5371978588c83af00f49e7edc36dcf3bb559cbae27681c5e24a71284463ebe03a1fb65e6ecafa1db0620e3fc8
languageName: node
linkType: hard
@@ -25477,16 +25436,7 @@ __metadata:
languageName: node
linkType: hard
-"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.2.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.2":
- version: 7.6.2
- resolution: "semver@npm:7.6.2"
- bin:
- semver: bin/semver.js
- checksum: 10c0/97d3441e97ace8be4b1976433d1c32658f6afaff09f143e52c593bae7eef33de19e3e369c88bd985ce1042c6f441c80c6803078d1de2a9988080b66684cbb30c
- languageName: node
- linkType: hard
-
-"semver@npm:^7.6.3":
+"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.2.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.2, semver@npm:^7.6.3":
version: 7.6.3
resolution: "semver@npm:7.6.3"
bin:
@@ -26285,6 +26235,11 @@ __metadata:
dependencies:
"@storybook/core": "workspace:*"
typescript: "npm:^5.3.2"
+ peerDependencies:
+ prettier: ^2 || ^3
+ peerDependenciesMeta:
+ prettier:
+ optional: true
bin:
getstorybook: ./bin/index.cjs
sb: ./bin/index.cjs
@@ -27690,15 +27645,6 @@ __metadata:
languageName: node
linkType: hard
-"uglify-js@npm:^3.1.4":
- version: 3.17.4
- resolution: "uglify-js@npm:3.17.4"
- bin:
- uglifyjs: bin/uglifyjs
- checksum: 10c0/8b7fcdca69deb284fed7d2025b73eb747ce37f9aca6af53422844f46427152d5440601b6e2a033e77856a2f0591e4167153d5a21b68674ad11f662034ec13ced
- languageName: node
- linkType: hard
-
"unbox-primitive@npm:^1.0.2":
version: 1.0.2
resolution: "unbox-primitive@npm:1.0.2"
@@ -28560,9 +28506,9 @@ __metadata:
languageName: node
linkType: hard
-"vite-plugin-storybook-nextjs@npm:^1.0.10":
- version: 1.0.10
- resolution: "vite-plugin-storybook-nextjs@npm:1.0.10"
+"vite-plugin-storybook-nextjs@npm:^1.0.11":
+ version: 1.0.11
+ resolution: "vite-plugin-storybook-nextjs@npm:1.0.11"
dependencies:
"@next/env": "npm:^14.2.5"
image-size: "npm:^1.1.1"
@@ -28578,7 +28524,7 @@ __metadata:
dependenciesMeta:
sharp:
optional: true
- checksum: 10c0/e0e373ef94e1761b871b2cc846c205a846901d93c7e61f9d9ee3c69740681e42e6403a7d61109c59f2d98d5829476c3e6d4e9d5a329c4bd51e758b763fa8ea9e
+ checksum: 10c0/9652b76c13a682b688d9a4f617b1a66263f25f395a99af8e258bedef4f3b3ce1c856ec1ff66cc0359d6aedc96adee9750fd6b0432514dd575ad7896cd1de70df
languageName: node
linkType: hard
@@ -29462,13 +29408,6 @@ __metadata:
languageName: node
linkType: hard
-"wordwrap@npm:^1.0.0":
- version: 1.0.0
- resolution: "wordwrap@npm:1.0.0"
- checksum: 10c0/7ed2e44f3c33c5c3e3771134d2b0aee4314c9e49c749e37f464bf69f2bcdf0cbf9419ca638098e2717cff4875c47f56a007532f6111c3319f557a2ca91278e92
- languageName: node
- linkType: hard
-
"workerpool@npm:^3.1.1":
version: 3.1.2
resolution: "workerpool@npm:3.1.2"
diff --git a/docs/_assets/writing-tests/vitest-plugin-test-failure.png b/docs/_assets/writing-tests/vitest-plugin-test-failure.png
new file mode 100644
index 000000000000..6c2277c9e547
Binary files /dev/null and b/docs/_assets/writing-tests/vitest-plugin-test-failure.png differ
diff --git a/docs/_assets/writing-tests/vitest-plugin-vscode.png b/docs/_assets/writing-tests/vitest-plugin-vscode.png
new file mode 100644
index 000000000000..3ce5c748aa17
Binary files /dev/null and b/docs/_assets/writing-tests/vitest-plugin-vscode.png differ
diff --git a/docs/_snippets/addon-test-install.md b/docs/_snippets/addon-test-install.md
new file mode 100644
index 000000000000..b516f291b85f
--- /dev/null
+++ b/docs/_snippets/addon-test-install.md
@@ -0,0 +1,11 @@
+```shell renderer="common" language="js" packageManager="npx"
+npx storybook add @storybook/experimental-addon-test
+```
+
+```shell renderer="common" language="js" packageManager="pnpm"
+pnpm exec storybook add @storybook/experimental-addon-test
+```
+
+```shell renderer="common" language="js" packageManager="yarn"
+yarn exec storybook add @storybook/experimental-addon-test
+```
diff --git a/docs/_snippets/main-config-test-disable-disableaddons.md b/docs/_snippets/main-config-test-disable-disableaddons.md
index ec4adfee235c..09f449b185b0 100644
--- a/docs/_snippets/main-config-test-disable-disableaddons.md
+++ b/docs/_snippets/main-config-test-disable-disableaddons.md
@@ -3,12 +3,7 @@ export default {
// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite)
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
- addons: [
- '@storybook/addon-links',
- '@storybook/addon-essentials',
- '@storybook/addon-interactions',
- '@storybook/addon-a11y',
- ],
+ addons: ['@storybook/addon-essentials', '@storybook/addon-interactions', '@storybook/addon-a11y'],
build: {
test: {
disabledAddons: ['@storybook/addon-a11y'],
@@ -24,12 +19,7 @@ import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
- addons: [
- '@storybook/addon-links',
- '@storybook/addon-essentials',
- '@storybook/addon-interactions',
- '@storybook/addon-a11y',
- ],
+ addons: ['@storybook/addon-essentials', '@storybook/addon-interactions', '@storybook/addon-a11y'],
build: {
test: {
disabledAddons: ['@storybook/addon-a11y'],
@@ -39,4 +29,3 @@ const config: StorybookConfig = {
export default config;
```
-
diff --git a/docs/_snippets/portable-stories-vitest-set-project-annotations-simple.md b/docs/_snippets/portable-stories-vitest-set-project-annotations-simple.md
new file mode 100644
index 000000000000..0cd16ae92250
--- /dev/null
+++ b/docs/_snippets/portable-stories-vitest-set-project-annotations-simple.md
@@ -0,0 +1,35 @@
+```tsx filename="setupTest.ts" renderer="react" language="ts"
+import { beforeAll } from 'vitest';
+// 👇 If you're using Next.js, import from @storybook/nextjs
+// If you're using Next.js with Vite, import from @storybook/experimental-nextjs-vite
+import { setProjectAnnotations } from '@storybook/react';
+import * as previewAnnotations from './.storybook/preview';
+
+const annotations = setProjectAnnotations([previewAnnotations]);
+
+// Run Storybook's beforeAll hook
+beforeAll(annotations.beforeAll);
+```
+
+```tsx filename="setupTest.ts" renderer="svelte" language="ts"
+import { beforeAll } from 'vitest';
+// 👇 If you're using Sveltekit, import from @storybook/sveltekit
+import { setProjectAnnotations } from '@storybook/svelte';
+import * as previewAnnotations from './.storybook/preview';
+
+const annotations = setProjectAnnotations([previewAnnotations]);
+
+// Run Storybook's beforeAll hook
+beforeAll(annotations.beforeAll);
+```
+
+```tsx filename="setupTest.ts" renderer="vue" language="ts"
+import { beforeAll } from 'vitest';
+import { setProjectAnnotations } from '@storybook/vue3';
+import * as previewAnnotations from './.storybook/preview';
+
+const annotations = setProjectAnnotations([previewAnnotations]);
+
+// Run Storybook's beforeAll hook
+beforeAll(annotations.beforeAll);
+```
diff --git a/docs/_snippets/portable-stories-vitest-set-project-annotations.md b/docs/_snippets/portable-stories-vitest-set-project-annotations.md
index 4e75fad1ae3c..e43dce86d505 100644
--- a/docs/_snippets/portable-stories-vitest-set-project-annotations.md
+++ b/docs/_snippets/portable-stories-vitest-set-project-annotations.md
@@ -1,14 +1,13 @@
```tsx filename="setupTest.ts" renderer="react" language="ts"
import { beforeAll } from 'vitest';
+// 👇 If you're using Next.js, import from @storybook/nextjs
+// If you're using Next.js with Vite, import from @storybook/experimental-nextjs-vite
import { setProjectAnnotations } from '@storybook/react';
// 👇 Import the exported annotations, if any, from the addons you're using; otherwise remove this
import * as addonAnnotations from 'my-addon/preview';
import * as previewAnnotations from './.storybook/preview';
-const annotations = setProjectAnnotations([
- previewAnnotations,
- addonAnnotations
-]);
+const annotations = setProjectAnnotations([previewAnnotations, addonAnnotations]);
// Run Storybook's beforeAll hook
beforeAll(annotations.beforeAll);
@@ -16,15 +15,13 @@ beforeAll(annotations.beforeAll);
```tsx filename="setupTest.ts" renderer="svelte" language="ts"
import { beforeAll } from 'vitest';
+// 👇 If you're using Sveltekit, import from @storybook/sveltekit
import { setProjectAnnotations } from '@storybook/svelte';
// 👇 Import the exported annotations, if any, from the addons you're using; otherwise remove this
import * as addonAnnotations from 'my-addon/preview';
import * as previewAnnotations from './.storybook/preview';
-const annotations = setProjectAnnotations([
- previewAnnotations,
- addonAnnotations
-]);
+const annotations = setProjectAnnotations([previewAnnotations, addonAnnotations]);
// Run Storybook's beforeAll hook
beforeAll(annotations.beforeAll);
@@ -37,10 +34,7 @@ import { setProjectAnnotations } from '@storybook/vue3';
import * as addonAnnotations from 'my-addon/preview';
import * as previewAnnotations from './.storybook/preview';
-const annotations = setProjectAnnotations([
- previewAnnotations,
- addonAnnotations
-]);
+const annotations = setProjectAnnotations([previewAnnotations, addonAnnotations]);
// Run Storybook's beforeAll hook
beforeAll(annotations.beforeAll);
diff --git a/docs/_snippets/storybook-coverage-addon-optimized-config.md b/docs/_snippets/storybook-coverage-addon-optimized-config.md
index 2fd3172b912d..cb443a5f1a70 100644
--- a/docs/_snippets/storybook-coverage-addon-optimized-config.md
+++ b/docs/_snippets/storybook-coverage-addon-optimized-config.md
@@ -4,7 +4,6 @@ export default {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
- '@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-coverage',
@@ -25,7 +24,6 @@ const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
- '@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-coverage',
@@ -39,4 +37,3 @@ const config: StorybookConfig = {
export default config;
```
-
diff --git a/docs/_snippets/storybook-vite-builder-aliasing.md b/docs/_snippets/storybook-vite-builder-aliasing.md
index 3481d8869313..2c9af4f2860a 100644
--- a/docs/_snippets/storybook-vite-builder-aliasing.md
+++ b/docs/_snippets/storybook-vite-builder-aliasing.md
@@ -1,7 +1,7 @@
```js filename=".storybook/main.js|ts" renderer="common" language="js"
export default {
stories: ['../src/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
- addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
+ addons: ['@storybook/addon-essentials'],
core: {
builder: '@storybook/builder-vite',
},
@@ -18,4 +18,3 @@ export default {
},
};
```
-
diff --git a/docs/_snippets/storybook-vite-builder-react-docgen.md b/docs/_snippets/storybook-vite-builder-react-docgen.md
index 733b8be00652..936bbbf99722 100644
--- a/docs/_snippets/storybook-vite-builder-react-docgen.md
+++ b/docs/_snippets/storybook-vite-builder-react-docgen.md
@@ -1,7 +1,7 @@
```js filename=".storybook/main.js|ts" renderer="common" language="js"
export default {
stories: ['../src/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
- addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
+ addons: ['@storybook/addon-essentials'],
core: {
builder: '@storybook/builder-vite',
},
@@ -12,4 +12,3 @@ export default {
},
};
```
-
diff --git a/docs/_snippets/storybook-vite-builder-register.md b/docs/_snippets/storybook-vite-builder-register.md
index 1740909197f6..952f747a212c 100644
--- a/docs/_snippets/storybook-vite-builder-register.md
+++ b/docs/_snippets/storybook-vite-builder-register.md
@@ -1,10 +1,9 @@
```js filename=".storybook/main.js|ts" renderer="common" language="js"
export default {
stories: ['../src/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
- addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
+ addons: ['@storybook/addon-essentials'],
core: {
builder: '@storybook/builder-vite', // 👈 The builder enabled here.
},
};
```
-
diff --git a/docs/_snippets/test-runner-a11y-configure.md b/docs/_snippets/test-runner-a11y-configure.md
index 1218954e8f91..cae622a42136 100644
--- a/docs/_snippets/test-runner-a11y-configure.md
+++ b/docs/_snippets/test-runner-a11y-configure.md
@@ -20,7 +20,8 @@ module.exports = {
rules: storyContext.parameters?.a11y?.config?.rules,
});
- await checkA11y(page, '#storybook-root', {
+ const element = storyContext.parameters?.a11y?.element ?? '#storybook-root';
+ await checkA11y(page, element, {
detailedReport: true,
detailedReportOptions: {
html: true,
@@ -53,7 +54,8 @@ const config: TestRunnerConfig = {
rules: storyContext.parameters?.a11y?.config?.rules,
});
- await checkA11y(page, '#storybook-root', {
+ const element = storyContext.parameters?.a11y?.element ?? '#storybook-root';
+ await checkA11y(page, element, {
detailedReport: true,
detailedReportOptions: {
html: true,
@@ -64,4 +66,3 @@ const config: TestRunnerConfig = {
export default config;
```
-
diff --git a/docs/_snippets/vitest-plugin-run-tests.md b/docs/_snippets/vitest-plugin-run-tests.md
new file mode 100644
index 000000000000..3b91e2d284ea
--- /dev/null
+++ b/docs/_snippets/vitest-plugin-run-tests.md
@@ -0,0 +1,11 @@
+```shell renderer="common" language="js" packageManager="npm"
+npm run test
+```
+
+```shell renderer="common" language="js" packageManager="pnpm"
+pnpm run test
+```
+
+```shell renderer="common" language="js" packageManager="yarn"
+yarn test
+```
diff --git a/docs/_snippets/vitest-plugin-vitest-config-alias.md b/docs/_snippets/vitest-plugin-vitest-config-alias.md
new file mode 100644
index 000000000000..5777c37f2616
--- /dev/null
+++ b/docs/_snippets/vitest-plugin-vitest-config-alias.md
@@ -0,0 +1,29 @@
+```js filename=".storybook/main.js" renderer="common" tabTitle="Before"
+import { mergeConfig } from 'vite';
+
+export default {
+ // ...
+ viteFinal: async (viteConfig) => {
+ return mergeConfig(viteConfig, {
+ resolve: {
+ alias: {
+ '@components': '/src/components',
+ // ...
+ },
+ },
+ });
+ },
+};
+```
+
+```js filename="vitest.config.ts" renderer="common" tabTitle="After"
+export default defineConfig({
+ // ...
+ resolve: {
+ alias: {
+ '@components': '/src/components',
+ // ...
+ },
+ },
+});
+```
diff --git a/docs/_snippets/vitest-plugin-vitest-config.md b/docs/_snippets/vitest-plugin-vitest-config.md
new file mode 100644
index 000000000000..0ddc6054f9df
--- /dev/null
+++ b/docs/_snippets/vitest-plugin-vitest-config.md
@@ -0,0 +1,118 @@
+```ts filename="vitest.config.ts" renderer="react"
+import { defineConfig, mergeConfig } from 'vitest/config';
+import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
+// 👇 If you're using Next.js, apply this framework plugin as well
+// import { storybookNextjsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin';
+
+import viteConfig from './vite.config';
+
+export default mergeConfig(
+ viteConfig,
+ defineConfig({
+ plugins: [
+ storybookTest({
+ // This should match your package.json script to run Storybook
+ // The --ci flag will skip prompts and not open a browser
+ storybookScript: 'yarn storybook --ci',
+ }),
+ // storybookNextjsPlugin(),
+ ],
+ test: {
+ // Glob pattern to find story files
+ include: ['src/**/*.stories.?(m)[jt]s?(x)'],
+ // Enable browser mode
+ browser: {
+ enabled: true,
+ name: 'chromium',
+ // Make sure to install Playwright
+ provider: 'playwright',
+ headless: true,
+ },
+ // Speed up tests and better match how they run in Storybook itself
+ // https://vitest.dev/config/#isolate
+ // Consider removing this if you have flaky tests
+ isolate: false,
+ setupFiles: ['./.storybook/vitest.setup.ts'],
+ },
+ })
+);
+```
+
+```ts filename="vitest.config.ts" renderer="vue"
+import { defineConfig, mergeConfig } from 'vitest/config';
+import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
+import { storybookVuePlugin } from '@storybook/vue3-vite/vite-plugin';
+
+import viteConfig from './vite.config';
+
+export default mergeConfig(
+ viteConfig,
+ defineConfig({
+ plugins: [
+ storybookTest({
+ // This should match your package.json script to run Storybook
+ // The --ci flag will skip prompts and not open a browser
+ storybookScript: 'yarn storybook --ci',
+ }),
+ storybookVuePlugin(),
+ ],
+ test: {
+ // Glob pattern to find story files
+ include: ['src/**/*.stories.?(m)[jt]s?(x)'],
+ // Enable browser mode
+ browser: {
+ enabled: true,
+ name: 'chromium',
+ // Make sure to install Playwright
+ provider: 'playwright',
+ headless: true,
+ },
+ // Speed up tests and better match how they run in Storybook itself
+ // https://vitest.dev/config/#isolate
+ // Consider removing this if you have flaky tests
+ isolate: false,
+ setupFiles: ['./.storybook/vitest.setup.ts'],
+ },
+ })
+);
+```
+
+```ts filename="vitest.config.ts" renderer="svelte"
+import { defineConfig, mergeConfig } from 'vitest/config';
+import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
+// 👇 If you're using Sveltekit, apply this framework plugin as well
+// import { storybookSveltekitPlugin } from '@storybook/sveltekit/vite-plugin';
+
+import viteConfig from './vite.config';
+
+export default mergeConfig(
+ viteConfig,
+ defineConfig({
+ plugins: [
+ storybookTest({
+ // This should match your package.json script to run Storybook
+ // The --ci flag will skip prompts and not open a browser
+ storybookScript: 'yarn storybook --ci',
+ }),
+ // storybookSveltekitPlugin(),
+ ],
+ test: {
+ // Glob pattern to find story files
+ include: ['src/**/*.stories.?(m)[jt]s?(x)'],
+ // Enable browser mode
+ browser: {
+ enabled: true,
+ name: 'chromium',
+ // Make sure to install Playwright
+ provider: 'playwright',
+ headless: true,
+ },
+ // Speed up tests and better match how they run in Storybook itself
+ // https://vitest.dev/config/#isolate
+ // Consider removing this if you have flaky tests
+ isolate: false,
+ setupFiles: ['./.storybook/vitest.setup.ts'],
+ },
+ })
+);
+```
diff --git a/docs/_snippets/vitest-plugin-vitest-workspace.md b/docs/_snippets/vitest-plugin-vitest-workspace.md
new file mode 100644
index 000000000000..1e2db2169e16
--- /dev/null
+++ b/docs/_snippets/vitest-plugin-vitest-workspace.md
@@ -0,0 +1,128 @@
+```ts title="vitest.workspace.ts" renderer="react"
+import { defineWorkspace } from 'vitest/config';
+import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
+// 👇 If you're using Next.js, apply this framework plugin as well
+// import { storybookNextjsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin';
+
+export default defineWorkspace([
+ // This is the path to your existing Vitest config file
+ './vitest.config.ts',
+ {
+ // This is the path to your existing Vite config file
+ extends: './vite.config.ts',
+ plugins: [
+ storybookTest({
+ // This should match your package.json script to run Storybook
+ // The --ci flag will skip prompts and not open a browser
+ storybookScript: 'yarn storybook --ci',
+ }),
+ // storybookNextjsPlugin(),
+ ],
+ test: {
+ name: 'storybook',
+ // Glob pattern to find story files
+ include: ['src/**/*.stories.?(m)[jt]s?(x)'],
+ // Enable browser mode
+ browser: {
+ enabled: true,
+ name: 'chromium',
+ // Make sure to install Playwright
+ provider: 'playwright',
+ headless: true,
+ },
+ // Speed up tests and better match how they run in Storybook itself
+ // https://vitest.dev/config/#isolate
+ // Consider removing this if you have flaky tests
+ isolate: false,
+ setupFiles: ['./.storybook/vitest.setup.ts'],
+ },
+ },
+]);
+```
+
+```ts filename="vitest.config.ts" renderer="vue"
+import { defineConfig, mergeConfig } from 'vitest/config';
+import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
+import { storybookVuePlugin } from '@storybook/vue3-vite/vite-plugin';
+
+import viteConfig from './vite.config';
+
+export default defineWorkspace([
+ // This is the path to your existing Vitest config file
+ './vitest.config.ts',
+ {
+ // This is the path to your existing Vite config file
+ extends: './vite.config.ts',
+ plugins: [
+ storybookTest({
+ // This should match your package.json script to run Storybook
+ // The --ci flag will skip prompts and not open a browser
+ storybookScript: 'yarn storybook --ci',
+ }),
+ storybookVuePlugin(),
+ ],
+ test: {
+ name: 'storybook',
+ // Glob pattern to find story files
+ include: ['src/**/*.stories.?(m)[jt]s?(x)'],
+ // Enable browser mode
+ browser: {
+ enabled: true,
+ name: 'chromium',
+ // Make sure to install Playwright
+ provider: 'playwright',
+ headless: true,
+ },
+ // Speed up tests and better match how they run in Storybook itself
+ // https://vitest.dev/config/#isolate
+ // Consider removing this if you have flaky tests
+ isolate: false,
+ setupFiles: ['./.storybook/vitest.setup.ts'],
+ },
+ },
+]);
+```
+
+```ts filename="vitest.config.ts" renderer="svelte"
+import { defineConfig, mergeConfig } from 'vitest/config';
+import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
+// 👇 If you're using Sveltekit, apply this framework plugin as well
+// import { storybookNextjsPlugin } from '@storybook/sveltekit/vite-plugin';
+
+import viteConfig from './vite.config';
+
+export default defineWorkspace([
+ // This is the path to your existing Vitest config file
+ './vitest.config.ts',
+ {
+ // This is the path to your existing Vite config file
+ extends: './vite.config.ts',
+ plugins: [
+ storybookTest({
+ // This should match your package.json script to run Storybook
+ // The --ci flag will skip prompts and not open a browser
+ storybookScript: 'yarn storybook --ci',
+ }),
+ // storybookSveltekitPlugin(),
+ ],
+ test: {
+ name: 'storybook',
+ // Glob pattern to find story files
+ include: ['src/**/*.stories.?(m)[jt]s?(x)'],
+ // Enable browser mode
+ browser: {
+ enabled: true,
+ name: 'chromium',
+ // Make sure to install Playwright
+ provider: 'playwright',
+ headless: true,
+ },
+ // Speed up tests and better match how they run in Storybook itself
+ // https://vitest.dev/config/#isolate
+ // Consider removing this if you have flaky tests
+ isolate: false,
+ setupFiles: ['./.storybook/vitest.setup.ts'],
+ },
+ },
+]);
+```
diff --git a/docs/api/cli-options.mdx b/docs/api/cli-options.mdx
index b5b8a3fd9eda..dff6c5531acf 100644
--- a/docs/api/cli-options.mdx
+++ b/docs/api/cli-options.mdx
@@ -274,7 +274,6 @@ Storybook Environment Info:
npmPackages:
@storybook/addon-essentials: ^7.6.6 => 7.6.6
@storybook/addon-interactions: ^7.6.6 => 7.6.6
- @storybook/addon-links: ^7.6.6 => 7.6.6
@storybook/addon-onboarding: ^1.0.10 => 1.0.10
@storybook/blocks: ^7.6.6 => 7.6.6
@storybook/preset-create-react-app: ^7.6.6 => 7.6.6
diff --git a/docs/api/main-config/main-config-indexers.mdx b/docs/api/main-config/main-config-indexers.mdx
index 9d31b0c169cc..67a6cf5c45b1 100644
--- a/docs/api/main-config/main-config-indexers.mdx
+++ b/docs/api/main-config/main-config-indexers.mdx
@@ -341,7 +341,7 @@ Some example usages of custom indexers include:
Generating stories with an alternative API
- 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.mdx) 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.
+ 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/api/portable-stories/portable-stories-jest.mdx b/docs/api/portable-stories/portable-stories-jest.mdx
index 5734fd2385e6..20514ec71691 100644
--- a/docs/api/portable-stories/portable-stories-jest.mdx
+++ b/docs/api/portable-stories/portable-stories-jest.mdx
@@ -2,7 +2,7 @@
title: 'Portable stories in Jest'
sidebar:
title: Jest
- order: 1
+ order: 2
---
diff --git a/docs/api/portable-stories/portable-stories-playwright.mdx b/docs/api/portable-stories/portable-stories-playwright.mdx
index e3973b4d7682..4fb2735b0591 100644
--- a/docs/api/portable-stories/portable-stories-playwright.mdx
+++ b/docs/api/portable-stories/portable-stories-playwright.mdx
@@ -2,7 +2,7 @@
title: 'Portable stories in Playwright CT'
sidebar:
title: Playwright
- order: 2
+ order: 3
---
(⚠️ **Experimental**)
diff --git a/docs/api/portable-stories/portable-stories-vitest.mdx b/docs/api/portable-stories/portable-stories-vitest.mdx
index 115f3d50b949..a79f12075642 100644
--- a/docs/api/portable-stories/portable-stories-vitest.mdx
+++ b/docs/api/portable-stories/portable-stories-vitest.mdx
@@ -2,7 +2,7 @@
title: 'Portable stories in Vitest'
sidebar:
title: Vitest
- order: 0
+ order: 1
---
diff --git a/docs/configure/telemetry.mdx b/docs/configure/telemetry.mdx
index d2333997a7d8..d4c3af5804da 100644
--- a/docs/configure/telemetry.mdx
+++ b/docs/configure/telemetry.mdx
@@ -127,9 +127,6 @@ Will generate the following output:
}
},
"addons": {
- "@storybook/addon-links": {
- "version": "7.1.0"
- },
"@storybook/addon-essentials": {
"version": "7.1.0"
},
diff --git a/docs/configure/upgrading.mdx b/docs/configure/upgrading.mdx
index ac75aff8a701..918182b7e931 100644
--- a/docs/configure/upgrading.mdx
+++ b/docs/configure/upgrading.mdx
@@ -31,7 +31,7 @@ After running the command, the script will:
* Run the relevant [automigrations](../migration-guide/index.mdx#automatic-upgrade) factoring in the [breaking changes](../migration-guide/index.mdx#major-breaking-changes) between your current version and the specified version
- In addition to running the command, we also recommend checking the [MIGRATION.md file](https://github.com/storybookjs/storybook/blob/next/MIGRATION.mdx), for the detailed log of relevant changes and deprecations that might affect your upgrade.
+ In addition to running the command, we also recommend checking the [MIGRATION.md file](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md), for the detailed log of relevant changes and deprecations that might affect your upgrade.
### Verifying the upgrade
diff --git a/docs/contribute/framework.mdx b/docs/contribute/framework.mdx
index 71ef9f1328cd..2b6cce765192 100644
--- a/docs/contribute/framework.mdx
+++ b/docs/contribute/framework.mdx
@@ -27,7 +27,7 @@ The library or libraries your framework supports may have different major versio
### 3. Write the documentation
-Before writing any code, write a helpful README that contains installation instructions and a list of available features. Use the [README for `@storybook/nextjs`](https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/README.mdx) as a template. Writing the documentation first helps guide your other work.
+Before writing any code, write a helpful README that contains installation instructions and a list of available features. Use the [README for `@storybook/nextjs`](https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/README.md) as a template. Writing the documentation first helps guide your other work.
### 4. Author the framework itself
diff --git a/docs/contribute/index.mdx b/docs/contribute/index.mdx
index dd27c951753b..db8d512ca179 100644
--- a/docs/contribute/index.mdx
+++ b/docs/contribute/index.mdx
@@ -10,7 +10,7 @@ Storybook is a community-oriented open source project that welcomes contribution
## Contributor covenant
-In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. [Continue reading our contributor covenant »](https://github.com/storybookjs/storybook/blob/next/CODE_OF_CONDUCT.mdx)
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. [Continue reading our contributor covenant »](https://github.com/storybookjs/storybook/blob/next/CODE_OF_CONDUCT.md)
## Ways to contribute
diff --git a/docs/faq.mdx b/docs/faq.mdx
index 57c010639e1d..5bfab8ad93d1 100644
--- a/docs/faq.mdx
+++ b/docs/faq.mdx
@@ -195,7 +195,7 @@ We're actively working to provide a better way to address this situation, but in
## Is it possible to browse the documentation for past versions of Storybook?
-With the release of version 6.0, we updated our documentation as well. That doesn't mean that the old documentation was removed. We kept it to help you with your Storybook migration process. Use the content from the table below in conjunction with our [migration guide](https://github.com/storybookjs/storybook/blob/next/MIGRATION.mdx).
+With the release of version 6.0, we updated our documentation as well. That doesn't mean that the old documentation was removed. We kept it to help you with your Storybook migration process. Use the content from the table below in conjunction with our [migration guide](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md).
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.
@@ -246,7 +246,7 @@ We're only covering versions 5.3 and 5.0 as they were important milestones for S
| | Environment variables | [See current documentation](./configure/environment-variables.mdx) | [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.mdx) | Non existing feature or undocumented | Non existing feature or undocumented |
| | Vite | [See current documentation](./builders/vite.mdx) | Non existing feature or undocumented | Non existing feature or undocumented |
-| | Webpack | [See current documentation](./builders/webpack.mdx) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.3/docs/src/pages/configurations/custom-webpack-config/index.mdx) | [See versioned documentation](https://github.com/storybookjs/storybook/tree/release/5.0/docs/src/pages/configurations/custom-webpack-config/index.mdx) |
+| | Webpack | [See current documentation](./builders/webpack.mdx) | [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.mdx) | Non existing feature or undocumented | Non existing feature or undocumented |
| Addons | Introduction | [See current documentation](./addons/index.mdx) | [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.mdx) | [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/) |
@@ -301,7 +301,7 @@ We're only covering versions 5.3 and 5.0 as they were important milestones for S
| | CLI options | [See current documentation](./api/cli-options.mdx) | [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) |
- 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/index.mdx#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.mdx) for reference.
+ 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/index.mdx#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.
## What icons are available for my toolbar or my addon?
diff --git a/docs/get-started/frameworks/nextjs.mdx b/docs/get-started/frameworks/nextjs.mdx
index 597522f03020..14e2c3e55294 100644
--- a/docs/get-started/frameworks/nextjs.mdx
+++ b/docs/get-started/frameworks/nextjs.mdx
@@ -107,7 +107,7 @@ Storybook for Next.js is a [framework](../../contribute/framework.mdx) that make
{/* prettier-ignore-end */}
- If your Storybook configuration contains custom Webpack operations in [`webpackFinal`](../../api/main-config/main-config-webpack-final.mdx), you will likely need to create equivalents in [`viteFinal`]((../../api/main-config/main-config-vite-final.mdx)).
+ If your Storybook configuration contains custom Webpack operations in [`webpackFinal`](../../api/main-config/main-config-webpack-final.mdx), you will likely need to create equivalents in [`viteFinal`](../../api/main-config/main-config-vite-final.mdx).
Finally, if you were using Storybook plugins to integrate with Next.js, those are no longer necessary when using this framework and can be removed:
diff --git a/docs/get-started/setup.mdx b/docs/get-started/setup.mdx
index 6794e694e35c..be35c28e11a2 100644
--- a/docs/get-started/setup.mdx
+++ b/docs/get-started/setup.mdx
@@ -77,4 +77,4 @@ Your project may have additional requirements before components can be rendered
## Load assets and resources
-We recommend serving external resources and assets requested in your components statically with Storybook. It ensures that assets are always available to your stories. Read our [documentation](../configure/integration/images-and-assets.mdx) to learn how to hosting static files with Storybook.
+We recommend serving external resources and assets requested in your components statically with Storybook. It ensures that assets are always available to your stories. Read our [documentation](../configure/integration/images-and-assets.mdx) to learn how to host static files with Storybook.
diff --git a/docs/migration-guide/from-older-version.mdx b/docs/migration-guide/from-older-version.mdx
index a5fb7e4cfc6c..ae1f484c7988 100644
--- a/docs/migration-guide/from-older-version.mdx
+++ b/docs/migration-guide/from-older-version.mdx
@@ -119,7 +119,7 @@ The automatic upgrade should get your Storybook into a working state. If you enc
If you prefer to debug yourself, here are a few useful things you can do to help narrow down the problem:
1. Try removing all addons that are not in the `@storybook` npm namespace (make sure you don't remove the `storybook` package). Community addons that work well with 7.x might not yet be compatible with 8.0, and this is the fastest way to isolate that possibility. If you find an addon that needs to be upgraded to work with Storybook 8, please post an issue on the addon’s repository, or better yet, a pull request to upgrade it!
-2. Another debugging technique is to bisect to older prerelease versions of Storybook to figure out which release broke your Storybook. For example, assuming that the current prerelease of Storybook is `8.0.0-beta.56`, you could set the version to `8.0.0-alpha.0` in your `package.json` and reinstall to verify that it still works (`alpha.0` should be nearly identical to `7.6.x`). If it works, you could then try `8.0.0-beta.0`, then `8.0.0-beta.28` and so forth. Once you’ve isolated the bad release, read through its [CHANGELOG](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.mdx) entry and perhaps there’s a change that jumps out as the culprit. If you find the problem, please submit an issue or pull request to the Storybook monorepo and we’ll do our best to take care of it quickly.
+2. Another debugging technique is to bisect to older prerelease versions of Storybook to figure out which release broke your Storybook. For example, assuming that the current prerelease of Storybook is `8.0.0-beta.56`, you could set the version to `8.0.0-alpha.0` in your `package.json` and reinstall to verify that it still works (`alpha.0` should be nearly identical to `7.6.x`). If it works, you could then try `8.0.0-beta.0`, then `8.0.0-beta.28` and so forth. Once you’ve isolated the bad release, read through its [CHANGELOG](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) entry and perhaps there’s a change that jumps out as the culprit. If you find the problem, please submit an issue or pull request to the Storybook monorepo and we’ll do our best to take care of it quickly.
## Optional migrations
diff --git a/docs/migration-guide/index.mdx b/docs/migration-guide/index.mdx
index 560d61b94cef..93e91a901910 100644
--- a/docs/migration-guide/index.mdx
+++ b/docs/migration-guide/index.mdx
@@ -133,7 +133,7 @@ The automatic upgrade should get your Storybook into a working state. If you enc
If you prefer to debug yourself, here are a few useful things you can do to help narrow down the problem:
1. Try removing all addons that are not in the `@storybook` npm namespace (make sure you don't remove the `storybook` package). Community addons that work well with 7.x might not yet be compatible with 8.0, and this is the fastest way to isolate that possibility. If you find an addon that needs to be upgraded to work with Storybook 8, please post an issue on the addon’s repository, or better yet, a pull request to upgrade it!
-2. Another debugging technique is to bisect to older prerelease versions of Storybook to figure out which release broke your Storybook. For example, assuming that the current prerelease of Storybook is `8.0.0-beta.56`, you could set the version to `8.0.0-alpha.0` in your `package.json` and reinstall to verify that it still works (`alpha.0` should be nearly identical to `7.6.x`). If it works, you could then try `8.0.0-beta.0`, then `8.0.0-beta.28` and so forth. Once you’ve isolated the bad release, read through its [CHANGELOG](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.mdx) entry and perhaps there’s a change that jumps out as the culprit. If you find the problem, please submit an issue or pull request to the Storybook monorepo and we’ll do our best to take care of it quickly.
+2. Another debugging technique is to bisect to older prerelease versions of Storybook to figure out which release broke your Storybook. For example, assuming that the current prerelease of Storybook is `8.0.0-beta.56`, you could set the version to `8.0.0-alpha.0` in your `package.json` and reinstall to verify that it still works (`alpha.0` should be nearly identical to `7.6.x`). If it works, you could then try `8.0.0-beta.0`, then `8.0.0-beta.28` and so forth. Once you’ve isolated the bad release, read through its [CHANGELOG](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) entry and perhaps there’s a change that jumps out as the culprit. If you find the problem, please submit an issue or pull request to the Storybook monorepo and we’ll do our best to take care of it quickly.
## Package structure changes
diff --git a/docs/versions/latest.json b/docs/versions/latest.json
index bc9c7b4e27d2..db378469890e 100644
--- a/docs/versions/latest.json
+++ b/docs/versions/latest.json
@@ -1 +1 @@
-{"version":"8.2.0","info":{"plain":""}}
+{"version":"8.3.0","info":{"plain":""}}
diff --git a/docs/versions/next.json b/docs/versions/next.json
index 4d28c32e82f6..ab225ca861a5 100644
--- a/docs/versions/next.json
+++ b/docs/versions/next.json
@@ -1 +1 @@
-{"version":"8.3.0-beta.4","info":{"plain":"- Test: Rename vitest plugin entrypoint - [#29067](https://github.com/storybookjs/storybook/pull/29067), thanks @yannbf!\n- UI: Fix sidebar not wrapping - [#29055](https://github.com/storybookjs/storybook/pull/29055), thanks @JReinhold!"}}
+{"version":"8.4.0-alpha.4","info":{"plain":"- Blocks: Prebundle `es-toolkit` - [#29259](https://github.com/storybookjs/storybook/pull/29259), thanks @JReinhold!\n- CLI: Update the React Native init to include v8 dependencies - [#29273](https://github.com/storybookjs/storybook/pull/29273), thanks @dannyhw!\n- Core: Upgrade `esbuild`, broadening version range - [#29254](https://github.com/storybookjs/storybook/pull/29254), thanks @ndelangen!\n- Vitest plugin: Fix renamed export stories - [#29250](https://github.com/storybookjs/storybook/pull/29250), thanks @shilman!"}}
diff --git a/docs/writing-tests/component-testing.mdx b/docs/writing-tests/component-testing.mdx
index cda397d8de6f..7e2db96c874f 100644
--- a/docs/writing-tests/component-testing.mdx
+++ b/docs/writing-tests/component-testing.mdx
@@ -21,7 +21,7 @@ You start by writing a [**story**](../writing-stories/index.mdx) to set up the c
* The test is written using Storybook-instrumented versions of [Vitest](https://vitest.dev/) and [Testing Library](https://testing-library.com/) coming from the [`@storybook/test`](https://npmjs.com/package/@storybook/test) package.
* [`@storybook/addon-interactions`](https://storybook.js.org/addons/@storybook/addon-interactions/) visualizes the test in Storybook and provides a playback interface for convenient browser-based debugging.
* [`@storybook/test-runner`](https://github.com/storybookjs/test-runner) is a standalone utility—powered by [Jest](https://jestjs.io/) and [Playwright](https://playwright.dev/)—that executes all of your interactions tests and catches broken stories.
- * The experimental [test runner with Vitest](./test-runner-with-vitest.mdx) is also available, which transforms your stories into Vitest tests and runs them in a browser.
+ * The experimental [Vitest plugin](./vitest-plugin.mdx) is also available, which transforms your stories into Vitest tests and runs them in a browser.
## Set up the interactions addon
diff --git a/docs/writing-tests/import-stories-in-tests/stories-in-end-to-end-tests.mdx b/docs/writing-tests/import-stories-in-tests/stories-in-end-to-end-tests.mdx
index 02a931f44f0d..c9a86b7eb7a8 100644
--- a/docs/writing-tests/import-stories-in-tests/stories-in-end-to-end-tests.mdx
+++ b/docs/writing-tests/import-stories-in-tests/stories-in-end-to-end-tests.mdx
@@ -2,7 +2,7 @@
title: 'Stories in end-to-end tests'
sidebar:
title: End-to-end tests
- order: 1
+ order: 2
---
Storybook seamlessly integrates with additional testing frameworks like [Cypress](https://www.cypress.io/) and [Playwright](https://playwright.dev/) to provide a comprehensive testing solution. By leveraging the Component Story Format (CSF), developers can write test cases that simulate user interactions and verify the behavior of individual components within the Storybook environment. This approach enables developers to thoroughly test their components' functionality, responsiveness, and visual appearance across different scenarios, resulting in more robust and reliable applications.
diff --git a/docs/writing-tests/import-stories-in-tests/stories-in-unit-tests.mdx b/docs/writing-tests/import-stories-in-tests/stories-in-unit-tests.mdx
index d975f910d500..89c32f1ab51b 100644
--- a/docs/writing-tests/import-stories-in-tests/stories-in-unit-tests.mdx
+++ b/docs/writing-tests/import-stories-in-tests/stories-in-unit-tests.mdx
@@ -2,7 +2,7 @@
title: 'Stories in unit tests'
sidebar:
title: Unit tests
- order: 0
+ order: 1
---
Teams test a variety of UI characteristics using different tools. Each tool requires you to replicate the same component state over and over. That’s a maintenance headache. Ideally, you’d set up your tests similarly and reuse that across tools.
diff --git a/docs/writing-tests/index.mdx b/docs/writing-tests/index.mdx
index e5f0524f7f7b..36f3ccefcfa7 100644
--- a/docs/writing-tests/index.mdx
+++ b/docs/writing-tests/index.mdx
@@ -25,4 +25,4 @@ Storybook also comes with tools, [a test runner](./test-runner.mdx), and handy i
* [Test runner](./test-runner.mdx) to automate test execution
* [Test coverage](./test-coverage.mdx) for measuring code coverage
* [End-to-end tests](./import-stories-in-tests/stories-in-end-to-end-tests.mdx) for simulating real user scenarios
-* [**Unit tests**](./import-stories-in-tests/stories-in-unit-tests.mdx) for functionality
+* [Unit tests](./import-stories-in-tests/stories-in-unit-tests.mdx) for functionality
diff --git a/docs/writing-tests/snapshot-testing/snapshot-testing.mdx b/docs/writing-tests/snapshot-testing/snapshot-testing.mdx
index c6ec1cc516e9..c898da615f83 100644
--- a/docs/writing-tests/snapshot-testing/snapshot-testing.mdx
+++ b/docs/writing-tests/snapshot-testing/snapshot-testing.mdx
@@ -1,5 +1,7 @@
---
title: 'Snapshot tests'
+sidebar:
+ order: 1
---
Snapshot tests compare the rendered markup of every story against known baselines. It’s a way to identify markup changes that trigger rendering errors and warnings.
diff --git a/docs/writing-tests/snapshot-testing/storyshots-migration-guide.mdx b/docs/writing-tests/snapshot-testing/storyshots-migration-guide.mdx
index 692b83e94201..80f79a21d3c4 100644
--- a/docs/writing-tests/snapshot-testing/storyshots-migration-guide.mdx
+++ b/docs/writing-tests/snapshot-testing/storyshots-migration-guide.mdx
@@ -1,5 +1,7 @@
---
title: 'Storyshots migration guide'
+sidebar:
+ order: 2
---
diff --git a/docs/writing-tests/vitest-plugin.mdx b/docs/writing-tests/vitest-plugin.mdx
new file mode 100644
index 000000000000..1a0e4b9708b2
--- /dev/null
+++ b/docs/writing-tests/vitest-plugin.mdx
@@ -0,0 +1,464 @@
+---
+title: 'Storybook Vitest plugin'
+sidebar:
+ order: 6
+ title: Vitest plugin
+---
+
+
+
+ The Storybook Vitest plugin is currently only supported in [React](?renderer=react), [Vue](?renderer=vue) and [Svelte](?renderer=svelte) projects, which use the [Vite builder](../builders/vite.mdx) (or the [Next.js framework](../get-started/frameworks/nextjs.mdx)).
+
+ If you are using a different renderer, you can use the [Storyboook test runner](./test-runner.mdx) to test your stories.
+
+
+ {/* End non-supported renderers */}
+
+
+
+
+(⚠️ **Experimental**)
+
+
+ While this feature is experimental, it is published within the `@storybook/experimental-addon-test` package and the API may change in future releases. We welcome feedback and contributions to help improve this feature.
+
+
+Storybook's Vitest plugin transforms your [stories](../writing-stories/index.mdx) into [Vitest](https://vitest.dev) tests using [portable stories](../api/portable-stories/portable-stories-vitest.mdx). Those tests can then be run alongside any other Vitest tests. This approach is faster and more flexible than the [test runner](./test-runner.mdx), which required a running Storybook instance to test your stories.
+
+We recommend (and configure, by default) running Vitest in [browser mode](https://vitest.dev/guide/browser/), using [Playwright's](https://playwright.dev) Chromium browser. Browser mode ensures your components are tested in a real browser environment, which is more accurate than simulations like JSDom or HappyDom. This is especially important for testing components that rely on browser APIs or features.
+
+Stories are tested in two ways: a smoke test to ensure it renders and, if a [play function](../writing-tests/component-testing#write-a-component-test) is defined, that function is run and any [assertions made](../writing-tests/component-testing.mdx#assert-tests-with-vitests-apis) within it are validated.
+
+## Install and set up
+
+Before installing, make sure your project meets the following requirements:
+
+- Storybook ≥ 8.3
+- A Storybook framework that uses Vite (e.g. [`vue3-vite`](../get-started/frameworks/vue3-vite.mdx)), or the [Storybook Next.js framework](../get-started/frameworks/nextjs.mdx)
+- Vitest ≥ 2.0
+ - If you're not using Vitest, it will be installed and configured for you when you install the addon
+- For Next.js projects, Next.js ≥ 14.1
+
+If you're not yet using Storybook 8.3, you can [upgrade your Storybook](../configure/upgrading.mdx) to the latest version:
+
+
+
+### Automatic setup
+
+Run the following command to install and configure the addon, which contains the plugin to run your stories as tests using Vitest:
+
+
+
+That [`add` command](../addons/install-addons.mdx#automatic-installation) will install and register the test addon. It will also inspect your project's Vite and Vitest setup, and install and configure them with sensible defaults, if necessary. You may need to adjust the configuration to fit your project's needs. The full configuration options can be found in the [API section](#options), below.
+
+### Manual setup
+
+For some project setups, the `add` command may be unable to automate the plugin setup and ask you to complete additional setup steps. Here's what to do:
+
+1. Make sure Vite and Vitest are configured in your project.
+1. Configure Vitest to use [browser mode](https://vitest.dev/guide/browser/).
+1. Install the addon, `@storybook/experimental-addon-test`, in your project and [register it in your Storybook configuration](http://storybook.js.org/docs/addons/install-addons#manual-installation).
+1. Create a test setup file, `.storybook/vitest.setup.ts`. You can use the [example setup file](#example-vitest-setup) as a guide.
+1. Adjust your Vitest configuration to include the plugin(s) and reference the setup file. You can use the [example configuration files](#example-configuration-files) as a guide.
+
+#### Framework plugins
+
+Some Storybook frameworks require additional setup to enable the framework's features to work with Vitest. Each of those frameworks exports a Vite plugin that you can use to configure your project correctly:
+
+
+ If you're using Next.js, first install the `@storybook/experimental-nextjs-vite` package:
+
+ {/* prettier-ignore-start */}
+
+
+
+ {/* prettier-ignore-end */}
+
+ Then apply the plugin from `@storybook/experimental-nextjs-vite/vite-plugin`:
+
+ ```js title="vitest.config.ts"
+ import { defineConfig, mergeConfig } from 'vitest/config';
+ import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
+ import { storybookNextjsPlugin } from '@storybook/experimental-nextjs-vite/vite-plugin';
+
+ import viteConfig from './vite.config';
+
+ export default mergeConfig(
+ viteConfig,
+ defineConfig({
+ plugins: [
+ storybookTest(),
+ storybookNextjsPlugin(), // 👈 Apply the framework plugin here
+ ],
+ // ...
+ })
+ );
+ ```
+
+
+
+ Vue projects should apply the plugin from `@storybook/vue3-vite/vite-plugin`:
+
+ ```js title="vitest.config.ts"
+ import { defineConfig, mergeConfig } from 'vitest/config';
+ import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
+ import { storybookVuePlugin } from '@storybook/vue3-vite/vite-plugin';
+
+ import viteConfig from './vite.config'
+
+ export default mergeConfig(
+ viteConfig,
+ defineConfig({
+ plugins: [
+ storybookTest(),
+ storybookVuePlugin(), // 👈 Apply the framework plugin here
+ ],
+ // ...
+ })
+ );
+ ```
+
+
+
+ If you're using SvelteKit, apply the plugin from `@storybook/sveltekit/vite-plugin`:
+
+ ```js title="vitest.config.ts"
+ import { defineConfig, mergeConfig } from 'vitest/config';
+ import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin';
+ import { storybookSveltekitPlugin } from '@storybook/sveltekit/vite-plugin';
+
+ import viteConfig from './vite.config';
+
+ export default mergeConfig(
+ viteConfig,
+ defineConfig({
+ plugins: [
+ storybookTest(),
+ storybookSveltekitPlugin(), // 👈 Apply the framework plugin here
+ ],
+ // ...
+ })
+ );
+ ```
+
+
+The above example uses the framework's plugin in a Vitest configuration file. You can also use it in a Vitest workspace file, if that is how your project is configured.
+
+### Example configuration files
+
+When the addon is set up automatically, it will create or adjust your Vitest configuration files for you. If you're setting up manually, you can use the following examples as a reference when configuring your project.
+
+
+ Example Vitest setup file
+
+ Storybook stories contain configuration defined in `.storybook/preview.js|ts`. To ensure that configuration is available to your tests, you can apply it in a Vitest setup file. Here's an example of how to do that:
+
+ {/* prettier-ignore-start */}
+
+
+
+ {/* prettier-ignore-end */}
+
+ The `setProjectAnnotations` function is part of the [portable stories API](../api/portable-stories/portable-stories-vitest.mdx#setprojectannotations), which is used internally by the Vitest plugin to transform your stories into tests.
+
+
+
+ Example Vitest config file
+
+ The most simple application of the plugin is to include it in your Vitest configuration file:
+
+ {/* prettier-ignore-start */}
+
+
+
+ {/* prettier-ignore-end */}
+
+
+
+
+ Example Vitest workspace file
+
+ If you're using a [Vitest workspace](https://vitest.dev/guide/workspace), you can define a new workspace project:
+
+ {/* prettier-ignore-start */}
+
+
+
+ {/* prettier-ignore-end */}
+
+
+
+## Usage
+
+There are three primary ways to run tests using the Vitest plugin:
+
+### CLI
+
+The plugin transforms your stories into real Vitest tests, so you run those tests just like you run any other Vitest tests in your project. Typically, you will have a `test` script in your `package.json` that runs your tests.
+
+If you don't already have a `test` script, you can add one that runs Vitest:
+
+```json title="package.json"
+{
+ "scripts": {
+ "test": "vitest"
+ }
+}
+```
+
+If you already have a `test` script that runs something other than Vitest, you can either adjust it to run Vitest (as above) or add a new script that runs Vitest:
+
+```json title="package.json"
+{
+ "scripts": {
+ "test-storybook": "vitest"
+ }
+}
+```
+
+When you run that script, the plugin will find and run your story-based tests. Here's an example of running your tests (in [watch mode](https://vitest.dev/guide/cli.html#vitest-watch), by default) using the Vitest CLI:
+
+
+
+### Editor extension
+
+Transforming your stories into Vitest tests with the plugin also enables you to run and debug tests using Vitest [IDE integrations](https://vitest.dev/guide/ide.html). This allows you to run tests directly from your editor, such as VSCode and JetBrains IDE.
+
+This screenshot shows how you can run your Vitest tests in VSCode using the [Vitest extension](https://marketplace.visualstudio.com/items?itemName=vitest.explorer). Stories are annotated with the test status, and, when a test fails, a link to the story is provided for [debugging](#debugging).
+
+![Screenshot of test failure in VSCode, showing a failure attached to a story](../_assets/writing-tests/vitest-plugin-vscode.png)
+
+### In CI
+
+For the most part, running your Storybook tests in CI is done [via the CLI](#cli). However, to have the test output link to your published Storybook on test failures, you need to provide the [`storybookUrl` option](#storybookurl) in the plugin configuration.
+
+Here's an example using GitHub Actions. The steps are similar for other CI providers, though details in the syntax or configuration may vary.
+
+When actions for services like Vercel, Netlify and others run a deployment job, they follow a pattern of emitting a `deployment_status` event containing the newly generated URL under `deployment_status.target_url`. This is the URL to the published Storybook instance. We then pass that URL to the plugin configuration using an environment variable, `SB_URL`. Finally, we update the plugin configuration to use that environment variable in the `storybookUrl` option.
+
+```yaml title=".github/workflows/test-storybook.yml"
+name: Storybook Tests
+on: deployment_status
+jobs:
+ test:
+ timeout-minutes: 60
+ runs-on: ubuntu-latest
+ if: github.event.deployment_status.state == 'success'
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: '18.x'
+ - name: Install dependencies
+ run: yarn
+ - name: Run Storybook tests
+ run: yarn test-storybook
+ env:
+ SB_URL: '${{ github.event.deployment_status.target_url }}'
+```
+
+```js title="vitest.workspace.ts"
+export default defineWorkspace([
+ // ...
+ {
+ // ...
+ {
+ plugins: [
+ storybookTest({
+ storybookScript: 'yarn storybook --ci',
+ storybookUrl: process.env.SB_URL
+ }),
+ ],
+ },
+ },
+])
+```
+
+### Debugging
+
+While the plugin does not require Storybook to run when testing, you may still want to run Storybook to debug your tests. To enable this, provide the [`storybookScript` option](#storybookscript) in the plugin configuration. When you run Vitest in watch mode, the plugin will start Storybook using this script and provide links to the story in the output on test failures. This allows you to quickly jump to the story in Storybook to debug the issue.
+
+You can also provide a [`storybookUrl` option](#storybookurl) to the plugin configuration. When you're not using watch mode and tests fail, the plugin will provide a link to the story using this URL in the output. This is useful when [running tests in CI](#in-ci) or other environments where Storybook is not already running.
+
+![Screenshot of test failure in the console, showing a failure with a link to the story](../_assets/writing-tests/vitest-plugin-test-failure.png)
+
+## Configuring tests
+
+Most of the configuration for the Vitest plugin's behavior is done in the Vitest configuration and setup files. However, you can also define configuration in your stories themselves, using [tags](../writing-stories/tags.mdx), to control how they are tested.
+
+By default, the plugin will run all stories with the `test` tag. You can adjust this behavior by providing the [`tags` option](#tags) in the plugin configuration. This allows you to include, exclude, or skip stories based on their tags.
+
+In this example, we'll apply the `stable` tag to all of the Button component's stories, except for ExperimentalFeatureStory, which will have the `experimental` tag:
+
+{/* prettier-ignore-start */}
+
+
+
+{/* prettier-ignore-end */}
+
+To connect those tags to our test behavior, we can adjust the plugin configuration to exclude the `experimental` tag:
+
+```js title="vitest.workspace.ts"
+export default defineWorkspace([
+ // ...
+ {
+ // ...
+ {
+ plugins: [
+ storybookTest({
+ // ...
+ tags: {
+ include: ['test'],
+ exclude: ['experimental'],
+ },
+ }),
+ ],
+ },
+ },
+])
+```
+
+If the same tag is in both the `include` and `exclude` arrays, the `exclude` behavior takes precedence.
+
+## Comparison to the test runner
+
+The [test runner](./test-runner.mdx) requires a running Storybook instance to test your stories, because it visits each one, executes the play function, and listens for results. This plugin, however, transforms your stories into tests using Vite and portable stories, so it does not need to run Storybook to test your stories. Beyond that core difference, there are a few other distinctions:
+
+Additionally, the test runner ran your stories as orchestrated tests in Jest, and that orchestration came with some complexity. By comparison, this plugin transforms your stories into real tests and then runs them using Vitest, which is simpler and more configurable.
+
+Finally, because of the simpler architecture and the use of Vitest, this plugin should be faster than the test runner for most projects. We'll do more benchmarking to quantify this in the future.
+
+## FAQ
+
+### How do I debug my tests in Storybook?
+
+The plugin will attempt to provide links to the story in Storybook when tests fail, for [debugging](#debugging) purposes.
+
+If the URLs are not working when running tests in watch mode, you should check two configuration options:
+
+- [`storybookUrl`](#storybookurl): Ensure this URL is correct and accessible. For example, the default is `http://localhost:6006`, which may not use the same port number you're using.
+- [`storybookScript`](#storybookscript): Ensure this script is correctly starting Storybook.
+
+If the URLs are not working when running tests in CI, you should ensure the Storybook is built and published before running the tests. You can then provide the URL to the published Storybook using the `storybookUrl` option. See the [In CI](#in-ci) section for an example.
+
+### How do I ensure my tests can find assets in the public directory?
+
+If your stories use assets in the public directory and you're not using the default public directory location (`public`), you need to adjust the Vitest configuration to include the public directory. You can do this by providing the [`publicDir` option in the Vitest configuration file](https://vitejs.dev/config/shared-options.html#publicdir).
+
+### How do I apply custom Vite configuration?
+
+If you have custom operations defined in [`viteFinal`](../api/main-config/main-config-vite-final.mdx) in your `.storybook/main.js|ts` file, you will need to translate those into the Vitest configuration. This is because the plugin does not use the Storybook Vite configuration.
+
+For example, to recreate an alias in Storybook's Vite configuration, you would need to apply that alias in the Vitest configuration:
+
+{/* prettier-ignore-start */}
+
+
+
+{/* prettier-ignore-end */}
+
+The above example places the Vite configuration in a Vitest configuration file. You can also place it in a Vitest workspace file, if that is how your project is configured.
+
+### How do I isolate Storybook tests from others?
+
+Some projects might contain a `test` property in their Vite configuration. Because the Vitest configuration used by this plugin extends that Vite config, the `test` properties are merged. This lack of isolation can cause issues with your Storybook tests.
+
+To isolate your Storybook tests from other tests, you need to move the `test` property from your Vite configuration to the Vitest configuration. The Vitest config used by the plugin can then safely extend your Vite config without merging the `test` property.
+
+### Why do we recommend browser mode?
+
+Vitest's browser mode runs your tests in a real browser (Chromium, via Playwright, in the default configuration). The alternative is a simulated browser environment, like JSDom or HappyDom, which can have differences in behavior compared to a real browser. For UI components, which can often depend on browser APIs or features, running tests in a real browser is more accurate.
+
+For more, see [Vitest's guide on using browser mode effectively](https://vitest.dev/guide/browser/#motivation).
+
+### How do I use WebDriver instead of Playwright?
+
+We recommend running tests in a browser using Playwright, but you can use WebDriverIO instead. To do so, you need to adjust the [browser provider in the Vitest configuration file](https://vitest.dev/config/#browser-provider).
+
+### How do I use a browser other than Chromium
+
+We recommend using Chromium, because it is most likely to best match the experience of a majority of your users. However, you can use other browsers by adjusting the [browser name in the Vitest configuration file](https://vitest.dev/config/#browser-name). Note that [Playwright and WebDriverIO support different browsers](https://vitest.dev/guide/browser/#browser-option-types).
+
+### How do I customize a test name?
+
+By default, the export name of a story is mapped to the test name. To create a more descriptive test description, you can provide a `name` property for the story. This allows you to include spaces, brackets, or other special characters.
+
+```js
+export const Story = {
+ name: 'custom, descriptive name'
+};
+```
+
+## API
+
+### Exports
+
+This plugin has the following exports:
+
+```js
+import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'
+```
+
+#### `storybookTest`
+
+Type: `function`
+
+A [Vitest plugin](https://vitejs.dev/guide/api-plugin) that transforms your stories into tests. It accepts an [options object](#options) for configuration.
+
+### Options
+
+The plugin is configured using an options object. Here are the available properties:
+
+#### `configDir`
+
+Type: `string`
+
+Default: `.storybook`
+
+The directory where the Storybook configuration is located, relative to the current working directory.
+
+If your [Storybook configuration](../configure/index.mdx) is not in the default location, you **must** specify the location here so the plugin can function correctly.
+
+#### `storybookScript`
+
+Type: `string`
+
+Optional script to run Storybook. If provided, Vitest will start Storybook using this script when run in watch mode. Only runs if the Storybook in `storybookUrl` is not already available.
+
+#### `storybookUrl`
+
+Type: `string`
+
+Default: `http://localhost:6006`
+
+The URL where Storybook is hosted. This is used for internal checks and to provide a [link to the story in the test output on failures](#debugging).
+
+#### `tags`
+
+Type:
+
+```ts
+{
+ include: string[];
+ exclude: string[];
+ skip: string[];
+}
+```
+
+Default:
+
+```ts
+{
+ include: ['test'],
+ exclude: [],
+ skip: [],
+}
+```
+
+[Tags](../writing-stories/tags.mdx) to include, exclude, or skip. These tags are defined as annotations in your story, meta, or preview.
+
+- **`include`**: Stories with these tags will be tested
+- **`exclude`**: Stories with these tags will not be tested, and will not be counted in the test results
+- **`skip`**: Stories with these tags will not be tested, and will be counted in the test results
+
+
\ No newline at end of file
diff --git a/package.json b/package.json
index d0eb7795735e..64a3e118ab67 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,7 @@
"get-report-message": "cd scripts; yarn get-report-message",
"get-template": "cd scripts; yarn get-template",
"i": "yarn --cwd scripts && yarn --cwd code",
+ "knip": "cd code; yarn knip",
"lint": "cd code; yarn lint",
"nx": "cd code; yarn nx",
"pretty-docs": "cd scripts; yarn install >/dev/null; yarn docs:prettier:write",
diff --git a/scripts/build-package.ts b/scripts/build-package.ts
index 60ac5b012e13..96ddb84c2fbc 100644
--- a/scripts/build-package.ts
+++ b/scripts/build-package.ts
@@ -1,8 +1,8 @@
-import chalk from 'chalk';
import { program } from 'commander';
import { execaCommand } from 'execa';
import { readJSON } from 'fs-extra';
import { posix, resolve, sep } from 'path';
+import picocolors from 'picocolors';
import prompts from 'prompts';
import windowSize from 'window-size';
@@ -63,7 +63,9 @@ async function run() {
...packageTasks,
};
- const main = program.version('5.0.0').option('--all', `build everything ${chalk.gray('(all)')}`);
+ const main = program
+ .version('5.0.0')
+ .option('--all', `build everything ${picocolors.gray('(all)')}`);
Object.keys(tasks)
.reduce((acc, key) => acc.option(tasks[key].suffix, tasks[key].helpText), main)
@@ -154,10 +156,10 @@ async function run() {
);
sub.stdout?.on('data', (data) => {
- process.stdout.write(`${chalk.cyan(v.name)}:\n${data}`);
+ process.stdout.write(`${picocolors.cyan(v.name)}:\n${data}`);
});
sub.stderr?.on('data', (data) => {
- process.stderr.write(`${chalk.red(v.name)}:\n${data}`);
+ process.stderr.write(`${picocolors.red(v.name)}:\n${data}`);
});
});
}
diff --git a/scripts/check-package.ts b/scripts/check-package.ts
index c8873e6cf846..8b9c49985fb5 100644
--- a/scripts/check-package.ts
+++ b/scripts/check-package.ts
@@ -1,11 +1,11 @@
// This script makes sure that we can support type checking,
// without having to build dts files for all packages in the monorepo.
// It is not implemented yet for angular, svelte and vue.
-import chalk from 'chalk';
import { program } from 'commander';
import { execaCommand } from 'execa';
import { readJSON } from 'fs-extra';
import { resolve } from 'path';
+import picocolors from 'picocolors';
import prompts from 'prompts';
import windowSize from 'window-size';
@@ -53,7 +53,9 @@ async function run() {
...packageTasks,
};
- const main = program.version('5.0.0').option('--all', `check everything ${chalk.gray('(all)')}`);
+ const main = program
+ .version('5.0.0')
+ .option('--all', `check everything ${picocolors.gray('(all)')}`);
Object.keys(tasks)
.reduce((acc, key) => acc.option(tasks[key].suffix, tasks[key].helpText), main)
@@ -122,10 +124,10 @@ async function run() {
});
sub.stdout.on('data', (data) => {
- process.stdout.write(`${chalk.cyan(v.name)}:\n${data}`);
+ process.stdout.write(`${picocolors.cyan(v.name)}:\n${data}`);
});
sub.stderr.on('data', (data) => {
- process.stderr.write(`${chalk.red(v.name)}:\n${data}`);
+ process.stderr.write(`${picocolors.red(v.name)}:\n${data}`);
});
});
}
diff --git a/scripts/dangerfile.ts b/scripts/dangerfile.ts
index 51a144e550cf..93382ae441cf 100644
--- a/scripts/dangerfile.ts
+++ b/scripts/dangerfile.ts
@@ -1,4 +1,4 @@
-/* eslint-disable import/extensions */
+/* eslint-disable import/no-extraneous-dependencies, import/extensions */
import { execSync } from 'child_process';
import { danger, fail } from 'danger';
diff --git a/scripts/event-log-checker.ts b/scripts/event-log-checker.ts
index 4893eaeb449c..403bde0b51f5 100644
--- a/scripts/event-log-checker.ts
+++ b/scripts/event-log-checker.ts
@@ -1,5 +1,5 @@
import assert from 'assert';
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import versions from '../code/core/src/common/versions';
import { oneWayHash } from '../code/core/src/telemetry/one-way-hash';
@@ -89,8 +89,8 @@ async function run() {
});
} catch (err) {
if (err instanceof assert.AssertionError) {
- console.log(`Assertions failed for ${chalk.bold(templateName)}\n`);
- console.log(chalk.bold(chalk.red`✕ ${testMessage}:`));
+ console.log(`Assertions failed for ${picocolors.bold(templateName)}\n`);
+ console.log(picocolors.bold(picocolors.red(`✕ ${testMessage}:`)));
console.log(err);
process.exit(1);
}
diff --git a/scripts/get-template.ts b/scripts/get-template.ts
index 28b390c665bd..728632392489 100644
--- a/scripts/get-template.ts
+++ b/scripts/get-template.ts
@@ -1,7 +1,7 @@
-import chalk from 'chalk';
import { program } from 'commander';
import { pathExists, readFile } from 'fs-extra';
import { readdir } from 'fs/promises';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import yaml from 'yaml';
@@ -103,7 +103,7 @@ async function checkParallelism(cadence?: Cadence, scriptName?: TaskKey) {
let isIncorrect = false;
cadences.forEach((cad) => {
- summary.push(`\n${chalk.bold(cad)}`);
+ summary.push(`\n${picocolors.bold(cad)}`);
const cadenceTemplates = Object.entries(allTemplates).filter(([key]) =>
templatesByCadence[cad].includes(key as TemplateKey)
);
@@ -129,7 +129,7 @@ async function checkParallelism(cadence?: Cadence, scriptName?: TaskKey) {
if (newParallelism !== currentParallelism) {
summary.push(
- `-- ❌ ${tasksMap[script]} - parallelism: ${currentParallelism} ${chalk.bgRed(
+ `-- ❌ ${tasksMap[script]} - parallelism: ${currentParallelism} ${picocolors.bgRed(
`(should be ${newParallelism})`
)}`
);
diff --git a/scripts/knip.config.ts b/scripts/knip.config.ts
new file mode 100644
index 000000000000..ef23f8c556bd
--- /dev/null
+++ b/scripts/knip.config.ts
@@ -0,0 +1,96 @@
+import { join, relative } from 'node:path';
+
+import fg from 'fast-glob';
+import type { KnipConfig } from 'knip';
+import { match } from 'minimatch';
+
+// Files we want to exclude from analysis should be negated project patterns, not `ignores`
+// docs: https://knip.dev/guides/configuring-project-files
+const project = [
+ 'src/**/*.{js,jsx,ts,tsx}',
+ '!**/__search-files-tests__/**',
+ '!**/__testfixtures__/**',
+ '!**/__mocks-ng-workspace__/**',
+ '!**/__mockdata__/**',
+];
+
+// Adding an explicit MDX "compiler", as the dependency knip looks for isn't listed (@mdx-js/mdx or astro)
+// Alternatively, we could ignore a few false positives
+// docs: https://knip.dev/features/compilers
+const importMatcher = /import[^'"]+['"]([^'"]+)['"]/g;
+const fencedCodeBlockMatcher = /```[\s\S]*?```/g;
+const mdx = (text: string) =>
+ [...text.replace(fencedCodeBlockMatcher, '').matchAll(importMatcher)].join('\n');
+
+const baseConfig = {
+ ignoreWorkspaces: ['renderers/svelte'], // ignored: Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in code/node_modules/@sveltejs/vite-plugin-svelte/package.json
+
+ // storybook itself configured (only) in root
+ storybook: { entry: ['**/*.@(mdx|stories.@(mdx|js|jsx|mjs|ts|tsx))'] },
+
+ workspaces: {
+ '.': {
+ project,
+ },
+ 'addons/*': {
+ project,
+ },
+ 'builders/*': {
+ project,
+ },
+ core: {
+ entry: ['src/index.ts', 'src/cli/bin/index.ts', 'src/*/{globals*,index,runtime}.ts'],
+ project,
+ },
+ 'frameworks/*': {
+ entry: [
+ // these extra entries we only need for frameworks/angular and frameworks/ember it seems
+ 'src/index.ts',
+ 'src/builders/{build,start}-storybook/index.ts',
+ 'src/**/docs/{index,config}.{js,ts}',
+ ],
+ project,
+ },
+ 'lib/*': {
+ project,
+ },
+ 'presets/*': {
+ project,
+ },
+ 'renderers/*': {
+ project,
+ },
+ },
+ compilers: {
+ mdx,
+ },
+} satisfies KnipConfig;
+
+// Adds package.json#bundler.entries etc. to each workspace config `entry: []`
+export const addBundlerEntries = async (config: KnipConfig) => {
+ const baseDir = join(__dirname, '../code');
+ const rootManifest = await import(join(baseDir, 'package.json'));
+ const workspaceDirs = await fg(rootManifest.workspaces.packages, {
+ cwd: baseDir,
+ onlyDirectories: true,
+ });
+ const workspaceDirectories = workspaceDirs.map((dir) => relative(baseDir, join(baseDir, dir)));
+ for (const wsDir of workspaceDirectories) {
+ for (const configKey of Object.keys(baseConfig.workspaces)) {
+ if (match([wsDir], configKey)) {
+ const manifest = await import(join(baseDir, wsDir, 'package.json'));
+ const configEntries = (config.workspaces[configKey].entry as string[]) ?? [];
+ const bundler = manifest?.bundler;
+ for (const value of Object.values(bundler ?? {})) {
+ if (Array.isArray(value)) {
+ configEntries.push(...value);
+ }
+ }
+ config.workspaces[configKey].entry = Array.from(new Set(configEntries));
+ }
+ }
+ }
+ return config;
+};
+
+export default addBundlerEntries(baseConfig);
diff --git a/scripts/package.json b/scripts/package.json
index 2cd8402ddcd8..d38128434b76 100644
--- a/scripts/package.json
+++ b/scripts/package.json
@@ -53,7 +53,7 @@
]
},
"resolutions": {
- "esbuild": "^0.23.0",
+ "esbuild": "^0.24.0",
"serialize-javascript": "^3.1.0",
"type-fest": "~2.19"
},
@@ -79,7 +79,6 @@
"@types/fs-extra": "^11.0.4",
"@types/http-server": "^0.12.4",
"@types/jest": "^29.5.12",
- "@types/lodash": "^4.17.6",
"@types/node": "^22.0.0",
"@types/prettier": "^3.0.0",
"@types/pretty-hrtime": "^1.0.3",
@@ -97,7 +96,6 @@
"@vitest/coverage-v8": "^2.0.5",
"ansi-regex": "^6.0.1",
"browser-assert": "^1.2.1",
- "chalk": "^4.1.0",
"chromatic": "^11.5.5",
"codecov": "^3.8.1",
"commander": "^12.1.0",
@@ -108,7 +106,8 @@
"detect-port": "^1.6.1",
"ejs": "^3.1.10",
"ejs-lint": "^2.0.0",
- "esbuild": "^0.23.0",
+ "es-toolkit": "^1.21.0",
+ "esbuild": "^0.24.0",
"esbuild-plugin-alias": "^0.2.1",
"eslint": "^8.57.0",
"eslint-config-airbnb-typescript": "^18.0.0",
@@ -135,14 +134,16 @@
"husky": "^4.3.7",
"json5": "^2.2.3",
"junit-xml": "^1.2.0",
+ "knip": "^5.30.1",
"lint-staged": "^15.2.7",
- "lodash": "^4.17.21",
"memoizerific": "^1.11.3",
+ "minimatch": "^10.0.1",
"node-gyp": "^9.3.1",
"nx": "18.0.6",
"ora": "^5.4.1",
"p-limit": "^3.1.0",
"p-retry": "^5.1.2",
+ "picocolors": "^1.1.0",
"playwright": "1.46.0",
"playwright-core": "1.46.0",
"prettier": "^3.3.2",
diff --git a/scripts/prepare/addon-bundle.ts b/scripts/prepare/addon-bundle.ts
index 14c62791514e..fa5e14a0e4c4 100755
--- a/scripts/prepare/addon-bundle.ts
+++ b/scripts/prepare/addon-bundle.ts
@@ -1,5 +1,7 @@
+import { writeFile } from 'node:fs/promises';
import { builtinModules } from 'node:module';
+import type { Metafile } from 'esbuild';
import aliasPlugin from 'esbuild-plugin-alias';
import * as fs from 'fs-extra';
import { glob } from 'glob';
@@ -13,6 +15,7 @@ import type { PackageJson } from 'type-fest';
import { globalPackages as globalManagerPackages } from '../../code/core/src/manager/globals/globals';
import { globalPackages as globalPreviewPackages } from '../../code/core/src/preview/globals/globals';
import { exec } from '../utils/exec';
+import { esbuild } from './tools';
/* TYPES */
@@ -34,6 +37,8 @@ type DtsConfigSection = Pick;
/* MAIN */
+const OUT_DIR = join(process.cwd(), 'dist');
+
export const nodeInternals = [
'module',
'node:module',
@@ -61,19 +66,30 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
await exec(`jiti ${pre}`, { cwd });
}
+ const metafilesDir = join(
+ __dirname,
+ '..',
+ '..',
+ 'code',
+ 'bench',
+ 'esbuild-metafiles',
+ name.replace('@storybook/', '')
+ );
+
const reset = hasFlag(flags, 'reset');
const watch = hasFlag(flags, 'watch');
const optimized = hasFlag(flags, 'optimized');
if (reset) {
- await fs.emptyDir(join(process.cwd(), 'dist'));
+ await fs.emptyDir(OUT_DIR);
+ await fs.emptyDir(metafilesDir);
}
- const tasks: Promise[] = [];
+ const tasks: (() => Promise)[] = [];
- const outDir = join(process.cwd(), 'dist');
const commonOptions: Options = {
- outDir,
+ outDir: OUT_DIR,
+ metafile: true,
silent: true,
treeshake: true,
shims: false,
@@ -117,37 +133,42 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
optimized,
});
- tasks.push(
- build({
- ...commonOptions,
- ...(optimized ? dtsConfig : {}),
- ...browserOptions,
- entry: exportEntries,
- external: [...commonExternals, ...globalManagerPackages, ...globalPreviewPackages],
- }),
- build({
- ...commonOptions,
- ...(optimized ? dtsConfig : {}),
- entry: exportEntries,
- format: ['cjs'],
- target: browserOptions.target,
- platform: 'neutral',
- external: [...commonExternals, ...globalManagerPackages, ...globalPreviewPackages],
- esbuildOptions: (options) => {
- options.platform = 'neutral';
- Object.assign(options, getESBuildOptions(optimized));
- },
- })
- );
+ tasks.push(async () => {
+ await Promise.all([
+ build({
+ ...commonOptions,
+ ...(optimized ? dtsConfig : {}),
+ ...browserOptions,
+ entry: exportEntries,
+ external: [...commonExternals, ...globalManagerPackages, ...globalPreviewPackages],
+ }),
+ build({
+ ...commonOptions,
+ ...(optimized ? dtsConfig : {}),
+ entry: exportEntries,
+ format: ['cjs'],
+ target: browserOptions.target,
+ platform: 'neutral',
+ external: [...commonExternals, ...globalManagerPackages, ...globalPreviewPackages],
+ esbuildOptions: (options) => {
+ options.platform = 'neutral';
+ Object.assign(options, getESBuildOptions(optimized));
+ },
+ }),
+ ]);
+ if (!watch) {
+ await readMetafiles({ formats: ['esm', 'cjs'] });
+ }
+ });
if (tsConfigExists && !optimized) {
- tasks.push(...exportEntries.map(generateDTSMapperFile));
+ tasks.push(...exportEntries.map((entry) => () => generateDTSMapperFile(entry)));
}
}
if (managerEntries.length > 0) {
- tasks.push(
- build({
+ tasks.push(async () => {
+ await build({
...commonOptions,
...browserOptions,
entry: managerEntries.map((e: string) => slash(join(cwd, e))),
@@ -155,8 +176,11 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
js: '.js',
}),
external: [...commonExternals, ...globalManagerPackages],
- })
- );
+ });
+ if (!watch) {
+ await readMetafiles({ formats: ['esm'] });
+ }
+ });
}
if (previewEntries.length > 0) {
@@ -165,19 +189,22 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
entries: previewEntries,
optimized,
});
- tasks.push(
- build({
+ tasks.push(async () => {
+ await build({
...commonOptions,
...(optimized ? dtsConfig : {}),
...browserOptions,
format: ['esm', 'cjs'],
entry: previewEntries.map((e: string) => slash(join(cwd, e))),
external: [...commonExternals, ...globalPreviewPackages],
- })
- );
+ });
+ if (!watch) {
+ await readMetafiles({ formats: ['esm', 'cjs'] });
+ }
+ });
if (tsConfigExists && !optimized) {
- tasks.push(...previewEntries.map(generateDTSMapperFile));
+ tasks.push(...previewEntries.map((entry) => () => generateDTSMapperFile(entry)));
}
}
@@ -187,56 +214,60 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
entries: nodeEntries,
optimized,
});
- tasks.push(
- build({
- ...commonOptions,
- entry: nodeEntries.map((e: string) => slash(join(cwd, e))),
- format: ['cjs'],
- target: 'node18',
- platform: 'node',
- external: commonExternals,
- esbuildOptions: (c) => {
- c.platform = 'node';
- Object.assign(c, getESBuildOptions(optimized));
- },
- })
- );
- tasks.push(
- build({
- ...commonOptions,
- ...(optimized ? dtsConfig : {}),
- entry: nodeEntries.map((e: string) => slash(join(cwd, e))),
- format: ['esm'],
- target: 'node18',
- platform: 'neutral',
- banner: {
- js: dedent`
- import ESM_COMPAT_Module from "node:module";
- import { fileURLToPath as ESM_COMPAT_fileURLToPath } from 'node:url';
- import { dirname as ESM_COMPAT_dirname } from 'node:path';
- const __filename = ESM_COMPAT_fileURLToPath(import.meta.url);
- const __dirname = ESM_COMPAT_dirname(__filename);
- const require = ESM_COMPAT_Module.createRequire(import.meta.url);
- `,
- },
- external: [...commonExternals, ...nodeInternals],
- esbuildOptions: (c) => {
- c.mainFields = ['main', 'module', 'node'];
- c.conditions = ['node', 'module', 'import', 'require'];
- c.platform = 'neutral';
- Object.assign(c, getESBuildOptions(optimized));
- },
- })
- );
+ tasks.push(async () => {
+ await Promise.all([
+ build({
+ ...commonOptions,
+ entry: nodeEntries.map((e: string) => slash(join(cwd, e))),
+ format: ['cjs'],
+ target: 'node18',
+ platform: 'node',
+ external: commonExternals,
+ esbuildOptions: (c) => {
+ c.platform = 'node';
+ Object.assign(c, getESBuildOptions(optimized));
+ },
+ }),
+ build({
+ ...commonOptions,
+ ...(optimized ? dtsConfig : {}),
+ entry: nodeEntries.map((e: string) => slash(join(cwd, e))),
+ format: ['esm'],
+ target: 'node18',
+ platform: 'neutral',
+ banner: {
+ js: dedent`
+ import ESM_COMPAT_Module from "node:module";
+ import { fileURLToPath as ESM_COMPAT_fileURLToPath } from 'node:url';
+ import { dirname as ESM_COMPAT_dirname } from 'node:path';
+ const __filename = ESM_COMPAT_fileURLToPath(import.meta.url);
+ const __dirname = ESM_COMPAT_dirname(__filename);
+ const require = ESM_COMPAT_Module.createRequire(import.meta.url);
+ `,
+ },
+ external: [...commonExternals, ...nodeInternals],
+ esbuildOptions: (c) => {
+ c.mainFields = ['main', 'module', 'node'];
+ c.conditions = ['node', 'module', 'import', 'require'];
+ c.platform = 'neutral';
+ Object.assign(c, getESBuildOptions(optimized));
+ },
+ }),
+ ]);
+ if (!watch) {
+ await readMetafiles({ formats: ['esm', 'cjs'] });
+ }
+ });
if (tsConfigExists && !optimized) {
- tasks.push(...nodeEntries.map(generateDTSMapperFile));
+ tasks.push(...nodeEntries.map((entry) => () => generateDTSMapperFile(entry)));
}
}
- await Promise.all(tasks);
-
- const dtsFiles = await glob(outDir + '/**/*.d.ts');
+ for (const task of tasks) {
+ await task();
+ }
+ const dtsFiles = await glob(OUT_DIR + '/**/*.d.ts');
await Promise.all(
dtsFiles.map(async (file) => {
const content = await fs.readFile(file, 'utf-8');
@@ -247,6 +278,10 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
})
);
+ if (!watch) {
+ await saveMetafiles({ metafilesDir });
+ }
+
if (post) {
await exec(`jiti ${post}`, { cwd }, { debug: true });
}
@@ -311,6 +346,34 @@ async function generateDTSMapperFile(file: string) {
);
}
+const metafile: Metafile = {
+ inputs: {},
+ outputs: {},
+};
+
+async function readMetafiles({ formats }: { formats: Formats[] }) {
+ await Promise.all(
+ formats.map(async (format) => {
+ const fromFilename = `metafile-${format}.json`;
+ const currentMetafile = await fs.readJson(join(OUT_DIR, fromFilename));
+ metafile.inputs = { ...metafile.inputs, ...currentMetafile.inputs };
+ metafile.outputs = { ...metafile.outputs, ...currentMetafile.outputs };
+
+ await fs.rm(join(OUT_DIR, fromFilename));
+ })
+ );
+}
+
+async function saveMetafiles({ metafilesDir }: { metafilesDir: string }) {
+ await fs.ensureDir(metafilesDir);
+
+ await writeFile(join(metafilesDir, 'metafile.json'), JSON.stringify(metafile, null, 2));
+ await writeFile(
+ join(metafilesDir, 'metafile.txt'),
+ await esbuild.analyzeMetafile(metafile, { color: false, verbose: false })
+ );
+}
+
const hasFlag = (flags: string[], name: string) => !!flags.find((s) => s.startsWith(`--${name}`));
/* SELF EXECUTION */
diff --git a/scripts/prepare/bundle.ts b/scripts/prepare/bundle.ts
index 81a7b301fe41..4651774257c8 100755
--- a/scripts/prepare/bundle.ts
+++ b/scripts/prepare/bundle.ts
@@ -1,5 +1,7 @@
+import { writeFile } from 'node:fs/promises';
import { dirname, join, parse, posix, relative, resolve, sep } from 'node:path';
+import type { Metafile } from 'esbuild';
import aliasPlugin from 'esbuild-plugin-alias';
import * as fs from 'fs-extra';
import { glob } from 'glob';
@@ -10,6 +12,7 @@ import { build } from 'tsup';
import type { PackageJson } from 'type-fest';
import { exec } from '../utils/exec';
+import { esbuild } from './tools';
/* TYPES */
@@ -30,6 +33,8 @@ type DtsConfigSection = Pick;
/* MAIN */
+const OUT_DIR = join(process.cwd(), 'dist');
+
const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
const {
name,
@@ -50,17 +55,26 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
await exec(`jiti ${pre}`, { cwd });
}
+ const metafilesDir = join(
+ __dirname,
+ '..',
+ '..',
+ 'code',
+ 'bench',
+ 'esbuild-metafiles',
+ name.replace('@storybook', '')
+ );
+
const reset = hasFlag(flags, 'reset');
const watch = hasFlag(flags, 'watch');
const optimized = hasFlag(flags, 'optimized');
-
if (reset) {
- await fs.emptyDir(join(process.cwd(), 'dist'));
+ await fs.emptyDir(OUT_DIR);
+ await fs.emptyDir(metafilesDir);
}
const tasks: Promise[] = [];
- const outDir = join(process.cwd(), 'dist');
const externals = [
name,
...extraExternals,
@@ -93,8 +107,9 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
entry: nonPresetEntries,
shims: false,
watch,
- outDir,
+ outDir: OUT_DIR,
sourcemap: false,
+ metafile: true,
format: ['esm'],
target: platform === 'node' ? ['node18'] : ['chrome100', 'safari15', 'firefox91'],
clean: false,
@@ -127,8 +142,9 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
silent: true,
entry: allEntries,
watch,
- outDir,
+ outDir: OUT_DIR,
sourcemap: false,
+ metafile: true,
format: ['cjs'],
target: 'node18',
...(dtsBuild === 'cjs' ? dtsConfig : {}),
@@ -150,7 +166,11 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
await Promise.all(tasks);
- const dtsFiles = await glob(outDir + '/**/*.d.ts');
+ if (!watch) {
+ await saveMetafiles({ metafilesDir, formats });
+ }
+
+ const dtsFiles = await glob(OUT_DIR + '/**/*.d.ts');
await Promise.all(
dtsFiles.map(async (file) => {
const content = await fs.readFile(file, 'utf-8');
@@ -225,6 +245,37 @@ async function generateDTSMapperFile(file: string) {
);
}
+async function saveMetafiles({
+ metafilesDir,
+ formats,
+}: {
+ metafilesDir: string;
+ formats: Formats[];
+}) {
+ await fs.ensureDir(metafilesDir);
+ const metafile: Metafile = {
+ inputs: {},
+ outputs: {},
+ };
+
+ await Promise.all(
+ formats.map(async (format) => {
+ const fromFilename = `metafile-${format}.json`;
+ const currentMetafile = await fs.readJson(join(OUT_DIR, fromFilename));
+ metafile.inputs = { ...metafile.inputs, ...currentMetafile.inputs };
+ metafile.outputs = { ...metafile.outputs, ...currentMetafile.outputs };
+
+ await fs.rm(join(OUT_DIR, fromFilename));
+ })
+ );
+
+ await writeFile(join(metafilesDir, 'metafile.json'), JSON.stringify(metafile, null, 2));
+ await writeFile(
+ join(metafilesDir, 'metafile.txt'),
+ await esbuild.analyzeMetafile(metafile, { color: false, verbose: false })
+ );
+}
+
const hasFlag = (flags: string[], name: string) => !!flags.find((s) => s.startsWith(`--${name}`));
/* SELF EXECUTION */
diff --git a/scripts/prepare/tools.ts b/scripts/prepare/tools.ts
index 5b81ed2b3f62..eb4214a945ba 100644
--- a/scripts/prepare/tools.ts
+++ b/scripts/prepare/tools.ts
@@ -3,12 +3,12 @@ import { dirname, join } from 'node:path';
import * as process from 'node:process';
import { globalExternals } from '@fal-works/esbuild-plugin-global-externals';
-import chalk from 'chalk';
import { spawn } from 'cross-spawn';
import * as esbuild from 'esbuild';
import { readJson } from 'fs-extra';
import { glob } from 'glob';
import limit from 'p-limit';
+import picocolors from 'picocolors';
import * as prettier from 'prettier';
import prettyTime from 'pretty-hrtime';
import * as rollup from 'rollup';
@@ -103,7 +103,7 @@ export {
process,
esbuild,
prettyTime,
- chalk,
+ picocolors,
dedent,
limit,
sortPackageJson,
diff --git a/scripts/release/cancel-preparation-runs.ts b/scripts/release/cancel-preparation-runs.ts
index aa5916de8208..b3d922dbef5a 100644
--- a/scripts/release/cancel-preparation-runs.ts
+++ b/scripts/release/cancel-preparation-runs.ts
@@ -2,8 +2,8 @@
* This script cancels all running preparation workflows in GitHub. It will fetch all active runs
* for the preparation workflows, and cancel them.
*/
-import chalk from 'chalk';
import { program } from 'commander';
+import picocolors from 'picocolors';
import { dedent } from 'ts-dedent';
import { esMain } from '../utils/esmain';
@@ -35,12 +35,12 @@ export const run = async () => {
)?.id;
console.log(`Found workflow IDs for the preparation workflows:
- ${chalk.blue(PREPARE_PATCH_WORKFLOW_PATH)}: ${chalk.green(preparePatchWorkflowId)}
- ${chalk.blue(PREPARE_NON_PATCH_WORKFLOW_PATH)}: ${chalk.green(prepareNonPatchWorkflowId)}`);
+ ${picocolors.blue(PREPARE_PATCH_WORKFLOW_PATH)}: ${picocolors.green(preparePatchWorkflowId)}
+ ${picocolors.blue(PREPARE_NON_PATCH_WORKFLOW_PATH)}: ${picocolors.green(prepareNonPatchWorkflowId)}`);
if (!preparePatchWorkflowId || !prepareNonPatchWorkflowId) {
throw new Error(dedent`🚨 Could not find workflow IDs for the preparation workflows
- - Looked for paths: "${chalk.blue(PREPARE_PATCH_WORKFLOW_PATH)}" and "${chalk.blue(
+ - Looked for paths: "${picocolors.blue(PREPARE_PATCH_WORKFLOW_PATH)}" and "${picocolors.blue(
PREPARE_NON_PATCH_WORKFLOW_PATH
)}", are they still correct?
- Found workflows:
@@ -75,7 +75,10 @@ export const run = async () => {
console.log(`🔍 Found ${runsToCancel.length} runs to cancel. Cancelling them now:
${runsToCancel
- .map((r) => `${chalk.green(r.path)} - ${chalk.green(r.id)}: ${chalk.blue(r.status)}`)
+ .map(
+ (r) =>
+ `${picocolors.green(r.path)} - ${picocolors.green(r.id)}: ${picocolors.blue(r.status)}`
+ )
.join('\n ')}`);
const result = await Promise.allSettled(
diff --git a/scripts/release/generate-pr-description.ts b/scripts/release/generate-pr-description.ts
index f1a79da2d6db..0d569e20e01b 100644
--- a/scripts/release/generate-pr-description.ts
+++ b/scripts/release/generate-pr-description.ts
@@ -1,6 +1,6 @@
import { setOutput } from '@actions/core';
-import chalk from 'chalk';
import { program } from 'commander';
+import picocolors from 'picocolors';
import semver from 'semver';
import { dedent } from 'ts-dedent';
import { z } from 'zod';
@@ -246,9 +246,9 @@ export const run = async (rawOptions: unknown) => {
const currentVersion = options.currentVersion || (await getCurrentVersion());
console.log(
- `💬 Generating PR description for ${chalk.blue(nextVersion)} between ${chalk.green(
+ `💬 Generating PR description for ${picocolors.blue(nextVersion)} between ${picocolors.green(
currentVersion
- )} and ${chalk.green('HEAD')}`
+ )} and ${picocolors.green('HEAD')}`
);
const { changes, changelogText } = await getChanges({
@@ -289,7 +289,7 @@ export const run = async (rawOptions: unknown) => {
if (process.env.GITHUB_ACTIONS === 'true') {
setOutput('description', description);
}
- console.log(`✅ Generated PR description for ${chalk.blue(nextVersion)}`);
+ console.log(`✅ Generated PR description for ${picocolors.blue(nextVersion)}`);
if (verbose) {
console.log(description);
}
diff --git a/scripts/release/get-changelog-from-file.ts b/scripts/release/get-changelog-from-file.ts
index f428310cc656..354605e04644 100644
--- a/scripts/release/get-changelog-from-file.ts
+++ b/scripts/release/get-changelog-from-file.ts
@@ -1,9 +1,9 @@
import { join } from 'node:path';
import { setOutput } from '@actions/core';
-import chalk from 'chalk';
import { program } from 'commander';
import { readFile } from 'fs-extra';
+import picocolors from 'picocolors';
import semver from 'semver';
import { dedent } from 'ts-dedent';
@@ -29,13 +29,13 @@ export const getChangelogFromFile = async (args: {
const changelogFilename = isPrerelease ? 'CHANGELOG.prerelease.md' : 'CHANGELOG.md';
const changelogPath = join(__dirname, '..', '..', changelogFilename);
- console.log(`📝 Getting changelog from ${chalk.blue(changelogPath)}`);
+ console.log(`📝 Getting changelog from ${picocolors.blue(changelogPath)}`);
const fullChangelog = await readFile(changelogPath, 'utf-8');
const changelogForVersion = fullChangelog.split(/(^|\n)## /).find((v) => v.startsWith(version));
if (!changelogForVersion) {
throw new Error(
- `Could not find changelog entry for version ${chalk.blue(version)} in ${chalk.green(
+ `Could not find changelog entry for version ${picocolors.blue(version)} in ${picocolors.green(
changelogPath
)}`
);
diff --git a/scripts/release/get-current-version.ts b/scripts/release/get-current-version.ts
index f640ccb5907e..8530f6cf9646 100644
--- a/scripts/release/get-current-version.ts
+++ b/scripts/release/get-current-version.ts
@@ -1,8 +1,8 @@
import { join } from 'node:path';
import { setOutput } from '@actions/core';
-import chalk from 'chalk';
import { readJson } from 'fs-extra';
+import picocolors from 'picocolors';
import { esMain } from '../utils/esmain';
@@ -15,7 +15,7 @@ export const getCurrentVersion = async () => {
if (process.env.GITHUB_ACTIONS === 'true') {
setOutput('current-version', version);
}
- console.log(`📦 Current version is ${chalk.green(version)}`);
+ console.log(`📦 Current version is ${picocolors.green(version)}`);
return version;
};
diff --git a/scripts/release/get-version-changelog.ts b/scripts/release/get-version-changelog.ts
index 6e1af6a7b5b7..e85ac5257643 100644
--- a/scripts/release/get-version-changelog.ts
+++ b/scripts/release/get-version-changelog.ts
@@ -1,6 +1,6 @@
import { setOutput } from '@actions/core';
-import chalk from 'chalk';
import { program } from 'commander';
+import picocolors from 'picocolors';
import { esMain } from '../utils/esmain';
import { getCurrentVersion } from './get-current-version';
@@ -17,7 +17,7 @@ program
export const getVersionChangelog = async (args: { version?: string; verbose?: boolean }) => {
const version = args.version || (await getCurrentVersion());
- console.log(`📝 Getting changelog for version ${chalk.blue(version)}`);
+ console.log(`📝 Getting changelog for version ${picocolors.blue(version)}`);
const { changelogText } = await getChanges({ from: version, version, verbose: args.verbose });
diff --git a/scripts/release/is-pr-frozen.ts b/scripts/release/is-pr-frozen.ts
index af5a957af8ae..d8c80708fc71 100644
--- a/scripts/release/is-pr-frozen.ts
+++ b/scripts/release/is-pr-frozen.ts
@@ -1,9 +1,9 @@
import { join } from 'node:path';
import { setOutput } from '@actions/core';
-import chalk from 'chalk';
import { program } from 'commander';
import { readJson } from 'fs-extra';
+import picocolors from 'picocolors';
import { esMain } from '../utils/esmain';
import { getPullInfoFromCommit } from './utils/get-github-info';
@@ -39,7 +39,7 @@ const getRepo = async (verbose?: boolean): Promise => {
const pushUrl = originRemote.refs.push;
const repo = pushUrl.replace(/\.git$/, '').replace(/.*:(\/\/github\.com\/)*/, '');
if (verbose) {
- console.log(`📦 Extracted repo: ${chalk.blue(repo)}`);
+ console.log(`📦 Extracted repo: ${picocolors.blue(repo)}`);
}
return repo;
};
@@ -50,7 +50,7 @@ export const run = async (options: unknown) => {
const version = await getCurrentVersion();
const branch = `version-${patch ? 'patch' : 'non-patch'}-from-${version}`;
- console.log(`💬 Determining if pull request from branch '${chalk.blue(branch)}' is frozen`);
+ console.log(`💬 Determining if pull request from branch '${picocolors.blue(branch)}' is frozen`);
console.log(`⬇️ Fetching remote 'origin/${branch}'...`);
try {
diff --git a/scripts/release/is-prerelease.ts b/scripts/release/is-prerelease.ts
index d92f17279b82..65b2ddf0ddc7 100644
--- a/scripts/release/is-prerelease.ts
+++ b/scripts/release/is-prerelease.ts
@@ -1,6 +1,6 @@
import { setOutput } from '@actions/core';
-import chalk from 'chalk';
import { program } from 'commander';
+import picocolors from 'picocolors';
import semver from 'semver';
import { esMain } from '../utils/esmain';
@@ -17,10 +17,10 @@ program
export const isPrerelease = async (args: { version?: string; verbose?: boolean }) => {
if (args.verbose) {
if (args.version) {
- console.log(`📦 Checking if version ${chalk.blue(args.version)} is a prerelease`);
+ console.log(`📦 Checking if version ${picocolors.blue(args.version)} is a prerelease`);
} else {
console.log(
- `📦 Checking if current version in ${chalk.blue('code/package.json')} is a prerelease`
+ `📦 Checking if current version in ${picocolors.blue('code/package.json')} is a prerelease`
);
}
}
@@ -31,8 +31,8 @@ export const isPrerelease = async (args: { version?: string; verbose?: boolean }
setOutput('prerelease', result);
}
console.log(
- `📦 Version ${chalk.blue(version)} ${
- result ? chalk.green('IS') : chalk.red('IS NOT')
+ `📦 Version ${picocolors.blue(version)} ${
+ result ? picocolors.green('IS') : picocolors.red('IS NOT')
} a prerelease`
);
diff --git a/scripts/release/is-version-published.ts b/scripts/release/is-version-published.ts
index 6af757eb654c..a6a6891b2124 100644
--- a/scripts/release/is-version-published.ts
+++ b/scripts/release/is-version-published.ts
@@ -1,6 +1,6 @@
import { setOutput } from '@actions/core';
-import chalk from 'chalk';
import { program } from 'commander';
+import picocolors from 'picocolors';
import { esMain } from '../utils/esmain';
import { getCurrentVersion } from './get-current-version';
@@ -19,12 +19,14 @@ const isVersionPublished = async ({
version: string;
verbose?: boolean;
}) => {
- const prettyPackage = `${chalk.blue(packageName)}@${chalk.green(version)}`;
+ const prettyPackage = `${picocolors.blue(packageName)}@${picocolors.green(version)}`;
console.log(`⛅ Checking if ${prettyPackage} is published...`);
if (verbose) {
console.log(`Fetching from npm:`);
- console.log(`https://registry.npmjs.org/${chalk.blue(packageName)}/${chalk.green(version)}`);
+ console.log(
+ `https://registry.npmjs.org/${picocolors.blue(packageName)}/${picocolors.green(version)}`
+ );
}
const response = await fetch(`https://registry.npmjs.org/${packageName}/${version}`);
if (response.status === 404) {
diff --git a/scripts/release/pick-patches.ts b/scripts/release/pick-patches.ts
index 824f8c6d0cd9..d25667cc4974 100644
--- a/scripts/release/pick-patches.ts
+++ b/scripts/release/pick-patches.ts
@@ -1,7 +1,7 @@
import { setOutput } from '@actions/core';
-import chalk from 'chalk';
import { program } from 'commander';
import ora from 'ora';
+import picocolors from 'picocolors';
import invariant from 'tiny-invariant';
import { esMain } from '../utils/esmain';
@@ -25,7 +25,7 @@ interface PR {
}
function formatPR(pr: PR): string {
- return `https://github.com/${OWNER}/${REPO}/pull/${pr.number} "${pr.title}" ${chalk.yellow(
+ return `https://github.com/${OWNER}/${REPO}/pull/${pr.number} "${pr.title}" ${picocolors.yellow(
pr.mergeCommit
)}`;
}
@@ -71,7 +71,7 @@ export const run = async (_: unknown) => {
}
failedCherryPicks.push(pr.mergeCommit);
prSpinner.info(
- `This PR can be picked manually with: ${chalk.grey(
+ `This PR can be picked manually with: ${picocolors.gray(
`git cherry-pick -m1 -x ${pr.mergeCommit}`
)}`
);
diff --git a/scripts/release/publish.ts b/scripts/release/publish.ts
index 65347010181d..83839dd4a003 100644
--- a/scripts/release/publish.ts
+++ b/scripts/release/publish.ts
@@ -1,10 +1,10 @@
import { join } from 'node:path';
-import chalk from 'chalk';
import { program } from 'commander';
import { execaCommand } from 'execa';
import { readJson } from 'fs-extra';
import pRetry from 'p-retry';
+import picocolors from 'picocolors';
import semver from 'semver';
import dedent from 'ts-dedent';
import { z } from 'zod';
@@ -51,7 +51,7 @@ const getCurrentVersion = async (verbose?: boolean) => {
console.log(`📐 Reading current version of Storybook...`);
}
const { version } = await readJson(CODE_PACKAGE_JSON_PATH);
- console.log(`📐 Current version of Storybook is ${chalk.green(version)}`);
+ console.log(`📐 Current version of Storybook is ${picocolors.green(version)}`);
return version;
};
@@ -64,13 +64,13 @@ const isCurrentVersionPublished = async ({
currentVersion: string;
verbose?: boolean;
}) => {
- const prettyPackage = `${chalk.blue(packageName)}@${chalk.green(currentVersion)}`;
+ const prettyPackage = `${picocolors.blue(packageName)}@${picocolors.green(currentVersion)}`;
console.log(`⛅ Checking if ${prettyPackage} is published...`);
if (verbose) {
console.log(`Fetching from npm:`);
console.log(
- `https://registry.npmjs.org/${chalk.blue(packageName)}/${chalk.green(currentVersion)}`
+ `https://registry.npmjs.org/${picocolors.blue(packageName)}/${picocolors.green(currentVersion)}`
);
}
const response = await fetch(`https://registry.npmjs.org/${packageName}/${currentVersion}`);
@@ -133,7 +133,7 @@ const publishAllPackages = async ({
}
if (dryRun) {
console.log(`📦 Dry run, skipping publish. Would have executed:
- ${chalk.blue(command)}`);
+ ${picocolors.blue(command)}`);
return;
}
@@ -155,7 +155,7 @@ const publishAllPackages = async ({
retries: 4,
onFailedAttempt: (error) =>
console.log(
- chalk.yellow(
+ picocolors.yellow(
dedent`❗One or more packages failed to publish, retrying...
This was attempt number ${error.attemptNumber}, there are ${error.retriesLeft} retries left. 🤞`
)
@@ -180,15 +180,15 @@ export const run = async (options: unknown) => {
});
if (isAlreadyPublished) {
throw new Error(
- `⛔ Current version (${chalk.green(currentVersion)}) is already published, aborting.`
+ `⛔ Current version (${picocolors.green(currentVersion)}) is already published, aborting.`
);
}
await buildAllPackages();
await publishAllPackages({ tag, verbose, dryRun });
console.log(
- `✅ Published all packages with version ${chalk.green(currentVersion)}${
- tag ? ` at tag ${chalk.blue(tag)}` : ''
+ `✅ Published all packages with version ${picocolors.green(currentVersion)}${
+ tag ? ` at tag ${picocolors.blue(tag)}` : ''
}`
);
};
diff --git a/scripts/release/unreleased-changes-exists.ts b/scripts/release/unreleased-changes-exists.ts
index 49c50ccd7d42..ad869461107b 100644
--- a/scripts/release/unreleased-changes-exists.ts
+++ b/scripts/release/unreleased-changes-exists.ts
@@ -1,7 +1,7 @@
import { setOutput } from '@actions/core';
-import chalk from 'chalk';
import { program } from 'commander';
-import { intersection } from 'lodash';
+import { intersection } from 'es-toolkit';
+import picocolors from 'picocolors';
import { z } from 'zod';
import { esMain } from '../utils/esmain';
@@ -68,11 +68,11 @@ export const run = async (
}
if (hasChangesToRelease) {
console.log(
- `${chalk.green('🦋 The following changes are releasable')}:
-${chalk.blue(changesToRelease.map(({ title, pull }) => ` #${pull}: ${title}`).join('\n'))}`
+ `${picocolors.green('🦋 The following changes are releasable')}:
+${picocolors.blue(changesToRelease.map(({ title, pull }) => ` #${pull}: ${title}`).join('\n'))}`
);
} else {
- console.log(chalk.red('🫙 No changes to release!'));
+ console.log(picocolors.red('🫙 No changes to release!'));
}
return { changesToRelease, hasChangesToRelease };
diff --git a/scripts/release/utils/get-changes.ts b/scripts/release/utils/get-changes.ts
index 5588a10eba52..f7f255ddd451 100644
--- a/scripts/release/utils/get-changes.ts
+++ b/scripts/release/utils/get-changes.ts
@@ -1,4 +1,4 @@
-import chalk from 'chalk';
+import picocolors from 'picocolors';
import semver from 'semver';
import type { PullRequestInfo } from './get-github-info';
@@ -27,13 +27,15 @@ export const LABELS_BY_IMPORTANCE = {
const getCommitAt = async (id: string, verbose?: boolean) => {
if (!semver.valid(id)) {
- console.log(`🔍 ${chalk.red(id)} is not a valid semver string, assuming it is a commit hash`);
+ console.log(
+ `🔍 ${picocolors.red(id)} is not a valid semver string, assuming it is a commit hash`
+ );
return id;
}
const version = id.startsWith('v') ? id : `v${id}`;
const commitSha = (await git.raw(['rev-list', '-n', '1', version])).split('\n')[0];
if (verbose) {
- console.log(`🔍 Commit at tag ${chalk.green(version)}: ${chalk.blue(commitSha)}`);
+ console.log(`🔍 Commit at tag ${picocolors.green(version)}: ${picocolors.blue(commitSha)}`);
}
return commitSha;
};
@@ -50,12 +52,12 @@ export const getFromCommit = async (from?: string | undefined, verbose?: boolean
}
actualFrom = latest;
if (verbose) {
- console.log(`🔍 No 'from' specified, found latest tag: ${chalk.blue(latest)}`);
+ console.log(`🔍 No 'from' specified, found latest tag: ${picocolors.blue(latest)}`);
}
}
const commit = await getCommitAt(actualFrom!, verbose);
if (verbose) {
- console.log(`🔍 Found 'from' commit: ${chalk.blue(commit)}`);
+ console.log(`🔍 Found 'from' commit: ${picocolors.blue(commit)}`);
}
return commit;
};
@@ -64,14 +66,14 @@ export const getToCommit = async (to?: string | undefined, verbose?: boolean) =>
if (!to) {
const head = await git.revparse('HEAD');
if (verbose) {
- console.log(`🔍 No 'to' specified, HEAD is at commit: ${chalk.blue(head)}`);
+ console.log(`🔍 No 'to' specified, HEAD is at commit: ${picocolors.blue(head)}`);
}
return head;
}
const commit = await getCommitAt(to, verbose);
if (verbose) {
- console.log(`🔍 Found 'to' commit: ${chalk.blue(commit)}`);
+ console.log(`🔍 Found 'to' commit: ${picocolors.blue(commit)}`);
}
return commit;
};
@@ -88,9 +90,9 @@ export const getAllCommitsBetween = async ({
const logResult = await git.log({ from, to, '--first-parent': null });
if (verbose) {
console.log(
- `🔍 Found ${chalk.blue(logResult.total)} commits between ${chalk.green(
+ `🔍 Found ${picocolors.blue(logResult.total)} commits between ${picocolors.green(
`${from}`
- )} and ${chalk.green(`${to}`)}:`
+ )} and ${picocolors.green(`${to}`)}:`
);
console.dir(logResult.all, { depth: null, colors: true });
}
@@ -110,7 +112,7 @@ export const getRepo = async (verbose?: boolean): Promise => {
const pushUrl = originRemote.refs.push;
const repo = pushUrl.replace(/\.git$/, '').replace(/.*:(\/\/github\.com\/)*/, '');
if (verbose) {
- console.log(`📦 Extracted repo: ${chalk.blue(repo)}`);
+ console.log(`📦 Extracted repo: ${picocolors.blue(repo)}`);
}
return repo;
};
@@ -234,7 +236,7 @@ export const getChanges = async ({
unpickedPatches?: boolean;
verbose?: boolean;
}) => {
- console.log(`💬 Getting changes for ${chalk.blue(version)}`);
+ console.log(`💬 Getting changes for ${picocolors.blue(version)}`);
let commits;
if (unpickedPatches) {
diff --git a/scripts/release/version.ts b/scripts/release/version.ts
index 422de3501bc0..bdaef52fa18e 100644
--- a/scripts/release/version.ts
+++ b/scripts/release/version.ts
@@ -1,10 +1,10 @@
import { join } from 'node:path';
import { setOutput } from '@actions/core';
-import chalk from 'chalk';
import { program } from 'commander';
import { execaCommand } from 'execa';
import { readFile, readJson, writeFile, writeJson } from 'fs-extra';
+import picocolors from 'picocolors';
import semver from 'semver';
import { z } from 'zod';
@@ -116,14 +116,14 @@ const getCurrentVersion = async () => {
};
const bumpCodeVersion = async (nextVersion: string) => {
- console.log(`🤜 Bumping version of ${chalk.cyan('code')}'s package.json...`);
+ console.log(`🤜 Bumping version of ${picocolors.cyan('code')}'s package.json...`);
const codePkgJson = await readJson(CODE_PACKAGE_JSON_PATH);
codePkgJson.version = nextVersion;
await writeJson(CODE_PACKAGE_JSON_PATH, codePkgJson, { spaces: 2 });
- console.log(`✅ Bumped version of ${chalk.cyan('code')}'s package.json`);
+ console.log(`✅ Bumped version of ${picocolors.cyan('code')}'s package.json`);
};
const bumpVersionSources = async (currentVersion: string, nextVersion: string) => {
@@ -131,7 +131,7 @@ const bumpVersionSources = async (currentVersion: string, nextVersion: string) =
join(CODE_DIR_PATH, 'core', 'src', 'manager-api', 'version.ts'),
join(CODE_DIR_PATH, 'core', 'src', 'common', 'versions.ts'),
];
- console.log(`🤜 Bumping versions in...:\n ${chalk.cyan(filesToUpdate.join('\n '))}`);
+ console.log(`🤜 Bumping versions in...:\n ${picocolors.cyan(filesToUpdate.join('\n '))}`);
await Promise.all(
filesToUpdate.map(async (filename) => {
@@ -141,7 +141,7 @@ const bumpVersionSources = async (currentVersion: string, nextVersion: string) =
})
);
- console.log(`✅ Bumped versions in:\n ${chalk.cyan(filesToUpdate.join('\n '))}`);
+ console.log(`✅ Bumped versions in:\n ${picocolors.cyan(filesToUpdate.join('\n '))}`);
};
const bumpAllPackageJsons = async ({
@@ -154,7 +154,7 @@ const bumpAllPackageJsons = async ({
verbose?: boolean;
}) => {
console.log(
- `🤜 Bumping versions and dependencies in ${chalk.cyan(
+ `🤜 Bumping versions and dependencies in ${picocolors.cyan(
`all ${packages.length} package.json`
)}'s...`
);
@@ -171,7 +171,7 @@ const bumpAllPackageJsons = async ({
packageJson.version = nextVersion;
if (verbose) {
console.log(
- ` Bumping ${chalk.blue(pkg.name)}'s version to ${chalk.yellow(nextVersion)}`
+ ` Bumping ${picocolors.blue(pkg.name)}'s version to ${picocolors.yellow(nextVersion)}`
);
}
await writeJson(packageJsonPath, packageJson, { spaces: 2 });
@@ -181,15 +181,15 @@ const bumpAllPackageJsons = async ({
const bumpDeferred = async (nextVersion: string) => {
console.log(
- `⏳ Setting a ${chalk.cyan('deferred')} version bump with ${chalk.blue(
+ `⏳ Setting a ${picocolors.cyan('deferred')} version bump with ${picocolors.blue(
'code/package.json#deferredNextVersion'
- )} = ${chalk.yellow(nextVersion)}...`
+ )} = ${picocolors.yellow(nextVersion)}...`
);
const codePkgJson = await readJson(CODE_PACKAGE_JSON_PATH);
if (codePkgJson.deferredNextVersion) {
console.warn(
- `❗ A "deferredNextVersion" property already exists with the value of ${chalk.cyan(
+ `❗ A "deferredNextVersion" property already exists with the value of ${picocolors.cyan(
codePkgJson.deferredNextVersion
)}. This will be overwritten and ignored.`
);
@@ -198,12 +198,12 @@ const bumpDeferred = async (nextVersion: string) => {
codePkgJson.deferredNextVersion = nextVersion;
await writeJson(CODE_PACKAGE_JSON_PATH, codePkgJson, { spaces: 2 });
- console.log(`✅ Set a ${chalk.cyan('deferred')} version bump. Not bumping any packages.`);
+ console.log(`✅ Set a ${picocolors.cyan('deferred')} version bump. Not bumping any packages.`);
};
const applyDeferredVersionBump = async () => {
console.log(
- `⏩ Applying previously deferred version bump set at ${chalk.blue(
+ `⏩ Applying previously deferred version bump set at ${picocolors.blue(
'code/package.json#deferredNextVersion'
)}...`
);
@@ -221,9 +221,9 @@ const applyDeferredVersionBump = async () => {
await writeJson(CODE_PACKAGE_JSON_PATH, codePkgJson, { spaces: 2 });
console.log(
- `✅ Extracted and removed deferred version ${chalk.green(
+ `✅ Extracted and removed deferred version ${picocolors.green(
deferredNextVersion
- )} from ${chalk.blue('code/package.json#deferredNextVersion')}`
+ )} from ${picocolors.blue('code/package.json#deferredNextVersion')}`
);
return deferredNextVersion;
@@ -240,11 +240,11 @@ export const run = async (options: unknown) => {
const [packages, currentVersion] = await Promise.all([getWorkspaces(), getCurrentVersion()]);
console.log(
- `📦 found ${packages.length} storybook packages at version ${chalk.red(currentVersion)}`
+ `📦 found ${packages.length} storybook packages at version ${picocolors.red(currentVersion)}`
);
if (verbose) {
const formattedPackages = packages.map(
- (pkg) => `${chalk.green(pkg.name.padEnd(60))}: ${chalk.cyan(pkg.location)}`
+ (pkg) => `${picocolors.green(pkg.name.padEnd(60))}: ${picocolors.cyan(pkg.location)}`
);
console.log(`📦 Packages:
${formattedPackages.join('\n ')}`);
@@ -255,42 +255,46 @@ export const run = async (options: unknown) => {
if ('apply' in options && options.apply) {
nextVersion = await applyDeferredVersionBump();
} else if ('exact' in options && options.exact) {
- console.log(`📈 Exact version selected: ${chalk.green(options.exact)}`);
+ console.log(`📈 Exact version selected: ${picocolors.green(options.exact)}`);
nextVersion = options.exact;
} else {
const { releaseType, preId } = options as BumpOptions;
- console.log(`📈 Release type selected: ${chalk.green(releaseType)}`);
+ console.log(`📈 Release type selected: ${picocolors.green(releaseType)}`);
if (preId) {
- console.log(`🆔 Version prerelease identifier selected: ${chalk.yellow(preId)}`);
+ console.log(`🆔 Version prerelease identifier selected: ${picocolors.yellow(preId)}`);
}
nextVersion = semver.inc(currentVersion, releaseType, preId);
console.log(
- `⏭ Bumping version ${chalk.blue(currentVersion)} with release type ${chalk.green(
+ `⏭ Bumping version ${picocolors.blue(currentVersion)} with release type ${picocolors.green(
releaseType
)}${
- preId ? ` and ${chalk.yellow(preId)}` : ''
- } results in version: ${chalk.bgGreenBright.bold(nextVersion)}`
+ preId ? ` and ${picocolors.yellow(preId)}` : ''
+ } results in version: ${picocolors.bold(picocolors.greenBright(nextVersion))}`
);
}
if ('deferred' in options && options.deferred) {
await bumpDeferred(nextVersion);
} else {
- console.log(`⏭ Bumping all packages to ${chalk.blue(nextVersion)}...`);
+ console.log(`⏭ Bumping all packages to ${picocolors.blue(nextVersion)}...`);
await bumpCodeVersion(nextVersion);
await bumpVersionSources(currentVersion, nextVersion);
await bumpAllPackageJsons({ packages, nextVersion, verbose });
- console.log(`⬆️ Updating lock file with ${chalk.blue('yarn install --mode=update-lockfile')}`);
+ console.log(
+ `⬆️ Updating lock file with ${picocolors.blue('yarn install --mode=update-lockfile')}`
+ );
await execaCommand(`yarn install --mode=update-lockfile`, {
cwd: join(CODE_DIR_PATH),
stdio: verbose ? 'inherit' : undefined,
cleanup: true,
});
- console.log(`✅ Updated lock file with ${chalk.blue('yarn install --mode=update-lockfile')}`);
+ console.log(
+ `✅ Updated lock file with ${picocolors.blue('yarn install --mode=update-lockfile')}`
+ );
}
if (process.env.GITHUB_ACTIONS === 'true') {
diff --git a/scripts/release/write-changelog.ts b/scripts/release/write-changelog.ts
index 8f927b3c6434..59e0d3273958 100644
--- a/scripts/release/write-changelog.ts
+++ b/scripts/release/write-changelog.ts
@@ -1,8 +1,8 @@
import { join } from 'node:path';
-import chalk from 'chalk';
import { program } from 'commander';
import { readFile, writeFile, writeJson } from 'fs-extra';
+import picocolors from 'picocolors';
import semver from 'semver';
import { z } from 'zod';
@@ -47,7 +47,7 @@ const validateOptions = (args: unknown[], options: { [key: string]: any }): opti
optionsSchema.parse(options);
if (args.length !== 1 || !semver.valid(args[0] as string)) {
console.error(
- `🚨 Invalid arguments, expected a single argument with the version to generate changelog for, eg. ${chalk.green(
+ `🚨 Invalid arguments, expected a single argument with the version to generate changelog for, eg. ${picocolors.green(
'7.1.0-beta.8'
)}`
);
@@ -70,7 +70,7 @@ const writeToChangelogFile = async ({
const changelogPath = join(__dirname, '..', '..', changelogFilename);
if (verbose) {
- console.log(`📝 Writing changelog to ${chalk.blue(changelogPath)}`);
+ console.log(`📝 Writing changelog to ${picocolors.blue(changelogPath)}`);
}
const currentChangelog = await readFile(changelogPath, 'utf-8');
@@ -93,7 +93,7 @@ const writeToDocsVersionFile = async ({
const filepath = join(__dirname, '..', '..', 'docs', 'versions', filename);
if (verbose) {
- console.log(`📝 Writing changelog to ${chalk.blue(filepath)}`);
+ console.log(`📝 Writing changelog to ${picocolors.blue(filepath)}`);
}
const textWithoutHeading = changelogText.split('\n').slice(2).join('\n').replaceAll('"', '\\"');
@@ -116,9 +116,9 @@ export const run = async (args: unknown[], options: unknown) => {
const version = args[0] as string;
console.log(
- `💬 Generating changelog for ${chalk.blue(version)} between ${chalk.green(
+ `💬 Generating changelog for ${picocolors.blue(version)} between ${picocolors.green(
from || 'latest'
- )} and ${chalk.green(to || 'HEAD')}`
+ )} and ${picocolors.green(to || 'HEAD')}`
);
const { changelogText } = await getChanges({ version, from, to, unpickedPatches, verbose });
diff --git a/scripts/run-registry.ts b/scripts/run-registry.ts
index 4f837b952794..825788b24f51 100755
--- a/scripts/run-registry.ts
+++ b/scripts/run-registry.ts
@@ -4,11 +4,11 @@ import http from 'node:http';
import type { Server } from 'node:http';
import { join, resolve as resolvePath } from 'node:path';
-import chalk from 'chalk';
import { program } from 'commander';
import { execa, execaSync } from 'execa';
import { pathExists, readJSON, remove } from 'fs-extra';
import pLimit from 'p-limit';
+import picocolors from 'picocolors';
import { parseConfigFile, runServer } from 'verdaccio';
import { maxConcurrentTasks } from './utils/concurrency';
@@ -197,7 +197,9 @@ const run = async () => {
}
);
- logger.log(`📦 found ${packages.length} storybook packages at version ${chalk.blue(version)}`);
+ logger.log(
+ `📦 found ${packages.length} storybook packages at version ${picocolors.blue(version)}`
+ );
if (opts.publish) {
await publish(packages, 'http://localhost:6002');
diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts
index 07c518a9986e..a2df1e8e1691 100644
--- a/scripts/tasks/sandbox-parts.ts
+++ b/scripts/tasks/sandbox-parts.ts
@@ -1,5 +1,6 @@
// This file requires many imports from `../code`, which requires both an install and bootstrap of
// the repo to work properly. So we load it async in the task runner *after* those steps.
+import { isFunction } from 'es-toolkit';
import {
copy,
ensureDir,
@@ -12,7 +13,6 @@ import {
writeJson,
} from 'fs-extra';
import JSON5 from 'json5';
-import { isFunction } from 'lodash';
import { createRequire } from 'module';
import { join, relative, resolve, sep } from 'path';
import slash from 'slash';
@@ -717,7 +717,7 @@ export const extendMain: Task['run'] = async ({ template, sandboxDir, key }, { d
addRefs(mainConfig);
}
- const templateConfig = isFunction(template.modifications?.mainConfig)
+ const templateConfig: any = isFunction(template.modifications?.mainConfig)
? template.modifications?.mainConfig(mainConfig)
: template.modifications?.mainConfig || {};
const configToAdd = {
diff --git a/scripts/utils/exec.ts b/scripts/utils/exec.ts
index 2998e4877acb..c12c9eea2f9f 100644
--- a/scripts/utils/exec.ts
+++ b/scripts/utils/exec.ts
@@ -1,6 +1,6 @@
-import chalk from 'chalk';
import type { ExecaChildProcess, Options } from 'execa';
import { execa } from 'execa';
+import picocolors from 'picocolors';
const logger = console;
@@ -50,7 +50,7 @@ export const exec = async (
}
} catch (err) {
if (!(typeof err === 'object' && 'killed' in err && err.killed)) {
- logger.error(chalk.red(`An error occurred while executing: \`${command}\``));
+ logger.error(picocolors.red(`An error occurred while executing: \`${command}\``));
logger.log(`${errorMessage}\n`);
}
diff --git a/scripts/utils/options.ts b/scripts/utils/options.ts
index 5add8c25dd29..f3caf64e28b2 100644
--- a/scripts/utils/options.ts
+++ b/scripts/utils/options.ts
@@ -1,8 +1,7 @@
/** Use commander and prompts to gather a list of options for a script */
-import chalk from 'chalk';
import { type Command, type Option as CommanderOption, program } from 'commander';
-// eslint-disable-next-line import/extensions
-import kebabCase from 'lodash/kebabCase.js';
+import { kebabCase } from 'es-toolkit/compat';
+import picocolors from 'picocolors';
import prompts from 'prompts';
import type { Falsy, PrevCaller, PromptObject, PromptType } from 'prompts';
import { dedent } from 'ts-dedent';
@@ -131,9 +130,9 @@ export function getOptions(
const checkStringValue = (raw: string) => {
if (option.values && !option.values.includes(raw)) {
- const possibleOptions = chalk.cyan(option.values.join('\n'));
+ const possibleOptions = picocolors.cyan(option.values.join('\n'));
throw new Error(
- dedent`Unexpected value '${chalk.yellow(raw)}' for option '${chalk.magenta(key)}'.
+ dedent`Unexpected value '${picocolors.yellow(raw)}' for option '${picocolors.magenta(key)}'.
These are the possible options:
${possibleOptions}\n\n`
diff --git a/scripts/yarn.lock b/scripts/yarn.lock
index 204801ac47ac..a17539cb16d5 100644
--- a/scripts/yarn.lock
+++ b/scripts/yarn.lock
@@ -263,170 +263,170 @@ __metadata:
languageName: node
linkType: hard
-"@esbuild/aix-ppc64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/aix-ppc64@npm:0.23.0"
+"@esbuild/aix-ppc64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/aix-ppc64@npm:0.24.0"
conditions: os=aix & cpu=ppc64
languageName: node
linkType: hard
-"@esbuild/android-arm64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/android-arm64@npm:0.23.0"
+"@esbuild/android-arm64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/android-arm64@npm:0.24.0"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/android-arm@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/android-arm@npm:0.23.0"
+"@esbuild/android-arm@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/android-arm@npm:0.24.0"
conditions: os=android & cpu=arm
languageName: node
linkType: hard
-"@esbuild/android-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/android-x64@npm:0.23.0"
+"@esbuild/android-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/android-x64@npm:0.24.0"
conditions: os=android & cpu=x64
languageName: node
linkType: hard
-"@esbuild/darwin-arm64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/darwin-arm64@npm:0.23.0"
+"@esbuild/darwin-arm64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/darwin-arm64@npm:0.24.0"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/darwin-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/darwin-x64@npm:0.23.0"
+"@esbuild/darwin-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/darwin-x64@npm:0.24.0"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
-"@esbuild/freebsd-arm64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/freebsd-arm64@npm:0.23.0"
+"@esbuild/freebsd-arm64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/freebsd-arm64@npm:0.24.0"
conditions: os=freebsd & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/freebsd-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/freebsd-x64@npm:0.23.0"
+"@esbuild/freebsd-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/freebsd-x64@npm:0.24.0"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
-"@esbuild/linux-arm64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-arm64@npm:0.23.0"
+"@esbuild/linux-arm64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-arm64@npm:0.24.0"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/linux-arm@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-arm@npm:0.23.0"
+"@esbuild/linux-arm@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-arm@npm:0.24.0"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
-"@esbuild/linux-ia32@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-ia32@npm:0.23.0"
+"@esbuild/linux-ia32@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-ia32@npm:0.24.0"
conditions: os=linux & cpu=ia32
languageName: node
linkType: hard
-"@esbuild/linux-loong64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-loong64@npm:0.23.0"
+"@esbuild/linux-loong64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-loong64@npm:0.24.0"
conditions: os=linux & cpu=loong64
languageName: node
linkType: hard
-"@esbuild/linux-mips64el@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-mips64el@npm:0.23.0"
+"@esbuild/linux-mips64el@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-mips64el@npm:0.24.0"
conditions: os=linux & cpu=mips64el
languageName: node
linkType: hard
-"@esbuild/linux-ppc64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-ppc64@npm:0.23.0"
+"@esbuild/linux-ppc64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-ppc64@npm:0.24.0"
conditions: os=linux & cpu=ppc64
languageName: node
linkType: hard
-"@esbuild/linux-riscv64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-riscv64@npm:0.23.0"
+"@esbuild/linux-riscv64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-riscv64@npm:0.24.0"
conditions: os=linux & cpu=riscv64
languageName: node
linkType: hard
-"@esbuild/linux-s390x@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-s390x@npm:0.23.0"
+"@esbuild/linux-s390x@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-s390x@npm:0.24.0"
conditions: os=linux & cpu=s390x
languageName: node
linkType: hard
-"@esbuild/linux-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/linux-x64@npm:0.23.0"
+"@esbuild/linux-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/linux-x64@npm:0.24.0"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
-"@esbuild/netbsd-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/netbsd-x64@npm:0.23.0"
+"@esbuild/netbsd-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/netbsd-x64@npm:0.24.0"
conditions: os=netbsd & cpu=x64
languageName: node
linkType: hard
-"@esbuild/openbsd-arm64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/openbsd-arm64@npm:0.23.0"
+"@esbuild/openbsd-arm64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/openbsd-arm64@npm:0.24.0"
conditions: os=openbsd & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/openbsd-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/openbsd-x64@npm:0.23.0"
+"@esbuild/openbsd-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/openbsd-x64@npm:0.24.0"
conditions: os=openbsd & cpu=x64
languageName: node
linkType: hard
-"@esbuild/sunos-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/sunos-x64@npm:0.23.0"
+"@esbuild/sunos-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/sunos-x64@npm:0.24.0"
conditions: os=sunos & cpu=x64
languageName: node
linkType: hard
-"@esbuild/win32-arm64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/win32-arm64@npm:0.23.0"
+"@esbuild/win32-arm64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/win32-arm64@npm:0.24.0"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/win32-ia32@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/win32-ia32@npm:0.23.0"
+"@esbuild/win32-ia32@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/win32-ia32@npm:0.24.0"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
-"@esbuild/win32-x64@npm:0.23.0":
- version: 0.23.0
- resolution: "@esbuild/win32-x64@npm:0.23.0"
+"@esbuild/win32-x64@npm:0.24.0":
+ version: 0.24.0
+ resolution: "@esbuild/win32-x64@npm:0.24.0"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
@@ -768,7 +768,7 @@ __metadata:
languageName: node
linkType: hard
-"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8":
+"@nodelib/fs.walk@npm:1.2.8, @nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8":
version: 1.2.8
resolution: "@nodelib/fs.walk@npm:1.2.8"
dependencies:
@@ -1459,6 +1459,19 @@ __metadata:
languageName: node
linkType: hard
+"@snyk/github-codeowners@npm:1.1.0":
+ version: 1.1.0
+ resolution: "@snyk/github-codeowners@npm:1.1.0"
+ dependencies:
+ commander: "npm:^4.1.1"
+ ignore: "npm:^5.1.8"
+ p-map: "npm:^4.0.0"
+ bin:
+ github-codeowners: dist/cli.js
+ checksum: 10c0/92d860a904a1e67f8563d4ac4d540cc613f71193f7968933b4a4b1526e80a97f536f52d27762c158e3e39d48c2f3db4906ec78846309351c741abb1a28653af9
+ languageName: node
+ linkType: hard
+
"@storybook/csf@npm:^0.0.1":
version: 0.0.1
resolution: "@storybook/csf@npm:0.0.1"
@@ -1529,7 +1542,6 @@ __metadata:
"@types/fs-extra": "npm:^11.0.4"
"@types/http-server": "npm:^0.12.4"
"@types/jest": "npm:^29.5.12"
- "@types/lodash": "npm:^4.17.6"
"@types/node": "npm:^22.0.0"
"@types/prettier": "npm:^3.0.0"
"@types/pretty-hrtime": "npm:^1.0.3"
@@ -1548,7 +1560,6 @@ __metadata:
"@vitest/coverage-v8": "npm:^2.0.5"
ansi-regex: "npm:^6.0.1"
browser-assert: "npm:^1.2.1"
- chalk: "npm:^4.1.0"
chromatic: "npm:^11.5.5"
codecov: "npm:^3.8.1"
commander: "npm:^12.1.0"
@@ -1559,7 +1570,8 @@ __metadata:
detect-port: "npm:^1.6.1"
ejs: "npm:^3.1.10"
ejs-lint: "npm:^2.0.0"
- esbuild: "npm:^0.23.0"
+ es-toolkit: "npm:^1.21.0"
+ esbuild: "npm:^0.24.0"
esbuild-plugin-alias: "npm:^0.2.1"
eslint: "npm:^8.57.0"
eslint-config-airbnb-typescript: "npm:^18.0.0"
@@ -1587,14 +1599,16 @@ __metadata:
jiti: "npm:^1.21.6"
json5: "npm:^2.2.3"
junit-xml: "npm:^1.2.0"
+ knip: "npm:^5.30.1"
lint-staged: "npm:^15.2.7"
- lodash: "npm:^4.17.21"
memoizerific: "npm:^1.11.3"
+ minimatch: "npm:^10.0.1"
node-gyp: "npm:^9.3.1"
nx: "npm:18.0.6"
ora: "npm:^5.4.1"
p-limit: "npm:^3.1.0"
p-retry: "npm:^5.1.2"
+ picocolors: "npm:^1.1.0"
playwright: "npm:1.46.0"
playwright-core: "npm:1.46.0"
prettier: "npm:^3.3.2"
@@ -1973,7 +1987,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/lodash@npm:^4.14.175, @types/lodash@npm:^4.17.6":
+"@types/lodash@npm:^4.14.175":
version: 4.17.6
resolution: "@types/lodash@npm:4.17.6"
checksum: 10c0/3b197ac47af9443fee8c4719c5ffde527d7febc018b827d44a6bc2523c728c7adfdd25196fdcfe3eed827993e0c41a917d0da6e78938b18b2be94164789f1117
@@ -4164,7 +4178,7 @@ __metadata:
languageName: node
linkType: hard
-"commander@npm:^4.0.0":
+"commander@npm:^4.0.0, commander@npm:^4.1.1":
version: 4.1.1
resolution: "commander@npm:4.1.1"
checksum: 10c0/84a76c08fe6cc08c9c93f62ac573d2907d8e79138999312c92d4155bc2325d487d64d13f669b2000c9f8caf70493c1be2dac74fec3c51d5a04f8bc3ae1830bab
@@ -4952,6 +4966,19 @@ __metadata:
languageName: node
linkType: hard
+"easy-table@npm:1.2.0":
+ version: 1.2.0
+ resolution: "easy-table@npm:1.2.0"
+ dependencies:
+ ansi-regex: "npm:^5.0.1"
+ wcwidth: "npm:^1.0.1"
+ dependenciesMeta:
+ wcwidth:
+ optional: true
+ checksum: 10c0/2d37937cd608586ba02e1ec479f90ccec581d366b3b0d1bb26b99ee6005f8d724e32a07a873759893461ca45b99e2d08c30326529d967ce9eedc1e9b68d4aa63
+ languageName: node
+ linkType: hard
+
"ecc-jsbn@npm:~0.1.1":
version: 0.1.2
resolution: "ecc-jsbn@npm:0.1.2"
@@ -5071,6 +5098,16 @@ __metadata:
languageName: node
linkType: hard
+"enhanced-resolve@npm:^5.17.1":
+ version: 5.17.1
+ resolution: "enhanced-resolve@npm:5.17.1"
+ dependencies:
+ graceful-fs: "npm:^4.2.4"
+ tapable: "npm:^2.2.0"
+ checksum: 10c0/81a0515675eca17efdba2cf5bad87abc91a528fc1191aad50e275e74f045b41506167d420099022da7181c8d787170ea41e4a11a0b10b7a16f6237daecb15370
+ languageName: node
+ linkType: hard
+
"enquirer@npm:~2.3.6":
version: 2.3.6
resolution: "enquirer@npm:2.3.6"
@@ -5272,6 +5309,13 @@ __metadata:
languageName: node
linkType: hard
+"es-toolkit@npm:^1.21.0":
+ version: 1.21.0
+ resolution: "es-toolkit@npm:1.21.0"
+ checksum: 10c0/894a63f8ce5b2e5c1be242c8e8eace6364ea1212d01cdf89594d2cc582c5e1574114ad2ee7022ad5206561c4d5170511d83b38853257249860e56178768854ea
+ languageName: node
+ linkType: hard
+
"esbuild-plugin-alias@npm:^0.2.1":
version: 0.2.1
resolution: "esbuild-plugin-alias@npm:0.2.1"
@@ -5279,34 +5323,34 @@ __metadata:
languageName: node
linkType: hard
-"esbuild@npm:^0.23.0":
- version: 0.23.0
- resolution: "esbuild@npm:0.23.0"
- dependencies:
- "@esbuild/aix-ppc64": "npm:0.23.0"
- "@esbuild/android-arm": "npm:0.23.0"
- "@esbuild/android-arm64": "npm:0.23.0"
- "@esbuild/android-x64": "npm:0.23.0"
- "@esbuild/darwin-arm64": "npm:0.23.0"
- "@esbuild/darwin-x64": "npm:0.23.0"
- "@esbuild/freebsd-arm64": "npm:0.23.0"
- "@esbuild/freebsd-x64": "npm:0.23.0"
- "@esbuild/linux-arm": "npm:0.23.0"
- "@esbuild/linux-arm64": "npm:0.23.0"
- "@esbuild/linux-ia32": "npm:0.23.0"
- "@esbuild/linux-loong64": "npm:0.23.0"
- "@esbuild/linux-mips64el": "npm:0.23.0"
- "@esbuild/linux-ppc64": "npm:0.23.0"
- "@esbuild/linux-riscv64": "npm:0.23.0"
- "@esbuild/linux-s390x": "npm:0.23.0"
- "@esbuild/linux-x64": "npm:0.23.0"
- "@esbuild/netbsd-x64": "npm:0.23.0"
- "@esbuild/openbsd-arm64": "npm:0.23.0"
- "@esbuild/openbsd-x64": "npm:0.23.0"
- "@esbuild/sunos-x64": "npm:0.23.0"
- "@esbuild/win32-arm64": "npm:0.23.0"
- "@esbuild/win32-ia32": "npm:0.23.0"
- "@esbuild/win32-x64": "npm:0.23.0"
+"esbuild@npm:^0.24.0":
+ version: 0.24.0
+ resolution: "esbuild@npm:0.24.0"
+ dependencies:
+ "@esbuild/aix-ppc64": "npm:0.24.0"
+ "@esbuild/android-arm": "npm:0.24.0"
+ "@esbuild/android-arm64": "npm:0.24.0"
+ "@esbuild/android-x64": "npm:0.24.0"
+ "@esbuild/darwin-arm64": "npm:0.24.0"
+ "@esbuild/darwin-x64": "npm:0.24.0"
+ "@esbuild/freebsd-arm64": "npm:0.24.0"
+ "@esbuild/freebsd-x64": "npm:0.24.0"
+ "@esbuild/linux-arm": "npm:0.24.0"
+ "@esbuild/linux-arm64": "npm:0.24.0"
+ "@esbuild/linux-ia32": "npm:0.24.0"
+ "@esbuild/linux-loong64": "npm:0.24.0"
+ "@esbuild/linux-mips64el": "npm:0.24.0"
+ "@esbuild/linux-ppc64": "npm:0.24.0"
+ "@esbuild/linux-riscv64": "npm:0.24.0"
+ "@esbuild/linux-s390x": "npm:0.24.0"
+ "@esbuild/linux-x64": "npm:0.24.0"
+ "@esbuild/netbsd-x64": "npm:0.24.0"
+ "@esbuild/openbsd-arm64": "npm:0.24.0"
+ "@esbuild/openbsd-x64": "npm:0.24.0"
+ "@esbuild/sunos-x64": "npm:0.24.0"
+ "@esbuild/win32-arm64": "npm:0.24.0"
+ "@esbuild/win32-ia32": "npm:0.24.0"
+ "@esbuild/win32-x64": "npm:0.24.0"
dependenciesMeta:
"@esbuild/aix-ppc64":
optional: true
@@ -5358,7 +5402,7 @@ __metadata:
optional: true
bin:
esbuild: bin/esbuild
- checksum: 10c0/08c148c067795165798c0467ce02d2d1ecedc096989bded5f0d795c61a1fcbec6c14d0a3c9f4ad6185cc29ec52087acaa335ed6d98be6ad57f7fa4264626bde0
+ checksum: 10c0/9f1aadd8d64f3bff422ae78387e66e51a5e09de6935a6f987b6e4e189ed00fdc2d1bc03d2e33633b094008529c8b6e06c7ad1a9782fb09fec223bf95998c0683
languageName: node
linkType: hard
@@ -6853,7 +6897,7 @@ __metadata:
languageName: node
linkType: hard
-"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.1.3, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9":
+"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.1.3, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9":
version: 4.2.11
resolution: "graceful-fs@npm:4.2.11"
checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2
@@ -7299,10 +7343,10 @@ __metadata:
languageName: node
linkType: hard
-"ignore@npm:^5.0.0, ignore@npm:^5.0.4, ignore@npm:^5.0.5, ignore@npm:^5.1.1, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.1":
- version: 5.3.1
- resolution: "ignore@npm:5.3.1"
- checksum: 10c0/703f7f45ffb2a27fb2c5a8db0c32e7dee66b33a225d28e8db4e1be6474795f606686a6e3bcc50e1aa12f2042db4c9d4a7d60af3250511de74620fbed052ea4cd
+"ignore@npm:^5.0.0, ignore@npm:^5.0.4, ignore@npm:^5.0.5, ignore@npm:^5.1.1, ignore@npm:^5.1.8, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.1":
+ version: 5.3.2
+ resolution: "ignore@npm:5.3.2"
+ checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337
languageName: node
linkType: hard
@@ -8368,6 +8412,36 @@ __metadata:
languageName: node
linkType: hard
+"knip@npm:^5.30.1":
+ version: 5.30.1
+ resolution: "knip@npm:5.30.1"
+ dependencies:
+ "@nodelib/fs.walk": "npm:1.2.8"
+ "@snyk/github-codeowners": "npm:1.1.0"
+ easy-table: "npm:1.2.0"
+ enhanced-resolve: "npm:^5.17.1"
+ fast-glob: "npm:^3.3.2"
+ jiti: "npm:^1.21.6"
+ js-yaml: "npm:^4.1.0"
+ minimist: "npm:^1.2.8"
+ picocolors: "npm:^1.0.0"
+ picomatch: "npm:^4.0.1"
+ pretty-ms: "npm:^9.0.0"
+ smol-toml: "npm:^1.1.4"
+ strip-json-comments: "npm:5.0.1"
+ summary: "npm:2.1.0"
+ zod: "npm:^3.22.4"
+ zod-validation-error: "npm:^3.0.3"
+ peerDependencies:
+ "@types/node": ">=18"
+ typescript: ">=5.0.4"
+ bin:
+ knip: bin/knip.js
+ knip-bun: bin/knip-bun.js
+ checksum: 10c0/1c470266d7967dad0f2fb1b1f6c8a797bc7bea6c1f0021c52dce40e067ae87217eb17ff11a4164e3a5244bf3d0672f9e8678e4c3898260c48b9c020132ebb29a
+ languageName: node
+ linkType: hard
+
"language-subtag-registry@npm:^0.3.20":
version: 0.3.22
resolution: "language-subtag-registry@npm:0.3.22"
@@ -9593,6 +9667,15 @@ __metadata:
languageName: node
linkType: hard
+"minimatch@npm:^10.0.1":
+ version: 10.0.1
+ resolution: "minimatch@npm:10.0.1"
+ dependencies:
+ brace-expansion: "npm:^2.0.1"
+ checksum: 10c0/e6c29a81fe83e1877ad51348306be2e8aeca18c88fdee7a99df44322314279e15799e41d7cb274e4e8bb0b451a3bc622d6182e157dfa1717d6cda75e9cd8cd5d
+ languageName: node
+ linkType: hard
+
"minimatch@npm:^5.0.1":
version: 5.1.6
resolution: "minimatch@npm:5.1.6"
@@ -10544,6 +10627,13 @@ __metadata:
languageName: node
linkType: hard
+"parse-ms@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "parse-ms@npm:4.0.0"
+ checksum: 10c0/a7900f4f1ebac24cbf5e9708c16fb2fd482517fad353aecd7aefb8c2ba2f85ce017913ccb8925d231770404780df46244ea6fec598b3bde6490882358b4d2d16
+ languageName: node
+ linkType: hard
+
"parse-passwd@npm:^1.0.0":
version: 1.0.0
resolution: "parse-passwd@npm:1.0.0"
@@ -10672,10 +10762,10 @@ __metadata:
languageName: node
linkType: hard
-"picocolors@npm:^1.0.0":
- version: 1.0.0
- resolution: "picocolors@npm:1.0.0"
- checksum: 10c0/20a5b249e331c14479d94ec6817a182fd7a5680debae82705747b2db7ec50009a5f6648d0621c561b0572703f84dbef0858abcbd5856d3c5511426afcb1961f7
+"picocolors@npm:^1.0.0, picocolors@npm:^1.1.0":
+ version: 1.1.0
+ resolution: "picocolors@npm:1.1.0"
+ checksum: 10c0/86946f6032148801ef09c051c6fb13b5cf942eaf147e30ea79edb91dd32d700934edebe782a1078ff859fb2b816792e97ef4dab03d7f0b804f6b01a0df35e023
languageName: node
linkType: hard
@@ -10686,6 +10776,13 @@ __metadata:
languageName: node
linkType: hard
+"picomatch@npm:^4.0.1":
+ version: 4.0.2
+ resolution: "picomatch@npm:4.0.2"
+ checksum: 10c0/7c51f3ad2bb42c776f49ebf964c644958158be30d0a510efd5a395e8d49cb5acfed5b82c0c5b365523ce18e6ab85013c9ebe574f60305892ec3fa8eee8304ccc
+ languageName: node
+ linkType: hard
+
"pidtree@npm:~0.6.0":
version: 0.6.0
resolution: "pidtree@npm:0.6.0"
@@ -11069,6 +11166,15 @@ __metadata:
languageName: node
linkType: hard
+"pretty-ms@npm:^9.0.0":
+ version: 9.1.0
+ resolution: "pretty-ms@npm:9.1.0"
+ dependencies:
+ parse-ms: "npm:^4.0.0"
+ checksum: 10c0/fd111aad8800a04dfd654e6016da69bdaa6fc6a4c280f8e727cffd8b5960558e94942f1a94d4aa6e4d179561a0fbb0366a9ebe0ccefbbb0f8ff853b129cdefb9
+ languageName: node
+ linkType: hard
+
"prettyjson@npm:^1.2.1":
version: 1.2.5
resolution: "prettyjson@npm:1.2.5"
@@ -12486,6 +12592,13 @@ __metadata:
languageName: node
linkType: hard
+"smol-toml@npm:^1.1.4":
+ version: 1.3.0
+ resolution: "smol-toml@npm:1.3.0"
+ checksum: 10c0/442b4d033236ff6dd05bf91d57695fd9070a8221af080a5b2782cb2d9fad8bc31f698c61de5308a351907c1200202ba3ee51d52c5704f5349149e7c374f5fe90
+ languageName: node
+ linkType: hard
+
"socks-proxy-agent@npm:^7.0.0":
version: 7.0.0
resolution: "socks-proxy-agent@npm:7.0.0"
@@ -12960,6 +13073,13 @@ __metadata:
languageName: node
linkType: hard
+"strip-json-comments@npm:5.0.1":
+ version: 5.0.1
+ resolution: "strip-json-comments@npm:5.0.1"
+ checksum: 10c0/c9d9d55a0167c57aa688df3aa20628cf6f46f0344038f189eaa9d159978e80b2bfa6da541a40d83f7bde8a3554596259bf6b70578b2172356536a0e3fa5a0982
+ languageName: node
+ linkType: hard
+
"strip-json-comments@npm:^3.1.1":
version: 3.1.1
resolution: "strip-json-comments@npm:3.1.1"
@@ -13005,6 +13125,13 @@ __metadata:
languageName: node
linkType: hard
+"summary@npm:2.1.0":
+ version: 2.1.0
+ resolution: "summary@npm:2.1.0"
+ checksum: 10c0/2743c1f940fb303c496ef1b085e654704a6c16872957b6b76648c34bd32c8f0b7a3c5ec4e0f8bfb71dcb8473e34d172fef31026b85562af589cf220aa901698d
+ languageName: node
+ linkType: hard
+
"supports-color@npm:^5.0.0, supports-color@npm:^5.3.0":
version: 5.5.0
resolution: "supports-color@npm:5.5.0"
@@ -13066,6 +13193,13 @@ __metadata:
languageName: node
linkType: hard
+"tapable@npm:^2.2.0":
+ version: 2.2.1
+ resolution: "tapable@npm:2.2.1"
+ checksum: 10c0/bc40e6efe1e554d075469cedaba69a30eeb373552aaf41caeaaa45bf56ffacc2674261b106245bd566b35d8f3329b52d838e851ee0a852120acae26e622925c9
+ languageName: node
+ linkType: hard
+
"tar-stream@npm:^1.5.2":
version: 1.6.2
resolution: "tar-stream@npm:1.6.2"
@@ -14833,7 +14967,16 @@ __metadata:
languageName: node
linkType: hard
-"zod@npm:^3.23.8":
+"zod-validation-error@npm:^3.0.3":
+ version: 3.3.1
+ resolution: "zod-validation-error@npm:3.3.1"
+ peerDependencies:
+ zod: ^3.18.0
+ checksum: 10c0/53869a8478f42cd38f51e159431fe7af9e0b456e8078c6d9d906adb212753788defa9c8bd7374e9ecd4a688b6736fcfa091aebac65054328b8cfdecce9395d8e
+ languageName: node
+ linkType: hard
+
+"zod@npm:^3.22.4, zod@npm:^3.23.8":
version: 3.23.8
resolution: "zod@npm:3.23.8"
checksum: 10c0/8f14c87d6b1b53c944c25ce7a28616896319d95bc46a9660fe441adc0ed0a81253b02b5abdaeffedbeb23bdd25a0bf1c29d2c12dd919aef6447652dd295e3e69