vite: CSS files cannot be treeshaken with side effects
Describe the bug

vue-cli(webpack) VS vite

Reproduction
https://github.com/BuptStEve/vite-css-treeshake
-
npm startto run vue-cli -
npm run viteto run vite -
npm run buildto run vue-cli -
npm run vite:buildto run vite
System Info
System:
OS: macOS 11.4
CPU: (8) x64 Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
Memory: 2.44 GB / 16.00 GB
Shell: 5.8 - /bin/zsh
Binaries:
Node: 14.17.1 - ~/.nvm/versions/node/v14.17.1/bin/node
Yarn: 1.22.4 - /usr/local/bin/yarn
npm: 6.14.13 - ~/.nvm/versions/node/v14.17.1/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
Browsers:
Chrome: 92.0.4515.107
Safari: 14.1.1
npmPackages:
vite: ^2.4.3 => 2.4.3
Used Package Manager
yarn
Logs
No response
Validations
- Follow our Code of Conduct
- Read the Contributing Guidelines.
- Read the docs.
- Check that there isn’t already an issue that reports the same bug to avoid creating a duplicate.
- Make sure this is a Vite issue and not a framework-specific issue. For example, if it’s a Vue SFC related bug, it should likely be reported to https://github.com/vuejs/vue-next instead.
- Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
About this issue
- Original URL
- State: open
- Created 3 years ago
- Reactions: 4
- Comments: 19 (14 by maintainers)
Yeah, this feature is useful for UI library to auto import component’s own style by default. And when you build app, it also helps treeshake unused ones(with style).
@mayank99 I don’t think there’s a workaround today, but I agree there should be a way around this, like the
sideEffectsfield discussed above. I’ll put this on the team board to confirm if this is the design we want, or if there’s an alternate API design.I have a Vuetify 3 + Vite project and was hoping to get rid of the unnecessary 20K lines of css from my final bundle.
@bluwy I think your first understanding about
sideEffectsfield is correct. According to the webpack docs here, it shows"sideEffects": [ "**/*.css" ],means**/*.csshas side-effectful. (Not side-effect-free)When this was changed like this, webpack did not output any css.
So in conclusion I think when there is
sideEffectsfield, Vite should respect the field for css files (and others). For example, ifsideEffectsfield exists and does not include something like*.css, vite should stop treating css files as side-effect-ful.It seems rollup treats each
exportstatements as side-effect free if the barrel file is marked as side effect free. https://stackblitz.com/edit/rollup-repro-jgfcu7?file=package.json,rollup.config.jsAlso it seems the repro now works: https://stackblitz.com/edit/github-sqz9ee?file=package.json With Vite 2.x, the CSS file is
.a{color:#00f}.a,.b{color:#ff0}. With Vite 3.x, the CSS file doesn’t exist at all. With Vite 4.x, the CSS file is.a{color:#00f}. Interesting that the behavior changes 🤔I took another look at the repro and understand the issue better now. It isn’t related to CSS at all. Webpack has a unique optimization where if you mark a barrel file as side-effect free, then any re-exports will redirect to the re-exported module directly. Specifically
optimization.sideEffectsandoptimization.providedExportsare at play here, which is also enabled in dev. For example:Vite intentionally doesn’t have these optimizations in dev because they have perf penalties. If there’s interest for it, I think https://github.com/vitejs/vite/issues/8237 is the right issue to follow. As even if we respect
sideEffectsin user project today, it will only work in build which can cause unintended inconsistencies.For this issue, I’ll focus on CSS treeshaking only and will close this once it’s resolved. To get the repro resolved, https://github.com/vitejs/vite/issues/8237 should be followed instead.
Ah, I overlooked it and assumed it’s the normal global CSS imports 🤦. Webpack does recommend adding
*.csstosideEffectsfield.I’m currently looking into this as it’ll benefit UI frameworks with scoped CSS. Especially for component libraries or barrel files, as today, all the scoped CSS will get bundled even if the related component is not bundled.
However marking CSS are side-effect-free is technically not the solution. For example, if you have
import "./global.css"in your entrypoint JS, “side-effect-free” means that the import can be removed completely (and its related CSS asset) because you’re not really using anything from the module anyway, which is incorrect.I’m currently trying to figure an API to say “this CSS import is related to this default/named export. if the export is treeshaken, also treeshake the CSS”. I’m not sure if it’s possible to do it automatically. Also, this issue affects CSS modules today.
@bluwy Is there a way to work around this behavior?
Components often use scoped selectors to ensure there are no global side-effects, so it would be nice to tell vite to treat these css imports as side-effect free. I understand that this is a feature request but there must be something we (component library authors) can do or document today to reduce bundle size?
I’m confused, if the package.json marked CSS files as side-effectful, shouldn’t Webpack include the CSS that would result in what Vite produces? Vite currently treats all CSS as side-effectful, and side-effect modules aren’t treeshaken out because they aren’t pure. So from my point of view, Vite is doing the right thing here. Can you shed some light on the issue here?