Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Vite storybook build incorrectly bundles side-effects shared between preview.js and component #30768

Open
askoufis opened this issue Mar 6, 2025 · 2 comments

Comments

@askoufis
Copy link

askoufis commented Mar 6, 2025

Describe the bug

A side-effect module imported inside both preview.js and a component results in an incorrect bundle being created by storybook build. Specifically, the ordering of these side-effects is important, but the resulting bundle does not preserve this ordering, resulting in an error.

These side-effects execute correctly in storybook dev.

Reproduction link

https://github.com/askoufis/storybook-sideffect-repro

Reproduction steps

  1. git clone https://github.com/askoufis/storybook-sideffect-repro
  2. cd storybook-sideffect-repro/app
  3. pnpm install
  4. pnpm dev. The Button primary story works as expected.
  5. pnpm build && pnpm serve. Opening storybook results in an error being thrown.

System

Storybook Environment Info:

  System:
    OS: macOS 15.3.1
    CPU: (10) arm64 Apple M1 Pro
    Shell: 4.0.0 - /opt/homebrew/bin/fish
  Binaries:
    Node: 18.20.1 - ~/.local/share/mise/installs/node/18.20.1/bin/node
    Yarn: 1.22.22 - ~/.local/share/mise/installs/node/18.20.1/bin/yarn
    npm: 10.5.0 - ~/.local/share/mise/installs/node/18.20.1/bin/npm
    pnpm: 9.15.4 - ~/.local/share/mise/installs/node/18.20.1/bin/pnpm <----- active
  Browsers:
    Chrome: 134.0.6998.44
    Edge: 133.0.3065.92
    Safari: 18.3

Additional context

Our design system requires injecting a CSS reset via a side-effect import before using any components. We rely on import order to ensure that any other component-specific CSS is injected after the CSS reset. We enforce this by setting a flag as a side-effect in the CSS reset import, and then checking that flag as a side-effect when our design system's provider is imported.

It looks like this:

// App.tsx

// Imports CSS as a side-effect, sets a flag
import 'braid-design-system/reset';
// Checks the flag set by the above import
import { BraidProvider } from 'braid-design-system';

Every component must be wrapped inside our design system's provider in order to function. So naturally preview.js is a logical place to both import our CSS reset and declare a decorator that wraps all stories with a BraidProvider.

Currently we have a webpack-based Storybook that functions correctly with this setup. However, when attempting to migrate to Vite, we found that CSS was being bundled in a different order than declared during storybook build. Specifically, the reset CSS was injected after component-specific CSS, causing styling issues. CSS is in the correct order during storybook dev.

I initially though this somehow being caused by our CSS-in-JS solution Vanilla Extract, but that turned out to be a red herring.

My reproduction has narrowed the bug down to just some simple side-effects. In short: a side-effect shared by both preview.js and a component Button.jsx results in a bundle that does not respect the ordering of these side-effects.

The reproduction is a simplification of our design system's CSS reset functionality. It is implemented as a flag that is set (via side-effect-package/set) and then checked (via side-effect-package/check) within preview.js, and then the component Button.jsx once again checks this flag value.

During storybook dev, these side-effects fire as expected. The flag is set to true, and then checked. During storybook build however, the bundle does not include the code in side-effect-package/set, only side-effect-package/check. This check runs, and it fails because the flag was never set.

It's worth noting that side-effect-package has "sideEffects": true in its package.json, so it should be apparent to any bundler that it contains side-effects.

Another point to note is that the issue only occurs if both preview.js and Button.jsx share a side-effect import. If you comment out import "side-effect-package/check" and instead uncomment import "side-effect-package/other", all side-effects are bundle correctly and no errors are thrown.

I've tried debugging why this particular bundling quirk is occurring, but I can't quite find the right Rollup plugin hook to give me the information I need. Specifically: why are the side-effects from preview.js not being include in their entirety in the bundle.

This seems like a trivial side-effect issue, but it speaks to a larger discrepancy between vite's dev server and production bundle.

Copy link
Contributor

greptile-apps bot commented Mar 6, 2025

** Disclaimer** This information might be inaccurate, due to it being generated automatically
This appears to be a Vite bundling issue with side effects in the preview bundle. The fix requires: 1. Modify the Vite builder configuration in code/builders/builder-vite/src/index.ts to ensure side effects from preview.js are properly included: ts // Add to vite config options build: { rollupOptions: { preserveEntrySignatures: 'strict', output: { preserveModules: true, preserveModulesRoot: 'src' } } } 2. Update the preview entry handling in code/builders/builder-vite/src/presets/preview-preset.ts to explicitly mark preview.js imports as side effects: ts plugins: [ { name: 'storybook:preview-side-effects', enforce: 'pre', resolveId(id, importer) { if (id.includes('preview')) { return { id, moduleSideEffects: true }; } } } ] This will ensure Vite properly bundles and orders shared side effects between preview.js and components.

About Greptile

This response provides a starting point for your research, not a precise solution.

Help us improve! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.

Edit Issue Bot Settings · Greptile

@askoufis
Copy link
Author

askoufis commented Mar 7, 2025

As suggested by the bot, setting output: { preserveModules: true } does fix the issue, but at the cost of generating significantly more chunks in the final build. This seems like a blunt hammer solution that doesn't really address the core issue: incorrect treatment of side-effects in preview.js.

The bot's other suggestion, setting moduleSideEffects: true for the preview module in resolveId, breaks the build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants