Skip to content

fix: deduplicate content script CSS in build output#2268

Open
oc-wonton wants to merge 2 commits intowxt-dev:mainfrom
oc-wonton:fix/content-script-css-dedup-1987
Open

fix: deduplicate content script CSS in build output#2268
oc-wonton wants to merge 2 commits intowxt-dev:mainfrom
oc-wonton:fix/content-script-css-dedup-1987

Conversation

@oc-wonton
Copy link
Copy Markdown

Description

When content scripts import CSS files, Vite outputs the CSS in two locations:

  1. content-scripts/{name}.css (referenced in manifest)
  2. assets/{name}.css (duplicate, not needed)

This adds a post-build deduplication step that removes identical CSS files from assets/ when they already exist in content-scripts/ with the same base name and content.

Related Issues

Fixes #1987

Type of Change

  • Bug fix

Implementation

  • New deduplicateCss() utility in packages/wxt/src/core/utils/building/deduplicate-css.ts
  • Called after entrypoint builds merge but before manifest generation (in rebuild.ts)
  • Conservative matching: only removes files with same base name AND identical content
  • Preserves legitimate CSS files in assets/ (e.g., unlisted-style entrypoints)

Testing

  • 8 unit tests covering: duplicate removal, different content preservation, different name preservation, multiple files, empty cases, missing files, multi-step builds
  • 1 e2e test verifying CSS only appears in content-scripts/ and not duplicated in assets/
✓ src/core/utils/building/__tests__/deduplicate-css.test.ts (8 tests) 71ms
  Test Files  1 passed (1)
       Tests  8 passed (8)

Additional Notes

Based on maintainer guidance in #1987: each content script is built in a separate Vite build, causing CSS to appear in both locations. This fix deduplicates at the output level rather than changing the build pipeline.

When content scripts import CSS files, Vite outputs the CSS in two locations:
1. content-scripts/{name}.css (referenced in manifest)
2. assets/{name}.css (duplicate, not needed)

This change adds a post-build deduplication step that removes identical
CSS files from assets/ when they already exist in content-scripts/ with
the same base name and content. This reduces extension package size by
eliminating unnecessary duplicate files.

The deduplication logic:
- Only compares files with matching base names (e.g., both named "content.css")
- Verifies content is identical before removing
- Preserves legitimate CSS files in assets/ (unlisted-style entrypoints)
- Only removes true duplicates created by Vite's build process

Fixes wxt-dev#1987

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@oc-wonton oc-wonton requested a review from aklinker1 as a code owner April 15, 2026 14:32
@github-actions github-actions Bot added the pkg/wxt Includes changes to the `packages/wxt` directory label Apr 15, 2026
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 15, 2026

Deploy Preview for creative-fairy-df92c4 ready!

Name Link
🔨 Latest commit eec9583
🔍 Latest deploy log https://app.netlify.com/projects/creative-fairy-df92c4/deploys/69eb2d5f98a8ac00080a72eb
😎 Deploy Preview https://deploy-preview-2268--creative-fairy-df92c4.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@oyzamil
Copy link
Copy Markdown
Contributor

oyzamil commented Apr 16, 2026

@oc-wonton When using tailwindcss in content-script and other pages like popup, options etc.

The CSS file is created 2 times. Will this pull also fix it or not?

See the 2 files. I am also curious why the CSS file is named as my component name, Button-id.css; it should be styles.css

image

@oyzamil
Copy link
Copy Markdown
Contributor

oyzamil commented Apr 20, 2026

@aklinker1 Please review this.

Comment on lines +26 to +30
afterEach(async () => {
// Cleanup
await rm(testOutDir, { recursive: true, force: true });
});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

That isn't necessary, because you're doing it in beforeEach

const output: Omit<BuildOutput, 'manifest'> = {
steps: [
{
entrypoints: [] as any,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
entrypoints: [] as any,
entrypoints: [],

This isn't working?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It isn't possible to avoid creating duplicated css, instead of removing it after create?

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 24, 2026

Open in StackBlitz

@wxt-dev/analytics

npm i https://pkg.pr.new/@wxt-dev/analytics@2268

@wxt-dev/auto-icons

npm i https://pkg.pr.new/@wxt-dev/auto-icons@2268

@wxt-dev/browser

npm i https://pkg.pr.new/@wxt-dev/browser@2268

@wxt-dev/i18n

npm i https://pkg.pr.new/@wxt-dev/i18n@2268

@wxt-dev/is-background

npm i https://pkg.pr.new/@wxt-dev/is-background@2268

@wxt-dev/module-react

npm i https://pkg.pr.new/@wxt-dev/module-react@2268

@wxt-dev/module-solid

npm i https://pkg.pr.new/@wxt-dev/module-solid@2268

@wxt-dev/module-svelte

npm i https://pkg.pr.new/@wxt-dev/module-svelte@2268

@wxt-dev/module-vue

npm i https://pkg.pr.new/@wxt-dev/module-vue@2268

@wxt-dev/runner

npm i https://pkg.pr.new/@wxt-dev/runner@2268

@wxt-dev/storage

npm i https://pkg.pr.new/@wxt-dev/storage@2268

@wxt-dev/unocss

npm i https://pkg.pr.new/@wxt-dev/unocss@2268

@wxt-dev/webextension-polyfill

npm i https://pkg.pr.new/@wxt-dev/webextension-polyfill@2268

wxt

npm i https://pkg.pr.new/wxt@2268

commit: bacf0a9

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

Labels

pkg/wxt Includes changes to the `packages/wxt` directory

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Content Script CSS is added twice to build output

3 participants