From 64fbe39ad01c17cff9ea4c18c69bb25509fc6b3b Mon Sep 17 00:00:00 2001 From: "yevhenii.honcharenko" Date: Tue, 3 Sep 2024 14:36:31 +0300 Subject: [PATCH 01/58] NextJS: fix sass-loader version for compatibility with nodejs > 20 --- code/frameworks/nextjs/package.json | 2 +- code/yarn.lock | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json index e0f5cbb511e0..7a10bfa6e8e3 100644 --- a/code/frameworks/nextjs/package.json +++ b/code/frameworks/nextjs/package.json @@ -157,7 +157,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.1", diff --git a/code/yarn.lock b/code/yarn.lock index 83b4b2ba2bdc..358cfc7dee3c 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6332,7 +6332,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" @@ -25159,15 +25159,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 @@ -25180,7 +25179,7 @@ __metadata: optional: true sass-embedded: optional: true - checksum: 10c0/e1ef655f3898cc4c45f02b3c627f8baf998139993a9a79c524153a80814282bfe20d8d8d703b8cf1d05457c1930940b65e2156d11285ed0861f9a1016f993e53 + checksum: 10c0/5e955a4ffce35ee0a46fce677ce51eaa69587fb5371978588c83af00f49e7edc36dcf3bb559cbae27681c5e24a71284463ebe03a1fb65e6ecafa1db0620e3fc8 languageName: node linkType: hard From aff35807c5096eb8dcd4950b031007e28e686871 Mon Sep 17 00:00:00 2001 From: Alexander Chudesnov Date: Wed, 4 Sep 2024 19:28:35 +0200 Subject: [PATCH 02/58] Replace .at() call with [] in Vite Builder codegen Unblocks loading Storybook instances built with Vite devices with pre-2022 browser versions. Resolves #29030 --- code/builders/builder-vite/src/codegen-modern-iframe-script.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..700adefffddb 100644 --- a/code/builders/builder-vite/src/codegen-modern-iframe-script.ts +++ b/code/builders/builder-vite/src/codegen-modern-iframe-script.ts @@ -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); From 6f19cac299d25ab3d935124c341522d05cf18923 Mon Sep 17 00:00:00 2001 From: Michael Cebrian Date: Sat, 7 Sep 2024 06:02:59 -0400 Subject: [PATCH 03/58] Fix Angular sourceDecorator to apply excludeDecorators flag --- .../frameworks/angular/src/client/docs/sourceDecorator.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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; From 651674adcd313940f9c3f7134f591bc942226ddc Mon Sep 17 00:00:00 2001 From: kazuyainoue0124 <88877589+kazuyainoue0124@users.noreply.github.com> Date: Thu, 12 Sep 2024 08:21:25 +0900 Subject: [PATCH 04/58] api.md typo --- code/addons/themes/docs/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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'; From 4af51ac4c2d184fc4a4eba85a1bcd5c931dda23d Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Thu, 12 Sep 2024 15:13:51 +0200 Subject: [PATCH 05/58] Use ansi-to-html for colored test errors --- code/addons/interactions/package.json | 1 + .../interactions/src/components/Interaction.tsx | 5 +++-- .../src/components/InteractionsPanel.tsx | 12 ++++++++---- .../src/components/MatcherResult.tsx | 10 +++++++++- code/addons/interactions/src/utils.ts | 16 ++++++++++++++++ code/yarn.lock | 1 + 6 files changed, 38 insertions(+), 7 deletions(-) diff --git a/code/addons/interactions/package.json b/code/addons/interactions/package.json index dd30fb2650d4..9deb3a01105f 100644 --- a/code/addons/interactions/package.json +++ b/code/addons/interactions/package.json @@ -62,6 +62,7 @@ "@devtools-ds/object-inspector": "^1.1.2", "@storybook/icons": "^1.2.5", "@types/node": "^22.0.0", + "ansi-to-html": "^0.7.2", "formik": "^2.2.9", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/code/addons/interactions/src/components/Interaction.tsx b/code/addons/interactions/src/components/Interaction.tsx index c9b557cddc68..610d0c1b032b 100644 --- a/code/addons/interactions/src/components/Interaction.tsx +++ b/code/addons/interactions/src/components/Interaction.tsx @@ -8,7 +8,7 @@ import { type Call, CallStates, type ControlStates } from '@storybook/instrument import { transparentize } from 'polished'; -import { isChaiError, isJestError } from '../utils'; +import { isChaiError, isJestError, useAnsiToHtmlFilter } from '../utils'; import type { Controls } from './InteractionsPanel'; import { MatcherResult } from './MatcherResult'; import { MethodCall } from './MethodCall'; @@ -116,6 +116,7 @@ const RowMessage = styled('div')(({ theme }) => ({ })); export const Exception = ({ exception }: { exception: Call['exception'] }) => { + const filter = useAnsiToHtmlFilter(); if (isJestError(exception)) { return ; } @@ -135,7 +136,7 @@ export const Exception = ({ exception }: { exception: Call['exception'] }) => { const more = paragraphs.length > 1; return ( -
{paragraphs[0]}
+

       {more && 

See the full stack trace in the browser console.

}
); diff --git a/code/addons/interactions/src/components/InteractionsPanel.tsx b/code/addons/interactions/src/components/InteractionsPanel.tsx index c86a1df8ffde..643732f250ca 100644 --- a/code/addons/interactions/src/components/InteractionsPanel.tsx +++ b/code/addons/interactions/src/components/InteractionsPanel.tsx @@ -6,7 +6,7 @@ import { type Call, CallStates, type ControlStates } from '@storybook/instrument import { transparentize } from 'polished'; -import { isTestAssertionError } from '../utils'; +import { isTestAssertionError, useAnsiToHtmlFilter } from '../utils'; import { Empty } from './EmptyState'; import { Interaction } from './Interaction'; import { Subnav } from './Subnav'; @@ -97,6 +97,7 @@ export const InteractionsPanel: React.FC = React.memo( onScrollToEnd, endRef, }) { + const filter = useAnsiToHtmlFilter(); return ( {(interactions.length > 0 || hasException) && ( @@ -131,9 +132,12 @@ export const InteractionsPanel: React.FC = 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/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/yarn.lock b/code/yarn.lock index 52f251440bb3..1c9424cca89c 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5492,6 +5492,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" From e693dcd92622651f51ae3e6e69a566cdf6dfe6fe Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Fri, 13 Sep 2024 11:11:18 +0200 Subject: [PATCH 06/58] Fix check --- code/renderers/react/src/__test__/portable-stories.test.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/code/renderers/react/src/__test__/portable-stories.test.tsx b/code/renderers/react/src/__test__/portable-stories.test.tsx index 94de89e093a5..85f33c9a714a 100644 --- a/code/renderers/react/src/__test__/portable-stories.test.tsx +++ b/code/renderers/react/src/__test__/portable-stories.test.tsx @@ -8,7 +8,8 @@ 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'; @@ -124,7 +125,7 @@ describe('projectAnnotations', () => { const Story = composeStory( ButtonStories.WithActionArgType, ButtonStories.default, - addonActionsPreview + addonActionsPreview as ProjectAnnotations ); expect(Story.args.someActionArg).toHaveProperty('isAction', true); }); From 3d4f7ec05c89fcb64bb34c7b1be49657c059c093 Mon Sep 17 00:00:00 2001 From: 3w36zj6 <52315048+3w36zj6@users.noreply.github.com> Date: Sat, 14 Sep 2024 00:56:03 +0900 Subject: [PATCH 07/58] Ensure `.gitignore` updated via CLI ends with a newline --- code/lib/create-storybook/src/initiate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/lib/create-storybook/src/initiate.ts b/code/lib/create-storybook/src/initiate.ts index 787e16288cc2..bed1870fd2a9 100644 --- a/code/lib/create-storybook/src/initiate.ts +++ b/code/lib/create-storybook/src/initiate.ts @@ -375,7 +375,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'); } } From 247efff2d76bb2cda236b3aa3d06a07e9b7bc263 Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 00:14:30 +0200 Subject: [PATCH 08/58] Remove `fs-extra` from `presets/server` --- code/renderers/server/package.json | 2 -- code/renderers/server/src/preset.ts | 7 ++++--- code/yarn.lock | 2 -- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json index 8214690cb7de..67d3343a72b3 100644 --- a/code/renderers/server/package.json +++ b/code/renderers/server/package.json @@ -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..04937a0b8c14 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: 'utf-8' }); 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/yarn.lock b/code/yarn.lock index 52f251440bb3..57d65cf7988c 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6984,8 +6984,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" From 2ac99b3addc2842434563686e6e4c1f0a6838ca4 Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 00:17:40 +0200 Subject: [PATCH 09/58] Remove `fs-extra` from `presets/react-webpack` --- code/presets/react-webpack/package.json | 1 - code/yarn.lock | 1 - 2 files changed, 2 deletions(-) diff --git a/code/presets/react-webpack/package.json b/code/presets/react-webpack/package.json index d22f61a94932..8f3e04e671b7 100644 --- a/code/presets/react-webpack/package.json +++ b/code/presets/react-webpack/package.json @@ -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/yarn.lock b/code/yarn.lock index 57d65cf7988c..9a9b1cedaa13 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6583,7 +6583,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" From ce73ddb77c3bf76f6f4c39d64a79ea2570693e4c Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 12:20:06 +0200 Subject: [PATCH 10/58] Remove `fs-extra` from `lib/create-storybook` --- code/lib/create-storybook/package.json | 1 - .../src/generators/baseGenerator.ts | 6 +++-- .../src/generators/configure.test.ts | 23 +++++++++++-------- .../src/generators/configure.ts | 16 +++++++++---- .../src/scaffold-new-project.ts | 6 +++-- code/yarn.lock | 1 - 6 files changed, 32 insertions(+), 21 deletions(-) diff --git a/code/lib/create-storybook/package.json b/code/lib/create-storybook/package.json index 2a08fc5deed2..612b559bcae2 100644 --- a/code/lib/create-storybook/package.json +++ b/code/lib/create-storybook/package.json @@ -61,7 +61,6 @@ "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", diff --git a/code/lib/create-storybook/src/generators/baseGenerator.ts b/code/lib/create-storybook/src/generators/baseGenerator.ts index 20318b78a68e..f5d0c6ed828c 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'; @@ -328,7 +328,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..db099baf5590 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'); @@ -89,7 +92,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'); @@ -123,7 +126,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 +155,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 +179,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 +202,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/scaffold-new-project.ts b/code/lib/create-storybook/src/scaffold-new-project.ts index f80ff2281f23..f2d64fa794bd 100644 --- a/code/lib/create-storybook/src/scaffold-new-project.ts +++ b/code/lib/create-storybook/src/scaffold-new-project.ts @@ -1,3 +1,6 @@ +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'; @@ -6,7 +9,6 @@ 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 prompts from 'prompts'; import { dedent } from 'ts-dedent'; @@ -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, { diff --git a/code/yarn.lock b/code/yarn.lock index 9a9b1cedaa13..ce362b0e899b 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -12498,7 +12498,6 @@ __metadata: 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" prettier: "npm:^3.1.1" prompts: "npm:^2.4.0" From 5ed3ab439808616329a22e1959d9f8573d2ee0cf Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 13:31:23 +0200 Subject: [PATCH 11/58] Remove `fs-extra` from `lib/cli-storybook` --- code/__mocks__/fs/promises.ts | 34 +++++++++++++++++++ code/lib/cli-storybook/package.json | 1 - .../automigrate/fixes/eslint-plugin.test.ts | 6 ++-- .../automigrate/fixes/initial-globals.test.ts | 7 ++-- .../src/automigrate/fixes/initial-globals.ts | 3 +- .../src/automigrate/fixes/mdx-1-to-3.ts | 6 ++-- .../fixes/remove-global-client-apis.test.ts | 7 ++-- .../fixes/remove-global-client-apis.ts | 5 +-- .../cli-storybook/src/automigrate/index.ts | 7 ++-- code/lib/cli-storybook/src/doctor/index.ts | 7 ++-- code/lib/cli-storybook/src/link.ts | 12 ++++--- code/lib/cli-storybook/src/sandbox.ts | 3 +- code/yarn.lock | 1 - 13 files changed, 69 insertions(+), 30 deletions(-) create mode 100644 code/__mocks__/fs/promises.ts diff --git a/code/__mocks__/fs/promises.ts b/code/__mocks__/fs/promises.ts new file mode 100644 index 000000000000..44a0bbd1a516 --- /dev/null +++ b/code/__mocks__/fs/promises.ts @@ -0,0 +1,34 @@ +import { vi } from 'vitest'; + +// This is a custom function that our tests can use during setup to specify +// what the files on the "mock" filesystem should look like when any of the +// `fs` APIs are used. +let mockFiles = Object.create(null); + +// eslint-disable-next-line no-underscore-dangle, @typescript-eslint/naming-convention +export function __setMockFiles(newMockFiles: Record) { + mockFiles = newMockFiles; +} + +// A custom version of `readdirSync` that reads from the special mocked out +// file list set via __setMockFiles +export const writeFile = vi.fn(async (filePath: string, content: string) => { + mockFiles[filePath] = content; +}); +export const readFile = vi.fn(async (filePath: string) => mockFiles[filePath]); +export const lstat = vi.fn(async (filePath: string) => ({ + isFile: () => !!mockFiles[filePath], +})); +export const readdir = vi.fn(); +export const readlink = vi.fn(); +export const realpath = vi.fn(); + +export default { + __setMockFiles, + writeFile, + readFile, + lstat, + readdir, + readlink, + realpath, +}; diff --git a/code/lib/cli-storybook/package.json b/code/lib/cli-storybook/package.json index 5e13a89d5b30..7623c68b2f4d 100644 --- a/code/lib/cli-storybook/package.json +++ b/code/lib/cli-storybook/package.json @@ -51,7 +51,6 @@ "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", 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/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..fe40749ffa16 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 { dedent } from 'ts-dedent'; import type { Fix } from '../types'; 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..d5277a5e8c92 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 { dedent } from 'ts-dedent'; import type { Fix } from '../types'; @@ -74,14 +74,14 @@ export const mdx1to3: Fix = { 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: 'utf-8' }); 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/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..e7caf9027d4b 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 { readFile } from 'node:fs/promises'; + import chalk from 'chalk'; -import { readFile } from 'fs-extra'; 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)) { diff --git a/code/lib/cli-storybook/src/automigrate/index.ts b/code/lib/cli-storybook/src/automigrate/index.ts index 690c3d1b15f7..ff727783dff3 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 { @@ -10,7 +12,6 @@ import { import boxen from 'boxen'; import chalk from 'chalk'; -import { createWriteStream, move, remove } from 'fs-extra'; import prompts from 'prompts'; import semver from 'semver'; import invariant from 'tiny-invariant'; @@ -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) { diff --git a/code/lib/cli-storybook/src/doctor/index.ts b/code/lib/cli-storybook/src/doctor/index.ts index 08e798288753..53439e244f00 100644 --- a/code/lib/cli-storybook/src/doctor/index.ts +++ b/code/lib/cli-storybook/src/doctor/index.ts @@ -1,3 +1,5 @@ +import { createWriteStream } from 'node:fs'; +import { rename, rm } from 'node:fs/promises'; import { join } from 'node:path'; import { JsPackageManagerFactory, temporaryFile } from 'storybook/internal/common'; @@ -5,7 +7,6 @@ import type { PackageManagerName } from 'storybook/internal/common'; import boxen from 'boxen'; import chalk from 'chalk'; -import { createWriteStream, move, remove } from 'fs-extra'; import { dedent } from 'ts-dedent'; import { cleanLog } from '../automigrate/helpers/cleanLog'; @@ -161,11 +162,11 @@ export const doctor = async ({ logger.info(`Full logs are available in ${chalk.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..a858847e26b2 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'; type ExecOptions = Parameters[2]; @@ -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: 'utf-8' })); 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: 'utf-8' }) + ); 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..4bbe097d7d16 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'; @@ -7,7 +9,6 @@ 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 prompts from 'prompts'; import { lt, prerelease } from 'semver'; diff --git a/code/yarn.lock b/code/yarn.lock index ce362b0e899b..997e639d2e70 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5896,7 +5896,6 @@ __metadata: 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" From 2e89262e63c10c41d5b24484dc1a0139efc1496f Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 13:32:21 +0200 Subject: [PATCH 12/58] Remove `fs-extra` from `frameworks/nextjs` --- code/frameworks/nextjs/package.json | 1 - code/yarn.lock | 1 - 2 files changed, 2 deletions(-) diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json index 856982c811dd..6b28e51f8e7b 100644 --- a/code/frameworks/nextjs/package.json +++ b/code/frameworks/nextjs/package.json @@ -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", diff --git a/code/yarn.lock b/code/yarn.lock index 997e639d2e70..3115e30df1dd 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6433,7 +6433,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" From b080e7153ac422cb9667dd6adacee405929e67e8 Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 13:38:45 +0200 Subject: [PATCH 13/58] Remove `fs-extra` from `builders/builder-webpack5` --- code/builders/builder-webpack5/package.json | 1 - code/builders/builder-webpack5/src/index.ts | 6 ++++-- code/yarn.lock | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/builders/builder-webpack5/package.json b/code/builders/builder-webpack5/package.json index 913991d99689..2d4c5330bd01 100644 --- a/code/builders/builder-webpack5/package.json +++ b/code/builders/builder-webpack5/package.json @@ -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..7d12dabb4b6f 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,9 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, const previewDirOrigin = previewResolvedDir; const previewDirTarget = join(options.outputDir || '', `sb-preview`); - const previewFiles = fs.copy(previewDirOrigin, previewDirTarget, { + // TODO: `fsPromises.cp` is marked as experimental in Node 16-21. Ask in the PR whether we should + // use it anyway or stick to `fs-extra` for now. + const previewFiles = cp(previewDirOrigin, previewDirTarget, { filter: (src) => { const { ext } = parse(src); if (ext) { diff --git a/code/yarn.lock b/code/yarn.lock index 3115e30df1dd..66d1f39375f7 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5843,7 +5843,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" From b9c600ad157e6e949b0111336b90036d01dbb5ef Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 13:44:12 +0200 Subject: [PATCH 14/58] Remove `fs-extra` from `builders/builder-vite` --- code/builders/builder-vite/package.json | 1 - code/builders/builder-vite/src/index.ts | 13 +++++++------ .../src/plugins/external-globals-plugin.ts | 8 ++++++-- code/yarn.lock | 1 - 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/code/builders/builder-vite/package.json b/code/builders/builder-vite/package.json index 5c8bd9b47ea1..94fde0bf8cfb 100644 --- a/code/builders/builder-vite/package.json +++ b/code/builders/builder-vite/package.json @@ -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", "magic-string": "^0.30.0", "ts-dedent": "^2.0.0" }, diff --git a/code/builders/builder-vite/src/index.ts b/code/builders/builder-vite/src/index.ts index a8a775a13344..fb841b1c473d 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: 'utf-8', + }); const generated = await transformIframeHtml(indexHtml, options); const transformed = await server.transformIndexHtml('/iframe.html', generated); res.setHeader('Content-Type', 'text/html'); @@ -85,7 +84,9 @@ export const build: ViteBuilder['build'] = async ({ options }) => { const previewDirOrigin = previewResolvedDir; const previewDirTarget = join(options.outputDir || '', `sb-preview`); - const previewFiles = fs.copy(previewDirOrigin, previewDirTarget, { + // TODO: `fsPromises.cp` is marked as experimental in Node 16-21. Ask in the PR whether we should + // use it anyway or stick to `fs-extra` for now. + const previewFiles = cp(previewDirOrigin, previewDirTarget, { filter: (src) => { const { ext } = parse(src); if (ext) { 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 a71f9f3b0fe8..f7798ccff136 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,10 @@ +import { existsSync, openSync } from 'node:fs'; +import { writeFile } from 'node:fs/promises'; import { join } from 'node:path'; import { init, parse } from 'es-module-lexer'; import findCacheDirectory from 'find-cache-dir'; -import { ensureFile, writeFile } from 'fs-extra'; +import { ensureFile } from 'fs-extra'; import MagicString from 'magic-string'; import type { Alias, Plugin } from 'vite'; @@ -59,7 +61,9 @@ 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)) { + openSync(externalCachePath, 'w'); + } await writeFile(externalCachePath, `module.exports = ${externals[externalKey]};`); }) ); diff --git a/code/yarn.lock b/code/yarn.lock index 66d1f39375f7..99ad7d5bdb79 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5802,7 +5802,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" magic-string: "npm:^0.30.0" slash: "npm:^5.0.0" From 67dbe11f02d5c4b8978769e586adb315069572b6 Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 13:45:28 +0200 Subject: [PATCH 15/58] Remove `fs-extra` from `addons/docs` --- code/addons/docs/package.json | 1 - code/yarn.lock | 1 - 2 files changed, 2 deletions(-) diff --git a/code/addons/docs/package.json b/code/addons/docs/package.json index d905b004df47..7e38a4d00383 100644 --- a/code/addons/docs/package.json +++ b/code/addons/docs/package.json @@ -103,7 +103,6 @@ "@storybook/global": "^5.0.0", "@storybook/react-dom-shim": "workspace:*", "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "fs-extra": "^11.1.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "rehype-external-links": "^3.0.0", diff --git a/code/yarn.lock b/code/yarn.lock index 99ad7d5bdb79..70a5397e0d27 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5437,7 +5437,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" From cf80eaa1889424ccf0db50a156ef69ac02c4bdde Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 13:46:23 +0200 Subject: [PATCH 16/58] Remove rogue import in `builders/builder-vite` --- .../builders/builder-vite/src/plugins/external-globals-plugin.ts | 1 - 1 file changed, 1 deletion(-) 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 f7798ccff136..7813e1c50030 100644 --- a/code/builders/builder-vite/src/plugins/external-globals-plugin.ts +++ b/code/builders/builder-vite/src/plugins/external-globals-plugin.ts @@ -4,7 +4,6 @@ import { join } from 'node:path'; import { init, parse } from 'es-module-lexer'; import findCacheDirectory from 'find-cache-dir'; -import { ensureFile } from 'fs-extra'; import MagicString from 'magic-string'; import type { Alias, Plugin } from 'vite'; From 353dfb10a28b64902d0c163e5863d913504fd35b Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 14:05:40 +0200 Subject: [PATCH 17/58] Remove rogue comment --- code/__mocks__/fs/promises.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/__mocks__/fs/promises.ts b/code/__mocks__/fs/promises.ts index 44a0bbd1a516..d6673a26e648 100644 --- a/code/__mocks__/fs/promises.ts +++ b/code/__mocks__/fs/promises.ts @@ -10,8 +10,6 @@ export function __setMockFiles(newMockFiles: Record) { mockFiles = newMockFiles; } -// A custom version of `readdirSync` that reads from the special mocked out -// file list set via __setMockFiles export const writeFile = vi.fn(async (filePath: string, content: string) => { mockFiles[filePath] = content; }); From 08c6eef3ffeea4081e67acbec140919b3bbcdd9c Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 18:18:49 +0200 Subject: [PATCH 18/58] Remove `fs-extra` from `presets/server-webpack` --- code/presets/server-webpack/package.json | 1 - .../src/lib/compiler/json-to-csf-compiler.test.ts | 7 ++++--- code/yarn.lock | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/code/presets/server-webpack/package.json b/code/presets/server-webpack/package.json index 48fc4ac5927c..0dcf0288a02e 100644 --- a/code/presets/server-webpack/package.json +++ b/code/presets/server-webpack/package.json @@ -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/yarn.lock b/code/yarn.lock index f8bffd7aee76..2b753fe187ab 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6604,7 +6604,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" From a09abbc1c8d25d12f36cd4333ba6c5adf57a471a Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 18:32:26 +0200 Subject: [PATCH 19/58] Remove `fs-extra` from `renderers/svelte` VS Code complains about not being able to find Node types, but building the package works. Need to investigate. --- code/renderers/svelte/package.json | 1 - code/renderers/svelte/scripts/copy-unbundled-to-dist.ts | 5 +++-- code/yarn.lock | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/code/renderers/svelte/package.json b/code/renderers/svelte/package.json index aa804d3d0ce6..5972c0ef2ac8 100644 --- a/code/renderers/svelte/package.json +++ b/code/renderers/svelte/package.json @@ -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/yarn.lock b/code/yarn.lock index 2b753fe187ab..a3672777a19b 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -7052,7 +7052,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" From 93b866b4a1ee7c345421568f7ab13eb4b07213f2 Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 18:37:15 +0200 Subject: [PATCH 20/58] Remove `fs-extra` from `addons/links` VS Code complains about not being able to find Node types, but building the package works. Need to investigate. --- code/addons/links/package.json | 1 - code/addons/links/scripts/fix-preview-api-reference.ts | 2 +- code/yarn.lock | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/code/addons/links/package.json b/code/addons/links/package.json index c11fa3e056b3..ca95baea88ac 100644 --- a/code/addons/links/package.json +++ b/code/addons/links/package.json @@ -71,7 +71,6 @@ "ts-dedent": "^2.0.0" }, "devDependencies": { - "fs-extra": "^11.1.0", "typescript": "^5.3.2" }, "peerDependencies": { 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/yarn.lock b/code/yarn.lock index a3672777a19b..e477552d26d5 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5527,7 +5527,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: From 7c14a13412fd5bed24074145b7ef79d372319b43 Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 18:59:18 +0200 Subject: [PATCH 21/58] core: Remove `fs-extra` from `src/telemetry` --- code/__mocks__/fs.js | 29 ----------------- code/__mocks__/fs.ts | 32 +++++++++++++++++++ .../src/telemetry/get-monorepo-type.test.ts | 7 ++-- code/core/src/telemetry/get-monorepo-type.ts | 7 ++-- code/core/src/telemetry/package-json.ts | 5 ++- 5 files changed, 41 insertions(+), 39 deletions(-) delete mode 100644 code/__mocks__/fs.js create mode 100644 code/__mocks__/fs.ts diff --git a/code/__mocks__/fs.js b/code/__mocks__/fs.js deleted file mode 100644 index 9e608c3ff038..000000000000 --- a/code/__mocks__/fs.js +++ /dev/null @@ -1,29 +0,0 @@ -import { vi } from 'vitest'; - -const fs = vi.createMockFromModule('fs'); - -// This is a custom function that our tests can use during setup to specify -// what the files on the "mock" filesystem should look like when any of the -// `fs` APIs are used. -let mockFiles = Object.create(null); - -// eslint-disable-next-line no-underscore-dangle, @typescript-eslint/naming-convention -function __setMockFiles(newMockFiles) { - mockFiles = newMockFiles; -} - -// A custom version of `readdirSync` that reads from the special mocked out -// file list set via __setMockFiles -const readFileSync = (filePath = '') => mockFiles[filePath]; -const existsSync = (filePath) => !!mockFiles[filePath]; -const lstatSync = (filePath) => ({ - isFile: () => !!mockFiles[filePath], -}); - -// eslint-disable-next-line no-underscore-dangle -fs.__setMockFiles = __setMockFiles; -fs.readFileSync = readFileSync; -fs.existsSync = existsSync; -fs.lstatSync = lstatSync; - -module.exports = fs; diff --git a/code/__mocks__/fs.ts b/code/__mocks__/fs.ts new file mode 100644 index 000000000000..50e34e8e4ca1 --- /dev/null +++ b/code/__mocks__/fs.ts @@ -0,0 +1,32 @@ +import { vi } from 'vitest'; + +// This is a custom function that our tests can use during setup to specify +// what the files on the "mock" filesystem should look like when any of the +// `fs` APIs are used. +let mockFiles = Object.create(null); + +// eslint-disable-next-line no-underscore-dangle, @typescript-eslint/naming-convention +export function __setMockFiles(newMockFiles: Record) { + mockFiles = newMockFiles; +} + +export const readFileSync = (filePath = '') => mockFiles[filePath]; +export const existsSync = (filePath: string) => !!mockFiles[filePath]; +export const lstatSync = (filePath: string) => ({ + isFile: () => !!mockFiles[filePath], +}); +export const realpathSync = vi.fn(); +export const readdir = vi.fn(); +export const readdirSync = vi.fn(); +export const readlinkSync = vi.fn(); + +export default { + __setMockFiles, + readFileSync, + existsSync, + lstatSync, + realpathSync, + readdir, + readdirSync, + readlinkSync, +}; 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..e004365fcbb0 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: 'utf-8' }) + ) as PackageJson; if (packageJson?.workspaces) { return 'Workspaces'; diff --git a/code/core/src/telemetry/package-json.ts b/code/core/src/telemetry/package-json.ts index b88ebc237ddc..3bf006115c4a 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: 'utf-8' })); return packageJson; }; From 7816541a57b21879837664261bc0deb3e0d33a44 Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 19:12:08 +0200 Subject: [PATCH 22/58] core: Remove `fs-extra` from `src/common/utils` --- .../src/common/js-package-manager/PNPMProxy.ts | 3 +-- code/core/src/common/utils/cli.ts | 16 +++++++--------- code/core/src/common/utils/get-storybook-info.ts | 7 ++----- code/core/src/common/utils/get-storybook-refs.ts | 8 +++++--- 4 files changed, 15 insertions(+), 19 deletions(-) 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..c733febf6aea 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: 'utf-8' })) || {}; 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: 'utf-8' })) || {}; if (storybook?.url) { return { id: name, ...storybook, version }; From fb2455bfbab954d570e165c50f6a79eb7e16dd37 Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 19:31:03 +0200 Subject: [PATCH 23/58] core: Remove `fs-extra` from `src/core-server/utils` --- .../src/core-server/presets/common-preset.ts | 11 +++---- .../src/core-server/presets/favicon.test.ts | 30 ++++++++----------- .../core-server/utils/StoryIndexGenerator.ts | 11 +++---- .../utils/__tests__/server-statics.test.ts | 13 ++++---- .../utils/copy-all-static-files.ts | 10 +++++-- code/core/src/core-server/utils/metadata.ts | 5 ++-- .../src/core-server/utils/output-stats.ts | 4 +-- .../core/src/core-server/utils/server-init.ts | 9 +++--- .../src/core-server/utils/server-statics.ts | 4 +-- .../src/core-server/utils/stories-json.ts | 4 +-- .../utils/warnWhenUsingArgTypesRegex.ts | 7 +++-- code/core/src/core-server/utils/whats-new.ts | 5 ++-- 12 files changed, 58 insertions(+), 55 deletions(-) diff --git a/code/core/src/core-server/presets/common-preset.ts b/code/core/src/core-server/presets/common-preset.ts index bca9a1c9cfe6..9a32187c11f6 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: 'utf-8' }); 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..b3bc3239b983 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'; @@ -23,7 +25,6 @@ import { sortStoriesV7, userOrAutoTitleFromSpecifier } from '@storybook/core/pre import chalk from 'chalk'; import { findUp } from 'find-up'; -import fs from 'fs-extra'; import slash from 'slash'; import invariant from 'tiny-invariant'; import { dedent } from 'ts-dedent'; @@ -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); @@ -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: 'utf-8' })).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..f0b3d331033d 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,3 +1,4 @@ +import { cp } from 'node:fs/promises'; import { join, relative } from 'node:path'; import { getDirectoryFromWorkingDir } from '@storybook/core/common'; @@ -5,7 +6,6 @@ import { getDirectoryFromWorkingDir } from '@storybook/core/common'; import { logger } from '@storybook/core/node-logger'; import chalk from 'chalk'; -import fs from 'fs-extra'; import { parseStaticDir } from './server-statics'; @@ -26,10 +26,13 @@ export async function copyAllStaticFiles(staticDirs: any[] | undefined, outputDi // 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, { + // TODO: `fsPromises.cp` is marked as experimental in Node 16-21. Ask in the PR whether we should + // use it anyway or stick to `fs-extra` for now. + await cp(staticPath, targetPath, { dereference: true, preserveTimestamps: true, filter: (_, dest) => !skipPaths.includes(dest), + recursive: true, }); } catch (e) { if (e instanceof Error) { @@ -68,10 +71,11 @@ export async function copyAllStaticFilesRelativeToMain( `=> Copying static files: ${chalk.cyan(print(from))} at ${chalk.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/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-stats.ts b/code/core/src/core-server/utils/output-stats.ts index c3a5b9f8aad9..1d5047862247 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'; @@ -6,7 +7,6 @@ import { logger } from '@storybook/core/node-logger'; import { stringifyStream } from '@discoveryjs/json-ext'; import chalk from 'chalk'; -import fs from 'fs-extra'; export async function outputStats(directory: string, previewStats?: any, managerStats?: any) { if (previewStats) { @@ -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..cdcd3c920543 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: 'utf-8' }))), + cert: await readFile(options.sslCert, { encoding: 'utf-8' }), + key: await readFile(options.sslKey, { encoding: 'utf-8' }), }; 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..edd1a4f45fb2 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'; @@ -8,7 +9,6 @@ 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 { dedent } from 'ts-dedent'; export async function useStatics(router: Router, options: Options) { @@ -69,7 +69,7 @@ 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)} diff --git a/code/core/src/core-server/utils/stories-json.ts b/code/core/src/core-server/utils/stories-json.ts index 8956a4ed7ab1..d34ba9d72186 100644 --- a/code/core/src/core-server/utils/stories-json.ts +++ b/code/core/src/core-server/utils/stories-json.ts @@ -1,3 +1,4 @@ +import { writeFile } from 'node:fs/promises'; import { basename } from 'node:path'; import type { NormalizedStoriesSpecifier, StoryIndex } from '@storybook/core/types'; @@ -5,7 +6,6 @@ import type { NormalizedStoriesSpecifier, StoryIndex } from '@storybook/core/typ import { STORY_INDEX_INVALIDATED } from '@storybook/core/core-events'; import type { Request, Response, Router } from 'express'; -import { writeJSON } from 'fs-extra'; import debounce from 'lodash/debounce.js'; import type { StoryIndexGenerator } from './StoryIndexGenerator'; @@ -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/warnWhenUsingArgTypesRegex.ts b/code/core/src/core-server/utils/warnWhenUsingArgTypesRegex.ts index dd543bff54bc..d8fa4dcc521c 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 { 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) => 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 }); } From 2dcc7b75e719487e6bb6c27cf844c8bb51b68d4c Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 19:38:58 +0200 Subject: [PATCH 24/58] Add `recursive: true` to `cp` calls --- code/builders/builder-vite/src/index.ts | 1 + code/builders/builder-webpack5/src/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/code/builders/builder-vite/src/index.ts b/code/builders/builder-vite/src/index.ts index fb841b1c473d..7a8771a5efe9 100644 --- a/code/builders/builder-vite/src/index.ts +++ b/code/builders/builder-vite/src/index.ts @@ -94,6 +94,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-webpack5/src/index.ts b/code/builders/builder-webpack5/src/index.ts index 7d12dabb4b6f..bf87a0700131 100644 --- a/code/builders/builder-webpack5/src/index.ts +++ b/code/builders/builder-webpack5/src/index.ts @@ -302,6 +302,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, } return true; }, + recursive: true, }); const [webpackCompilationOutput] = await Promise.all([webpackCompilation, previewFiles]); From ca39982d8873141a36b8c56a2594af9d3027599d Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 19:39:31 +0200 Subject: [PATCH 25/58] core: Remove `fs-extra` from `src/core-server` --- code/core/src/core-server/build-dev.ts | 4 ++-- code/core/src/core-server/build-static.ts | 15 +++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/code/core/src/core-server/build-dev.ts b/code/core/src/core-server/build-dev.ts index 5c87a7d9ccd8..2f1e08703a21 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: 'utf-8' }); // 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..d0835d0d8a67 100644 --- a/code/core/src/core-server/build-static.ts +++ b/code/core/src/core-server/build-static.ts @@ -1,3 +1,5 @@ +import { cp, mkdir, readdir } from 'node:fs/promises'; +import { rm } from 'node:fs/promises'; import { dirname, join, relative, resolve } from 'node:path'; import { @@ -14,7 +16,6 @@ import { global } from '@storybook/global'; import { logger } from '@storybook/core/node-logger'; import chalk from 'chalk'; -import { copy, emptyDir, ensureDir } from 'fs-extra'; import { StoryIndexGenerator } from './utils/StoryIndexGenerator'; import { buildOrThrow } from './utils/build-or-throw'; @@ -43,8 +44,12 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption if (options.outputDir === '/') { throw new Error("Won't remove directory '/'. Check your outputDir!"); } - await emptyDir(options.outputDir); - await ensureDir(options.outputDir); + + const outputDirFiles = await readdir(options.outputDir); + if (outputDirFiles.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 +132,9 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption dirname(require.resolve('@storybook/core/package.json')), 'assets/browser' ); - effects.push(copy(coreServerPublicDir, options.outputDir)); + // TODO: `fsPromises.cp` is marked as experimental in Node 16-21. Ask in the PR whether we should + // use it anyway or stick to `fs-extra` for now. + effects.push(cp(coreServerPublicDir, options.outputDir, { recursive: true })); let initializedStoryIndexGenerator: Promise = Promise.resolve(undefined); From a42ff7e9590c5747395c379c8c39c7fe0af980f1 Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 19:53:13 +0200 Subject: [PATCH 26/58] Close the file descriptor --- .../builder-vite/src/plugins/external-globals-plugin.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 7813e1c50030..6477ade92b4a 100644 --- a/code/builders/builder-vite/src/plugins/external-globals-plugin.ts +++ b/code/builders/builder-vite/src/plugins/external-globals-plugin.ts @@ -1,4 +1,4 @@ -import { existsSync, openSync } from 'node:fs'; +import { closeSync, existsSync, openSync } from 'node:fs'; import { writeFile } from 'node:fs/promises'; import { join } from 'node:path'; @@ -61,7 +61,7 @@ export async function externalGlobalsPlugin(externals: Record) { const externalCachePath = join(cachePath, `${externalKey}.js`); newAlias.push({ find: new RegExp(`^${externalKey}$`), replacement: externalCachePath }); if (!existsSync(externalCachePath)) { - openSync(externalCachePath, 'w'); + closeSync(openSync(externalCachePath, 'w')); } await writeFile(externalCachePath, `module.exports = ${externals[externalKey]};`); }) From 174868afbaa4775e2fc9621665b0d651fbe93dee Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 19:57:46 +0200 Subject: [PATCH 27/58] core: Remove `fs-extra` from `src/builder-manager` --- code/core/src/builder-manager/index.ts | 15 +++--- code/core/src/builder-manager/utils/files.ts | 9 ++-- .../builder-manager/utils/managerEntries.ts | 9 ++-- .../src/builder-manager/utils/template.ts | 4 +- code/core/src/cli/detect.test.ts | 4 -- code/core/src/cli/eslintPlugin.ts | 10 ++-- code/core/src/cli/helpers.test.ts | 46 ++++++++----------- code/core/src/cli/helpers.ts | 30 ++++++------ 8 files changed, 64 insertions(+), 63 deletions(-) diff --git a/code/core/src/builder-manager/index.ts b/code/core/src/builder-manager/index.ts index 1e246b5d1cb1..a1589af27b23 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,9 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, yield; - const managerFiles = fs.copy(coreDirOrigin, coreDirTarget, { + // TODO: `fsPromises.cp` is marked as experimental in Node 16-21. Ask in the PR whether we should + // use it anyway or stick to `fs-extra` for now. + const managerFiles = cp(coreDirOrigin, coreDirTarget, { filter: (src) => { const { ext } = parse(src); if (ext) { @@ -264,6 +266,7 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, } return true; }, + recursive: true, }); const { cssFiles, jsFiles } = await readOrderedFiles(addonsDir, compilation?.outputFiles); @@ -288,11 +291,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..b83cb2e7a13e 100644 --- a/code/core/src/builder-manager/utils/files.ts +++ b/code/core/src/builder-manager/utils/files.ts @@ -1,7 +1,8 @@ +import { closeSync, existsSync, openSync } from 'node:fs'; +import { writeFile } from 'node:fs/promises'; import { 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,10 @@ 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)) { + closeSync(openSync(location, 'w')); + } + 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..3cb2f1d3aea9 100644 --- a/code/core/src/builder-manager/utils/managerEntries.ts +++ b/code/core/src/builder-manager/utils/managerEntries.ts @@ -1,8 +1,9 @@ +import { closeSync, existsSync, openSync } from 'node:fs'; +import { writeFile } from 'node:fs/promises'; import { 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,10 @@ 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)) { + closeSync(openSync(location, 'w')); + } + 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/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..e6a37adfd64e 100644 --- a/code/core/src/cli/eslintPlugin.ts +++ b/code/core/src/cli/eslintPlugin.ts @@ -1,4 +1,5 @@ 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'; @@ -7,7 +8,6 @@ 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 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: 'utf-8' })) 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); 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..1b1448e7b469 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 { @@ -12,7 +13,6 @@ import type { SupportedFrameworks, SupportedRenderers } from '@storybook/core/ty import chalk from 'chalk'; import { findUpSync } from 'find-up'; -import { copy, copySync, pathExists, readFile, writeFile } from 'fs-extra'; import { coerce, satisfies } from 'semver'; import stripJsonComments from 'strip-json-comments'; import invariant from 'tiny-invariant'; @@ -130,7 +130,9 @@ export function copyTemplate(templateRoot: string, destination = '.') { throw new Error(`Couldn't find template dir`); } - copySync(templateDir, destination, { overwrite: true }); + // TODO: `fsPromises.cpSync` is marked as experimental in Node 16-21. Ask in the PR whether we + // should use it anyway or stick to `fs-extra` for now. + cpSync(templateDir, destination, { recursive: true }); } type CopyTemplateFilesOptions = { @@ -197,42 +199,44 @@ 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'; }; const destinationPath = destination ?? (await targetPath()); + // TODO: `fsPromises.cp` is marked as experimental in Node 16-21. Ask in the PR whether we should + // use it anyway or stick to `fs-extra` for now. 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 +252,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]}`); From 643acbfc2a03faa240fa01693e11bd1c54a293e2 Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 20:01:00 +0200 Subject: [PATCH 28/58] core: Remove `fs-extra` from `scripts` --- code/core/package.json | 2 -- code/core/scripts/helpers/dependencies.ts | 7 ++++--- code/core/scripts/helpers/generatePackageJsonFile.ts | 5 ++--- code/core/scripts/helpers/generateTypesMapperFiles.ts | 7 ++++--- code/core/scripts/prep.ts | 8 ++++---- code/yarn.lock | 2 -- 6 files changed, 14 insertions(+), 17 deletions(-) diff --git a/code/core/package.json b/code/core/package.json index 181b04312985..e9464c5339b6 100644 --- a/code/core/package.json +++ b/code/core/package.json @@ -320,7 +320,6 @@ "@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", @@ -371,7 +370,6 @@ "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", diff --git a/code/core/scripts/helpers/dependencies.ts b/code/core/scripts/helpers/dependencies.ts index 82b22a022ad4..e84e80931e75 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: 'utf-8' }) + ); 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..1d14e141504a 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: 'utf-8' })); /** * 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/generateTypesMapperFiles.ts b/code/core/scripts/helpers/generateTypesMapperFiles.ts index 2f79f80643d8..11845a5e3239 100644 --- a/code/core/scripts/helpers/generateTypesMapperFiles.ts +++ b/code/core/scripts/helpers/generateTypesMapperFiles.ts @@ -1,8 +1,7 @@ +import { closeSync, existsSync, openSync } from 'node:fs'; import { writeFile } from 'node:fs/promises'; import { join, relative } from 'node:path'; -import { ensureFile } from 'fs-extra'; - import { dedent } from '../../../../scripts/prepare/tools'; import type { getEntries } from '../entries'; @@ -34,7 +33,9 @@ export async function generateTypesMapperFiles(entries: ReturnType { const location = filePath.replace('src', 'dist').replace(/\.tsx?/, '.d.ts'); - await ensureFile(location); + if (!existsSync(location)) { + closeSync(openSync(location, 'w')); + } await writeFile(location, await generateTypesMapperContent(filePath)); }) ); diff --git a/code/core/scripts/prep.ts b/code/core/scripts/prep.ts index 49c2bd00cfa0..20c979ebd9aa 100644 --- a/code/core/scripts/prep.ts +++ b/code/core/scripts/prep.ts @@ -1,10 +1,8 @@ /* 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 { chalk, dedent, @@ -320,7 +318,9 @@ async function run() { const outName = keys.length === 1 ? dirname(keys[0]).replace('dist/', '') : `meta-${format}-${index}`; - await ensureDir('report'); + if (!existsSync('report')) { + mkdirSync('report'); + } await writeFile(`report/${outName}.json`, JSON.stringify(out.metafile, null, 2)); await writeFile( `report/${outName}.txt`, diff --git a/code/yarn.lock b/code/yarn.lock index e477552d26d5..544c7fa773c5 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6034,7 +6034,6 @@ __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" @@ -6087,7 +6086,6 @@ __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" From 89cdff3bec640b6bd8d8831d578cf4b9cc9858af Mon Sep 17 00:00:00 2001 From: ziebam Date: Sat, 14 Sep 2024 20:04:38 +0200 Subject: [PATCH 29/58] Remove `fs-extra` from `code` --- code/package.json | 1 - code/yarn.lock | 1 - 2 files changed, 2 deletions(-) diff --git a/code/package.json b/code/package.json index e348ef665900..000f4d270a0b 100644 --- a/code/package.json +++ b/code/package.json @@ -194,7 +194,6 @@ "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", diff --git a/code/yarn.lock b/code/yarn.lock index 544c7fa773c5..ae4ac70a6765 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6893,7 +6893,6 @@ __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" From 0d10b9586305315994a2b2e8716d18492481fc0a Mon Sep 17 00:00:00 2001 From: ziebam Date: Sun, 15 Sep 2024 11:40:41 +0200 Subject: [PATCH 30/58] Replace 'utf-8' with 'utf8' for consistency with Node docs --- code/builders/builder-vite/src/index.ts | 2 +- code/core/scripts/helpers/dependencies.ts | 2 +- code/core/scripts/helpers/generatePackageJsonFile.ts | 2 +- code/core/src/cli/eslintPlugin.ts | 2 +- code/core/src/common/utils/get-storybook-refs.ts | 4 ++-- code/core/src/core-server/build-dev.ts | 2 +- code/core/src/core-server/presets/common-preset.ts | 2 +- code/core/src/core-server/utils/StoryIndexGenerator.ts | 2 +- code/core/src/core-server/utils/server-init.ts | 6 +++--- code/core/src/telemetry/get-monorepo-type.ts | 2 +- code/core/src/telemetry/package-json.ts | 2 +- code/lib/cli-storybook/src/automigrate/fixes/mdx-1-to-3.ts | 2 +- code/lib/cli-storybook/src/link.ts | 4 ++-- code/renderers/server/src/preset.ts | 2 +- 14 files changed, 18 insertions(+), 18 deletions(-) diff --git a/code/builders/builder-vite/src/index.ts b/code/builders/builder-vite/src/index.ts index 7a8771a5efe9..cdd4ed972eef 100644 --- a/code/builders/builder-vite/src/index.ts +++ b/code/builders/builder-vite/src/index.ts @@ -35,7 +35,7 @@ function iframeMiddleware(options: Options, server: ViteDevServer): RequestHandl } const indexHtml = await readFile(require.resolve('@storybook/builder-vite/input/iframe.html'), { - encoding: 'utf-8', + encoding: 'utf8', }); const generated = await transformIframeHtml(indexHtml, options); const transformed = await server.transformIndexHtml('/iframe.html', generated); diff --git a/code/core/scripts/helpers/dependencies.ts b/code/core/scripts/helpers/dependencies.ts index e84e80931e75..89e67fc554f3 100644 --- a/code/core/scripts/helpers/dependencies.ts +++ b/code/core/scripts/helpers/dependencies.ts @@ -18,7 +18,7 @@ export async function flattenDependencies( return; } const { dependencies = {}, peerDependencies = {} } = JSON.parse( - await readFile(path, { encoding: 'utf-8' }) + 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 1d14e141504a..271f775cba23 100644 --- a/code/core/scripts/helpers/generatePackageJsonFile.ts +++ b/code/core/scripts/helpers/generatePackageJsonFile.ts @@ -10,7 +10,7 @@ const cwd = process.cwd(); export async function generatePackageJsonFile(entries: ReturnType) { const location = join(cwd, 'package.json'); - const pkgJson = JSON.parse(await readFile(location, { encoding: 'utf-8' })); + 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/src/cli/eslintPlugin.ts b/code/core/src/cli/eslintPlugin.ts index e6a37adfd64e..fafb91b2652a 100644 --- a/code/core/src/cli/eslintPlugin.ts +++ b/code/core/src/cli/eslintPlugin.ts @@ -72,7 +72,7 @@ export async function configureEslintPlugin( if (eslintFile) { paddedLog(`Configuring Storybook ESLint plugin at ${eslintFile}`); if (eslintFile.endsWith('json')) { - const eslintConfig = JSON.parse(await readFile(eslintFile, { encoding: 'utf-8' })) as { + const eslintConfig = JSON.parse(await readFile(eslintFile, { encoding: 'utf8' })) as { extends?: string[]; }; const existingExtends = normalizeExtends(eslintConfig.extends).filter(Boolean); diff --git a/code/core/src/common/utils/get-storybook-refs.ts b/code/core/src/common/utils/get-storybook-refs.ts index c733febf6aea..4108ba969412 100644 --- a/code/core/src/common/utils/get-storybook-refs.ts +++ b/code/core/src/common/utils/get-storybook-refs.ts @@ -16,7 +16,7 @@ export const getAutoRefs = async (options: Options): Promise const directory = dirname(location); const { dependencies = [], devDependencies = [] } = - JSON.parse(await readFile(location, { encoding: 'utf-8' })) || {}; + JSON.parse(await readFile(location, { encoding: 'utf8' })) || {}; const deps = Object.keys({ ...dependencies, ...devDependencies }); const list = await Promise.all( @@ -25,7 +25,7 @@ export const getAutoRefs = async (options: Options): Promise const l = resolveFrom(directory, join(d, 'package.json')); const { storybook, name, version } = - JSON.parse(await readFile(l, { encoding: 'utf-8' })) || {}; + JSON.parse(await readFile(l, { encoding: 'utf8' })) || {}; if (storybook?.url) { return { id: name, ...storybook, version }; diff --git a/code/core/src/core-server/build-dev.ts b/code/core/src/core-server/build-dev.ts index 2f1e08703a21..c8b336a22e09 100644 --- a/code/core/src/core-server/build-dev.ts +++ b/code/core/src/core-server/build-dev.ts @@ -156,7 +156,7 @@ export async function buildDevStandalone( if (/\.c[jt]s$/.test(mainJsPath)) { deprecate(deprecationMessage); } - const mainJsContent = await readFile(mainJsPath, { encoding: '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/presets/common-preset.ts b/code/core/src/core-server/presets/common-preset.ts index 9a32187c11f6..10f8be05614f 100644 --- a/code/core/src/core-server/presets/common-preset.ts +++ b/code/core/src/core-server/presets/common-preset.ts @@ -258,7 +258,7 @@ 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 (existsSync(location)) { - const contents = readFile(location, { encoding: 'utf-8' }); + 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/utils/StoryIndexGenerator.ts b/code/core/src/core-server/utils/StoryIndexGenerator.ts index b3bc3239b983..fb2a0f4b887c 100644 --- a/code/core/src/core-server/utils/StoryIndexGenerator.ts +++ b/code/core/src/core-server/utils/StoryIndexGenerator.ts @@ -756,7 +756,7 @@ export class StoryIndexGenerator { .map((ext) => join(this.options.configDir, `preview.${ext}`)) .find((fname) => existsSync(fname)); - return previewFile && (await readFile(previewFile, { encoding: 'utf-8' })).toString(); + return previewFile && (await readFile(previewFile, { encoding: 'utf8' })).toString(); } getProjectTags(previewCode?: string) { diff --git a/code/core/src/core-server/utils/server-init.ts b/code/core/src/core-server/utils/server-init.ts index cdcd3c920543..60710cb19cfb 100644 --- a/code/core/src/core-server/utils/server-init.ts +++ b/code/core/src/core-server/utils/server-init.ts @@ -30,9 +30,9 @@ export async function getServer( } const sslOptions = { - ca: await Promise.all((options.sslCa || []).map((ca) => readFile(ca, { encoding: 'utf-8' }))), - cert: await readFile(options.sslCert, { encoding: 'utf-8' }), - key: await readFile(options.sslKey, { encoding: '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/telemetry/get-monorepo-type.ts b/code/core/src/telemetry/get-monorepo-type.ts index e004365fcbb0..143241b0a214 100644 --- a/code/core/src/telemetry/get-monorepo-type.ts +++ b/code/core/src/telemetry/get-monorepo-type.ts @@ -36,7 +36,7 @@ export const getMonorepoType = (): MonorepoType => { } const packageJson = JSON.parse( - readFileSync(join(projectRootPath, 'package.json'), { encoding: 'utf-8' }) + readFileSync(join(projectRootPath, 'package.json'), { encoding: 'utf8' }) ) as PackageJson; if (packageJson?.workspaces) { diff --git a/code/core/src/telemetry/package-json.ts b/code/core/src/telemetry/package-json.ts index 3bf006115c4a..4104425fc2d0 100644 --- a/code/core/src/telemetry/package-json.ts +++ b/code/core/src/telemetry/package-json.ts @@ -24,6 +24,6 @@ export const getActualPackageJson = async (packageName: string) => { const resolvedPackageJson = require.resolve(join(packageName, 'package.json'), { paths: [process.cwd()], }); - const packageJson = JSON.parse(await readFile(resolvedPackageJson, { encoding: 'utf-8' })); + const packageJson = JSON.parse(await readFile(resolvedPackageJson, { encoding: 'utf8' })); return packageJson; }; 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 d5277a5e8c92..bf87b5fed74f 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 @@ -74,7 +74,7 @@ export const mdx1to3: Fix = { async run({ result: { storiesMdxFiles }, dryRun }) { await Promise.all([ ...storiesMdxFiles.map(async (fname) => { - const contents = await readFile(fname, { encoding: 'utf-8' }); + const contents = await readFile(fname, { encoding: 'utf8' }); const updated = fixMdxComments(fixMdxStyleTags(contents)); if (updated === contents) { logger.info(`🆗 Unmodified ${basename(fname)}`); diff --git a/code/lib/cli-storybook/src/link.ts b/code/lib/cli-storybook/src/link.ts index a858847e26b2..8f0b2ed6718c 100644 --- a/code/lib/cli-storybook/src/link.ts +++ b/code/lib/cli-storybook/src/link.ts @@ -61,7 +61,7 @@ export const exec = async ( export const link = async ({ target, local, start }: LinkOptions) => { const storybookDir = process.cwd(); try { - const packageJson = JSON.parse(await readFile('package.json', { encoding: 'utf-8' })); + const packageJson = JSON.parse(await readFile('package.json', { encoding: 'utf8' })); if (packageJson.name !== '@storybook/root') { throw new Error(); } @@ -87,7 +87,7 @@ export const link = async ({ target, local, start }: LinkOptions) => { } const reproPackageJson = JSON.parse( - await readFile(join(reproDir, 'package.json'), { encoding: 'utf-8' }) + await readFile(join(reproDir, 'package.json'), { encoding: 'utf8' }) ); const version = spawnSync('yarn', ['--version'], { diff --git a/code/renderers/server/src/preset.ts b/code/renderers/server/src/preset.ts index 04937a0b8c14..197a303bb15b 100644 --- a/code/renderers/server/src/preset.ts +++ b/code/renderers/server/src/preset.ts @@ -18,7 +18,7 @@ export const experimental_indexers: PresetProperty<'experimental_indexers'> = ( { test: /(stories|story)\.(json|ya?ml)$/, createIndex: async (fileName) => { - const rawFile = await readFile(fileName, { encoding: 'utf-8' }); + const rawFile = await readFile(fileName, { encoding: 'utf8' }); const content: FileContent = fileName.endsWith('.json') ? JSON.parse(rawFile) : yaml.parse(rawFile); From 8c58016b55eaffa301118d0b68628b01c18f448e Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 16 Sep 2024 12:00:10 +0200 Subject: [PATCH 31/58] CLI: Fix skip-install for stable latest releases --- .../JsPackageManager.test.ts | 54 +++++++++++++++++++ .../js-package-manager/JsPackageManager.ts | 12 ++--- 2 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 code/core/src/common/js-package-manager/JsPackageManager.test.ts 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..305ba8d99ed2 100644 --- a/code/core/src/common/js-package-manager/JsPackageManager.ts +++ b/code/core/src/common/js-package-manager/JsPackageManager.ts @@ -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}`; }) ); } From a3d75f58037260703c9d6d5fd70735a81352fbfb Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 16 Sep 2024 12:23:52 +0200 Subject: [PATCH 32/58] Core: Do not prebundle jsdoc-type-pratt-parser --- code/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/core/package.json b/code/core/package.json index 181b04312985..cd21b877c41c 100644 --- a/code/core/package.json +++ b/code/core/package.json @@ -283,6 +283,7 @@ "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.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", @@ -378,7 +379,6 @@ "globby": "^14.0.1", "handlebars": "^4.7.7", "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", From b40b44fe6d3c506b5c658f510f83592bebb036f7 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 16 Sep 2024 14:17:05 +0200 Subject: [PATCH 33/58] Core: Do not prebundle better-opn --- code/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/core/package.json b/code/core/package.json index 181b04312985..1d0ea0942bed 100644 --- a/code/core/package.json +++ b/code/core/package.json @@ -279,6 +279,7 @@ "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-register": "^3.5.0", @@ -340,7 +341,6 @@ "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", From 8346b85a547f37c801d68e3df9a6c5c0a70b8f43 Mon Sep 17 00:00:00 2001 From: ziebam Date: Mon, 16 Sep 2024 20:25:26 +0200 Subject: [PATCH 34/58] Remove the TODOs regarding `fsPromises.cp` --- code/builders/builder-vite/src/index.ts | 2 -- code/builders/builder-webpack5/src/index.ts | 2 -- code/core/src/builder-manager/index.ts | 2 -- code/core/src/cli/helpers.ts | 4 ---- code/core/src/core-server/build-static.ts | 2 -- code/core/src/core-server/utils/copy-all-static-files.ts | 2 -- 6 files changed, 14 deletions(-) diff --git a/code/builders/builder-vite/src/index.ts b/code/builders/builder-vite/src/index.ts index cdd4ed972eef..0962e1676e4e 100644 --- a/code/builders/builder-vite/src/index.ts +++ b/code/builders/builder-vite/src/index.ts @@ -84,8 +84,6 @@ export const build: ViteBuilder['build'] = async ({ options }) => { const previewDirOrigin = previewResolvedDir; const previewDirTarget = join(options.outputDir || '', `sb-preview`); - // TODO: `fsPromises.cp` is marked as experimental in Node 16-21. Ask in the PR whether we should - // use it anyway or stick to `fs-extra` for now. const previewFiles = cp(previewDirOrigin, previewDirTarget, { filter: (src) => { const { ext } = parse(src); diff --git a/code/builders/builder-webpack5/src/index.ts b/code/builders/builder-webpack5/src/index.ts index bf87a0700131..ffc11812562b 100644 --- a/code/builders/builder-webpack5/src/index.ts +++ b/code/builders/builder-webpack5/src/index.ts @@ -292,8 +292,6 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, const previewDirOrigin = previewResolvedDir; const previewDirTarget = join(options.outputDir || '', `sb-preview`); - // TODO: `fsPromises.cp` is marked as experimental in Node 16-21. Ask in the PR whether we should - // use it anyway or stick to `fs-extra` for now. const previewFiles = cp(previewDirOrigin, previewDirTarget, { filter: (src) => { const { ext } = parse(src); diff --git a/code/core/src/builder-manager/index.ts b/code/core/src/builder-manager/index.ts index a1589af27b23..b068caf206c7 100644 --- a/code/core/src/builder-manager/index.ts +++ b/code/core/src/builder-manager/index.ts @@ -256,8 +256,6 @@ const builder: BuilderFunction = async function* builderGeneratorFn({ startTime, yield; - // TODO: `fsPromises.cp` is marked as experimental in Node 16-21. Ask in the PR whether we should - // use it anyway or stick to `fs-extra` for now. const managerFiles = cp(coreDirOrigin, coreDirTarget, { filter: (src) => { const { ext } = parse(src); diff --git a/code/core/src/cli/helpers.ts b/code/core/src/cli/helpers.ts index 1b1448e7b469..5f567ebe5cbc 100644 --- a/code/core/src/cli/helpers.ts +++ b/code/core/src/cli/helpers.ts @@ -130,8 +130,6 @@ export function copyTemplate(templateRoot: string, destination = '.') { throw new Error(`Couldn't find template dir`); } - // TODO: `fsPromises.cpSync` is marked as experimental in Node 16-21. Ask in the PR whether we - // should use it anyway or stick to `fs-extra` for now. cpSync(templateDir, destination, { recursive: true }); } @@ -229,8 +227,6 @@ export async function copyTemplateFiles({ }; const destinationPath = destination ?? (await targetPath()); - // TODO: `fsPromises.cp` is marked as experimental in Node 16-21. Ask in the PR whether we should - // use it anyway or stick to `fs-extra` for now. if (commonAssetsDir) { await cp(commonAssetsDir, destinationPath, { recursive: true, diff --git a/code/core/src/core-server/build-static.ts b/code/core/src/core-server/build-static.ts index d0835d0d8a67..f1c9f9625910 100644 --- a/code/core/src/core-server/build-static.ts +++ b/code/core/src/core-server/build-static.ts @@ -132,8 +132,6 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption dirname(require.resolve('@storybook/core/package.json')), 'assets/browser' ); - // TODO: `fsPromises.cp` is marked as experimental in Node 16-21. Ask in the PR whether we should - // use it anyway or stick to `fs-extra` for now. effects.push(cp(coreServerPublicDir, options.outputDir, { recursive: true })); let initializedStoryIndexGenerator: Promise = 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 f0b3d331033d..66a0194cfd94 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 @@ -26,8 +26,6 @@ export async function copyAllStaticFiles(staticDirs: any[] | undefined, outputDi // 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)); - // TODO: `fsPromises.cp` is marked as experimental in Node 16-21. Ask in the PR whether we should - // use it anyway or stick to `fs-extra` for now. await cp(staticPath, targetPath, { dereference: true, preserveTimestamps: true, From 12f802955bcf45b3487615308da45055ab5a86b4 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 16 Sep 2024 21:12:09 +0200 Subject: [PATCH 35/58] Preact: Fix E2E test Since v10.24.0, Preact pretends to be React version 18.3.1 instead of 17.0.2. This change has caused an assertion failure in one of our E2E tests where we have assumed to see React 17.0.2 printed in one of our stories. --- code/e2e-tests/addon-docs.spec.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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/; From f0593f9e9ef1de714ce91400eab373703bfee6fc Mon Sep 17 00:00:00 2001 From: ziebam Date: Mon, 16 Sep 2024 21:49:11 +0200 Subject: [PATCH 36/58] Rewrite how `ensureFile` is replaced --- .../builder-vite/src/plugins/external-globals-plugin.ts | 9 +++++---- code/core/scripts/helpers/generateTypesMapperFiles.ts | 9 +++++---- code/core/src/builder-manager/utils/files.ts | 9 +++++---- code/core/src/builder-manager/utils/managerEntries.ts | 9 +++++---- 4 files changed, 20 insertions(+), 16 deletions(-) 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 6477ade92b4a..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,6 +1,6 @@ -import { closeSync, existsSync, openSync } from 'node:fs'; -import { writeFile } from 'node:fs/promises'; -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'; @@ -61,7 +61,8 @@ export async function externalGlobalsPlugin(externals: Record) { const externalCachePath = join(cachePath, `${externalKey}.js`); newAlias.push({ find: new RegExp(`^${externalKey}$`), replacement: externalCachePath }); if (!existsSync(externalCachePath)) { - closeSync(openSync(externalCachePath, 'w')); + const directory = dirname(externalCachePath); + await mkdir(directory, { recursive: true }); } await writeFile(externalCachePath, `module.exports = ${externals[externalKey]};`); }) diff --git a/code/core/scripts/helpers/generateTypesMapperFiles.ts b/code/core/scripts/helpers/generateTypesMapperFiles.ts index 11845a5e3239..5283321c84c0 100644 --- a/code/core/scripts/helpers/generateTypesMapperFiles.ts +++ b/code/core/scripts/helpers/generateTypesMapperFiles.ts @@ -1,6 +1,6 @@ -import { closeSync, existsSync, openSync } from 'node:fs'; -import { writeFile } from 'node:fs/promises'; -import { join, relative } from 'node:path'; +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 +34,8 @@ export async function generateTypesMapperFiles(entries: ReturnType { const location = filePath.replace('src', 'dist').replace(/\.tsx?/, '.d.ts'); if (!existsSync(location)) { - closeSync(openSync(location, 'w')); + const directory = dirname(location); + await mkdir(directory, { recursive: true }); } await writeFile(location, await generateTypesMapperContent(filePath)); }) diff --git a/code/core/src/builder-manager/utils/files.ts b/code/core/src/builder-manager/utils/files.ts index b83cb2e7a13e..352d558dd981 100644 --- a/code/core/src/builder-manager/utils/files.ts +++ b/code/core/src/builder-manager/utils/files.ts @@ -1,6 +1,6 @@ -import { closeSync, existsSync, openSync } from 'node:fs'; -import { writeFile } from 'node:fs/promises'; -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 slash from 'slash'; @@ -17,7 +17,8 @@ export async function readOrderedFiles( const { location, url } = sanitizePath(file, addonsDir); if (!existsSync(location)) { - closeSync(openSync(location, 'w')); + 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 3cb2f1d3aea9..787116d05c0e 100644 --- a/code/core/src/builder-manager/utils/managerEntries.ts +++ b/code/core/src/builder-manager/utils/managerEntries.ts @@ -1,6 +1,6 @@ -import { closeSync, existsSync, openSync } from 'node:fs'; -import { writeFile } from 'node:fs/promises'; -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'; @@ -57,7 +57,8 @@ export async function wrapManagerEntries(entrypoints: string[], uniqueId?: strin ); if (!existsSync(location)) { - closeSync(openSync(location, 'w')); + const directory = dirname(location); + await mkdir(directory, { recursive: true }); } await writeFile(location, `import '${slash(entry)}';`); From ea609bfdadefc78d4f241b38166c83c407c2ba40 Mon Sep 17 00:00:00 2001 From: ziebam Date: Mon, 16 Sep 2024 22:10:51 +0200 Subject: [PATCH 37/58] Remove `fs-extra` `from lib/cli/scripts` --- code/lib/cli/scripts/update-core-portal.ts | 11 +++++++---- code/lib/cli/scripts/utils.ts | 10 ++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) 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); }; From b034203200861887eb5d8f0eba28b30047f7a2c7 Mon Sep 17 00:00:00 2001 From: ziebam Date: Mon, 16 Sep 2024 22:23:35 +0200 Subject: [PATCH 38/58] Fix the `emptyDir` + `ensureDir` replacement --- code/core/src/core-server/build-static.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/code/core/src/core-server/build-static.ts b/code/core/src/core-server/build-static.ts index f1c9f9625910..45ba7c7bc6d3 100644 --- a/code/core/src/core-server/build-static.ts +++ b/code/core/src/core-server/build-static.ts @@ -1,3 +1,4 @@ +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'; @@ -45,8 +46,18 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption throw new Error("Won't remove directory '/'. Check your outputDir!"); } - const outputDirFiles = await readdir(options.outputDir); - if (outputDirFiles.length > 0) { + 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 }); } From 7262c526493a3ed3f866e4efd13cdca9523ff4f9 Mon Sep 17 00:00:00 2001 From: storybook-bot <32066757+storybook-bot@users.noreply.github.com> Date: Mon, 16 Sep 2024 20:31:45 +0000 Subject: [PATCH 39/58] Update CHANGELOG.md for v8.3.1 [skip ci] --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0b00c7b7217..32d3d51cbc00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 8.3.1 + +- Angular: Fix sourceDecorator to apply excludeDecorators flag - [#29069](https://github.com/storybookjs/storybook/pull/29069), thanks @JSMike! +- Core: Do not prebundle better-opn - [#29137](https://github.com/storybookjs/storybook/pull/29137), thanks @valentinpalkovic! +- Core: Do not prebundle jsdoc-type-pratt-parser - [#29134](https://github.com/storybookjs/storybook/pull/29134), thanks @valentinpalkovic! +- Next.js: Upgrade sass-loader from ^12 to ^13 - [#29040](https://github.com/storybookjs/storybook/pull/29040), thanks @HoncharenkoZhenya! + ## 8.3.0 Fresh out of the oven! Storybook 8.3 brings you: From 8115974dbd56af2de7a7c9ffa3b91583a7b7d9b2 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 17 Sep 2024 10:19:22 +0200 Subject: [PATCH 40/58] remove @types/fs-extra --- code/lib/cli-storybook/src/sandbox.ts | 2 +- code/package.json | 1 - code/yarn.lock | 11 ----------- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/code/lib/cli-storybook/src/sandbox.ts b/code/lib/cli-storybook/src/sandbox.ts index 4bbe097d7d16..9b02ca82dd4e 100644 --- a/code/lib/cli-storybook/src/sandbox.ts +++ b/code/lib/cli-storybook/src/sandbox.ts @@ -207,7 +207,7 @@ 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); throw new Error(dedent` diff --git a/code/package.json b/code/package.json index 000f4d270a0b..af2a344764ec 100644 --- a/code/package.json +++ b/code/package.json @@ -168,7 +168,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", diff --git a/code/yarn.lock b/code/yarn.lock index f4822025b30a..33d11fccd4f5 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6867,7 +6867,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" @@ -7855,16 +7854,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" From 4ebba9a7377a78c80084bf7a078c6e9109a4ccda Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Tue, 17 Sep 2024 10:49:22 +0200 Subject: [PATCH 41/58] Build Storybook on CI (skip install case) --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3e5c0b499cc6..72f3bf9dc225 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -628,6 +628,8 @@ jobs: mkdir empty-<< parameters.template >>-no-install cd empty-<< parameters.template >>-no-install npx storybook init --yes --skip-install + npm install + npm run build-storybook environment: IN_STORYBOOK_SANDBOX: true STORYBOOK_INIT_EMPTY_TYPE: << parameters.template >> From 2d2943b7822187de506ae30880e72ddb6b746a7b Mon Sep 17 00:00:00 2001 From: InfiniteXyy Date: Wed, 18 Sep 2024 15:03:28 +0800 Subject: [PATCH 42/58] feat[vitest-plugin]: support read story name for vitest test description --- .../vitest-plugin/transformer.test.ts | 36 +++++++++++++++++++ .../csf-tools/vitest-plugin/transformer.ts | 26 +++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) 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..4c974a08ca36 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,42 @@ describe('transformer', () => { `); }); + it("should use the story's explicitly settled name if it's present", async () => { + const code = ` + export default { + component: Button, + } + export const Primary = { + name: "basic Primary Button scenario", + args: { + label: 'Primary Button', + }, + }; + `; + + 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: "basic Primary Button scenario", + args: { + label: 'Primary Button' + } + }; + const _isRunningFromThisFile = import.meta.url.includes(globalThis.__vitest_worker__.filepath ?? _expect.getState().testPath); + if (_isRunningFromThisFile) { + _test("basic Primary Button scenario", _testStory("Primary", Primary, _meta, [])); + } + `); + }); + it('should add test statement to const declared exported 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..ed1e768bfc91 100644 --- a/code/core/src/csf-tools/vitest-plugin/transformer.ts +++ b/code/core/src/csf-tools/vitest-plugin/transformer.ts @@ -207,10 +207,34 @@ export async function vitestTransform({ exportName: string; node: t.Node; }): t.ExpressionStatement => { + // Get test name from the story's name property, and fallback to exportName + const testName = (function getTestName(): string { + if ( + node.type === 'ExportNamedDeclaration' && + node.declaration?.type === 'VariableDeclaration' + ) { + const storyDeclarator = node.declaration.declarations[0]; + if (storyDeclarator.init?.type === 'ObjectExpression') { + for (const prop of storyDeclarator.init.properties) { + if ( + // Filter out the "name" property and return its value + prop.type === 'ObjectProperty' && + prop.key.type === 'Identifier' && + prop.value.type === 'StringLiteral' && + prop.key.name === 'name' + ) { + return prop.value.value; + } + } + } + } + return exportName; + })(); + // Create the _test expression directly using the exportName identifier const testStoryCall = t.expressionStatement( t.callExpression(vitestTestId, [ - t.stringLiteral(exportName), + t.stringLiteral(testName), t.callExpression(testStoryId, [ t.stringLiteral(exportName), t.identifier(exportName), From 5ac38c3bef606d8db7b64b90497478e53d6e9043 Mon Sep 17 00:00:00 2001 From: InfiniteXyy Date: Wed, 18 Sep 2024 15:20:04 +0800 Subject: [PATCH 43/58] chore: update vitest document --- docs/writing-tests/vitest-plugin.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/writing-tests/vitest-plugin.mdx b/docs/writing-tests/vitest-plugin.mdx index 09940ef230d9..b0a5fae00377 100644 --- a/docs/writing-tests/vitest-plugin.mdx +++ b/docs/writing-tests/vitest-plugin.mdx @@ -379,6 +379,10 @@ We recommend running tests in a browser using Playwright, but you can use WebDri 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 customized the test description + +We are using the exportName of a story definition by default, but you can provide a `name` property for the story to customize the test description. This is useful when you want have places, brackets or other special characters in the test description. + ## API ### Exports From a70830b17f912da3690a891bb64a1f7bcad3014e Mon Sep 17 00:00:00 2001 From: InfiniteXyy Date: Wed, 18 Sep 2024 15:27:57 +0800 Subject: [PATCH 44/58] chore: remove iife --- .../csf-tools/vitest-plugin/transformer.ts | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/code/core/src/csf-tools/vitest-plugin/transformer.ts b/code/core/src/csf-tools/vitest-plugin/transformer.ts index ed1e768bfc91..01cf6a1bf804 100644 --- a/code/core/src/csf-tools/vitest-plugin/transformer.ts +++ b/code/core/src/csf-tools/vitest-plugin/transformer.ts @@ -208,28 +208,27 @@ export async function vitestTransform({ node: t.Node; }): t.ExpressionStatement => { // Get test name from the story's name property, and fallback to exportName - const testName = (function getTestName(): string { - if ( - node.type === 'ExportNamedDeclaration' && - node.declaration?.type === 'VariableDeclaration' - ) { - const storyDeclarator = node.declaration.declarations[0]; - if (storyDeclarator.init?.type === 'ObjectExpression') { - for (const prop of storyDeclarator.init.properties) { - if ( - // Filter out the "name" property and return its value - prop.type === 'ObjectProperty' && - prop.key.type === 'Identifier' && - prop.value.type === 'StringLiteral' && - prop.key.name === 'name' - ) { - return prop.value.value; - } + let testName = exportName; + if ( + node.type === 'ExportNamedDeclaration' && + node.declaration?.type === 'VariableDeclaration' + ) { + const storyDeclarator = node.declaration.declarations[0]; + if (storyDeclarator.init?.type === 'ObjectExpression') { + // Find the "name" property and set its value to testName + for (const prop of storyDeclarator.init.properties) { + if ( + prop.type === 'ObjectProperty' && + prop.key.type === 'Identifier' && + prop.value.type === 'StringLiteral' && + prop.key.name === 'name' + ) { + testName = prop.value.value; + break; } } } - return exportName; - })(); + } // Create the _test expression directly using the exportName identifier const testStoryCall = t.expressionStatement( From 0d238e2d4382e03c5d65f8a7267fdcc774a86816 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Thu, 19 Sep 2024 10:46:56 +0200 Subject: [PATCH 45/58] Core: Do not add packageManager field to package.json during storybook dev --- .../JsPackageManagerFactory.ts | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) 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; From 221f6a78332adb67450ad94f172aac6cf490de12 Mon Sep 17 00:00:00 2001 From: storybook-bot <32066757+storybook-bot@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:05:43 +0000 Subject: [PATCH 46/58] Update CHANGELOG.md for v8.3.2 [skip ci] --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32d3d51cbc00..0bae092457f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 8.3.2 + +- CLI: Fix skip-install for stable latest releases - [#29133](https://github.com/storybookjs/storybook/pull/29133), thanks @valentinpalkovic! +- Core: Do not add packageManager field to package.json during `storybook dev` - [#29152](https://github.com/storybookjs/storybook/pull/29152), thanks @valentinpalkovic! + ## 8.3.1 - Angular: Fix sourceDecorator to apply excludeDecorators flag - [#29069](https://github.com/storybookjs/storybook/pull/29069), thanks @JSMike! From 3bae5cf8abff96e9b066d07618a550975c82eaf5 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Fri, 20 Sep 2024 22:47:26 +0800 Subject: [PATCH 47/58] Chore: Add jiti as dev dependency to core --- code/core/package.json | 1 + code/yarn.lock | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/code/core/package.json b/code/core/package.json index e4d64b8fb473..893e2c0234f2 100644 --- a/code/core/package.json +++ b/code/core/package.json @@ -376,6 +376,7 @@ "glob": "^10.0.0", "globby": "^14.0.1", "handlebars": "^4.7.7", + "jiti": "^1.21.6", "js-yaml": "^4.1.0", "lazy-universal-dotenv": "^4.0.0", "leven": "^4.0.0", diff --git a/code/yarn.lock b/code/yarn.lock index 33d11fccd4f5..3cf456e4f9ab 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6091,6 +6091,7 @@ __metadata: 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" @@ -18340,6 +18341,15 @@ __metadata: languageName: node linkType: hard +"jiti@npm:^1.21.6": + version: 1.21.6 + resolution: "jiti@npm:1.21.6" + bin: + jiti: bin/jiti.js + checksum: 10c0/05b9ed58cd30d0c3ccd3c98209339e74f50abd9a17e716f65db46b6a35812103f6bde6e134be7124d01745586bca8cc5dae1d0d952267c3ebe55171949c32e56 + languageName: node + linkType: hard + "jju@npm:^1.4.0": version: 1.4.0 resolution: "jju@npm:1.4.0" From b11edcfd5016b7f30ffb098a0a150cb61796926d Mon Sep 17 00:00:00 2001 From: Mike Cebrian Date: Fri, 20 Sep 2024 20:09:35 -0400 Subject: [PATCH 48/58] Update TooltipLinkList.tsx to add href to item object for onclick events https://github.com/storybookjs/storybook/blob/v8.3.2/code/core/src/manager/components/sidebar/RefIndicator.tsx#L226 requires item.href --- .../src/components/components/tooltip/TooltipLinkList.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/core/src/components/components/tooltip/TooltipLinkList.tsx b/code/core/src/components/components/tooltip/TooltipLinkList.tsx index 51540335d034..43c7f4c7ccf4 100644 --- a/code/core/src/components/components/tooltip/TooltipLinkList.tsx +++ b/code/core/src/components/components/tooltip/TooltipLinkList.tsx @@ -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 ; From 188271777e9edc0076f7399eddaecdd5487cb722 Mon Sep 17 00:00:00 2001 From: InfiniteXyy Date: Sat, 21 Sep 2024 17:17:25 +0800 Subject: [PATCH 49/58] chore: refactor with parsedStory --- .../vitest-plugin/transformer.test.ts | 98 +++++++++++++------ .../csf-tools/vitest-plugin/transformer.ts | 34 ++----- docs/writing-tests/vitest-plugin.mdx | 7 +- 3 files changed, 79 insertions(+), 60 deletions(-) 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 4c974a08ca36..fdbfd2bb5b43 100644 --- a/code/core/src/csf-tools/vitest-plugin/transformer.test.ts +++ b/code/core/src/csf-tools/vitest-plugin/transformer.test.ts @@ -213,40 +213,76 @@ describe('transformer', () => { `); }); - it("should use the story's explicitly settled name if it's present", async () => { - const code = ` - export default { - component: Button, - } - export const Primary = { - name: "basic Primary Button scenario", - args: { - label: 'Primary Button', - }, - }; - `; + describe("use the story's name as test title", () => { + it('should support csf v3 with object config', 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, [])); + } + `); + }); - const result = await transform({ code }); + it('should support csf v3 with function config', async () => { + const code = ` + export default { component: Button }; + export const Story = () => { 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 Story = () => { + name: 'custom name'; + }; + const _isRunningFromThisFile = import.meta.url.includes(globalThis.__vitest_worker__.filepath ?? _expect.getState().testPath); + if (_isRunningFromThisFile) { + _test("Story", _testStory("Story", Story, _meta, [])); + } + `); + }); - 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: "basic Primary Button scenario", - args: { - label: 'Primary Button' + it('should support csf v1/v2', 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, [])); } - }; - const _isRunningFromThisFile = import.meta.url.includes(globalThis.__vitest_worker__.filepath ?? _expect.getState().testPath); - if (_isRunningFromThisFile) { - _test("basic Primary Button scenario", _testStory("Primary", Primary, _meta, [])); - } - `); + `); + }); }); it('should add test statement to const declared exported stories', async () => { diff --git a/code/core/src/csf-tools/vitest-plugin/transformer.ts b/code/core/src/csf-tools/vitest-plugin/transformer.ts index 01cf6a1bf804..778ea752f1d0 100644 --- a/code/core/src/csf-tools/vitest-plugin/transformer.ts +++ b/code/core/src/csf-tools/vitest-plugin/transformer.ts @@ -202,38 +202,17 @@ export async function vitestTransform({ const getTestStatementForStory = ({ exportName, + testTitle, node, }: { exportName: string; + testTitle: string; node: t.Node; }): t.ExpressionStatement => { - // Get test name from the story's name property, and fallback to exportName - let testName = exportName; - if ( - node.type === 'ExportNamedDeclaration' && - node.declaration?.type === 'VariableDeclaration' - ) { - const storyDeclarator = node.declaration.declarations[0]; - if (storyDeclarator.init?.type === 'ObjectExpression') { - // Find the "name" property and set its value to testName - for (const prop of storyDeclarator.init.properties) { - if ( - prop.type === 'ObjectProperty' && - prop.key.type === 'Identifier' && - prop.value.type === 'StringLiteral' && - prop.key.name === 'name' - ) { - testName = prop.value.value; - break; - } - } - } - } - // Create the _test expression directly using the exportName identifier const testStoryCall = t.expressionStatement( t.callExpression(vitestTestId, [ - t.stringLiteral(testName), + t.stringLiteral(testTitle), t.callExpression(testStoryId, [ t.stringLiteral(exportName), t.identifier(exportName), @@ -262,10 +241,9 @@ export async function vitestTransform({ return; } - return getTestStatementForStory({ - exportName, - node, - }); + // 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, exportName, node }); }) .filter((st) => !!st) as t.ExpressionStatement[]; diff --git a/docs/writing-tests/vitest-plugin.mdx b/docs/writing-tests/vitest-plugin.mdx index b0a5fae00377..2bf22f19f080 100644 --- a/docs/writing-tests/vitest-plugin.mdx +++ b/docs/writing-tests/vitest-plugin.mdx @@ -381,7 +381,12 @@ We recommend using Chromium, because it is most likely to best match the experie ### How do I customized the test description -We are using the exportName of a story definition by default, but you can provide a `name` property for the story to customize the test description. This is useful when you want have places, brackets or other special characters in the test description. +By default, the export name of a story is mapped to the test name. You can provide a `name` property for the story to customize the test description, allowing you to write more descriptive names. This is useful when you want use spaces, brackets or other special characters in the test description. +```js +export const Story = { + name: 'custom, descriptive name' +}; +``` ## API From d43564ecb4b946018c9d77fb167ec59210a15359 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 22 Sep 2024 23:47:30 +0800 Subject: [PATCH 50/58] Vite: Add jsdoc-type-pratt-parser to `optimizeDeps` --- code/builders/builder-vite/src/optimizeDeps.ts | 1 + 1 file changed, 1 insertion(+) 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', From d02d6b49e54c68c7da4f43cfdaa1bc7ae1b713fa Mon Sep 17 00:00:00 2001 From: hyeongrok7874 Date: Mon, 23 Sep 2024 07:02:09 +0900 Subject: [PATCH 51/58] feat: Add InitialViewportKeyUnion type --- code/addons/viewport/src/defaults.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/code/addons/viewport/src/defaults.ts b/code/addons/viewport/src/defaults.ts index 4190ccb7f3cc..014c90f22b34 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 InitialViewportKeyUnion = keyof typeof INITIAL_VIEWPORTS_DATA; + +export const INITIAL_VIEWPORTS: ViewportMap = INITIAL_VIEWPORTS_DATA; + export const DEFAULT_VIEWPORT = 'responsive'; export const MINIMAL_VIEWPORTS: ViewportMap = { From d2a98f190d51f2b249fe74187e3018a0b33c502d Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Mon, 23 Sep 2024 11:42:39 +0200 Subject: [PATCH 52/58] update tests --- .../vitest-plugin/transformer.test.ts | 27 ++----------------- 1 file changed, 2 insertions(+), 25 deletions(-) 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 fdbfd2bb5b43..84d9ac273718 100644 --- a/code/core/src/csf-tools/vitest-plugin/transformer.test.ts +++ b/code/core/src/csf-tools/vitest-plugin/transformer.test.ts @@ -214,7 +214,7 @@ describe('transformer', () => { }); describe("use the story's name as test title", () => { - it('should support csf v3 with object config', async () => { + it('should support CSF v3 via name property', async () => { const code = ` export default { component: Button } export const Primary = { name: "custom name" };`; @@ -238,30 +238,7 @@ describe('transformer', () => { `); }); - it('should support csf v3 with function config', async () => { - const code = ` - export default { component: Button }; - export const Story = () => { 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 Story = () => { - name: 'custom name'; - }; - const _isRunningFromThisFile = import.meta.url.includes(globalThis.__vitest_worker__.filepath ?? _expect.getState().testPath); - if (_isRunningFromThisFile) { - _test("Story", _testStory("Story", Story, _meta, [])); - } - `); - }); - - it('should support csf v1/v2', async () => { + it('should support CSF v1/v2 via storyName property', async () => { const code = ` export default { component: Button } export const Story = () => {} From d5ba8e56e787a3e860e053fc076fe6b818376ab9 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Mon, 23 Sep 2024 05:49:48 -0400 Subject: [PATCH 53/58] React-Vite: Downgrade react-docgen-typescript plugin --- code/frameworks/react-vite/package.json | 2 +- code/yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/code/frameworks/react-vite/package.json b/code/frameworks/react-vite/package.json index ba7b57702d3f..fa1a682656bb 100644 --- a/code/frameworks/react-vite/package.json +++ b/code/frameworks/react-vite/package.json @@ -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/yarn.lock b/code/yarn.lock index 33d11fccd4f5..d08dcad8d77a 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -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 @@ -6699,7 +6699,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:*" From f665c36e3d02e621ea989f502c23dfbbc5321d50 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Mon, 23 Sep 2024 17:48:40 +0200 Subject: [PATCH 54/58] update faq --- docs/writing-tests/vitest-plugin.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/writing-tests/vitest-plugin.mdx b/docs/writing-tests/vitest-plugin.mdx index 2bf22f19f080..1a0e4b9708b2 100644 --- a/docs/writing-tests/vitest-plugin.mdx +++ b/docs/writing-tests/vitest-plugin.mdx @@ -379,9 +379,10 @@ We recommend running tests in a browser using Playwright, but you can use WebDri 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 customized the test description +### 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. -By default, the export name of a story is mapped to the test name. You can provide a `name` property for the story to customize the test description, allowing you to write more descriptive names. This is useful when you want use spaces, brackets or other special characters in the test description. ```js export const Story = { name: 'custom, descriptive name' From af8d7573c6b8330431e15f51cdd18420e5a49fa7 Mon Sep 17 00:00:00 2001 From: andrasczeh <34349609+andrasczeh@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:01:06 +0200 Subject: [PATCH 55/58] #23619 Show constraints in error when getting depndencies --- code/core/src/common/js-package-manager/JsPackageManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/core/src/common/js-package-manager/JsPackageManager.ts b/code/core/src/common/js-package-manager/JsPackageManager.ts index 305ba8d99ed2..1d1f2ef5367c 100644 --- a/code/core/src/common/js-package-manager/JsPackageManager.ts +++ b/code/core/src/common/js-package-manager/JsPackageManager.ts @@ -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; } From dd170eb92c01e686801ff2cd0b00659a956b0b71 Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 24 Sep 2024 12:40:58 +0200 Subject: [PATCH 56/58] Fix types --- code/core/src/components/components/tooltip/ListItem.tsx | 2 +- code/core/src/components/components/tooltip/TooltipLinkList.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 43c7f4c7ccf4..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; } From 538de4239c406d0a394ab5fcb8ab93917a41d34a Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Tue, 24 Sep 2024 12:45:40 +0200 Subject: [PATCH 57/58] Update code/addons/viewport/src/defaults.ts --- code/addons/viewport/src/defaults.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/addons/viewport/src/defaults.ts b/code/addons/viewport/src/defaults.ts index 014c90f22b34..d29e380ac230 100644 --- a/code/addons/viewport/src/defaults.ts +++ b/code/addons/viewport/src/defaults.ts @@ -227,7 +227,7 @@ const INITIAL_VIEWPORTS_DATA = { }, } as const; -export type InitialViewportKeyUnion = keyof typeof INITIAL_VIEWPORTS_DATA; +export type InitialViewportKeys = keyof typeof INITIAL_VIEWPORTS_DATA; export const INITIAL_VIEWPORTS: ViewportMap = INITIAL_VIEWPORTS_DATA; From 02598e58c162f06d10c621afd79222b3482923d8 Mon Sep 17 00:00:00 2001 From: storybook-bot <32066757+storybook-bot@users.noreply.github.com> Date: Tue, 24 Sep 2024 11:24:35 +0000 Subject: [PATCH 58/58] Write changelog for 8.4.0-alpha.1 [skip ci] --- CHANGELOG.prerelease.md | 19 +++++++++++++++++++ code/package.json | 3 ++- docs/versions/next.json | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index 692c7edaf2e9..b9179e8d6dcc 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,22 @@ +## 8.4.0-alpha.1 + +- Addon Test: Support story name as test description - [#29147](https://github.com/storybookjs/storybook/pull/29147), thanks @InfiniteXyy! +- Addon-Interactions: Use ansi-to-html for colored test errors - [#29110](https://github.com/storybookjs/storybook/pull/29110), thanks @kasperpeulen! +- Angular: Fix sourceDecorator to apply excludeDecorators flag - [#29069](https://github.com/storybookjs/storybook/pull/29069), thanks @JSMike! +- Builder-vite: Replace .at() call with [] in codegen - [#29048](https://github.com/storybookjs/storybook/pull/29048), thanks @Chudesnov! +- CLI: Ensure `.gitignore` updated via CLI ends with a newline - [#29124](https://github.com/storybookjs/storybook/pull/29124), thanks @3w36zj6! +- CLI: Fix skip-install for stable latest releases - [#29133](https://github.com/storybookjs/storybook/pull/29133), thanks @valentinpalkovic! +- CLI: Show constraints in error when getting depndencies - [#29187](https://github.com/storybookjs/storybook/pull/29187), thanks @andrasczeh! +- Core: Do not add packageManager field to package.json during `storybook dev` - [#29152](https://github.com/storybookjs/storybook/pull/29152), thanks @valentinpalkovic! +- Core: Do not prebundle better-opn - [#29137](https://github.com/storybookjs/storybook/pull/29137), thanks @valentinpalkovic! +- Core: Do not prebundle jsdoc-type-pratt-parser - [#29134](https://github.com/storybookjs/storybook/pull/29134), thanks @valentinpalkovic! +- Core: Replace `fs-extra` with the native APIs - [#29126](https://github.com/storybookjs/storybook/pull/29126), thanks @ziebam! +- Next.js: Upgrade sass-loader from ^12 to ^13 - [#29040](https://github.com/storybookjs/storybook/pull/29040), thanks @HoncharenkoZhenya! +- React-Vite: Downgrade react-docgen-typescript plugin - [#29184](https://github.com/storybookjs/storybook/pull/29184), thanks @shilman! +- UI: Fix composed storybook TooltipLinkList bug where href isn't passed forward - [#29175](https://github.com/storybookjs/storybook/pull/29175), thanks @JSMike! +- Viewport-addon: Add InitialViewportKeys type to viewport addon - [#29182](https://github.com/storybookjs/storybook/pull/29182), thanks @hyeongrok7874! +- Vite: Add jsdoc-type-pratt-parser to `optimizeDeps` - [#29179](https://github.com/storybookjs/storybook/pull/29179), thanks @tobiasdiez! + ## 8.4.0-alpha.0 diff --git a/code/package.json b/code/package.json index af2a344764ec..a6bd01ea00a2 100644 --- a/code/package.json +++ b/code/package.json @@ -293,5 +293,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "8.4.0-alpha.1" } diff --git a/docs/versions/next.json b/docs/versions/next.json index 9963ff265181..144772e63ef6 100644 --- a/docs/versions/next.json +++ b/docs/versions/next.json @@ -1 +1 @@ -{"version":"8.4.0-alpha.0","info":{"plain":""}} +{"version":"8.4.0-alpha.1","info":{"plain":"- Addon Test: Support story name as test description - [#29147](https://github.com/storybookjs/storybook/pull/29147), thanks @InfiniteXyy!\n- Addon-Interactions: Use ansi-to-html for colored test errors - [#29110](https://github.com/storybookjs/storybook/pull/29110), thanks @kasperpeulen!\n- Angular: Fix sourceDecorator to apply excludeDecorators flag - [#29069](https://github.com/storybookjs/storybook/pull/29069), thanks @JSMike!\n- Builder-vite: Replace .at() call with [] in codegen - [#29048](https://github.com/storybookjs/storybook/pull/29048), thanks @Chudesnov!\n- CLI: Ensure `.gitignore` updated via CLI ends with a newline - [#29124](https://github.com/storybookjs/storybook/pull/29124), thanks @3w36zj6!\n- CLI: Fix skip-install for stable latest releases - [#29133](https://github.com/storybookjs/storybook/pull/29133), thanks @valentinpalkovic!\n- CLI: Show constraints in error when getting depndencies - [#29187](https://github.com/storybookjs/storybook/pull/29187), thanks @andrasczeh!\n- Core: Do not add packageManager field to package.json during `storybook dev` - [#29152](https://github.com/storybookjs/storybook/pull/29152), thanks @valentinpalkovic!\n- Core: Do not prebundle better-opn - [#29137](https://github.com/storybookjs/storybook/pull/29137), thanks @valentinpalkovic!\n- Core: Do not prebundle jsdoc-type-pratt-parser - [#29134](https://github.com/storybookjs/storybook/pull/29134), thanks @valentinpalkovic!\n- Core: Replace `fs-extra` with the native APIs - [#29126](https://github.com/storybookjs/storybook/pull/29126), thanks @ziebam!\n- Next.js: Upgrade sass-loader from ^12 to ^13 - [#29040](https://github.com/storybookjs/storybook/pull/29040), thanks @HoncharenkoZhenya!\n- React-Vite: Downgrade react-docgen-typescript plugin - [#29184](https://github.com/storybookjs/storybook/pull/29184), thanks @shilman!\n- UI: Fix composed storybook TooltipLinkList bug where href isn't passed forward - [#29175](https://github.com/storybookjs/storybook/pull/29175), thanks @JSMike!\n- Viewport-addon: Add InitialViewportKeys type to viewport addon - [#29182](https://github.com/storybookjs/storybook/pull/29182), thanks @hyeongrok7874!\n- Vite: Add jsdoc-type-pratt-parser to `optimizeDeps` - [#29179](https://github.com/storybookjs/storybook/pull/29179), thanks @tobiasdiez!"}}