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

Support Tree shaking of CSS #506

Open
ankurp opened this issue Apr 20, 2017 · 21 comments
Open

Support Tree shaking of CSS #506

ankurp opened this issue Apr 20, 2017 · 21 comments

Comments

@ankurp
Copy link

ankurp commented Apr 20, 2017

Do you want to request a feature or report a bug?
Feature

What is the current behavior?
Webpack 2 support tree shaking and removal of dead code but this does not work for CSS. If we have a javascript file which exports different CSS files and we only import some of then, then css loader does not tree shake and remove the unused CSS.

Example if we had the code below, css-loader currently is including CSS for both Checkbox and Button but it should only include code for Checkbox since Button is never imported:

// componentcss.js
export const Checkbox = require('./Checkbox.css');
export const Button = require('./Button.css');

// index.js
import { Checkbox } from './componentcss';

What is the expected behavior?

css-loader should tree shake and remove unused CSS import when used webpack 2 with tree shaking feature.

If this is a feature request, what is motivation or use case for changing the behavior?

This will help out reduce the CSS bundle size out of box when using webpack 2

Please mention other relevant information such as your webpack version, Node.js version and Operating System.

This will work with webpack 2.

@michael-ciniawsky
Copy link
Member

michael-ciniawsky commented Apr 21, 2017

Send PR for discussion how this would/could look like, but tbh I have no idea how css-loader should do that, since it works on a per file (chunk) basis like loaders do in general 😛. There are future plans to natively support CSS in webpack without requiring style-loader/css-loader etc. anymore, but that's the sound of the intermediate feature and I guess it will require changes to webpack core itself, mainly bc only the whole compilation with all modules (chunks) available could tree shake (dedupe) repetitive parts. In short css-loader has no context about your dependency graph it solely resolves @import/url() paths within a file, minimize && scopes the CSS locally to the file via CSSModules. Based on your example css-loader would run twice without any knowledge about the contents and no way to dedupe that within the loader. It already exports a format to dedupe [[ css.id, css.mediaQuery, css.content, css.map, css.locals ]] which style-loader || ETWP currently use to remove/skip duplicates, but if this works under all circumstances isn't always guaranteed

@ankurp
Copy link
Author

ankurp commented Apr 24, 2017

There was a PR submitted but it did not get rebased and was closed because of no activity #402

Not sure if something similar can be implemented where css-loader uses ES6 import export statements instead of module.exports so that webpack 2 can tree shake and remove the unused modules.

@michael-ciniawsky
Copy link
Member

michael-ciniawsky commented Apr 24, 2017

The conversion to ES2015 Modules will definitely by coming soon, but this is a joined effort with changes needed in style-loader && extract-textwebpack-plugin aswell + in the PR the author referenced a webpack plugin prototypein addition, which then would do the whole 'tree shaking' part on the compilation level before the bundle get emitted 😛. That's what I was referring to in my ealier comment by 'needs changes to webpack itself' (webpack Core), bc a loader has not enough context to 'tree shake' anything, each loader run (per file/chunk) would at best be a branch of the 'tree'.

@simlrh
Copy link

simlrh commented Apr 25, 2017

Confirm that all css-loader needs to do to support CSS treeshaking is to emit ES2015 modules. The "deadcss" plugin runs a sub-compilation and checks the used exports of css-loader.

@marswong
Copy link

any update on this?

@alexander-akait
Copy link
Member

alexander-akait commented Jul 30, 2018

in css-loader@4 we drop support css modules in favor postcss plugins, looks you need postcss plugin for these

@vankop
Copy link

vankop commented Dec 17, 2019

@evilebottnawi Do you have any ideas to implement this in webpack 5? Maybe only for css modules..

@alexander-akait
Copy link
Member

@vankop i think we need postcss plugin for this

@vankop
Copy link

vankop commented Dec 18, 2019

Maybe my vision is mistaken, but how you want to tree shake css without webpack plugin? You need to understand which css classes does not imported in JS.

@alexander-akait
Copy link
Member

@vankop to be honestly, i think it is impossible, i really don't have ideas how we can remove unused selectors, we don't have infromation about which selectors used, so we can't remove them, also it is not safe, because you can asynchronously load CSS file and use any logic so removing selectors is not safe

@MaxmaxmaximusGitHub
Copy link

MaxmaxmaximusGitHub commented Jun 15, 2020

@evilebottnawi actually, such information is in css modules

image

image

i use just naruto, sasuke must be remove from bundle

@alexander-akait
Copy link
Member

@MaxmaxmaximusGitHub CSS has side effect, we don't know where you can use classes, you can just put them in index.html

@simlrh
Copy link

simlrh commented Jul 22, 2020

@evilebottnawi If you're using CSS Modules then all class names are local and can't be referenced globally.

I had this working in 2017 (#402 and various other plugins) but it involved a lot of breaking changes and there didn't seem to be the will to get it merged

@simlrh
Copy link

simlrh commented Jul 22, 2020

I just checked and the demo project I created still works: https://github.com/simlrh/dead-css-loader-demo

Styles all import from the semantic ui css, which is 290kb minified, and the resulting bundle.js (including the entire app, react, etc) is only 179kb

@alexander-akait
Copy link
Member

alexander-akait commented Jul 22, 2020

If you're using CSS Modules then all class names are local and can't be referenced globally.

And Yes and No, you still inject styles in DOM, so they still have side effects, and in theory you can use it in any file (just write hashing class 😄 ), I think we can implement plugin for this, it is not loader work

@nyngwang
Copy link

nyngwang commented Feb 16, 2024

@alexander-akait Is this question even valid?

  1. In webpack5, we can customize the optimization option in webpack.config.js, and some options like sideEffects, usedExports are there to achieve tree-shaking.
  2. Since css-loader transforms CSS files into js modules, those mechanisms triggered by optimization would certainly cover these CSS-in-js files as well, right?
  3. IIRC, the execution order is "loaders first, then plugins". So why do we need to use css-loader for tree-shaking?

@alexander-akait
Copy link
Member

We still need, because we can remove unused CSS classes from a CSS file

@nyngwang
Copy link

nyngwang commented Mar 4, 2024

@alexander-akait

[...] I think we can implement plugin for this, it is not loader work

So this issue can be closed?

@alexander-akait
Copy link
Member

No

@nyngwang
Copy link

nyngwang commented Mar 4, 2024

@alexander-akait So is it still possible to implement tree-shaking of unused CSS classes using css-loader? Also, is it correct to say that only CSS modules are tree-shakable?

@alexander-akait
Copy link
Member

It is possible, but I don't think we will implement it here, but if someone want it - feel free to send a PR, I want to keep this open

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

8 participants