Skip to content

CLI: Support addon-vitest setup when --skip-install is passed#33718

Merged
valentinpalkovic merged 11 commits into
nextfrom
valentin/bundle-addon-vitest-postinstall-into-storybook
Feb 4, 2026
Merged

CLI: Support addon-vitest setup when --skip-install is passed#33718
valentinpalkovic merged 11 commits into
nextfrom
valentin/bundle-addon-vitest-postinstall-into-storybook

Conversation

@valentinpalkovic
Copy link
Copy Markdown
Contributor

@valentinpalkovic valentinpalkovic commented Jan 30, 2026

Closes #32377

What I did

Run @storybook/addon-vitest's and @storybook/addon-a11y's postinstall even with --skip-install during init. Bundle and call the addon's postinstall directly during init, avoiding runtime resolution failures when deps aren’t installed.

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

  1. Run npx storybook@latest --skip-install and select "Recommended"
  2. See that .storybook/vitest.setup.ts has been created, and the Vite config has been modified.

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-33718-sha-6755af19. Try it out in a new sandbox by running npx storybook@0.0.0-pr-33718-sha-6755af19 sandbox or in an existing project with npx storybook@0.0.0-pr-33718-sha-6755af19 upgrade.

More information
Published version 0.0.0-pr-33718-sha-6755af19
Triggered by @valentinpalkovic
Repository storybookjs/storybook
Branch valentin/bundle-addon-vitest-postinstall-into-storybook
Commit 6755af19
Datetime Tue Feb 3 09:11:12 UTC 2026 (1770109872)
Workflow run 21624078404

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=33718

Summary by CodeRabbit

  • Bug Fixes

    • Automigration and addon installers now use a versioned Storybook CLI and fall back to a default when the installed version is unavailable.
    • Addon configuration no longer aborts early on install checks; partial addon failures are reported per-addon.
  • New Features

    • Installer commands can run using a remote package mode when local install is skipped.
  • Chores / Refactor

    • Template loading switched to dynamic module imports; postinstall routing standardized per addon.
    • Build tooling configured to include a raw-loader plugin dependency.
  • Types

    • Added ambient typing for wildcard "*?raw" imports.
  • Tests

    • Tests updated to use new template names and reflect updated addon configuration behavior.

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Jan 30, 2026

View your CI Pipeline Execution ↗ for commit 5f277da

Command Status Duration Result
nx run-many -t compile,check,knip,test,pretty-d... ❌ Failed 9m 53s View ↗

☁️ Nx Cloud last updated this comment at 2026-02-04 11:16:25 UTC

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

Per-addon postinstall now routes to dedicated handlers (Vitest/A11y) and falls back to a default Storybook version when installed version is missing; vitest templates load via runtime ES module imports and package-run APIs gained a useRemotePkg flag to allow remote (dlx) execution when installs were skipped.

Changes

Cohort / File(s) Summary
Per-addon postinstall & CLI call sites
code/lib/create-storybook/src/commands/AddonConfigurationCommand.ts, code/lib/create-storybook/src/initiate.ts, code/lib/create-storybook/src/commands/AddonConfigurationCommand.test.ts
Removed dependencyInstallationResult from call signatures; added per-addon routing to dedicated postinstall handlers for Vitest and A11y; record per-addon failures instead of an early short-circuit; updated tests and constructor/options to include packageManager.
Automigrate / Storybook version fallback
code/addons/a11y/src/postinstall.ts, code/addons/vitest/src/postinstall.ts, code/lib/cli-storybook/src/automigrate/index.ts
Import versions from storybook/internal/common; use versions.storybook when constructing versioned CLI specs (e.g., storybook@${versions.storybook}) and as a fallback when installed Storybook version is unavailable (removed hard-fail).
Vitest templates & runtime loading
code/addons/vitest/src/updateVitestFile.ts, code/addons/vitest/src/updateVitestFile.test.ts, code/addons/vitest/src/postinstall.ts, code/addons/vitest/src/typings.d.ts
Switched template resolution from filesystem reads to dynamic ES module imports; dropped .ts suffix from template module names; added *?raw ambient module typing to allow raw template imports.
Package manager API & execution mode
code/core/src/common/js-package-manager/JsPackageManager.ts, code/core/src/common/js-package-manager/PNPMProxy.ts, code/core/src/cli/AddonVitestService.ts
Extended runPackageCommand signature with optional useRemotePkg flag; PNPMProxy now switches command to dlx when useRemotePkg is true; AddonVitestService.installPlaywright accepts and forwards useRemotePkg.
Build tooling for raw imports
scripts/build/utils/generate-bundle.ts, scripts/package.json
Added esbuild-raw-plugin dependency and enabled rawPlugin() in esbuild shared plugins to support bundling raw template modules.

Sequence Diagram(s)

sequenceDiagram
  participant Initiate as Initiate
  participant AddonConfig as AddonConfigurationCommand
  participant Versions as "storybook/internal/common"
  participant Postinstall as Postinstall Handler
  participant PackageMgr as Package Manager

  Initiate->>AddonConfig: executeAddonConfiguration(addons, configDir, options)
  AddonConfig->>Versions: read versions.storybook
  loop per addon
    AddonConfig->>Postinstall: call specific handler (vitest / a11y / other) with options
    Postinstall->>PackageMgr: runPackageCommand(args..., useRemotePkg?)
    PackageMgr-->>Postinstall: execution result
    Postinstall-->>AddonConfig: postinstall result (success/failure)
  end
  AddonConfig-->>Initiate: aggregated results / manual instructions for failures
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings

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

🤖 Fix all issues with AI agents
In `@code/lib/create-storybook/src/commands/AddonConfigurationCommand.ts`:
- Line 6: The import in AddonConfigurationCommand.ts currently pulls the vitest
postinstall module via a fragile relative path (importing addonVitestPostinstall
from '../../../../addons/vitest/src/postinstall'); replace that with the package
export by importing from '@storybook/addon-vitest/postinstall' so the code uses
the public API (reference symbol: addonVitestPostinstall in
AddonConfigurationCommand.ts) and remains compatible with built/distributed
packages and internal restructuring.
🧹 Nitpick comments (2)
code/lib/create-storybook/src/commands/AddonConfigurationCommand.ts (2)

116-121: Consider using distinct styling for failed vs successful addon indicators.

Both success () and failure () messages use CLI_COLORS.muted, which dims the failure indicator. Users might miss failed addons in the output.

