Skip to content

Builder-Vite: Prevent config duplication#33883

Merged
valentinpalkovic merged 3 commits into
nextfrom
copilot/fix-duplicate-postcss-plugins
Feb 25, 2026
Merged

Builder-Vite: Prevent config duplication#33883
valentinpalkovic merged 3 commits into
nextfrom
copilot/fix-duplicate-postcss-plugins

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 20, 2026

The storybook:enforce-output-dir plugin's config hook was spreading the entire incoming Vite config ({ ...config, build: { outDir } }). Since Vite's mergeConfig concatenates arrays when merging plugin hook results back into the base config, any array-valued fields — including css.postcss.plugins — were duplicated on each resolution cycle, causing PostCSS plugins to run twice.

Changes

  • build.ts: Return only the partial config needed from the config hook instead of spreading the full config:
// Before — spreads full config, causing array fields like css.postcss.plugins to double
config: (config) => ({ ...config, build: { outDir: options.outputDir } }),

// After — returns only what needs to change; Vite merges the rest
config: () => ({ build: { outDir: options.outputDir } }),
  • build.test.ts: Added regression tests verifying that mergeConfig does not duplicate postcss plugins when the config hook returns a partial config, and that the old spread behavior provably caused the duplication.
Original prompt

This section details on the original issue you should resolve

<issue_title>[Bug]: @storybook/builder-vite duplicates postcss plugins during config resolution</issue_title>
<issue_description>### Describe the bug

In Storybook using @storybook/react-vite, the Vite config resolution process during build duplicates the viteConfig.css.postcss.plugins array. As a result, PostCSS plugins are applied twice to the same CSS, causing transformations to be double-applied, and leading to, for example, postcss-prefixwrap now producing .scope .scope .selector, which is obviously a breaking bug.

