Skip to content

Instantly share code, notes, and snippets.

@jantimon
Last active November 19, 2024 13:40
Show Gist options
  • Save jantimon/10b87c762e77dc31818920b5a1604c3e to your computer and use it in GitHub Desktop.
Save jantimon/10b87c762e77dc31818920b5a1604c3e to your computer and use it in GitHub Desktop.
Webpack Bundling - sideEffects: true

Webpack Bundling (sideEffects: true)

The following example shows the result of bundling of css modules in the following setting:

  • "sideEffect": "true" for all packages package.json
  • barrel files
  • mono repository

Because of "sideEffect": "true" webpack processes all imports when dealing with barrel files.

Here's the minimal setup that demonstrates the problem:

https://github.com/jantimon/reproduction-webpack-css-order/tree/chunk-splitting

@segments/carousel/
├── src/
│   ├── index.ts        # barrel file
│   ├── buttons.ts      # exports CarouselButton
│   ├── button.module.css
│   ├── slide.ts        # exports CarouselSlide
│   └── slide.module.css

@libraries/
├── blog/
│   ├── blog.ts         # imports CarouselSlide
│   └── blog.module.css
└── teaser/
    ├── src/
    │   ├── teaser.ts   # imports CarouselButton
    │   └── teaser.module.css

The carousel barrel file (index.ts) exports everything:

export * from './buttons';
export * from './slide';

Now here's where it gets interesting. The blog component only needs the slide:

import { CarouselSlide } from '@segments/carousel';
import styles from './blog.module.css';

And the teaser only needs the button:

import { CarouselButton } from '@segments/carousel';
import styles from './teaser.module.css';

However, when webpack builds this, both button.module.css AND slide.module.css end up in the final bundles for both blog and teaser. his happens because webpack follows the barrel file imports and includes all exported modules, regardless of what's actually used.

assets by path *.css 60.6 KiB
  asset a.css 30.3 KiB [emitted] (name: a)
  asset b.css 30.3 KiB [emitted] (name: b)

Chunk splitting doesn't help much as it will now detect all the css as common css although it is used only once:

+ optimization: {
+   splitChunks: {
+     chunks: 'all',
+   },
+ },
assets by path *.css 30.6 KiB
  asset 458.css 30.3 KiB [emitted]
  asset a.css 162 bytes [emitted] (name: a)
  asset b.css 162 bytes [emitted] (name: b)

In the Webpacks Bundle Graph you can see that both entries add all css files:

graph TD
    subgraph "Entry Points and Dependencies"
        EntryA["entryA.ts preOrder: 0, postOrder: 11"]
        EntryB["entryB.ts preOrder: 0, postOrder: 11"]
        
        TeaserIndex["@libraries/teaser/index.ts preOrder: 1, postOrder: 10"]
        BlogIndex["@libraries/blog/index.ts preOrder: 1, postOrder: 10"]
        
        TeaserTs["@libraries/teaser/teaser.ts preOrder: 2, postOrder: 9"]
        BlogTs["@libraries/blog/blog.ts preOrder: 2, postOrder: 9"]
        
        CarouselA["@segments/carousel/index.ts preOrder: 3, postOrder: 6"]
        CarouselB["@segments/carousel/index.ts preOrder: 3, postOrder: 6"]
        
        ButtonsA["@segments/carousel/buttons.ts preOrder: 4, postOrder: 2"]
        ButtonsB["@segments/carousel/buttons.ts preOrder: 4, postOrder: 2"]
        
        ButtonsCssA["button.module.css preOrder: 5, postOrder: 1"]
        ButtonsCssB["button.module.css preOrder: 5, postOrder: 1"]
        
        ButtonsCssContent1["button.module.css|0|||}} preOrder: 6, postOrder: 0"]
        ButtonsCssContent2["button.module.css|0|||}} preOrder: 6, postOrder: 0"]
        
        SlideA["@segments/carousel/slide.ts preOrder: 7, postOrder: 5"]
        SlideB["@segments/carousel/slide.ts preOrder: 7, postOrder: 5"]
        
        SlideCssA["slide.module.css preOrder: 8, postOrder: 4"]
        SlideCssB["slide.module.css preOrder: 8, postOrder: 4"]
        
        SlideCssContent1["slide.module.css|0|||}} preOrder: 9, postOrder: 3"]
        SlideCssContent2["slide.module.css|0|||}} preOrder: 9, postOrder: 3"]
        
        TeaserCss["teaser.module.css preOrder: 10, postOrder: 8"]
        BlogCss["blog.module.css preOrder: 10, postOrder: 8"]
        
        TeaserCssContent["teaser.module.css|0|||}} preOrder: 11, postOrder: 7"]
        BlogCssContent["blog.module.css|0|||}} preOrder: 11, postOrder: 7"]

        EntryA --> TeaserIndex
        EntryB --> BlogIndex
        
        TeaserIndex --> TeaserTs
        BlogIndex --> BlogTs
        
        TeaserTs --> CarouselA
        BlogTs --> CarouselB
        
        CarouselA --> ButtonsA
        CarouselB --> ButtonsB
        
        ButtonsA --> ButtonsCssA
        ButtonsB --> ButtonsCssB
        
        ButtonsCssA --> ButtonsCssContent1
        ButtonsCssB --> ButtonsCssContent2
        
        CarouselA --> SlideA
        CarouselB --> SlideB
        
        SlideA --> SlideCssA
        SlideB --> SlideCssB
        
        SlideCssA --> SlideCssContent1
        SlideCssB --> SlideCssContent2
        
        TeaserTs --> TeaserCss
        BlogTs --> BlogCss
        
        TeaserCss --> TeaserCssContent
        BlogCss --> BlogCssContent

        style EntryA fill:#AaEa4a,color:#111
        style EntryB fill:#AaEa4a,color:#111
        style ButtonsCssA fill:#A98bA1,color:#111
        style ButtonsCssB fill:#A98bA1,color:#111
        style SlideCssA fill:#A98bA1,color:#111
        style SlideCssB fill:#A98bA1,color:#111
        style TeaserCss fill:#A98bA1,color:#111
        style BlogCss fill:#A98bA1,color:#111
    end
Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment