Skip to content

Addon-Vitest: Skip postinstall setup when configured#33712

Merged
valentinpalkovic merged 3 commits intonextfrom
valentin/addon-vitest-skip-existing-configs
Jan 30, 2026
Merged

Addon-Vitest: Skip postinstall setup when configured#33712
valentinpalkovic merged 3 commits intonextfrom
valentin/addon-vitest-skip-existing-configs

Conversation

@valentinpalkovic
Copy link
Copy Markdown
Contributor

@valentinpalkovic valentinpalkovic commented Jan 29, 2026

Closes #33695

What I did

This PR refactors the @storybook/addon-vitest postinstall script to be more idempotent and user-friendly. Instead of failing when it encounters existing configurations or setup files, the script now intelligently detects existing setups and avoids redundant modifications.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

Caution

This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This pull request has been released as version 0.0.0-pr-33712-sha-bdf0cf9e. Try it out in a new sandbox by running npx storybook@0.0.0-pr-33712-sha-bdf0cf9e sandbox or in an existing project with npx storybook@0.0.0-pr-33712-sha-bdf0cf9e upgrade.

More information
Published version 0.0.0-pr-33712-sha-bdf0cf9e
Triggered by @valentinpalkovic
Repository storybookjs/storybook
Branch valentin/addon-vitest-skip-existing-configs
Commit bdf0cf9e
Datetime Fri Jan 30 07:06:56 UTC 2026 (1769756816)
Workflow run 21507518897

To request a new release of this pull request, mention the @storybookjs/core team.

core team members can create a new canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=33712

Summary by CodeRabbit

  • New Features

    • Postinstall now detects existing Vitest+Storybook setup and reuses existing setup files or skips redundant config updates instead of failing.
  • Tests

    • Added unit tests covering detection of Vitest/Storybook configuration.
  • Chores

    • Removed an internal exported error type related to the postinstall flow (internal cleanup).

✏️ Tip: You can customize this high-level summary in your review settings.

@valentinpalkovic valentinpalkovic self-assigned this Jan 29, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Jan 29, 2026

Fails
🚫 The "#### Manual testing" section must be filled in. Please describe how to test the changes you've made, step by step, so that reviewers can confirm your PR works as intended.

Generated by 🚫 dangerJS against bdf0cf9

@nx-cloud
Copy link
Copy Markdown

nx-cloud bot commented Jan 29, 2026

View your CI Pipeline Execution ↗ for commit bdf0cf9

Command Status Duration Result
nx run-many -t compile,check,knip,test,pretty-d... ✅ Succeeded 10m 54s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-30 07:18:55 UTC

@valentinpalkovic valentinpalkovic force-pushed the valentin/addon-vitest-skip-existing-configs branch from a03ec3f to 2b12f5b Compare January 29, 2026 14:57
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 29, 2026

📝 Walkthrough

Walkthrough

Adds AST-based detection (isConfigAlreadySetup) to the Vitest addon's postinstall flow, updates postinstall to reuse existing setup files or skip changes when config already contains the Storybook test plugin, adds unit tests, and removes a specific exported error class from core server errors.

Changes

Cohort / File(s) Summary
Tests
code/addons/vitest/src/postinstall.test.ts
Adds unit tests validating isConfigAlreadySetup returns true when the Storybook test plugin and setupFiles entry exist, and false when the plugin is absent.
Vitest addon postinstall
code/addons/vitest/src/postinstall.ts
Introduces isConfigAlreadySetup(_configPath, configContent) using Babel AST traversal to detect Storybook test plugin imports and usage; reworks postinstall flow to reuse existing .storybook/vitest.setup.ts, short-circuit when workspace/root configs already include the plugin, and logs reuse/skips. Adds STORYBOOK_TEST_PLUGIN_SOURCE constant and imports traverse.
Core errors
code/core/src/server-errors.ts
Removes exported AddonVitestPostinstallExistingSetupFileError class (deletes its declaration).

Sequence Diagram

sequenceDiagram
    actor User
    participant PostInstall as Postinstall Handler
    participant ConfigDetector as isConfigAlreadySetup
    participant AST as AST Parser/Traverse
    participant VitestConfig as Vitest Config File
    participant SetupFile as .storybook/vitest.setup.ts

    User->>PostInstall: Run postinstall
    PostInstall->>PostInstall: Detect workspace/root config presence
    PostInstall->>ConfigDetector: isConfigAlreadySetup(configPath, content)
    ConfigDetector->>VitestConfig: Read config content
    ConfigDetector->>AST: Parse & traverse AST
    AST-->>ConfigDetector: Plugin import/usage found?
    ConfigDetector-->>PostInstall: true/false
    alt true (already configured)
        PostInstall->>PostInstall: Log reuse/skip and exit
    else false (not configured)
        PostInstall->>SetupFile: Check for existing setup file
        alt exists
            PostInstall->>PostInstall: Reuse file and log
        else
            PostInstall->>SetupFile: Create setup file
            SetupFile-->>PostInstall: File created
        end
        PostInstall->>VitestConfig: Insert plugin in config (if root config)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings

Tip

🧪 Unit Test Generation v2 is now available!

We have significantly improved our unit test generation capabilities.

To enable: Add this to your .coderabbit.yaml configuration:

reviews:
  finishing_touches:
    unit_tests:
      enabled: true

Try it out by using the @coderabbitai generate unit tests command on your code files or under ✨ Finishing Touches on the walkthrough!

Have feedback? Share your thoughts on our Discord thread!


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
code/addons/vitest/src/postinstall.ts (1)

241-287: Avoid returning early so downstream setup still runs.
The early return skips a11y automigration and the final success/error messaging for workspace users, while the root-config path continues. Consider logging and then continuing (or guarding only the update block).

🛠️ Suggested fix
-    if (alreadyConfigured) {
-      logger.step(
-        CLI_COLORS.success('Vitest for Storybook is already properly configured. Skipping setup.')
-      );
-      return;
-    }
-
-    const workspaceTemplate = await loadTemplate('vitest.workspace.template.ts', {
-      EXTENDS_WORKSPACE: viteConfigFile
-        ? relative(dirname(vitestWorkspaceFile), viteConfigFile)
-        : '',
-      CONFIG_DIR: options.configDir,
-      SETUP_FILE: relative(dirname(vitestWorkspaceFile), existingSetupFile ?? vitestSetupFile),
-    }).then((t) => t.replace(`\n  'ROOT_CONFIG',`, '').replace(/\s+extends: '',/, ''));
-    const source = babelParse(workspaceTemplate);
-    const target = babelParse(workspaceFileContent);
-
-    const updated = updateWorkspaceFile(source, target);
-    if (updated) {
-      logger.step(`Updating your Vitest workspace file...`);
-
-      logger.log(`${vitestWorkspaceFile}`);
-
-      const formattedContent = await formatFileContent(vitestWorkspaceFile, generate(target).code);
-      await writeFile(vitestWorkspaceFile, formattedContent);
-    } else {
-      logger.error(
-        dedent`
-          Could not update existing Vitest workspace file:
-          ${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:
-          https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup-advanced
-        `
-      );
-      errors.push(
-        new AddonVitestPostinstallWorkspaceUpdateError({ filePath: vitestWorkspaceFile })
-      );
-    }
+    if (alreadyConfigured) {
+      logger.step(
+        CLI_COLORS.success('Vitest for Storybook is already properly configured. Skipping setup.')
+      );
+    } else {
+      const workspaceTemplate = await loadTemplate('vitest.workspace.template.ts', {
+        EXTENDS_WORKSPACE: viteConfigFile
+          ? relative(dirname(vitestWorkspaceFile), viteConfigFile)
+          : '',
+        CONFIG_DIR: options.configDir,
+        SETUP_FILE: relative(dirname(vitestWorkspaceFile), existingSetupFile ?? vitestSetupFile),
+      }).then((t) => t.replace(`\n  'ROOT_CONFIG',`, '').replace(/\s+extends: '',/, ''));
+      const source = babelParse(workspaceTemplate);
+      const target = babelParse(workspaceFileContent);
+
+      const updated = updateWorkspaceFile(source, target);
+      if (updated) {
+        logger.step(`Updating your Vitest workspace file...`);
+
+        logger.log(`${vitestWorkspaceFile}`);
+
+        const formattedContent = await formatFileContent(
+          vitestWorkspaceFile,
+          generate(target).code
+        );
+        await writeFile(vitestWorkspaceFile, formattedContent);
+      } else {
+        logger.error(
+          dedent`
+            Could not update existing Vitest workspace file:
+            ${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:
+            https://storybook.js.org/docs/next/${DOCUMENTATION_LINK}#manual-setup-advanced
+          `
+        );
+        errors.push(
+          new AddonVitestPostinstallWorkspaceUpdateError({ filePath: vitestWorkspaceFile })
+        );
+      }
+    }
🤖 Fix all issues with AI agents
In `@code/addons/vitest/src/postinstall.ts`:
- Around line 427-473: The current isConfigAlreadySetup only checks for a plugin
reference and returns true even if setupFiles is missing; update
isConfigAlreadySetup to require both the plugin reference and that the config
object defines a non-empty setupFiles entry. Keep the existing AST traversal
that collects pluginIdentifiers (ImportDeclaration) and detects plugin usage
(CallExpression), then add a traversal that finds the exported config object
(e.g., ExportDefaultDeclaration with an ObjectExpression and/or
AssignmentExpression to module.exports) and looks for a setupFiles property
whose value is an ArrayExpression with at least one element or a non-empty
string; only return true when pluginReferenced is true AND setupFiles is
present/valid. Ensure you update references inside isConfigAlreadySetup
(pluginIdentifiers, pluginReferenced, and the AST traversals) rather than
changing callers.
🧹 Nitpick comments (1)
code/addons/vitest/src/postinstall.test.ts (1)

31-49: Add a negative case for missing setupFiles.
Once isConfigAlreadySetup enforces setupFiles presence, a test where the plugin exists but setupFiles is missing will lock in the smart-skip behavior.

✅ Proposed test
   it('returns false when storybookTest plugin is not used', () => {
@@
     expect(isConfigAlreadySetup('/project/vitest.config.ts', config, setupPath)).toBe(false);
   });
+
+  it('returns false when setupFiles is missing even if the plugin is present', () => {
+    const config = `
+      import { defineConfig } from 'vitest/config';
+      import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
+
+      export default defineConfig({
+        test: {
+          projects: [
+            {
+              extends: true,
+              plugins: [storybookTest({ configDir: '.storybook' })],
+            },
+          ],
+        },
+      });
+    `;
+
+    expect(isConfigAlreadySetup('/project/vitest.config.ts', config, setupPath)).toBe(false);
+  });
 });
As per coding guidelines: Cover all branches, conditions, edge cases, error paths, and different input variations in unit tests.

Comment thread code/addons/vitest/src/postinstall.ts
Copy link
Copy Markdown
Member

@ghengeveld ghengeveld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice improvement. Only some minor remarks (feel free to ignore).

Comment on lines +427 to +429
function isStorybookTestPluginSource(value: string) {
return value === STORYBOOK_TEST_PLUGIN_SOURCE;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems a bit unnecessary to have a function for such a trivial thing?

Comment on lines +312 to +314
logger.step(
CLI_COLORS.success('Vitest for Storybook is already properly configured. Skipping setup.')
);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could reuse the message above.

@valentinpalkovic valentinpalkovic merged commit 0da33fb into next Jan 30, 2026
124 of 125 checks passed
@valentinpalkovic valentinpalkovic deleted the valentin/addon-vitest-skip-existing-configs branch January 30, 2026 09:41
@github-actions github-actions bot mentioned this pull request Jan 30, 2026
24 tasks
@valentinpalkovic valentinpalkovic added the patch:yes Bugfix & documentation PR that need to be picked to main branch label Feb 4, 2026
@github-actions github-actions bot mentioned this pull request Feb 4, 2026
7 tasks
valentinpalkovic added a commit that referenced this pull request Feb 4, 2026
…-existing-configs

Addon-Vitest: Skip postinstall setup when configured
(cherry picked from commit 0da33fb)
@github-actions github-actions bot added the patch:done Patch/release PRs already cherry-picked to main/release branch label Feb 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

addon: vitest bug ci:normal patch:done Patch/release PRs already cherry-picked to main/release branch patch:yes Bugfix & documentation PR that need to be picked to main branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Addon-Vitest: Add support for existing .storybook/vitest.setup.ts files during postinstall step

2 participants