diff --git a/.circleci/config.yml b/.circleci/config.yml index 2145f39bfaa7..2cc4eda78dc1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -950,7 +950,7 @@ jobs: mkdir features-1 cd features-1 npm set registry http://localhost:6001 - npx create-storybook --yes --package-manager npm --features docs test a11y + npx create-storybook --yes --package-manager npm --features docs test a11y --loglevel=debug npx vitest environment: IN_STORYBOOK_SANDBOX: true @@ -1172,7 +1172,7 @@ workflows: requires: - build - create-sandboxes: - parallelism: 34 + parallelism: 38 requires: - build - check-sandboxes: @@ -1180,7 +1180,7 @@ workflows: requires: - create-sandboxes - chromatic-sandboxes: - parallelism: 31 + parallelism: 35 requires: - create-sandboxes - e2e-production: @@ -1192,7 +1192,7 @@ workflows: requires: - create-sandboxes - test-runner-production: - parallelism: 29 + parallelism: 33 requires: - create-sandboxes - vitest-integration: @@ -1286,11 +1286,11 @@ workflows: requires: - unit-tests - create-sandboxes: - parallelism: 19 + parallelism: 21 requires: - build - chromatic-sandboxes: - parallelism: 16 + parallelism: 18 requires: - create-sandboxes - e2e-production: @@ -1302,7 +1302,7 @@ workflows: requires: - create-sandboxes - test-runner-production: - parallelism: 14 + parallelism: 16 requires: - create-sandboxes - vitest-integration: @@ -1381,11 +1381,11 @@ workflows: requires: - unit-tests - create-sandboxes: - parallelism: 13 + parallelism: 14 requires: - build - chromatic-sandboxes: - parallelism: 10 + parallelism: 11 requires: - create-sandboxes - e2e-production: @@ -1397,7 +1397,7 @@ workflows: requires: - create-sandboxes - test-runner-production: - parallelism: 8 + parallelism: 9 requires: - create-sandboxes - vitest-integration: diff --git a/.circleci/src/workflows/daily.yml b/.circleci/src/workflows/daily.yml index 5af817bd33c9..7f02e5d6117e 100644 --- a/.circleci/src/workflows/daily.yml +++ b/.circleci/src/workflows/daily.yml @@ -31,7 +31,7 @@ jobs: requires: - build - create-sandboxes: - parallelism: 34 + parallelism: 38 requires: - build - check-sandboxes: @@ -42,7 +42,7 @@ jobs: # requires: # - create-sandboxes - chromatic-sandboxes: - parallelism: 31 + parallelism: 35 requires: - create-sandboxes - e2e-production: @@ -54,7 +54,7 @@ jobs: requires: - create-sandboxes - test-runner-production: - parallelism: 29 + parallelism: 33 requires: - create-sandboxes - vitest-integration: diff --git a/.circleci/src/workflows/merged.yml b/.circleci/src/workflows/merged.yml index e0b5528a93bf..b0ae80bb63ea 100644 --- a/.circleci/src/workflows/merged.yml +++ b/.circleci/src/workflows/merged.yml @@ -34,11 +34,11 @@ jobs: requires: - unit-tests - create-sandboxes: - parallelism: 19 + parallelism: 21 requires: - build - chromatic-sandboxes: - parallelism: 16 + parallelism: 18 requires: - create-sandboxes - e2e-production: @@ -50,7 +50,7 @@ jobs: requires: - create-sandboxes - test-runner-production: - parallelism: 14 + parallelism: 16 requires: - create-sandboxes - vitest-integration: diff --git a/.circleci/src/workflows/normal.yml b/.circleci/src/workflows/normal.yml index 1fe5bf46ed63..a3f4ed1de6f6 100644 --- a/.circleci/src/workflows/normal.yml +++ b/.circleci/src/workflows/normal.yml @@ -34,11 +34,11 @@ jobs: requires: - unit-tests - create-sandboxes: - parallelism: 13 + parallelism: 14 requires: - build - chromatic-sandboxes: - parallelism: 10 + parallelism: 11 requires: - create-sandboxes - e2e-production: @@ -50,7 +50,7 @@ jobs: requires: - create-sandboxes - test-runner-production: - parallelism: 8 + parallelism: 9 requires: - create-sandboxes - vitest-integration: diff --git a/code/core/src/csf-tools/ConfigFile.ts b/code/core/src/csf-tools/ConfigFile.ts index 91e2e3d5c402..c5f376f5096f 100644 --- a/code/core/src/csf-tools/ConfigFile.ts +++ b/code/core/src/csf-tools/ConfigFile.ts @@ -1179,7 +1179,7 @@ export const isCsfFactoryPreview = (previewConfig: ConfigFile) => { return !!program.body.find((node) => { return ( t.isImportDeclaration(node) && - node.source.value.includes('@storybook') && + node.source.value.includes('storybook') && node.specifiers.some((specifier) => { return ( t.isImportSpecifier(specifier) && diff --git a/code/core/src/telemetry/get-framework-info.test.ts b/code/core/src/telemetry/get-framework-info.test.ts index 8fc25c4b99ca..948e1bb43720 100644 --- a/code/core/src/telemetry/get-framework-info.test.ts +++ b/code/core/src/telemetry/get-framework-info.test.ts @@ -1,58 +1,47 @@ -import { sep } from 'node:path'; - -import { describe, expect, it, vi } from 'vitest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; import type { StorybookConfig } from 'storybook/internal/types'; import { getFrameworkInfo } from './get-framework-info'; -import { getActualPackageJson } from './package-json'; -vi.mock('./package-json', () => ({ - getActualPackageJson: vi.fn(), +vi.mock('storybook/internal/common', () => ({ + getStorybookInfo: vi.fn(), })); describe('getFrameworkInfo', () => { - it('should return an empty object if mainConfig.framework is undefined', async () => { - const result = await getFrameworkInfo({} as StorybookConfig); - expect(result).toEqual({}); + const defaultInfo = { + frameworkPackage: '@storybook/react', + rendererPackage: '@storybook/react', + builderPackage: '@storybook/builder-vite', + }; + + beforeEach(async () => { + const { getStorybookInfo } = await import('storybook/internal/common'); + vi.mocked(getStorybookInfo).mockResolvedValue(defaultInfo as any); }); - it('should return an empty object if mainConfig.framework name is undefined', async () => { - const result = await getFrameworkInfo({ framework: {} } as StorybookConfig); - expect(result).toEqual({}); + it('returns framework/builder/renderer with empty options when no framework provided', async () => { + const result = await getFrameworkInfo({} as StorybookConfig, '/tmp/.storybook'); + expect(result).toEqual({ + framework: { name: defaultInfo.frameworkPackage, options: {} }, + builder: defaultInfo.builderPackage, + renderer: defaultInfo.rendererPackage, + }); }); - it('should call getActualPackageJson with the correct package name', async () => { - const packageName = '@storybook/react'; - const framework = { name: packageName }; - await getFrameworkInfo({ framework } as StorybookConfig); - expect(getActualPackageJson).toHaveBeenCalledWith(packageName); + it('passes configDir to getStorybookInfo', async () => { + const configDir = '/my/project/.storybook'; + const { getStorybookInfo } = await import('storybook/internal/common'); + await getFrameworkInfo({} as StorybookConfig, configDir); + expect(getStorybookInfo).toHaveBeenCalledWith(configDir); }); - it('should resolve the framework package json correctly and strip project paths in the metadata', async () => { - const packageName = `${process.cwd()}/@storybook/react`.split('/').join(sep); - const framework = { name: packageName }; - const frameworkPackageJson = { - name: packageName, - dependencies: { - '@storybook/react': '7.0.0', - '@storybook/builder-vite': '7.0.0', - }, - }; - - vi.mocked(getActualPackageJson).mockResolvedValueOnce(frameworkPackageJson); - - const result = await getFrameworkInfo({ framework } as StorybookConfig); - - expect(getActualPackageJson).toHaveBeenCalledWith(packageName); - - expect(result).toEqual({ - framework: { - name: '$SNIP/@storybook/react'.split('/').join(sep), - options: undefined, - }, - builder: '@storybook/builder-vite', - renderer: '@storybook/react', - }); + it('returns provided framework options when object is passed', async () => { + const options = { foo: 'bar' } as any; + const result = await getFrameworkInfo( + { framework: { name: '@storybook/react', options } } as any, + '/tmp/.storybook' + ); + expect(result.framework.options).toEqual(options); }); }); diff --git a/code/core/src/telemetry/get-framework-info.ts b/code/core/src/telemetry/get-framework-info.ts index d114501481f0..51dd5feeb78e 100644 --- a/code/core/src/telemetry/get-framework-info.ts +++ b/code/core/src/telemetry/get-framework-info.ts @@ -1,80 +1,24 @@ -import { normalize } from 'node:path'; +import { getStorybookInfo } from 'storybook/internal/common'; +import type { StorybookConfig } from 'storybook/internal/types'; -import { frameworkPackages } from 'storybook/internal/common'; -import type { PackageJson, StorybookConfig } from 'storybook/internal/types'; - -import { getActualPackageJson } from './package-json'; import { cleanPaths } from './sanitize'; -const knownRenderers = [ - 'html', - 'react', - 'svelte', - 'vue3', - 'preact', - 'server', - 'vue', - 'web-components', - 'angular', - 'ember', -]; - -const knownBuilders = ['builder-webpack5', 'builder-vite']; - -function findMatchingPackage(packageJson: PackageJson, suffixes: string[]) { - const { name = '', version, dependencies, devDependencies, peerDependencies } = packageJson; - - const allDependencies = { - // We include the framework itself because it may be a renderer too (e.g. angular) - [name]: version, - ...dependencies, - ...devDependencies, - ...peerDependencies, - }; - - return suffixes.map((suffix) => `@storybook/${suffix}`).find((pkg) => allDependencies[pkg]); -} - -export const getFrameworkPackageName = (packageNameOrPath: string) => { - const normalizedPath = normalize(packageNameOrPath).replace(new RegExp(/\\/, 'g'), '/'); - - const knownFramework = Object.keys(frameworkPackages).find((pkg) => normalizedPath.endsWith(pkg)); - - return knownFramework || cleanPaths(packageNameOrPath).replace(/.*node_modules[\\/]/, ''); +const cleanAndSanitizePath = (path: string) => { + return cleanPaths(path).replace(/.*node_modules[\\/]/, ''); }; -export async function getFrameworkInfo(mainConfig: StorybookConfig) { - if (!mainConfig?.framework) { - return {}; - } - - const rawName = - typeof mainConfig.framework === 'string' ? mainConfig.framework : mainConfig.framework?.name; - if (!rawName) { - return {}; - } - - const frameworkPackageJson = await getActualPackageJson(rawName); - - if (!frameworkPackageJson) { - return {}; - } - - const builder = findMatchingPackage(frameworkPackageJson, knownBuilders); - const renderer = findMatchingPackage(frameworkPackageJson, knownRenderers); +export async function getFrameworkInfo(mainConfig: StorybookConfig, configDir: string) { + const { frameworkPackage, rendererPackage, builderPackage } = await getStorybookInfo(configDir); - // TODO: Evaluate if this is correct after removing pnp compatibility code in SB11 - // parse framework name and strip off pnp paths etc. - const sanitizedFrameworkName = getFrameworkPackageName(rawName); const frameworkOptions = typeof mainConfig.framework === 'object' ? mainConfig.framework.options : {}; return { framework: { - name: sanitizedFrameworkName, + name: frameworkPackage ? cleanAndSanitizePath(frameworkPackage) : undefined, options: frameworkOptions, }, - builder, - renderer, + builder: builderPackage ? cleanAndSanitizePath(builderPackage) : undefined, + renderer: rendererPackage ? cleanAndSanitizePath(rendererPackage) : undefined, }; } diff --git a/code/core/src/telemetry/storybook-metadata.test.ts b/code/core/src/telemetry/storybook-metadata.test.ts index 6bc4fe11002f..600b2e4e60e7 100644 --- a/code/core/src/telemetry/storybook-metadata.test.ts +++ b/code/core/src/telemetry/storybook-metadata.test.ts @@ -40,19 +40,25 @@ const mainJsMock: StorybookConfig = { stories: [], }; +const defaultInfo = { + framework: SupportedFramework.REACT_VITE, + renderer: SupportedRenderer.REACT, + builder: SupportedBuilder.VITE, + frameworkPackage: '@storybook/react-vite', + rendererPackage: '@storybook/react', + builderPackage: '@storybook/builder-vite', + addons: [], + mainConfig: { + stories: [], + }, + mainConfigPath: '', + previewConfigPath: '', + managerConfigPath: '', + version: 'x.x.x', +}; + beforeEach(() => { - vi.mocked(getStorybookInfo).mockImplementation(async () => ({ - framework: SupportedFramework.REACT_VITE, - renderer: SupportedRenderer.REACT, - builder: SupportedBuilder.VITE, - addons: [], - mainConfig: { - stories: [], - }, - mainConfigPath: '', - previewConfigPath: '', - managerConfigPath: '', - })); + vi.mocked(getStorybookInfo).mockImplementation(async () => defaultInfo); vi.mocked(detect).mockImplementation(async () => ({ name: 'yarn', @@ -187,6 +193,12 @@ describe('storybook-metadata', () => { }); it('should parse pnp paths for unknown frameworks', async () => { + vi.mocked(getStorybookInfo).mockImplementation(async () => ({ + ...defaultInfo, + frameworkPackage: + '/Users/foo/my-project/.yarn/__virtual__/@storybook-react-vite-virtual-769c990b9/0/cache/@storybook-react-rust-npm-7.1.0-alpha.38-512b-a23.zip/node_modules/storybook-react-rust' as any, + })); + const unixResult = await computeStorybookMetadata({ packageJson: packageJsonMock, packageJsonPath, @@ -225,6 +237,11 @@ describe('storybook-metadata', () => { path.sep = '/'; cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue('/Users/foo/my-projects'); + vi.mocked(getStorybookInfo).mockImplementation(async () => ({ + ...defaultInfo, + frameworkPackage: '/Users/foo/my-projects/.storybook/some-local-framework' as any, + })); + const unixResult = await computeStorybookMetadata({ packageJson: packageJsonMock, packageJsonPath, @@ -240,10 +257,18 @@ describe('storybook-metadata', () => { expect(unixResult.framework).toEqual({ name: '$SNIP/.storybook/some-local-framework', }); + }); + it('should sanitize pnp paths for local frameworks on Windows', async () => { // @ts-expect-error the property is read only but we can change it for testing purposes path.sep = '\\'; cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue('C:\\Users\\foo\\my-project'); + + vi.mocked(getStorybookInfo).mockImplementation(async () => ({ + ...defaultInfo, + frameworkPackage: 'C:\\Users\\foo\\my-project\\.storybook\\some-local-framework' as any, + })); + const windowsResult = await computeStorybookMetadata({ packageJson: packageJsonMock, packageJsonPath, @@ -262,49 +287,6 @@ describe('storybook-metadata', () => { }); }); - it('should return frameworkOptions from mainjs', async () => { - const reactResult = await computeStorybookMetadata({ - packageJson: packageJsonMock, - packageJsonPath, - configDir: '.storybook', - mainConfig: { - ...mainJsMock, - framework: { - name: '@storybook/react-vite', - options: { - strictMode: false, - }, - }, - }, - }); - - expect(reactResult.framework).toEqual({ - name: '@storybook/react-vite', - options: { strictMode: false }, - }); - - const angularResult = await computeStorybookMetadata({ - packageJson: packageJsonMock, - packageJsonPath, - configDir: '.storybook', - mainConfig: { - ...mainJsMock, - framework: { - name: '@storybook/angular', - options: { - enableIvy: true, - enableNgcc: true, - }, - }, - }, - }); - - expect(angularResult.framework).toEqual({ - name: '@storybook/angular', - options: { enableIvy: true, enableNgcc: true }, - }); - }); - it('should separate storybook packages and addons', async () => { const result = await computeStorybookMetadata({ packageJson: { diff --git a/code/core/src/telemetry/storybook-metadata.ts b/code/core/src/telemetry/storybook-metadata.ts index c6f5db760e31..56d4f7a357cb 100644 --- a/code/core/src/telemetry/storybook-metadata.ts +++ b/code/core/src/telemetry/storybook-metadata.ts @@ -143,7 +143,7 @@ export const computeStorybookMetadata = async ({ metadata.typescriptOptions = mainConfig.typescript; } - const frameworkInfo = await getFrameworkInfo(mainConfig); + const frameworkInfo = await getFrameworkInfo(mainConfig, configDir); if (typeof mainConfig.refs === 'object') { metadata.refCount = Object.keys(mainConfig.refs).length; diff --git a/code/core/src/telemetry/types.ts b/code/core/src/telemetry/types.ts index c6ea3cd99354..823837ab6d73 100644 --- a/code/core/src/telemetry/types.ts +++ b/code/core/src/telemetry/types.ts @@ -54,7 +54,7 @@ export type StorybookMetadata = { userSince?: number; language: 'typescript' | 'javascript'; framework?: { - name: string; + name?: string; options?: any; }; builder?: string; diff --git a/code/core/template/stories/module-mocking.stories.ts b/code/core/template/stories/moduleMocking.stories.ts similarity index 100% rename from code/core/template/stories/module-mocking.stories.ts rename to code/core/template/stories/moduleMocking.stories.ts diff --git a/code/e2e-tests/module-mocking.spec.ts b/code/e2e-tests/module-mocking.spec.ts index 6173744726fb..33420650c7b7 100644 --- a/code/e2e-tests/module-mocking.spec.ts +++ b/code/e2e-tests/module-mocking.spec.ts @@ -57,7 +57,7 @@ test.describe('module-mocking', () => { test('should assert that utils import is mocked', async ({ page }) => { const sbPage = new SbPage(page, expect); - await sbPage.navigateToStory('core/module-mocking', 'basic'); + await sbPage.navigateToStory('core/moduleMocking', 'basic'); await sbPage.viewAddonPanel('Actions'); const logItem = sbPage.panelContent().filter({ diff --git a/code/lib/cli-storybook/src/sandbox-templates.ts b/code/lib/cli-storybook/src/sandbox-templates.ts index 3b2fd25e0052..df497a8569b5 100644 --- a/code/lib/cli-storybook/src/sandbox-templates.ts +++ b/code/lib/cli-storybook/src/sandbox-templates.ts @@ -83,6 +83,7 @@ export type Template = { */ modifications?: { skipTemplateStories?: boolean; + skipMocking?: boolean; mainConfig?: LoosenedStorybookConfig | ((config: ConfigFile) => LoosenedStorybookConfig); testBuild?: boolean; disableDocs?: boolean; @@ -105,7 +106,7 @@ export type Template = { }; type BaseTemplates = Template & { - name: `${string} ${`v${number}` | 'Latest' | 'Prerelease'} (${'Webpack' | 'Vite'} | ${ + name: `${string} ${`v${number}` | 'Latest' | 'Prerelease'} (${'Webpack' | 'Vite' | 'RsBuild'} | ${ | 'JavaScript' | 'TypeScript'})`; }; @@ -481,6 +482,26 @@ export const baseTemplates = { }, skipTasks: ['e2e-tests', 'bench', 'vitest-integration'], }, + 'react-rsbuild/default-ts': { + name: 'React Latest (RsBuild | TypeScript)', + script: 'yarn create rsbuild -d {{beforeDir}} -t react-ts --tools eslint', + expected: { + framework: 'storybook-react-rsbuild', + renderer: '@storybook/react', + builder: 'storybook-builder-rsbuild', + }, + modifications: { + extraDependencies: ['prop-types', 'storybook-react-rsbuild@^3.0.0-beta.1'], + useCsfFactory: true, + mainConfig: { + features: { + experimentalTestSyntax: true, + }, + }, + skipMocking: true, + }, + skipTasks: ['e2e-tests', 'e2e-tests-dev', 'bench', 'vitest-integration'], + }, 'solid-vite/default-js': { name: 'SolidJS Latest (Vite | JavaScript)', script: 'npx degit solidjs/templates/js {{beforeDir}}', @@ -521,6 +542,25 @@ export const baseTemplates = { }, skipTasks: ['bench'], }, + 'vue3-rsbuild/default-ts': { + name: 'Vue Latest (RsBuild | TypeScript)', + script: 'yarn create rsbuild -d {{beforeDir}} -t vue-ts --tools eslint', + expected: { + framework: 'storybook-vue3-rsbuild', + renderer: '@storybook/vue3', + builder: 'storybook-builder-rsbuild', + }, + modifications: { + extraDependencies: ['storybook-vue3-rsbuild@^3.0.0-beta.1'], + mainConfig: { + features: { + experimentalTestSyntax: true, + }, + }, + skipMocking: true, + }, + skipTasks: ['e2e-tests', 'e2e-tests-dev', 'bench', 'vitest-integration'], + }, // 'nuxt-vite/default-ts': { // name: 'Nuxt v3 (Vite | TypeScript)', // script: 'npx nuxi init --packageManager yarn --gitInit false -M @nuxt/ui {{beforeDir}}', @@ -559,6 +599,20 @@ export const baseTemplates = { type: ProjectType.HTML, }, }, + 'html-rsbuild/default-ts': { + name: 'HTML Latest (RsBuild | TypeScript)', + script: 'yarn create rsbuild -d {{beforeDir}} -t vanilla-ts --tools eslint', + expected: { + framework: 'storybook-html-rsbuild', + renderer: '@storybook/html', + builder: 'storybook-builder-rsbuild', + }, + modifications: { + extraDependencies: ['storybook-html-rsbuild@^3.0.0-beta.1'], + skipMocking: true, + }, + skipTasks: ['e2e-tests', 'e2e-tests-dev', 'bench', 'vitest-integration'], + }, 'svelte-vite/default-js': { name: 'Svelte Latest (Vite | JavaScript)', script: 'npm create vite --yes {{beforeDir}} -- --template svelte', @@ -644,6 +698,20 @@ export const baseTemplates = { // Remove smoke-test from the list once https://github.com/storybookjs/storybook/issues/19351 is fixed. skipTasks: ['smoke-test', 'e2e-tests', 'bench', 'vitest-integration'], }, + 'lit-rsbuild/default-ts': { + name: 'Web Components Latest (RsBuild | TypeScript)', + script: 'yarn create rsbuild -d {{beforeDir}} -t lit-ts --tools eslint', + expected: { + framework: 'storybook-web-components-rsbuild', + renderer: '@storybook/web-components', + builder: 'storybook-builder-rsbuild', + }, + modifications: { + extraDependencies: ['storybook-web-components-rsbuild@^3.0.0-beta.1'], + skipMocking: true, + }, + skipTasks: ['e2e-tests', 'e2e-tests-dev', 'bench', 'vitest-integration'], + }, 'preact-vite/default-js': { name: 'Preact Latest (Vite | JavaScript)', script: 'npm create vite --yes {{beforeDir}} -- --template preact', @@ -831,6 +899,7 @@ const benchTemplates = { isInternal: true, modifications: { skipTemplateStories: true, + skipMocking: true, }, skipTasks: [ 'e2e-tests', @@ -848,6 +917,7 @@ const benchTemplates = { isInternal: true, modifications: { skipTemplateStories: true, + skipMocking: true, }, skipTasks: [ 'e2e-tests', @@ -865,6 +935,7 @@ const benchTemplates = { modifications: { skipTemplateStories: true, disableDocs: true, + skipMocking: true, }, skipTasks: [ 'e2e-tests', @@ -883,6 +954,7 @@ const benchTemplates = { modifications: { skipTemplateStories: true, testBuild: true, + skipMocking: true, }, skipTasks: [ 'e2e-tests', @@ -900,6 +972,7 @@ const benchTemplates = { modifications: { skipTemplateStories: true, testBuild: true, + skipMocking: true, }, skipTasks: [ 'e2e-tests', @@ -935,6 +1008,7 @@ export const normal: TemplateKey[] = [ 'bench/react-vite-default-ts-test-build', 'bench/react-webpack-18-ts-test-build', 'ember/default-js', + 'react-rsbuild/default-ts', ]; export const merged: TemplateKey[] = [ @@ -945,6 +1019,7 @@ export const merged: TemplateKey[] = [ 'nextjs-vite/15-ts', 'preact-vite/default-ts', 'html-vite/default-ts', + 'vue3-rsbuild/default-ts', ]; export const daily: TemplateKey[] = [ @@ -967,6 +1042,8 @@ export const daily: TemplateKey[] = [ 'internal/react16-webpack', 'internal/react18-webpack-babel', 'react-native-web-vite/expo-ts', + 'lit-rsbuild/default-ts', + 'html-rsbuild/default-ts', // 'react-native-web-vite/rn-cli-ts', ]; diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index f180df1bc6bf..98e3de51ab48 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -353,15 +353,27 @@ function updateStoriesField(mainConfig: ConfigFile, isJs: boolean) { } // Add a stories field entry for the passed symlink -function addStoriesEntry(mainConfig: ConfigFile, path: string, disableDocs: boolean) { +function addStoriesEntry( + mainConfig: ConfigFile, + path: string, + disableDocs: boolean, + skipMocking: boolean +) { const stories = mainConfig.getFieldValue(['stories']) as string[]; + const basePattern = disableDocs + ? '**/*.stories.@(js|jsx|mjs|ts|tsx)' + : '**/*.@(mdx|stories.@(js|jsx|mjs|ts|tsx))'; + + // When skipMocking is true and we're linking core template stories, exclude any stories + // with "mocking" in their file name (not related to docs filtering). + const files = + skipMocking && path === 'core' ? basePattern.replace('**/*', '**/!(*Mocking)*') : basePattern; + const entry = { directory: slash(join('../template-stories', path)), titlePrefix: slash(path), - files: disableDocs - ? '**/*.stories.@(js|jsx|mjs|ts|tsx)' - : '**/*.@(mdx|stories.@(js|jsx|mjs|ts|tsx))', + files, }; mainConfig.setFieldValue(['stories'], [...stories, entry]); @@ -379,7 +391,14 @@ async function linkPackageStories( cwd, linkInDir, disableDocs, - }: { mainConfig: ConfigFile; cwd: string; linkInDir?: string; disableDocs: boolean }, + skipMocking, + }: { + mainConfig: ConfigFile; + cwd: string; + linkInDir?: string; + disableDocs: boolean; + skipMocking: boolean; + }, variant?: string ) { const storiesFolderName = variant ? getStoriesFolderWithVariant(variant) : 'stories'; @@ -397,7 +416,7 @@ async function linkPackageStories( await ensureSymlinkOrCopy(source, target); if (!linkInDir) { - addStoriesEntry(mainConfig, packageDir, disableDocs); + addStoriesEntry(mainConfig, packageDir, disableDocs, skipMocking); } // Add `previewAnnotation` entries of the form @@ -591,6 +610,7 @@ export const addStories: Task['run'] = async ( { addon: extraAddons, disableDocs } ) => { logger.log('💃 Adding stories'); + const skipMocking = template.modifications?.skipMocking; const cwd = sandboxDir; const storiesPath = (await findFirstPath([join('src', 'stories'), 'stories'], { cwd })) || 'stories'; @@ -629,6 +649,7 @@ export const addStories: Task['run'] = async ( cwd, linkInDir: resolve(cwd, storiesPath), disableDocs, + skipMocking, }); if ( @@ -643,6 +664,7 @@ export const addStories: Task['run'] = async ( cwd, linkInDir: resolve(cwd, storiesPath), disableDocs, + skipMocking, }, sandboxSpecificStoriesFolder ); @@ -661,6 +683,7 @@ export const addStories: Task['run'] = async ( cwd, linkInDir: resolve(cwd, storiesPath), disableDocs, + skipMocking, }); } @@ -676,6 +699,7 @@ export const addStories: Task['run'] = async ( cwd, linkInDir: resolve(cwd, storiesPath), disableDocs, + skipMocking, }, sandboxSpecificStoriesFolder ); @@ -689,12 +713,14 @@ export const addStories: Task['run'] = async ( mainConfig, cwd, disableDocs, + skipMocking, }); await linkPackageStories(await workspacePath('addon test package', '@storybook/addon-vitest'), { mainConfig, cwd, disableDocs, + skipMocking, }); } @@ -724,13 +750,24 @@ export const addStories: Task['run'] = async ( .filter((addon: string) => Object.keys(storybookPackages).find((pkg: string) => pkg === `@storybook/addon-${addon}`) ) + .filter((addon: string) => { + // RSBUILD frameworks are not configured to ignore docs addon stories, which are React based + if ( + template.expected.framework === 'storybook-vue3-rsbuild' || + template.expected.framework === 'storybook-web-components-rsbuild' || + template.expected.framework === 'storybook-html-rsbuild' + ) { + return addon !== 'docs'; + } + return true; + }) .map(async (addon) => workspacePath('addon', `@storybook/addon-${addon}`)) ); if (isCoreRenderer) { const existingStories = await filterExistsInCodeDir(addonDirs, join('template', 'stories')); for (const packageDir of existingStories) { - await linkPackageStories(packageDir, { mainConfig, cwd, disableDocs }); + await linkPackageStories(packageDir, { mainConfig, cwd, disableDocs, skipMocking }); } // Add some extra settings (see above for what these do) @@ -836,7 +873,7 @@ export const extendPreview: Task['run'] = async ({ template, sandboxDir }) => { previewConfig.setFieldValue(['tags'], ['vitest']); } - if (template.name.includes('Bench')) { + if (template.modifications?.skipMocking) { await writeConfig(previewConfig); return; } diff --git a/scripts/tasks/sandbox.ts b/scripts/tasks/sandbox.ts index 55d4dc08b9e9..7206615d0ac4 100644 --- a/scripts/tasks/sandbox.ts +++ b/scripts/tasks/sandbox.ts @@ -142,7 +142,7 @@ export const sandbox: Task = { } // not if sandbox is bench - if (!details.template.name.includes('Bench')) { + if (!details.template.modifications?.skipMocking) { await addGlobalMocks(details, options); } diff --git a/scripts/utils/yarn.ts b/scripts/utils/yarn.ts index a69237c40d21..0f202d105d6f 100644 --- a/scripts/utils/yarn.ts +++ b/scripts/utils/yarn.ts @@ -103,7 +103,11 @@ export const addWorkaroundResolutions = async ({ react: packageJson.dependencies.react, 'react-dom': packageJson.dependencies['react-dom'], } - : {}; + : key === 'react-rsbuild/default-ts' + ? { + 'react-docgen': '^8.0.2', + } + : {}; packageJson.resolutions = { ...packageJson.resolutions, @@ -143,7 +147,11 @@ export const configureYarn2ForVerdaccio = async ({ // React prereleases will have INCOMPATIBLE_PEER_DEPENDENCY errors because of transitive dependencies not allowing v19 betas key.includes('nextjs') || key.includes('react-vite/prerelease') || - key.includes('react-webpack/prerelease') + key.includes('react-webpack/prerelease') || + key.includes('react-rsbuild/default-ts') || + key.includes('vue-rsbuild/default-ts') || + key.includes('html-rsbuild/default-ts') || + key.includes('web-components-rsbuild/default-ts') ) { // Don't error with INCOMPATIBLE_PEER_DEPENDENCY for SvelteKit sandboxes, it is expected to happen with @sveltejs/vite-plugin-svelte command.push(