diff --git a/code/addons/docs/src/preset.ts b/code/addons/docs/src/preset.ts index c51e0f789e4a..614292098d43 100644 --- a/code/addons/docs/src/preset.ts +++ b/code/addons/docs/src/preset.ts @@ -212,11 +212,6 @@ export const resolvedReact = async (existing: any) => ({ mdx: existing?.mdx ?? dirname(require.resolve('@mdx-js/react')), }); -const optimizeViteDeps = [ - '@mdx-js/react', - '@storybook/addon-docs > acorn-jsx', - '@storybook/addon-docs', - 'markdown-to-jsx', -]; +const optimizeViteDeps = ['@mdx-js/react', '@storybook/addon-docs', 'markdown-to-jsx']; export { webpackX as webpack, docsX as docs, optimizeViteDeps }; diff --git a/code/addons/vitest/package.json b/code/addons/vitest/package.json index d6a528aab851..94e2c9ba6378 100644 --- a/code/addons/vitest/package.json +++ b/code/addons/vitest/package.json @@ -100,8 +100,8 @@ "@types/micromatch": "^4.0.0", "@types/node": "^22.0.0", "@types/semver": "^7", - "@vitest/browser": "^3.1.1", - "@vitest/runner": "^3.1.1", + "@vitest/browser": "^3.2.0", + "@vitest/runner": "^3.2.0", "boxen": "^8.0.1", "es-toolkit": "^1.36.0", "execa": "^8.0.1", @@ -119,7 +119,7 @@ "tree-kill": "^1.2.2", "ts-dedent": "^2.2.0", "typescript": "^5.8.3", - "vitest": "^3.1.1" + "vitest": "^3.2.0" }, "peerDependencies": { "@vitest/browser": "^3.0.0", diff --git a/code/addons/vitest/src/postinstall.ts b/code/addons/vitest/src/postinstall.ts index f232c9062f99..525ba6960514 100644 --- a/code/addons/vitest/src/postinstall.ts +++ b/code/addons/vitest/src/postinstall.ts @@ -14,16 +14,14 @@ import { serverResolve, transformImportFiles, validateFrameworkName, - versions, } from 'storybook/internal/common'; import { readConfig, writeConfig } from 'storybook/internal/csf-tools'; -import { colors, logger } from 'storybook/internal/node-logger'; +import { logger } from 'storybook/internal/node-logger'; // eslint-disable-next-line depend/ban-dependencies import { execa } from 'execa'; import { findUp } from 'find-up'; -import { dirname, extname, join, relative, resolve } from 'pathe'; -import picocolors from 'picocolors'; +import { dirname, join, relative, resolve } from 'pathe'; import prompts from 'prompts'; import { coerce, satisfies } from 'semver'; import { dedent } from 'ts-dedent'; @@ -39,6 +37,13 @@ const EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.cts', '.mts', '.cjs', '.mjs' const addonA11yName = '@storybook/addon-a11y'; +let hasErrors = false; + +const logErrors = (...args: Parameters) => { + hasErrors = true; + printError(...args); +}; + const findFile = async (basename: string, extensions = EXTENSIONS) => findUp( extensions.map((ext) => basename + ext), @@ -49,7 +54,7 @@ export default async function postInstall(options: PostinstallOptions) { printSuccess( '👋 Howdy!', dedent` - I'm the installation helper for ${colors.pink(ADDON_NAME)} + I'm the installation helper for ${ADDON_NAME} Hold on for a moment while I look at your project and get it set up... ` @@ -65,6 +70,9 @@ export default async function postInstall(options: PostinstallOptions) { const dependencies = ['vitest', '@vitest/browser', 'playwright'].filter((p) => !allDeps[p]); const vitestVersionSpecifier = await packageManager.getInstalledVersion('vitest'); const coercedVitestVersion = vitestVersionSpecifier ? coerce(vitestVersionSpecifier) : null; + const isVitest3_2OrNewer = vitestVersionSpecifier + ? satisfies(vitestVersionSpecifier, '>=3.2.0') + : true; const mainJsPath = serverResolve(resolve(options.configDir, 'main')) as string; const config = await readConfig(mainJsPath); @@ -148,13 +156,13 @@ export default async function postInstall(options: PostinstallOptions) { if (!isRendererSupported) { reasons.push(dedent` - • The addon cannot yet be used with ${picocolors.bold(colors.pink(info.frameworkPackageName))} + • The addon cannot yet be used with ${info.frameworkPackageName} `); } if (coercedVitestVersion && !satisfies(coercedVitestVersion, '>=3.0.0')) { reasons.push(dedent` - • The addon requires Vitest 3.0.0 or higher. You are currently using ${picocolors.bold(vitestVersionSpecifier)}. + • The addon requires Vitest 3.0.0 or higher. You are currently using ${vitestVersionSpecifier}. Please update all of your Vitest dependencies and try again. `); } @@ -164,7 +172,7 @@ export default async function postInstall(options: PostinstallOptions) { if (coercedMswVersion && !satisfies(coercedMswVersion, '>=2.0.0')) { reasons.push(dedent` - • The addon uses Vitest behind the scenes, which supports only version 2 and above of MSW. However, we have detected version ${picocolors.bold(coercedMswVersion.version)} in this project. + • The addon uses Vitest behind the scenes, which supports only version 2 and above of MSW. However, we have detected version ${coercedMswVersion.version} in this project. Please update the 'msw' package and try again. `); } @@ -173,7 +181,7 @@ export default async function postInstall(options: PostinstallOptions) { const nextVersion = await packageManager.getInstalledVersion('next'); if (!nextVersion) { reasons.push(dedent` - • You are using ${picocolors.bold(colors.pink('@storybook/nextjs'))} without having ${picocolors.bold(colors.pink('next'))} installed. + • You are using @storybook/nextjs without having "next" installed. Please install "next" or use a different Storybook framework integration and try again. `); } @@ -186,7 +194,7 @@ export default async function postInstall(options: PostinstallOptions) { reasons.push('--------------------------------'); reasons.push( dedent` - You can fix these issues and rerun the command to reinstall. If you wish to roll back the installation, remove ${picocolors.bold(colors.pink(ADDON_NAME))} from the "addons" array + You can fix these issues and rerun the command to reinstall. If you wish to roll back the installation, remove ${ADDON_NAME} from the "addons" array in your main Storybook config file and remove the dependency from your package.json file. ` ); @@ -195,14 +203,14 @@ export default async function postInstall(options: PostinstallOptions) { reasons.push( dedent` Please check the documentation for more information about its requirements and installation: - ${picocolors.cyan(`https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}`)} + https://storybook.js.org/docs/next/${DOCUMENTATION_LINK} ` ); } else { reasons.push( dedent` Fear not, however, you can follow the manual installation process instead at: - ${picocolors.cyan(`https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup`)} + https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup ` ); } @@ -216,7 +224,7 @@ export default async function postInstall(options: PostinstallOptions) { const result = await prerequisiteCheck(); if (result) { - printError('⛔️ Sorry!', result); + logErrors('⛔️ Sorry!', result); logger.line(1); return; } @@ -227,9 +235,9 @@ export default async function postInstall(options: PostinstallOptions) { dedent` It looks like you're using Next.js. - Adding ${picocolors.bold(colors.pink(`@storybook/nextjs-vite/vite-plugin`))} so you can use it with Vitest. + Adding "@storybook/nextjs-vite/vite-plugin" so you can use it with Vitest. - More info about the plugin at ${picocolors.cyan(`https://github.com/storybookjs/vite-plugin-storybook-nextjs`)} + More info about the plugin at https://github.com/storybookjs/vite-plugin-storybook-nextjs ` ); try { @@ -248,8 +256,8 @@ export default async function postInstall(options: PostinstallOptions) { dedent` You don't seem to have a coverage reporter installed. Vitest needs either V8 or Istanbul to generate coverage reports. - Adding ${picocolors.bold(colors.pink(`@vitest/coverage-v8`))} to enable coverage reporting. - Read more about Vitest coverage providers at ${picocolors.cyan(`https://vitest.dev/guide/coverage.html#coverage-providers`)} + Adding "@vitest/coverage-v8" to enable coverage reporting. + Read more about Vitest coverage providers at https://vitest.dev/guide/coverage.html#coverage-providers ` ); dependencies.push(`@vitest/coverage-v8`); // Version specifier is added below @@ -270,7 +278,7 @@ export default async function postInstall(options: PostinstallOptions) { ); logger.line(1); logger.plain(`${step} Installing dependencies:`); - logger.plain(colors.gray(' ' + versionedDependencies.join(', '))); + logger.plain(' ' + versionedDependencies.join(', ')); } await packageManager.installDependencies(); @@ -279,10 +287,10 @@ export default async function postInstall(options: PostinstallOptions) { if (options.skipInstall) { logger.plain('Skipping Playwright installation, please run this command manually:'); - logger.plain(colors.gray(' npx playwright install chromium --with-deps')); + logger.plain(' npx playwright install chromium --with-deps'); } else { logger.plain(`${step} Configuring Playwright with Chromium (this might take some time):`); - logger.plain(colors.gray(' npx playwright install chromium --with-deps')); + logger.plain(' npx playwright install chromium --with-deps'); await packageManager.executeCommand({ command: 'npx', args: ['playwright', 'install', 'chromium', '--with-deps'], @@ -294,14 +302,14 @@ export default async function postInstall(options: PostinstallOptions) { const vitestSetupFile = resolve(options.configDir, `vitest.setup.${fileExtension}`); if (existsSync(vitestSetupFile)) { - printError( + logErrors( '🚨 Oh no!', dedent` Found an existing Vitest setup file: - ${colors.gray(vitestSetupFile)} + ${vitestSetupFile} Please refer to the documentation to complete the setup manually: - ${picocolors.cyan(`https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup`)} + https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup ` ); logger.line(1); @@ -310,7 +318,7 @@ export default async function postInstall(options: PostinstallOptions) { logger.line(1); logger.plain(`${step} Creating a Vitest setup file for Storybook:`); - logger.plain(colors.gray(` ${vitestSetupFile}`)); + logger.plain(` ${vitestSetupFile}`); const previewExists = EXTENSIONS.map((ext) => resolve(options.configDir, `preview${ext}`)).some( existsSync @@ -377,22 +385,22 @@ export default async function postInstall(options: PostinstallOptions) { if (updated) { logger.line(1); logger.plain(`${step} Updating your Vitest workspace file:`); - logger.plain(colors.gray(` ${vitestWorkspaceFile}`)); + logger.plain(` ${vitestWorkspaceFile}`); const formattedContent = await formatFileContent(vitestWorkspaceFile, generate(target).code); await writeFile(vitestWorkspaceFile, formattedContent); } else { - printError( + logErrors( '🚨 Oh no!', dedent` Could not update existing Vitest workspace file: - ${colors.gray(vitestWorkspaceFile)} + ${vitestWorkspaceFile} I was able to configure most of the addon but could not safely extend your existing workspace file automatically, you must do it yourself. Please refer to the documentation to complete the setup manually: - ${picocolors.cyan(`https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup`)} + https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup ` ); logger.line(1); @@ -403,16 +411,23 @@ export default async function postInstall(options: PostinstallOptions) { else if (rootConfig) { let target, updated; const configFile = await fs.readFile(rootConfig, 'utf8'); - const hasWorkspaceConfig = configFile.includes('workspace:'); + const hasProjectsConfig = configFile.includes('projects:'); + const configFileHasTypeReference = configFile.match( + /\/\/\/\s*/ + ); - // For Vitest 3+ with an existing workspace option in the config file, we extend the workspace array, - // otherwise we fall back to creating a workspace file. - if (hasWorkspaceConfig) { - const configTemplate = await loadTemplate('vitest.config.template.ts', { + const templateName = + hasProjectsConfig || isVitest3_2OrNewer + ? 'vitest.config.3.2.template.ts' + : 'vitest.config.template.ts'; + + if (templateName) { + const configTemplate = await loadTemplate(templateName, { CONFIG_DIR: options.configDir, BROWSER_CONFIG: browserConfig, SETUP_FILE: relative(dirname(rootConfig), vitestSetupFile), }); + const source = babelParse(configTemplate); target = babelParse(configFile); updated = updateConfigFile(source, target); @@ -421,58 +436,42 @@ export default async function postInstall(options: PostinstallOptions) { if (target && updated) { logger.line(1); logger.plain(`${step} Updating your ${vitestConfigFile ? 'Vitest' : 'Vite'} config file:`); - logger.plain(colors.gray(` ${rootConfig}`)); + logger.plain(` ${rootConfig}`); const formattedContent = await formatFileContent(rootConfig, generate(target).code); - await writeFile(rootConfig, formattedContent); + await writeFile( + rootConfig, + configFileHasTypeReference + ? formattedContent + : '/// \n' + formattedContent + ); } else { - // Fall back to creating a workspace file if we can't update the config file. - printWarning( - '⚠️ Cannot update config file', + logErrors( + '🚨 Oh no!', dedent` - Could not update your existing ${vitestConfigFile ? 'Vitest' : 'Vite'} config file: - ${colors.gray(rootConfig)} + We were unable to update your existing ${vitestConfigFile ? 'Vitest' : 'Vite'} config file. - Your existing config file cannot be safely updated, so instead a new Vitest - workspace file will be created, extending from your config file. - - Please refer to the Vitest documentation to learn about the workspace file: - ${picocolors.cyan(`https://vitest.dev/guide/workspace.html`)} - ` + Please refer to the documentation to complete the setup manually: + https://storybook.js.org/docs/writing-tests/integrations/vitest-addon#manual-setup + ` ); - - const extension = extname(rootConfig).includes('ts') ? '.ts' : '.js'; - const newWorkspaceFile = resolve(dirname(rootConfig), `vitest.workspace${extension}`); - const workspaceTemplate = await loadTemplate('vitest.workspace.template.ts', { - ROOT_CONFIG: relative(dirname(newWorkspaceFile), rootConfig), - EXTENDS_WORKSPACE: viteConfigFile - ? relative(dirname(newWorkspaceFile), viteConfigFile) - : '', - CONFIG_DIR: options.configDir, - BROWSER_CONFIG: browserConfig, - SETUP_FILE: relative(dirname(newWorkspaceFile), vitestSetupFile), - }).then((t) => t.replace(/\s+extends: '',/, '')); - - logger.line(1); - logger.plain(`${step} Creating a Vitest workspace file:`); - logger.plain(colors.gray(` ${newWorkspaceFile}`)); - - const formattedContent = await formatFileContent(newWorkspaceFile, workspaceTemplate); - await writeFile(newWorkspaceFile, formattedContent); } } // If there's no existing Vitest/Vite config, we create a new Vitest config file. else { const newConfigFile = resolve(`vitest.config.${fileExtension}`); - const configTemplate = await loadTemplate('vitest.config.template.ts', { - CONFIG_DIR: options.configDir, - BROWSER_CONFIG: browserConfig, - SETUP_FILE: relative(dirname(newConfigFile), vitestSetupFile), - }); + const configTemplate = await loadTemplate( + isVitest3_2OrNewer ? 'vitest.config.3.2.template.ts' : 'vitest.config.template.ts', + { + CONFIG_DIR: options.configDir, + BROWSER_CONFIG: browserConfig, + SETUP_FILE: relative(dirname(newConfigFile), vitestSetupFile), + } + ); logger.line(1); logger.plain(`${step} Creating a Vitest config file:`); - logger.plain(colors.gray(` ${newConfigFile}`)); + logger.plain(` ${newConfigFile}`); const formattedContent = await formatFileContent(newConfigFile, configTemplate); await writeFile(newConfigFile, formattedContent); @@ -504,7 +503,7 @@ export default async function postInstall(options: PostinstallOptions) { stdio: 'inherit', }); } catch (e: unknown) { - printError( + logErrors( '🚨 Oh no!', dedent` We have detected that you have ${addonA11yName} installed but could not automatically set it up for @storybook/addon-vitest: @@ -512,7 +511,7 @@ export default async function postInstall(options: PostinstallOptions) { ${e instanceof Error ? e.message : String(e)} Please refer to the documentation to complete the setup manually: - ${picocolors.cyan(`https://storybook.js.org/docs/writing-tests/accessibility-testing#test-addon-integration`)} + https://storybook.js.org/docs/writing-tests/accessibility-testing#test-addon-integration ` ); } @@ -520,19 +519,32 @@ export default async function postInstall(options: PostinstallOptions) { const runCommand = rootConfig ? `npx vitest --project=storybook` : `npx vitest`; - printSuccess( - '🎉 All done!', - dedent` - @storybook/addon-vitest is now configured and you're ready to run your tests! + if (!hasErrors) { + printSuccess( + '🎉 All done!', + dedent` + @storybook/addon-vitest is now configured and you're ready to run your tests! + + Here are a couple of tips to get you started: + • You can run tests with "${runCommand}" + • When using the Vitest extension in your editor, all of your stories will be shown as tests! + + Check the documentation for more information about its features and options at: + https://storybook.js.org/docs/next/${DOCUMENTATION_LINK} + ` + ); + } else { + printWarning( + '⚠️ Done, but with errors!', + dedent` + @storybook/addon-vitest was installed successfully, but there were some errors during the setup process. - Here are a couple of tips to get you started: - • You can run tests with ${colors.gray(runCommand)} - • When using the Vitest extension in your editor, all of your stories will be shown as tests! + Please refer to the documentation to complete the setup manually and check the errors above: + https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup + ` + ); + } - Check the documentation for more information about its features and options at: - ${picocolors.cyan(`https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}`)} - ` - ); logger.line(1); } diff --git a/code/addons/vitest/src/updateVitestFile.test.ts b/code/addons/vitest/src/updateVitestFile.test.ts index 0b4f36f85198..27188a8e8ef4 100644 --- a/code/addons/vitest/src/updateVitestFile.test.ts +++ b/code/addons/vitest/src/updateVitestFile.test.ts @@ -158,6 +158,135 @@ describe('updateConfigFile', () => { expect(updated).toBe(false); }); + it('adds projects property to test config', async () => { + const source = babel.babelParse( + await loadTemplate('vitest.config.3.2.template.ts', { + CONFIG_DIR: '.storybook', + BROWSER_CONFIG: "{ provider: 'playwright' }", + SETUP_FILE: '../.storybook/vitest.setup.ts', + }) + ); + const target = babel.babelParse(` + /// + import { defineConfig } from 'vite' + import react from '@vitejs/plugin-react' + + // https://vite.dev/config/ + export default defineConfig({ + plugins: [react()], + test: { + globals: true, + }, + }) + `); + + const updated = updateConfigFile(source, target); + expect(updated).toBe(true); + + const { code } = babel.generate(target); + expect(code).toMatchInlineSnapshot(` + "/// + import { defineConfig } from 'vite'; + import react from '@vitejs/plugin-react'; + + // https://vite.dev/config/ + import path from 'node:path'; + import { fileURLToPath } from 'node:url'; + import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; + const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); + + // More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon + export default defineConfig({ + plugins: [react()], + test: { + globals: true, + projects: [{ + extends: true, + plugins: [ + // The plugin will run tests for the stories defined in your Storybook config + // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest + storybookTest({ + configDir: path.join(dirname, '.storybook') + })], + test: { + name: 'storybook', + browser: { + provider: 'playwright' + }, + setupFiles: ['../.storybook/vitest.setup.ts'] + } + }] + } + });" + `); + }); + + it('edits projects property of test config', async () => { + const source = babel.babelParse( + await loadTemplate('vitest.config.3.2.template.ts', { + CONFIG_DIR: '.storybook', + BROWSER_CONFIG: "{ provider: 'playwright' }", + SETUP_FILE: '../.storybook/vitest.setup.ts', + }) + ); + const target = babel.babelParse(` + /// + import { defineConfig } from 'vite' + import react from '@vitejs/plugin-react' + + // https://vite.dev/config/ + export default defineConfig({ + plugins: [react()], + test: { + globals: true, + projects: ['packages/*', {some: 'config'}] + } + }) + `); + + const updated = updateConfigFile(source, target); + expect(updated).toBe(true); + + const { code } = babel.generate(target); + expect(code).toMatchInlineSnapshot(` + "/// + import { defineConfig } from 'vite'; + import react from '@vitejs/plugin-react'; + + // https://vite.dev/config/ + import path from 'node:path'; + import { fileURLToPath } from 'node:url'; + import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; + const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); + + // More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon + export default defineConfig({ + plugins: [react()], + test: { + globals: true, + projects: ['packages/*', { + some: 'config' + }, { + extends: true, + plugins: [ + // The plugin will run tests for the stories defined in your Storybook config + // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest + storybookTest({ + configDir: path.join(dirname, '.storybook') + })], + test: { + name: 'storybook', + browser: { + provider: 'playwright' + }, + setupFiles: ['../.storybook/vitest.setup.ts'] + } + }] + } + });" + `); + }); + it('adds workspace property to test config', async () => { const source = babel.babelParse( await loadTemplate('vitest.config.template.ts', { diff --git a/code/addons/vitest/src/updateVitestFile.ts b/code/addons/vitest/src/updateVitestFile.ts index cc6f06a26763..44c6ff8ee90a 100644 --- a/code/addons/vitest/src/updateVitestFile.ts +++ b/code/addons/vitest/src/updateVitestFile.ts @@ -50,6 +50,31 @@ const mergeProperties = ( } }; +/** + * Merges a source Vitest configuration AST into a target configuration AST. + * + * This function intelligently combines configuration elements from a source file (typically a + * template) into an existing target configuration file, avoiding duplicates and preserving the + * structure of both files. + * + * The function performs the following operations: + * + * 1. **Import Merging**: Adds new import statements from source that don't exist in target (determined + * by local specifier name). Imports are inserted after existing imports. + * 2. **Variable Declaration Merging**: Copies variable declarations from source to target if they + * don't already exist (determined by variable name). Variables are inserted after imports. + * 3. **Configuration Object Merging**: Merges the configuration object properties from source into + * target's default export. Supports both direct object exports and function-wrapped exports + * (e.g., `defineConfig({})`). The merging is recursive: + * + * - Nested objects are merged deeply + * - Arrays are concatenated (shallow merge) + * - Primitive values are overwritten + * + * @param source - The source Babel AST (template configuration to merge from) + * @param target - The target Babel AST (existing configuration to merge into) + * @returns {boolean} - True if the target was modified, false otherwise + */ export const updateConfigFile = (source: BabelFile['ast'], target: BabelFile['ast']) => { let updated = false; for (const sourceNode of source.program.body) { diff --git a/code/addons/vitest/src/vitest-plugin/index.ts b/code/addons/vitest/src/vitest-plugin/index.ts index e5696bdcaf4c..fe6e0942e812 100644 --- a/code/addons/vitest/src/vitest-plugin/index.ts +++ b/code/addons/vitest/src/vitest-plugin/index.ts @@ -29,6 +29,7 @@ import sirv from 'sirv'; import { dedent } from 'ts-dedent'; // ! Relative import to prebundle it without needing to depend on the Vite builder +import { INCLUDE_CANDIDATES } from '../../../../builders/builder-vite/src/constants'; import { withoutVitePlugins } from '../../../../builders/builder-vite/src/utils/without-vite-plugins'; import type { InternalOptions, UserOptions } from './types'; @@ -151,6 +152,7 @@ export const storybookTest = async (options?: UserOptions): Promise => staticDirs, previewLevelTags, core, + extraOptimizeDeps, ] = await Promise.all([ getStoryGlobsAndFiles(presets, directories), presets.apply('framework', undefined), @@ -159,6 +161,7 @@ export const storybookTest = async (options?: UserOptions): Promise => presets.apply('staticDirs', []), extractTagsFromPreview(finalOptions.configDir), presets.apply('core'), + presets.apply('optimizeViteDeps', []), ]); const pluginsToIgnore = [ @@ -326,6 +329,8 @@ export const storybookTest = async (options?: UserOptions): Promise => optimizeDeps: { include: [ + ...extraOptimizeDeps, + ...INCLUDE_CANDIDATES, '@storybook/addon-vitest/internal/setup-file', '@storybook/addon-vitest/internal/global-setup', '@storybook/addon-vitest/internal/test-utils', diff --git a/code/addons/vitest/templates/vitest.config.3.2.template.ts b/code/addons/vitest/templates/vitest.config.3.2.template.ts new file mode 100644 index 000000000000..d261dc2d202a --- /dev/null +++ b/code/addons/vitest/templates/vitest.config.3.2.template.ts @@ -0,0 +1,30 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { defineConfig } from 'vitest/config'; + +import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; + +const dirname = + typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); + +// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon +export default defineConfig({ + test: { + projects: [ + { + extends: true, + plugins: [ + // The plugin will run tests for the stories defined in your Storybook config + // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest + storybookTest({ configDir: path.join(dirname, 'CONFIG_DIR') }), + ], + test: { + name: 'storybook', + browser: BROWSER_CONFIG, + setupFiles: ['SETUP_FILE'], + }, + }, + ], + }, +}); diff --git a/code/builders/builder-vite/src/constants.ts b/code/builders/builder-vite/src/constants.ts new file mode 100644 index 000000000000..8f998f3c6dc8 --- /dev/null +++ b/code/builders/builder-vite/src/constants.ts @@ -0,0 +1,148 @@ +// It ensures that vite converts cjs deps into esm without vite having to find them during startup and then having to log a message about them and restart +// TODO: Many of the deps might be prebundled now though, so probably worth trying to remove and see what happens +export const INCLUDE_CANDIDATES = [ + '@ampproject/remapping', + '@base2/pretty-print-object', + '@emotion/core', + '@emotion/is-prop-valid', + '@emotion/styled', + '@jridgewell/sourcemap-codec', + '@storybook/addon-a11y/preview', + '@storybook/addon-designs/blocks', + '@storybook/addon-docs/blocks', + '@storybook/addon-docs/preview', + '@storybook/addon-links/preview', + '@storybook/addon-themes', + '@storybook/addon-themes/preview', + '@storybook/html', + '@storybook/html/dist/entry-preview-docs.mjs', + '@storybook/html/dist/entry-preview.mjs', + '@storybook/nextjs-vite/dist/preview.mjs', + '@storybook/preact', + '@storybook/preact/dist/entry-preview-docs.mjs', + '@storybook/preact/dist/entry-preview.mjs', + '@storybook/react > acorn-jsx', + '@storybook/react', + '@storybook/react/dist/entry-preview-docs.mjs', + '@storybook/react/dist/entry-preview-rsc.mjs', + '@storybook/react/dist/entry-preview.mjs', + '@storybook/svelte', + '@storybook/svelte/dist/entry-preview-docs.mjs', + '@storybook/svelte/dist/entry-preview.mjs', + '@storybook/vue3', + '@storybook/vue3/dist/entry-preview-docs.mjs', + '@storybook/vue3/dist/entry-preview.mjs', + '@storybook/web-components', + '@storybook/web-components/dist/entry-preview-docs.mjs', + '@storybook/web-components/dist/entry-preview.mjs', + 'acorn-jsx', + 'acorn-walk', + 'acorn', + 'airbnb-js-shims', + 'ansi-to-html', + 'aria-query', + 'axe-core', + 'axobject-query', + 'chromatic/isChromatic', + 'color-convert', + 'deep-object-diff', + 'doctrine', + 'emotion-theming', + 'escodegen', + 'estraverse', + 'fast-deep-equal', + 'html-tags', + 'isobject', + 'loader-utils', + 'lodash/camelCase.js', + 'lodash/camelCase', + 'lodash/cloneDeep.js', + 'lodash/cloneDeep', + 'lodash/countBy.js', + 'lodash/countBy', + 'lodash/debounce.js', + 'lodash/debounce', + 'lodash/isEqual.js', + 'lodash/isEqual', + 'lodash/isFunction.js', + 'lodash/isFunction', + 'lodash/isPlainObject.js', + 'lodash/isPlainObject', + 'lodash/isString.js', + 'lodash/isString', + 'lodash/kebabCase.js', + 'lodash/kebabCase', + 'lodash/mapKeys.js', + 'lodash/mapKeys', + 'lodash/mapValues.js', + 'lodash/mapValues', + 'lodash/merge.js', + 'lodash/merge', + 'lodash/mergeWith.js', + 'lodash/mergeWith', + 'lodash/pick.js', + 'lodash/pick', + 'lodash/pickBy.js', + 'lodash/pickBy', + 'lodash/startCase.js', + 'lodash/startCase', + 'lodash/throttle.js', + 'lodash/throttle', + 'lodash/uniq.js', + 'lodash/uniq', + 'lodash/upperFirst.js', + 'lodash/upperFirst', + 'memoizerific', + 'mockdate', + 'msw-storybook-addon', + 'overlayscrollbars', + 'polished', + 'prettier/parser-babel', + 'prettier/parser-flow', + 'prettier/parser-typescript', + 'prop-types', + 'qs', + 'react-dom', + 'react-dom/client', + 'react-dom/test-utils', + 'react-fast-compare', + 'react-is', + 'react-textarea-autosize', + 'react', + 'react/jsx-dev-runtime', + 'react/jsx-runtime', + 'refractor/core', + 'refractor/lang/bash.js', + 'refractor/lang/css.js', + 'refractor/lang/graphql.js', + 'refractor/lang/js-extras.js', + 'refractor/lang/json.js', + 'refractor/lang/jsx.js', + 'refractor/lang/markdown.js', + 'refractor/lang/markup.js', + 'refractor/lang/tsx.js', + 'refractor/lang/typescript.js', + 'refractor/lang/yaml.js', + 'regenerator-runtime/runtime.js', + 'sb-original/default-loader', + 'sb-original/image-context', + 'semver', // TODO: Remove once https://github.com/npm/node-semver/issues/712 is fixed + 'slash', + 'store2', + 'storybook/actions', + 'storybook/actions/decorator', + 'storybook/internal/core-events', + 'storybook/internal/csf', + 'storybook/internal/preview-api', + 'storybook/internal/preview/runtime', + 'storybook/preview-api', + 'storybook/theming', + 'storybook/viewport', + 'synchronous-promise', + 'telejson', + 'ts-dedent', + 'unfetch', + 'util-deprecate', + 'vue', + 'warning', +]; diff --git a/code/builders/builder-vite/src/optimizeDeps.ts b/code/builders/builder-vite/src/optimizeDeps.ts index 9d4d782321aa..9b7729538732 100644 --- a/code/builders/builder-vite/src/optimizeDeps.ts +++ b/code/builders/builder-vite/src/optimizeDeps.ts @@ -4,151 +4,9 @@ import type { Options } from 'storybook/internal/types'; import type { UserConfig, InlineConfig as ViteInlineConfig } from 'vite'; +import { INCLUDE_CANDIDATES } from './constants'; import { listStories } from './list-stories'; -// It ensures that vite converts cjs deps into esm without vite having to find them during startup and then having to log a message about them and restart -// TODO: Many of the deps might be prebundled now though, so probably worth trying to remove and see what happens -const INCLUDE_CANDIDATES = [ - '@ampproject/remapping', - '@base2/pretty-print-object', - '@emotion/core', - '@emotion/is-prop-valid', - '@emotion/styled', - '@jridgewell/sourcemap-codec', - '@storybook/addon-a11y/preview', - '@storybook/addon-designs/blocks', - '@storybook/addon-docs/preview', - '@storybook/addon-links/preview', - '@storybook/addon-themes', - '@storybook/addon-themes/preview', - '@storybook/addon-docs/blocks', - '@storybook/nextjs-vite/dist/preview.mjs', - '@storybook/html', - '@storybook/html/dist/entry-preview-docs.mjs', - '@storybook/html/dist/entry-preview.mjs', - '@storybook/preact', - '@storybook/preact/dist/entry-preview-docs.mjs', - '@storybook/preact/dist/entry-preview.mjs', - '@storybook/react > acorn-jsx', - '@storybook/react', - '@storybook/react/dist/entry-preview-docs.mjs', - '@storybook/react/dist/entry-preview-rsc.mjs', - '@storybook/react/dist/entry-preview.mjs', - '@storybook/svelte', - '@storybook/svelte/dist/entry-preview-docs.mjs', - '@storybook/svelte/dist/entry-preview.mjs', - '@storybook/vue3', - '@storybook/vue3/dist/entry-preview-docs.mjs', - '@storybook/vue3/dist/entry-preview.mjs', - '@storybook/web-components', - '@storybook/web-components/dist/entry-preview-docs.mjs', - '@storybook/web-components/dist/entry-preview.mjs', - 'storybook/viewport', - 'acorn-jsx', - 'acorn-walk', - 'acorn', - 'airbnb-js-shims', - 'ansi-to-html', - 'aria-query', - 'axe-core', - 'axobject-query', - 'chromatic/isChromatic', - 'color-convert', - 'deep-object-diff', - 'doctrine', - 'emotion-theming', - 'escodegen', - 'estraverse', - 'fast-deep-equal', - 'html-tags', - 'isobject', - 'loader-utils', - 'lodash/camelCase.js', - 'lodash/camelCase', - 'lodash/cloneDeep.js', - 'lodash/cloneDeep', - 'lodash/countBy.js', - 'lodash/countBy', - 'lodash/debounce.js', - 'lodash/debounce', - 'lodash/isEqual.js', - 'lodash/isEqual', - 'lodash/isFunction.js', - 'lodash/isFunction', - 'lodash/isPlainObject.js', - 'lodash/isPlainObject', - 'lodash/isString.js', - 'lodash/isString', - 'lodash/kebabCase.js', - 'lodash/kebabCase', - 'lodash/mapKeys.js', - 'lodash/mapKeys', - 'lodash/mapValues.js', - 'lodash/mapValues', - 'lodash/merge.js', - 'lodash/merge', - 'lodash/mergeWith.js', - 'lodash/mergeWith', - 'lodash/pick.js', - 'lodash/pick', - 'lodash/pickBy.js', - 'lodash/pickBy', - 'lodash/startCase.js', - 'lodash/startCase', - 'lodash/throttle.js', - 'lodash/throttle', - 'lodash/uniq.js', - 'lodash/uniq', - 'lodash/upperFirst.js', - 'lodash/upperFirst', - 'memoizerific', - 'mockdate', - 'msw-storybook-addon', - 'overlayscrollbars', - 'polished', - 'prettier/parser-babel', - 'prettier/parser-flow', - 'prettier/parser-typescript', - 'prop-types', - 'qs', - 'react-dom', - 'react-dom/client', - 'react-dom/test-utils', - 'react-fast-compare', - 'react-is', - 'react-textarea-autosize', - 'react', - 'react/jsx-runtime', - 'react/jsx-dev-runtime', - 'refractor/core', - 'refractor/lang/bash.js', - 'refractor/lang/css.js', - 'refractor/lang/graphql.js', - 'refractor/lang/js-extras.js', - 'refractor/lang/json.js', - 'refractor/lang/jsx.js', - 'refractor/lang/markdown.js', - 'refractor/lang/markup.js', - 'refractor/lang/tsx.js', - 'refractor/lang/typescript.js', - 'refractor/lang/yaml.js', - 'regenerator-runtime/runtime.js', - 'semver', // TODO: Remove once https://github.com/npm/node-semver/issues/712 is fixed - 'sb-original/default-loader', - 'sb-original/image-context', - 'slash', - 'store2', - 'storybook/internal/preview/runtime', - 'storybook/internal/csf', - 'synchronous-promise', - 'telejson', - 'ts-dedent', - 'unfetch', - 'util-deprecate', - 'vue', - 'warning', -]; - /** * Helper function which allows us to `filter` with an async predicate. Uses Promise.all for * performance. diff --git a/code/renderers/react/src/portable-stories.tsx b/code/renderers/react/src/portable-stories.tsx index 152236b4b732..93a663853047 100644 --- a/code/renderers/react/src/portable-stories.tsx +++ b/code/renderers/react/src/portable-stories.tsx @@ -12,6 +12,7 @@ import type { } from 'storybook/internal/types'; import { + composeConfigs, composeStories as originalComposeStories, composeStory as originalComposeStory, setProjectAnnotations as originalSetProjectAnnotations, @@ -19,6 +20,7 @@ import { } from 'storybook/preview-api'; import * as reactProjectAnnotations from './entry-preview'; +import * as reactArgTypesAnnotations from './entry-preview-argtypes'; import type { Meta } from './public-types'; import type { ReactRenderer } from './types'; @@ -53,20 +55,24 @@ export function setProjectAnnotations( } // This will not be necessary once we have auto preset loading -export const INTERNAL_DEFAULT_PROJECT_ANNOTATIONS: ProjectAnnotations = { - ...reactProjectAnnotations, - /** @deprecated */ - renderToCanvas: async (renderContext, canvasElement) => { - if (renderContext.storyContext.testingLibraryRender == null) { - return reactProjectAnnotations.renderToCanvas(renderContext, canvasElement); - } - const { - storyContext: { context, unboundStoryFn: Story, testingLibraryRender: render }, - } = renderContext; - const { unmount } = render(, { container: context.canvasElement }); - return unmount; - }, -}; +export const INTERNAL_DEFAULT_PROJECT_ANNOTATIONS: ProjectAnnotations = + composeConfigs([ + reactProjectAnnotations, + reactArgTypesAnnotations, + { + /** @deprecated */ + renderToCanvas: async (renderContext, canvasElement) => { + if (renderContext.storyContext.testingLibraryRender == null) { + return reactProjectAnnotations.renderToCanvas(renderContext, canvasElement); + } + const { + storyContext: { context, unboundStoryFn: Story, testingLibraryRender: render }, + } = renderContext; + const { unmount } = render(, { container: context.canvasElement }); + return unmount; + }, + } as ProjectAnnotations, + ]); /** * Function that will receive a story along with meta (e.g. a default export from a .stories file) diff --git a/code/yarn.lock b/code/yarn.lock index c2797d7ce7a9..dac77a7ca0c7 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5584,6 +5584,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm-eabi@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.43.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@rollup/rollup-android-arm64@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-android-arm64@npm:4.34.8" @@ -5598,6 +5605,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm64@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-android-arm64@npm:4.43.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-arm64@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-darwin-arm64@npm:4.34.8" @@ -5612,6 +5626,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-arm64@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-darwin-arm64@npm:4.43.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-x64@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-darwin-x64@npm:4.34.8" @@ -5626,6 +5647,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-x64@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-darwin-x64@npm:4.43.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-freebsd-arm64@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-freebsd-arm64@npm:4.34.8" @@ -5640,6 +5668,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-freebsd-arm64@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.43.0" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-freebsd-x64@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-freebsd-x64@npm:4.34.8" @@ -5654,6 +5689,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-freebsd-x64@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-freebsd-x64@npm:4.43.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-linux-arm-gnueabihf@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.34.8" @@ -5668,6 +5710,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm-gnueabihf@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.43.0" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-arm-musleabihf@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.34.8" @@ -5682,6 +5731,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm-musleabihf@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.43.0" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-gnu@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.34.8" @@ -5696,6 +5752,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm64-gnu@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.43.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-musl@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-linux-arm64-musl@npm:4.34.8" @@ -5710,6 +5773,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm64-musl@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.43.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-loongarch64-gnu@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.34.8" @@ -5724,6 +5794,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-loongarch64-gnu@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.43.0" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-powerpc64le-gnu@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.34.8" @@ -5738,6 +5815,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.43.0" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-riscv64-gnu@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.34.8" @@ -5752,6 +5836,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-riscv64-gnu@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.43.0" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-riscv64-musl@npm:4.40.1": version: 4.40.1 resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.40.1" @@ -5759,6 +5850,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-riscv64-musl@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.43.0" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-s390x-gnu@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.34.8" @@ -5773,6 +5871,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-s390x-gnu@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.43.0" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-x64-gnu@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-linux-x64-gnu@npm:4.34.8" @@ -5787,6 +5892,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-x64-gnu@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.43.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-x64-musl@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-linux-x64-musl@npm:4.34.8" @@ -5801,6 +5913,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-x64-musl@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.43.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-win32-arm64-msvc@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.34.8" @@ -5815,6 +5934,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-arm64-msvc@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.43.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-win32-ia32-msvc@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.34.8" @@ -5829,6 +5955,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-ia32-msvc@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.43.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@rollup/rollup-win32-x64-msvc@npm:4.34.8": version: 4.34.8 resolution: "@rollup/rollup-win32-x64-msvc@npm:4.34.8" @@ -5843,6 +5976,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-x64-msvc@npm:4.43.0": + version: 4.43.0 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.43.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@rtsao/scc@npm:^1.1.0": version: 1.1.0 resolution: "@rtsao/scc@npm:1.1.0" @@ -6044,8 +6184,8 @@ __metadata: "@types/micromatch": "npm:^4.0.0" "@types/node": "npm:^22.0.0" "@types/semver": "npm:^7" - "@vitest/browser": "npm:^3.1.1" - "@vitest/runner": "npm:^3.1.1" + "@vitest/browser": "npm:^3.2.0" + "@vitest/runner": "npm:^3.2.0" boxen: "npm:^8.0.1" es-toolkit: "npm:^1.36.0" execa: "npm:^8.0.1" @@ -6064,7 +6204,7 @@ __metadata: tree-kill: "npm:^1.2.2" ts-dedent: "npm:^2.2.0" typescript: "npm:^5.8.3" - vitest: "npm:^3.1.1" + vitest: "npm:^3.2.0" peerDependencies: "@vitest/browser": ^3.0.0 "@vitest/runner": ^3.0.0 @@ -7361,6 +7501,15 @@ __metadata: languageName: node linkType: hard +"@types/chai@npm:^5.2.2": + version: 5.2.2 + resolution: "@types/chai@npm:5.2.2" + dependencies: + "@types/deep-eql": "npm:*" + checksum: 10c0/49282bf0e8246800ebb36f17256f97bd3a8c4fb31f92ad3c0eaa7623518d7e87f1eaad4ad206960fcaf7175854bdff4cb167e4fe96811e0081b4ada83dd533ec + languageName: node + linkType: hard + "@types/color-convert@npm:^2.0.0": version: 2.0.4 resolution: "@types/color-convert@npm:2.0.4" @@ -7414,6 +7563,13 @@ __metadata: languageName: node linkType: hard +"@types/deep-eql@npm:*": + version: 4.0.2 + resolution: "@types/deep-eql@npm:4.0.2" + checksum: 10c0/bf3f811843117900d7084b9d0c852da9a044d12eb40e6de73b552598a6843c21291a8a381b0532644574beecd5e3491c5ff3a0365ab86b15d59862c025384844 + languageName: node + linkType: hard + "@types/detect-port@npm:^1.3.0": version: 1.3.5 resolution: "@types/detect-port@npm:1.3.5" @@ -8405,6 +8561,33 @@ __metadata: languageName: node linkType: hard +"@vitest/browser@npm:^3.2.0": + version: 3.2.3 + resolution: "@vitest/browser@npm:3.2.3" + dependencies: + "@testing-library/dom": "npm:^10.4.0" + "@testing-library/user-event": "npm:^14.6.1" + "@vitest/mocker": "npm:3.2.3" + "@vitest/utils": "npm:3.2.3" + magic-string: "npm:^0.30.17" + sirv: "npm:^3.0.1" + tinyrainbow: "npm:^2.0.0" + ws: "npm:^8.18.2" + peerDependencies: + playwright: "*" + vitest: 3.2.3 + webdriverio: ^7.0.0 || ^8.0.0 || ^9.0.0 + peerDependenciesMeta: + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + checksum: 10c0/effc8affe57ab4bfe12cc71dc5506c85b034b018c1fc71fde8591be77755962f979e06aae23b7c80460d93b36f3106ab52611d839d2d45be08450e81fd80c8ee + languageName: node + linkType: hard + "@vitest/coverage-istanbul@npm:^3.1.1": version: 3.1.2 resolution: "@vitest/coverage-istanbul@npm:3.1.2" @@ -8475,6 +8658,19 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@npm:3.2.3": + version: 3.2.3 + resolution: "@vitest/expect@npm:3.2.3" + dependencies: + "@types/chai": "npm:^5.2.2" + "@vitest/spy": "npm:3.2.3" + "@vitest/utils": "npm:3.2.3" + chai: "npm:^5.2.0" + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/5eb6278be8f5294779472d1276e150a1b573274441a68c2681c447179abd22af451813fdfbe87e04f5909ca7a0926700f9b79022f227c9816e5d0fa8e0229e15 + languageName: node + linkType: hard + "@vitest/expect@patch:@vitest/expect@npm%3A3.0.9#~/.yarn/patches/@vitest-expect-npm-3.0.9-e2a2210fb4.patch": version: 3.0.9 resolution: "@vitest/expect@patch:@vitest/expect@npm%3A3.0.9#~/.yarn/patches/@vitest-expect-npm-3.0.9-e2a2210fb4.patch::version=3.0.9&hash=f52714" @@ -8506,6 +8702,25 @@ __metadata: languageName: node linkType: hard +"@vitest/mocker@npm:3.2.3": + version: 3.2.3 + resolution: "@vitest/mocker@npm:3.2.3" + dependencies: + "@vitest/spy": "npm:3.2.3" + estree-walker: "npm:^3.0.3" + magic-string: "npm:^0.30.17" + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + checksum: 10c0/b670f229c3b1de5561de3cbbecb18f964d4888355d7f1cb8bbff4350b2cfbe477bef834cc2f66af7727ca7dc567540018885eb652f46e0be1cda4015491dc0a9 + languageName: node + linkType: hard + "@vitest/pretty-format@npm:3.0.9": version: 3.0.9 resolution: "@vitest/pretty-format@npm:3.0.9" @@ -8524,7 +8739,16 @@ __metadata: languageName: node linkType: hard -"@vitest/runner@npm:3.1.2, @vitest/runner@npm:^3.1.1": +"@vitest/pretty-format@npm:3.2.3, @vitest/pretty-format@npm:^3.2.3": + version: 3.2.3 + resolution: "@vitest/pretty-format@npm:3.2.3" + dependencies: + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/e8fa7b97822c58404bef07d19fa9a49d5b7edb6797dd355584ad7246585bbbe9c55dd1fb05d0c3939b9c15fba05c3e134e2b96ea0cb64ca79a2b9dab60087a6a + languageName: node + linkType: hard + +"@vitest/runner@npm:3.1.2": version: 3.1.2 resolution: "@vitest/runner@npm:3.1.2" dependencies: @@ -8534,6 +8758,17 @@ __metadata: languageName: node linkType: hard +"@vitest/runner@npm:3.2.3, @vitest/runner@npm:^3.2.0": + version: 3.2.3 + resolution: "@vitest/runner@npm:3.2.3" + dependencies: + "@vitest/utils": "npm:3.2.3" + pathe: "npm:^2.0.3" + strip-literal: "npm:^3.0.0" + checksum: 10c0/c20cb6e2ac4fdfb3d4f5136714ea65f9063562d3afaa1574dc82f53d061444bc01583f9915346768ca75f5ea0658f02fb594752e21abbca5ab50290f58732147 + languageName: node + linkType: hard + "@vitest/snapshot@npm:3.1.2": version: 3.1.2 resolution: "@vitest/snapshot@npm:3.1.2" @@ -8545,6 +8780,17 @@ __metadata: languageName: node linkType: hard +"@vitest/snapshot@npm:3.2.3": + version: 3.2.3 + resolution: "@vitest/snapshot@npm:3.2.3" + dependencies: + "@vitest/pretty-format": "npm:3.2.3" + magic-string: "npm:^0.30.17" + pathe: "npm:^2.0.3" + checksum: 10c0/f6dd0248afb3f3cbcbbb9fd39c2c8273c4ec92176f65e6ba9d36a0c33552d3658013e3a02944e14c7637f51d6702a5c07963b59707ca459bd1ac31f39c81160c + languageName: node + linkType: hard + "@vitest/spy@npm:3.0.9": version: 3.0.9 resolution: "@vitest/spy@npm:3.0.9" @@ -8563,6 +8809,15 @@ __metadata: languageName: node linkType: hard +"@vitest/spy@npm:3.2.3": + version: 3.2.3 + resolution: "@vitest/spy@npm:3.2.3" + dependencies: + tinyspy: "npm:^4.0.3" + checksum: 10c0/ce77d5934ac4741513993aad9d8ff44ff03ff5cf5a177e010c7ffcd8d3060087e56df1938c1100d49de712daf952cd2c72dd83e1684d043e698bd2afe0025f5e + languageName: node + linkType: hard + "@vitest/utils@npm:3.0.9": version: 3.0.9 resolution: "@vitest/utils@npm:3.0.9" @@ -8585,6 +8840,17 @@ __metadata: languageName: node linkType: hard +"@vitest/utils@npm:3.2.3": + version: 3.2.3 + resolution: "@vitest/utils@npm:3.2.3" + dependencies: + "@vitest/pretty-format": "npm:3.2.3" + loupe: "npm:^3.1.3" + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/c7a785a73bc0d7c0202ced0d9912639b9deb6f05dd6c25700a13d97e13320ccec57660f11ad1f9225419ac485339fdf7af28c8d77456bcb9558e6c7d73ad538a + languageName: node + linkType: hard + "@volar/language-core@npm:2.4.13, @volar/language-core@npm:~2.4.11": version: 2.4.13 resolution: "@volar/language-core@npm:2.4.13" @@ -12103,6 +12369,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.4.1": + version: 4.4.1 + resolution: "debug@npm:4.4.1" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/d2b44bc1afd912b49bb7ebb0d50a860dc93a4dd7d946e8de94abc957bb63726b7dd5aa48c18c2386c379ec024c46692e15ed3ed97d481729f929201e671fcd55 + languageName: node + linkType: hard + "decamelize@npm:^1.2.0": version: 1.2.0 resolution: "decamelize@npm:1.2.0" @@ -13266,7 +13544,7 @@ __metadata: languageName: node linkType: hard -"es-module-lexer@npm:^1.2.1, es-module-lexer@npm:^1.5.0, es-module-lexer@npm:^1.5.4, es-module-lexer@npm:^1.6.0": +"es-module-lexer@npm:^1.2.1, es-module-lexer@npm:^1.5.0, es-module-lexer@npm:^1.5.4, es-module-lexer@npm:^1.6.0, es-module-lexer@npm:^1.7.0": version: 1.7.0 resolution: "es-module-lexer@npm:1.7.0" checksum: 10c0/4c935affcbfeba7fb4533e1da10fa8568043df1e3574b869385980de9e2d475ddc36769891936dbb07036edb3c3786a8b78ccf44964cd130dedc1f2c984b6c7b @@ -14602,6 +14880,18 @@ __metadata: languageName: node linkType: hard +"fdir@npm:^6.4.5": + version: 6.4.6 + resolution: "fdir@npm:6.4.6" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/45b559cff889934ebb8bc498351e5acba40750ada7e7d6bde197768d2fa67c149be8ae7f8ff34d03f4e1eb20f2764116e56440aaa2f6689e9a4aa7ef06acafe9 + languageName: node + linkType: hard + "fetch-retry@npm:^6.0.0": version: 6.0.0 resolution: "fetch-retry@npm:6.0.0" @@ -17481,6 +17771,13 @@ __metadata: languageName: node linkType: hard +"js-tokens@npm:^9.0.1": + version: 9.0.1 + resolution: "js-tokens@npm:9.0.1" + checksum: 10c0/68dcab8f233dde211a6b5fd98079783cbcd04b53617c1250e3553ee16ab3e6134f5e65478e41d82f6d351a052a63d71024553933808570f04dbf828d7921e80e + languageName: node + linkType: hard + "js-yaml@npm:^3.10.0, js-yaml@npm:^3.13.1": version: 3.14.1 resolution: "js-yaml@npm:3.14.1" @@ -19663,7 +19960,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.6, nanoid@npm:^3.3.8": +"nanoid@npm:^3.3.11, nanoid@npm:^3.3.6, nanoid@npm:^3.3.8": version: 3.3.11 resolution: "nanoid@npm:3.3.11" bin: @@ -21516,6 +21813,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.5.4": + version: 8.5.5 + resolution: "postcss@npm:8.5.5" + dependencies: + nanoid: "npm:^3.3.11" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/6415873fab84de05c2d8fd18f72ea6654bca437bb4b9f02ca819c438501e4b3a450023e575e17587c6eaa5bedddaaa4dad3af210f5cf166e30cec09cac58baf8 + languageName: node + linkType: hard + "preact@npm:^10.5.13": version: 10.26.5 resolution: "preact@npm:10.26.5" @@ -23393,6 +23701,81 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^4.40.0": + version: 4.43.0 + resolution: "rollup@npm:4.43.0" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.43.0" + "@rollup/rollup-android-arm64": "npm:4.43.0" + "@rollup/rollup-darwin-arm64": "npm:4.43.0" + "@rollup/rollup-darwin-x64": "npm:4.43.0" + "@rollup/rollup-freebsd-arm64": "npm:4.43.0" + "@rollup/rollup-freebsd-x64": "npm:4.43.0" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.43.0" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.43.0" + "@rollup/rollup-linux-arm64-gnu": "npm:4.43.0" + "@rollup/rollup-linux-arm64-musl": "npm:4.43.0" + "@rollup/rollup-linux-loongarch64-gnu": "npm:4.43.0" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.43.0" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.43.0" + "@rollup/rollup-linux-riscv64-musl": "npm:4.43.0" + "@rollup/rollup-linux-s390x-gnu": "npm:4.43.0" + "@rollup/rollup-linux-x64-gnu": "npm:4.43.0" + "@rollup/rollup-linux-x64-musl": "npm:4.43.0" + "@rollup/rollup-win32-arm64-msvc": "npm:4.43.0" + "@rollup/rollup-win32-ia32-msvc": "npm:4.43.0" + "@rollup/rollup-win32-x64-msvc": "npm:4.43.0" + "@types/estree": "npm:1.0.7" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-freebsd-arm64": + optional: true + "@rollup/rollup-freebsd-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-loongarch64-gnu": + optional: true + "@rollup/rollup-linux-powerpc64le-gnu": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-riscv64-musl": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/a14a16ee5433f9eddfe803ed1a3f4528e3e96f746e55bf88c5482f9a60a4ad61f507b59f46d5d9c8dc98bb7983483e0c94b760ae37c02157eba9da5665c1641b + languageName: node + linkType: hard + "rsvp@npm:^3.0.14, rsvp@npm:^3.0.18": version: 3.6.2 resolution: "rsvp@npm:3.6.2" @@ -24877,6 +25260,15 @@ __metadata: languageName: node linkType: hard +"strip-literal@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-literal@npm:3.0.0" + dependencies: + js-tokens: "npm:^9.0.1" + checksum: 10c0/d81657f84aba42d4bbaf2a677f7e7f34c1f3de5a6726db8bc1797f9c0b303ba54d4660383a74bde43df401cf37cce1dff2c842c55b077a4ceee11f9e31fba828 + languageName: node + linkType: hard + "stubs@npm:^3.0.0": version: 3.0.0 resolution: "stubs@npm:3.0.0" @@ -25335,6 +25727,16 @@ __metadata: languageName: node linkType: hard +"tinyglobby@npm:^0.2.14": + version: 0.2.14 + resolution: "tinyglobby@npm:0.2.14" + dependencies: + fdir: "npm:^6.4.4" + picomatch: "npm:^4.0.2" + checksum: 10c0/f789ed6c924287a9b7d3612056ed0cda67306cd2c80c249fd280cf1504742b12583a2089b61f4abbd24605f390809017240e250241f09938054c9b363e51c0a6 + languageName: node + linkType: hard + "tinypool@npm:^1.0.2": version: 1.0.2 resolution: "tinypool@npm:1.0.2" @@ -25342,6 +25744,13 @@ __metadata: languageName: node linkType: hard +"tinypool@npm:^1.1.0": + version: 1.1.0 + resolution: "tinypool@npm:1.1.0" + checksum: 10c0/deb6bde5e3d85d4ba043806c66f43fb5b649716312a47b52761a83668ffc71cd0ea4e24254c1b02a3702e5c27e02605f0189a1460f6284a5930a08bd0c06435c + languageName: node + linkType: hard + "tinyrainbow@npm:^2.0.0": version: 2.0.0 resolution: "tinyrainbow@npm:2.0.0" @@ -25356,6 +25765,13 @@ __metadata: languageName: node linkType: hard +"tinyspy@npm:^4.0.3": + version: 4.0.3 + resolution: "tinyspy@npm:4.0.3" + checksum: 10c0/0a92a18b5350945cc8a1da3a22c9ad9f4e2945df80aaa0c43e1b3a3cfb64d8501e607ebf0305e048e3c3d3e0e7f8eb10cea27dc17c21effb73e66c4a3be36373 + languageName: node + linkType: hard + "tmp@npm:0.0.28": version: 0.0.28 resolution: "tmp@npm:0.0.28" @@ -26430,6 +26846,21 @@ __metadata: languageName: node linkType: hard +"vite-node@npm:3.2.3": + version: 3.2.3 + resolution: "vite-node@npm:3.2.3" + dependencies: + cac: "npm:^6.7.14" + debug: "npm:^4.4.1" + es-module-lexer: "npm:^1.7.0" + pathe: "npm:^2.0.3" + vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0" + bin: + vite-node: vite-node.mjs + checksum: 10c0/b952b0d9e45662506ea7303ac87d08e02f1e3355777cf7d426f211292c4f87e8837aef589e552bb11404d1bc0a9bd18871ce6ba874b5f0bb171f8e010de20a11 + languageName: node + linkType: hard + "vite-plugin-babel@npm:^1.3.0": version: 1.3.0 resolution: "vite-plugin-babel@npm:1.3.0" @@ -26571,6 +27002,61 @@ __metadata: languageName: node linkType: hard +"vite@npm:^5.0.0 || ^6.0.0 || ^7.0.0-0": + version: 7.0.0-beta.1 + resolution: "vite@npm:7.0.0-beta.1" + dependencies: + esbuild: "npm:^0.25.0" + fdir: "npm:^6.4.5" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.2" + postcss: "npm:^8.5.4" + rollup: "npm:^4.40.0" + tinyglobby: "npm:^0.2.14" + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + jiti: ">=1.21.0" + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/0ab20103182244b310fb2bb4a9f3fb21110a61328052e73accfebf3503270bddd1d19417c2d17bd93f1108125da5ffbc52a1f05c8bf3f564907919ffc64d3d78 + languageName: node + linkType: hard + "vite@npm:^5.0.0 || ^6.0.0, vite@npm:^6.2.5": version: 6.3.3 resolution: "vite@npm:6.3.3" @@ -26708,6 +27194,62 @@ __metadata: languageName: node linkType: hard +"vitest@npm:^3.2.0": + version: 3.2.3 + resolution: "vitest@npm:3.2.3" + dependencies: + "@types/chai": "npm:^5.2.2" + "@vitest/expect": "npm:3.2.3" + "@vitest/mocker": "npm:3.2.3" + "@vitest/pretty-format": "npm:^3.2.3" + "@vitest/runner": "npm:3.2.3" + "@vitest/snapshot": "npm:3.2.3" + "@vitest/spy": "npm:3.2.3" + "@vitest/utils": "npm:3.2.3" + chai: "npm:^5.2.0" + debug: "npm:^4.4.1" + expect-type: "npm:^1.2.1" + magic-string: "npm:^0.30.17" + pathe: "npm:^2.0.3" + picomatch: "npm:^4.0.2" + std-env: "npm:^3.9.0" + tinybench: "npm:^2.9.0" + tinyexec: "npm:^0.3.2" + tinyglobby: "npm:^0.2.14" + tinypool: "npm:^1.1.0" + tinyrainbow: "npm:^2.0.0" + vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0" + vite-node: "npm:3.2.3" + why-is-node-running: "npm:^2.3.0" + peerDependencies: + "@edge-runtime/vm": "*" + "@types/debug": ^4.1.12 + "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 + "@vitest/browser": 3.2.3 + "@vitest/ui": 3.2.3 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@types/debug": + optional: true + "@types/node": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: vitest.mjs + checksum: 10c0/1d853016622f32020e91cc72348d0dc642bde2ddcbd648655a9d33d420375c7cbd6f1a6f5c4398a5d4f59b8c2b120e62eba49fb37f8042e5d4c688b7e60148ef + languageName: node + linkType: hard + "vlq@npm:^0.2.1": version: 0.2.3 resolution: "vlq@npm:0.2.3" @@ -27532,6 +28074,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:^8.18.2": + version: 8.18.2 + resolution: "ws@npm:8.18.2" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/4b50f67931b8c6943c893f59c524f0e4905bbd183016cfb0f2b8653aa7f28dad4e456b9d99d285bbb67cca4fedd9ce90dfdfaa82b898a11414ebd66ee99141e4 + languageName: node + linkType: hard + "xcase@npm:^2.0.1": version: 2.0.1 resolution: "xcase@npm:2.0.1" diff --git a/docs/_snippets/vitest-plugin-vitest-config.md b/docs/_snippets/vitest-plugin-vitest-config.md index 023fd9d8cefd..cfd91cbb3268 100644 --- a/docs/_snippets/vitest-plugin-vitest-config.md +++ b/docs/_snippets/vitest-plugin-vitest-config.md @@ -12,25 +12,30 @@ import viteConfig from './vite.config'; export default mergeConfig( viteConfig, defineConfig({ - plugins: [ - storybookTest({ - // The location of your Storybook config, main.js|ts - configDir: path.join(dirname, '.storybook'), - // This should match your package.json script to run Storybook - // The --ci flag will skip prompts and not open a browser - storybookScript: 'yarn storybook --ci', - }), - ], test: { - // Enable browser mode - browser: { - enabled: true, - // Make sure to install Playwright - provider: 'playwright', - headless: true, - instances: [{ browser: 'chromium' }], - }, - setupFiles: ['./.storybook/vitest.setup.ts'], + // Use `workspace` field in Vitest < 3.2 + projects: [{ + plugins: [ + storybookTest({ + // The location of your Storybook config, main.js|ts + configDir: path.join(dirname, '.storybook'), + // This should match your package.json script to run Storybook + // The --ci flag will skip prompts and not open a browser + storybookScript: 'yarn storybook --ci', + }) + ], + test: { + // Enable browser mode + browser: { + enabled: true, + // Make sure to install Playwright + provider: 'playwright', + headless: true, + instances: [{ browser: 'chromium' }], + }, + setupFiles: ['./.storybook/vitest.setup.ts'], + }, + }], }, }), ); @@ -39,7 +44,6 @@ export default mergeConfig( ```ts filename="vitest.config.ts" renderer="vue" import { defineConfig, mergeConfig } from 'vitest/config'; import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; -import { storybookVuePlugin } from '@storybook/vue3-vite/vite-plugin'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -51,26 +55,30 @@ const dirname = export default mergeConfig( viteConfig, defineConfig({ - plugins: [ - storybookTest({ - // The location of your Storybook config, main.js|ts - configDir: path.join(dirname, '.storybook'), - // This should match your package.json script to run Storybook - // The --ci flag will skip prompts and not open a browser - storybookScript: 'yarn storybook --ci', - }), - storybookVuePlugin(), - ], test: { - // Enable browser mode - browser: { - enabled: true, - // Make sure to install Playwright - provider: 'playwright', - headless: true, - instances: [{ browser: 'chromium' }], - }, - setupFiles: ['./.storybook/vitest.setup.ts'], + // Use `workspace` field in Vitest < 3.2 + projects: [{ + plugins: [ + storybookTest({ + // The location of your Storybook config, main.js|ts + configDir: path.join(dirname, '.storybook'), + // This should match your package.json script to run Storybook + // The --ci flag will skip prompts and not open a browser + storybookScript: 'yarn storybook --ci', + }) + ], + test: { + // Enable browser mode + browser: { + enabled: true, + // Make sure to install Playwright + provider: 'playwright', + headless: true, + instances: [{ browser: 'chromium' }], + }, + setupFiles: ['./.storybook/vitest.setup.ts'], + }, + }], }, }), ); @@ -90,25 +98,30 @@ import viteConfig from './vite.config'; export default mergeConfig( viteConfig, defineConfig({ - plugins: [ - storybookTest({ - // The location of your Storybook config, main.js|ts - configDir: path.join(dirname, '.storybook'), - // This should match your package.json script to run Storybook - // The --ci flag will skip prompts and not open a browser - storybookScript: 'yarn storybook --ci', - }), - ], test: { - // Enable browser mode - browser: { - enabled: true, - // Make sure to install Playwright - provider: 'playwright', - headless: true, - instances: [{ browser: 'chromium' }], - }, - setupFiles: ['./.storybook/vitest.setup.ts'], + // Use `workspace` field in Vitest < 3.2 + projects: [{ + plugins: [ + storybookTest({ + // The location of your Storybook config, main.js|ts + configDir: path.join(dirname, '.storybook'), + // This should match your package.json script to run Storybook + // The --ci flag will skip prompts and not open a browser + storybookScript: 'yarn storybook --ci', + }) + ], + test: { + // Enable browser mode + browser: { + enabled: true, + // Make sure to install Playwright + provider: 'playwright', + headless: true, + instances: [{ browser: 'chromium' }], + }, + setupFiles: ['./.storybook/vitest.setup.ts'], + }, + }], }, }), ); diff --git a/docs/writing-tests/integrations/vitest-addon.mdx b/docs/writing-tests/integrations/vitest-addon.mdx index 57a399d4971e..5d89f12cd5dc 100644 --- a/docs/writing-tests/integrations/vitest-addon.mdx +++ b/docs/writing-tests/integrations/vitest-addon.mdx @@ -97,7 +97,7 @@ When the addon is set up automatically, it will create or adjust your Vitest con
- Example Vitest workspace file + Example Vitest workspace file (Vitest < 3.2) If you're using a [Vitest workspace](https://vitest.dev/guide/workspace), you can define a new workspace project: diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index 094ed3dcf477..ae8fb2031516 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -13,6 +13,7 @@ import { writeFile, writeJson, } from 'fs-extra'; +import { readFile } from 'fs/promises'; import JSON5 from 'json5'; import { createRequire } from 'module'; import { join, relative, resolve, sep } from 'path'; @@ -386,7 +387,6 @@ export async function setupVitest(details: TemplateDetails, options: PassedOptio await writeJson(packageJsonPath, packageJson, { spaces: 2 }); const isVue = template.expected.renderer === '@storybook/vue3'; - const isNextjs = template.expected.framework.includes('nextjs'); // const isAngular = template.expected.framework === '@storybook/angular'; const portableStoriesFrameworks = [ @@ -439,152 +439,39 @@ export async function setupVitest(details: TemplateDetails, options: PassedOptio const vitestConfigFile = await findFirstPath(['vitest.config.ts', 'vitest.config.js'], opts); const workspaceFile = await findFirstPath(['vitest.workspace.ts', 'vitest.workspace.js'], opts); - if (workspaceFile) { - await writeFile( - join(sandboxDir, workspaceFile), - dedent` - import path from 'node:path'; - import { fileURLToPath } from 'node:url'; - import { defineWorkspace, defaultExclude } from "vitest/config"; - import { storybookTest } from "@storybook/addon-vitest/vitest-plugin"; - - ${viteConfigFile ? `import viteConfig from './${viteConfigFile}';` : ''} - - const dirname = - typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); - - export default defineWorkspace([ - { - ${!isNextjs ? `extends: "${viteConfigFile}",` : ''} - plugins: [ - storybookTest({ - configDir: path.join(dirname, '.storybook'), - storybookScript: "yarn storybook --ci", - tags: { - include: ["vitest"], - }, - }), - ], - ${ - isNextjs - ? `optimizeDeps: { - include: [ - "next/image", - "next/legacy/image", - "next/dist/compiled/react", - "sb-original/default-loader", - "sb-original/image-context", - ], - },` - : '' - } - resolve: { - preserveSymlinks: true, - }, - test: { - name: "storybook", - pool: "threads", - exclude: [ - ...defaultExclude, - // TODO: investigate TypeError: Cannot read properties of null (reading 'useContext') - "**/*argtypes*", - ], - /** - * TODO: Either fix or acknowledge limitation of: - * - storybook/preview-api hooks: - * -- UseState - */ - // @ts-expect-error this type does not exist but the property does! - testNamePattern: /^(?!.*(UseState)).*$/, - browser: { - enabled: true, - provider: "playwright", - headless: true, - instances: [{ - browser: 'chromium' - }] - }, - setupFiles: ["./.storybook/vitest.setup.ts"], - environment: "happy-dom", - }, - }, - ]); - ` - ); - } else { - const defaultConfigFile = template.name.includes('JavaScript') - ? 'vitest.config.js' - : 'vitest.config.ts'; - await writeFile( - join(sandboxDir, vitestConfigFile || viteConfigFile || defaultConfigFile), - dedent` - import path from 'node:path'; - import { fileURLToPath } from 'node:url'; - import { defineConfig, defaultExclude } from "vitest/config"; - import { storybookTest } from "@storybook/addon-vitest/vitest-plugin"; - - ${vitestConfigFile && viteConfigFile ? `import viteConfig from './${viteConfigFile}';` : ''} - - const dirname = - typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); - - export default defineConfig({ - ${!isNextjs ? `extends: "${viteConfigFile}",` : ''} - plugins: [ - storybookTest({ - configDir: path.join(dirname, '.storybook'), - storybookScript: "yarn storybook --ci", - tags: { - include: ["vitest"], - }, - }), - ], - ${ - isNextjs - ? `optimizeDeps: { - include: [ - "next/image", - "next/legacy/image", - "next/dist/compiled/react", - "sb-original/default-loader", - "sb-original/image-context", - ], - },` - : '' - } - resolve: { - preserveSymlinks: true, - }, - test: { - name: "storybook", - pool: "threads", - exclude: [ - ...defaultExclude, - // TODO: investigate TypeError: Cannot read properties of null (reading 'useContext') - "**/*argtypes*", - ], - /** - * TODO: Either fix or acknowledge limitation of: - * - storybook/preview-api hooks: - * -- UseState - */ - // @ts-expect-error this type does not exist but the property does! - testNamePattern: /^(?!.*(UseState)).*$/, - browser: { - enabled: true, - provider: "playwright", - headless: true, - instances: [{ - browser: 'chromium' - }] - }, - setupFiles: ["./.storybook/vitest.setup.ts"], - environment: "happy-dom", - }, - }); - ` - ); + const configFile = workspaceFile || vitestConfigFile || viteConfigFile; + if (!configFile) { + throw new Error(`No Vitest or Vite config file found in sandbox: ${sandboxDir}`); } + + let fileContent = await readFile(join(sandboxDir, configFile), 'utf-8'); + // Insert resolve: { preserveSymlinks: true } as a sibling to plugins in the top-level config object + // Handles both defineConfig({ ... }) and defineWorkspace([ ... , { ... }]) + fileContent = fileContent.replace(/(plugins\s*:\s*\[[^\]]*\],?)/, (match) => { + // Insert resolve after plugins + return `${match}\n resolve: {\n preserveSymlinks: true\n },`; + }); + // search for storybookTest({...}) and place `tags: 'vitest'` into it but tags option doesn't exist yet in the config. Also consider multi line + const storybookTestRegex = /storybookTest\((\{[\s\S]*?\})\)/g; + fileContent = fileContent.replace(storybookTestRegex, (match, args) => { + // Add tags as the last property before the closing } + const lastBraceIndex = args.lastIndexOf('}'); + if (lastBraceIndex !== -1) { + // Insert before the last } + const before = args.slice(0, lastBraceIndex).trimEnd(); + const needsComma = before.endsWith('{') ? '' : ','; + const after = args.slice(lastBraceIndex); + return `storybookTest(${before}${needsComma}\n tags: {\n include: ['vitest']\n }\n${after})`; + } + // If tags exists and is not empty, or any other case, return as is + return match; + }); + + await writeFile(join(sandboxDir, configFile), fileContent); + // Only run story tests which are tagged with 'vitest' + const previewConfig = await readConfig({ cwd: sandboxDir, fileName: 'preview' }); + previewConfig.setFieldValue(['tags'], ['vitest']); + await writeConfig(previewConfig); } export async function addExtraDependencies({