This started after Storybook introduced the storybook:enforce-output-dir Vite plugin (PR #33740), which adds config hooks (config / configEnvironment) and triggers an additional config merge where the PostCSS plugins array becomes duplicated

Image Image

Reproduction link

https://69971850cc8d9694b2603cf1-mhkdpdawpi.chromatic.com

Reproduction steps

  1. Clone https://github.com/anders-cognite/storybook-vite-postcss-plugins-duplicated-repro
  2. Run the build-storybook command.
  3. Inspect the generated CSS output.

Expected Behavior:
CSS selectors should be prefixed with a single .hello-world, due to the PostCSS PrefixWrap('.hello-world') plugin defined in vite.config.ts.

Actual Behavior:
The PostCSS plugin processes the CSS twice due to array duplication, leading to a double .hello-world .hello-world prefix.

Reproduction link is to the chromatic build.

System

System:
│  OS: Linux 6.6 Ubuntu 24.04.3 LTS 24.04.3 LTS (Noble Numbat)
│  CPU: (16) x64 Intel(R) Core(TM) Ultra 7 265H
│  Shell: 5.9 - /usr/bin/zsh
│  Binaries:
│  Node: 24.11.1 - /run/user/1000/fnm_multishells/217352_1771508128317/bin/node
│  npm: 11.6.2 - /run/user/1000/fnm_multishells/217352_1771508128317/bin/npm
│  pnpm: 10.24.0 - /run/user/1000/fnm_multishells/217352_1771508128317/bin/pnpm
│  <----- active
│  npmPackages:
│  @storybook/react-vite: ^10.2.10 => 10.2.10
│  chromatic: ^15.1.1 => 15.1.1
│  eslint-plugin-storybook: ^10.2.10 => 10.2.10
│  storybook: ^10.2.10 => 10.2.10

Additional context

No response</issue_description>

<agent_instructions>Verify the solution by adding a template story and adjusting the vite config in one of the sandboxes. Make a screenshot from the play function which verifies the css classname only appended once</agent_instructions>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed a configuration merging issue in the Vite builder plugin that resulted in duplicate config properties. Partial plugin configurations are now properly handled without unintended duplication during config merge operations.
  • Tests

    • Added regression tests for the plugin's configuration hook behavior to validate proper handling of partial configuration objects and merge scenarios.

…revent postcss plugin duplication

Co-authored-by: valentinpalkovic <5889929+valentinpalkovic@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix duplication of PostCSS plugins in Vite config fix(builder-vite): prevent postcss plugin duplication from enforce-output-dir config hook Feb 20, 2026
@valentinpalkovic valentinpalkovic marked this pull request as ready for review February 20, 2026 11:37
@valentinpalkovic valentinpalkovic changed the title fix(builder-vite): prevent postcss plugin duplication from enforce-output-dir config hook Builder-Vite: prevent postcss plugin duplication from enforce-output-dir config hook Feb 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 20, 2026

Fails
🚫 PR description is missing the mandatory "#### Manual testing" section. Please add it so that reviewers know how to manually test your changes.

Generated by 🚫 dangerJS against e9bbe2b

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Feb 20, 2026

View your CI Pipeline Execution ↗ for commit e9bbe2b

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

☁️ Nx Cloud last updated this comment at 2026-02-25 14:49:48 UTC

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 20, 2026

📝 Walkthrough

Walkthrough

A regression test file is introduced to verify the storybook:enforce-output-dir Vite plugin's behavior when merging partial versus complete configurations, alongside a modification to the plugin's config hook to return only the build.outDir property instead of spreading the entire config object.

Changes

Cohort / File(s) Summary
Test Suite
code/builders/builder-vite/src/build.test.ts
New regression test file with two test cases verifying Vite config merging behavior: one validating that a partial config return preserves existing plugins, and another demonstrating the duplication issue when the entire config is spread.
Plugin Configuration
code/builders/builder-vite/src/build.ts
Modified the config hook callback to return only { build: { outDir } } instead of { ...config, build: { outDir } }, removing the spread of prior config properties.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/builders/builder-vite/src/build.test.ts`:
- Around line 5-14: Update the placeholder issue reference in the top-of-file
comment so it points to the real issue number: replace "issues/XXXX" with
"issues/33874" (i.e., reference "#33874") in the block that documents the
regression for the storybook:enforce-output-dir plugin's config hook; keep the
rest of the explanatory text (about returning partial config and
css.postcss.plugins duplication) unchanged.

Comment on lines +5 to +14
/**
* Regression test for: https://github.com/storybookjs/storybook/issues/XXXX
*
* The storybook:enforce-output-dir plugin's config hook was previously returning the entire
* incoming config spread ({ ...config, build: { outDir } }). Since Vite merges plugin config
* hook results back into the base config by concatenating arrays, this caused
* css.postcss.plugins (and other array fields) to be duplicated on every resolution cycle.
*
* The fix: return only the partial config needed ({ build: { outDir } }).
*/
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 | 🟡 Minor

Update placeholder issue number to actual issue reference.

The comment references issues/XXXX but should reference the actual issue #33874 that this PR fixes.

📝 Suggested fix
 /**
- * Regression test for: https://github.com/storybookjs/storybook/issues/XXXX
+ * Regression test for: https://github.com/storybookjs/storybook/issues/33874
  *
📝 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
/**
* Regression test for: https://github.com/storybookjs/storybook/issues/XXXX
*
* The storybook:enforce-output-dir plugin's config hook was previously returning the entire
* incoming config spread ({ ...config, build: { outDir } }). Since Vite merges plugin config
* hook results back into the base config by concatenating arrays, this caused
* css.postcss.plugins (and other array fields) to be duplicated on every resolution cycle.
*
* The fix: return only the partial config needed ({ build: { outDir } }).
*/
/**
* Regression test for: https://github.com/storybookjs/storybook/issues/33874
*
* The storybook:enforce-output-dir plugin's config hook was previously returning the entire
* incoming config spread ({ ...config, build: { outDir } }). Since Vite merges plugin config
* hook results back into the base config by concatenating arrays, this caused
* css.postcss.plugins (and other array fields) to be duplicated on every resolution cycle.
*
* The fix: return only the partial config needed ({ build: { outDir } }).
*/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/builders/builder-vite/src/build.test.ts` around lines 5 - 14, Update the
placeholder issue reference in the top-of-file comment so it points to the real
issue number: replace "issues/XXXX" with "issues/33874" (i.e., reference
"#33874") in the block that documents the regression for the
storybook:enforce-output-dir plugin's config hook; keep the rest of the
explanatory text (about returning partial config and css.postcss.plugins
duplication) unchanged.

@valentinpalkovic valentinpalkovic added the patch:yes Bugfix & documentation PR that need to be picked to main branch label Feb 25, 2026
@valentinpalkovic valentinpalkovic merged commit 22eff0e into next Feb 25, 2026
15 of 25 checks passed
@valentinpalkovic valentinpalkovic deleted the copilot/fix-duplicate-postcss-plugins branch February 25, 2026 14:41
@valentinpalkovic valentinpalkovic changed the title Builder-Vite: prevent postcss plugin duplication from enforce-output-dir config hook Builder-Vite: Prevent config duplication Feb 25, 2026
yannbf pushed a commit that referenced this pull request Feb 26, 2026
…css-plugins

Builder-Vite: prevent postcss plugin duplication from enforce-output-dir config hook
(cherry picked from commit 22eff0e)
@github-actions github-actions Bot added the patch:done Patch/release PRs already cherry-picked to main/release branch label Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug builder-vite 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]: @storybook/builder-vite duplicates postcss plugins during config resolution

2 participants