♻️ Proposed enhancement for better visual distinction
     addons.forEach((addon, index) => {
       const error = addonResults.get(addon);
-      logger.log(CLI_COLORS.muted(error ? `❌ ${addon}` : `✅ ${addon}`), {
+      const message = error 
+        ? CLI_COLORS.error(`❌ ${addon}`)
+        : CLI_COLORS.muted(`✅ ${addon}`);
+      logger.log(message, {
         spacing: index === 0 ? 1 : 0,
       });
     });

64-96: Two cross-package source imports with asymmetric addon handling.

This file imports from sibling packages at two locations: line 5 (static import from addons/vitest/src/postinstall) and line 67 (dynamic import from cli-storybook/src/postinstallAddon).

The asymmetric handling (static for vitest, dynamic for others) is functional but creates inconsistency—both are source imports serving similar purposes (postinstall configuration). Consider standardizing the import approach for clarity.

If additional test addons need postinstall treatment in the future, a registry pattern could improve maintainability:

Registry pattern example
const BUNDLED_POSTINSTALL_HANDLERS: Record<string, PostinstallFn> = {
  '@storybook/addon-vitest': addonVitestPostinstall,
  // '@storybook/addon-a11y': addonA11yPostinstall, // when needed
};

Comment thread code/lib/create-storybook/src/commands/AddonConfigurationCommand.ts
@storybook-app-bot
Copy link
Copy Markdown

storybook-app-bot Bot commented Jan 30, 2026

Package Benchmarks

Commit: 6755af1, ran on 2 February 2026 at 15:39:36 UTC

The following packages have significant changes to their size or dependencies:

@storybook/cli

Before After Difference
Dependency count 183 183 0
Self size 776 KB 776 KB 🚨 +3 B 🚨
Dependency size 67.55 MB 67.60 MB 🚨 +42 KB 🚨
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 50 50 0
Self size 1000 KB 1.04 MB 🚨 +42 KB 🚨
Dependency size 36.92 MB 36.92 MB 🚨 +257 B 🚨
Bundle Size Analyzer node node

…ensions. Update template loading mechanism to dynamically import templates as plain text. Enhance error logging in AddonConfigurationCommand.
@valentinpalkovic valentinpalkovic moved this to Empathy Queue (prioritized) in Core Team Projects Jan 30, 2026
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

🤖 Fix all issues with AI agents
In `@code/addons/vitest/src/updateVitestFile.ts`:
- Around line 3-8: The loadTemplate function currently uses dynamic ESM import
(await import(`../templates/${name}`)) which fails for .txt template files;
replace the dynamic import with reading the template file as text using
fs.readFile and a URL built from import.meta.url (e.g., construct templatePath
via new URL(`../templates/${name}`, import.meta.url) and await
fs.readFile(templatePath, 'utf8')) so loadTemplate returns the plain text
template; update references to templateModule/default to use the read string,
and add a minimal fs import if missing.

Comment thread code/addons/vitest/src/updateVitestFile.ts
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: 2

🤖 Fix all issues with AI agents
In `@code/addons/vitest/src/updateVitestFile.test.ts`:
- Around line 34-36: The forEach callback on Object.entries(replacements) is
using a concise arrow body which causes an implicit return (lint error); change
the callback to a block body or replace the forEach with a for...of loop so the
function does not return a value. Locate the call to
Object.entries(replacements).forEach(...) in updateVitestFile.test.ts and either
convert the arrow to a block (e.g., ([key, value]) => { template =
template.replace(key, normalize(value)); }) or iterate with for (const
[key,value] of Object.entries(replacements)) { template = template.replace(key,
normalize(value)); } ensuring you still use normalize(value) and assign back to
template.
- Around line 25-40: Replace the inline mock factory with a spy-style mock and
move the implementation into test setup: call vi.mock('./updateVitestFile', {
spy: true }) at the top, then in beforeEach use
vi.mocked(loadTemplate).mockImplementation(async (name: string, replacements:
Record<string,string>) => { const templateModule = await
import(`../templates/${name}?raw`); let template = templateModule.default;
Object.entries(replacements).forEach(([key, value]) => { template =
template.replace(key, normalize(value)); }); return template; }); ensure you
reference the real symbol loadTemplate and use vi.mocked(loadTemplate) to type
the mock and change the forEach callback to a block form so it does not return a
value.

Comment thread code/addons/vitest/src/updateVitestFile.test.ts Outdated
Comment thread code/addons/vitest/src/updateVitestFile.test.ts Outdated
…in AddonConfigurationCommand. Enhance logging to inform users about addons that couldn't be configured and provide links to setup instructions.
@storybook-bot
Copy link
Copy Markdown
Contributor

Failed to publish canary version of this pull request, triggered by @valentinpalkovic. See the failed workflow run at: https://github.com/storybookjs/storybook/actions/runs/21587135362

@valentinpalkovic valentinpalkovic force-pushed the valentin/bundle-addon-vitest-postinstall-into-storybook branch from d7a5fdf to 1efe45f Compare February 2, 2026 11:31
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: 2

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)

161-166: ⚠️ Potential issue | 🟡 Minor

useRemotePkg will always be false here due to surrounding condition.

The installPlaywright call is inside the if (!options.skipInstall) block (line 162), which means options.skipInstall is always falsy at this point. Therefore, useRemotePkg: !!options.skipInstall will always evaluate to false.

Either:

  1. The useRemotePkg flag is unnecessary here (since we only reach this code when dependencies are installed), or
  2. The logic needs restructuring if Playwright should be installed via remote package in some scenarios.

Given the surrounding context (line 162 checks !options.skipInstall), option 1 seems correct—you can simplify to useRemotePkg: false or remove it entirely since the default is false.

🔧 Proposed fix
       await addonVitestService.installPlaywright({
         yes: options.yes,
-        useRemotePkg: !!options.skipInstall,
       });
🤖 Fix all issues with AI agents
In `@code/core/src/common/js-package-manager/PNPMProxy.ts`:
- Around line 80-93: The PNPMProxy.runPackageCommand adds a useRemotePkg option
that other proxies (NPMProxy, Yarn1Proxy, Yarn2Proxy) don’t support; remove the
inconsistency by deleting the useRemotePkg parameter and its usage in
PNPMProxy.runPackageCommand: update the method signature to remove useRemotePkg
and always call executeCommand with args prefixed by the local exec form (i.e.,
use 'exec' rather than the ternary [useRemotePkg ? 'dlx' : 'exec', ...args]),
keeping the function name and return type unchanged.

In `@code/lib/create-storybook/src/commands/AddonConfigurationCommand.ts`:
- Around line 65-70: The filter incorrectly checks for .result === 'failed' on
values from addonResults (which are either null or an Error), so update the
filter in the block that calls logManualAddonInstructions to use a truthy check
on addonResults.get(addon) (e.g., addons.filter(addon =>
!!addonResults.get(addon))) or equivalent to select addons with non-null error
entries; this fixes selection of failed addons when calling
logManualAddonInstructions.

Comment on lines 80 to 93
public runPackageCommand({
args,
useRemotePkg = false,
...options
}: Omit<ExecuteCommandOptions, 'command'> & { args: string[] }): ResultPromise {
}: Omit<ExecuteCommandOptions, 'command'> & {
args: string[];
useRemotePkg?: boolean;
}): ResultPromise {
return executeCommand({
command: 'pnpm',
args: ['exec', ...args],
args: [useRemotePkg ? 'dlx' : 'exec', ...args],
...options,
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for runPackageCommand implementations in other proxies
echo "=== Checking NPMProxy ==="
rg -n "runPackageCommand" code/core/src/common/js-package-manager/NPMProxy.ts -A 10 | head -30

echo ""
echo "=== Checking Yarn1Proxy ==="
rg -n "runPackageCommand" code/core/src/common/js-package-manager/Yarn1Proxy.ts -A 10 | head -30

echo ""
echo "=== Checking Yarn2Proxy ==="
rg -n "runPackageCommand" code/core/src/common/js-package-manager/Yarn2Proxy.ts -A 10 | head -30

echo ""
echo "=== Checking BunProxy ==="
rg -n "runPackageCommand" code/core/src/common/js-package-manager/BunProxy.ts -A 10 | head -30

Repository: storybookjs/storybook

Length of output: 1209


Address inconsistency: Other package manager proxies lack useRemotePkg support

The PNPMProxy implementation adds useRemotePkg support, but NPMProxy, Yarn1Proxy, and Yarn2Proxy do not have equivalent implementations. This creates an inconsistency across package managers:

  • NPMProxy: runPackageCommand accepts only basic options and args (no useRemotePkg)
  • Yarn1Proxy: runPackageCommand accepts only args and options (no useRemotePkg)
  • Yarn2Proxy: runPackageCommand accepts only args and options (no useRemotePkg)

Either add useRemotePkg support to all proxies, or remove it from PNPMProxy to maintain consistency across the codebase.

🤖 Prompt for AI Agents
In `@code/core/src/common/js-package-manager/PNPMProxy.ts` around lines 80 - 93,
The PNPMProxy.runPackageCommand adds a useRemotePkg option that other proxies
(NPMProxy, Yarn1Proxy, Yarn2Proxy) don’t support; remove the inconsistency by
deleting the useRemotePkg parameter and its usage in
PNPMProxy.runPackageCommand: update the method signature to remove useRemotePkg
and always call executeCommand with args prefixed by the local exec form (i.e.,
use 'exec' rather than the ternary [useRemotePkg ? 'dlx' : 'exec', ...args]),
keeping the function name and return type unchanged.

Comment on lines +65 to +70
// some addons failed
if (hasFailures) {
this.logManualAddonInstructions(
addons.filter((addon) => addonResults.get(addon)?.result === 'failed')
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Incorrect property access on addonResults values.

The addonResults map stores null for success and the error object for failures (see lines 145, 149). However, the filter on line 68 checks for .result === 'failed', which will never match because the stored values don't have a result property.

The filter should check for truthy values (non-null errors) instead:

🐛 Proposed fix
       if (hasFailures) {
         this.logManualAddonInstructions(
-          addons.filter((addon) => addonResults.get(addon)?.result === 'failed')
+          addons.filter((addon) => addonResults.get(addon) !== null)
         );
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// some addons failed
if (hasFailures) {
this.logManualAddonInstructions(
addons.filter((addon) => addonResults.get(addon)?.result === 'failed')
);
}
// some addons failed
if (hasFailures) {
this.logManualAddonInstructions(
addons.filter((addon) => addonResults.get(addon) !== null)
);
}
🤖 Prompt for AI Agents
In `@code/lib/create-storybook/src/commands/AddonConfigurationCommand.ts` around
lines 65 - 70, The filter incorrectly checks for .result === 'failed' on values
from addonResults (which are either null or an Error), so update the filter in
the block that calls logManualAddonInstructions to use a truthy check on
addonResults.get(addon) (e.g., addons.filter(addon =>
!!addonResults.get(addon))) or equivalent to select addons with non-null error
entries; this fixes selection of failed addons when calling
logManualAddonInstructions.


import { resolvePackageDir } from '../../../core/src/shared/utils/module';
async function getTemplatePath(name: string) {
switch (name) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should have a comment here, explaining why we need to do it this way.

@valentinpalkovic valentinpalkovic merged commit 6d0104a into next Feb 4, 2026
4 of 6 checks passed
@valentinpalkovic valentinpalkovic deleted the valentin/bundle-addon-vitest-postinstall-into-storybook branch February 4, 2026 11:04
@valentinpalkovic valentinpalkovic added the patch:yes Bugfix & documentation PR that need to be picked to main branch label Feb 4, 2026
valentinpalkovic added a commit that referenced this pull request Feb 4, 2026
…st-postinstall-into-storybook

CLI: Support addon-vitest setup when --skip-install is passed
(cherry picked from commit 6d0104a)
@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 cli 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

Status: Done

Development

Successfully merging this pull request may close these issues.

[Bug]: addon-vitest and addon-a11y postinstall is not executed when running create-storybook with --skip-install

3 participants