CSS referenced assets are included even when the file is tree shaken
Describe the bug
When a vite build includes a css file conditionally bound to environment variables the referenced assets by the CSS file always end up in the assets output folder even when the CSS file itself which references them does not.
index.css
@font-face {
font-family: 'Roboto Regular';
src: url('assets/roboto-regular.woff2') format('woff2');
font-weight: normal;
font-display: swap;
}
.env
VITE_INCLUDE_CSS="false"
index.js
if(import.meta.env.VITE_INCLUDE_CSS === 'true') {
import('./index.css')
}
Expected behavior: index.css and roboto-regular.woff2 are not included in the build output folder.
Actual behavior: roboto-regular.woff2 is included in the build output folder.
Reproduction
https://github.com/Csszabi98/css-tree-shake-asset-issue
System Info
System:
OS: macOS 12.4
CPU: (10) x64 Apple M1 Pro
Memory: 29.74 MB / 32.00 GB
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 14.19.1 - ~/Library/Caches/fnm_multishells/957_1657095588762/bin/node
npm: 6.14.16 - ~/Library/Caches/fnm_multishells/957_1657095588762/bin/npm
Browsers:
Brave Browser: 101.1.38.109
Chrome: 103.0.5060.114
Firefox: 101.0.1
Safari: 15.5
npmPackages:
@vitejs/plugin-vue: ^2.3.3 => 2.3.3
vite: ^2.9.9 => 2.9.14
Used Package Manager
npm
Logs
No response
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [X] 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 vuejs/core instead.
- [X] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- [X] The provided reproduction is a minimal reproducible example of the bug.
This seems to be because CSS in Vite are side-effectful by default, so Rollup would proceed to render the chunk even if it's unused. We can't change that though since it would break many other tooling, though Vite supports the ?inline query to disable side effects, and you have to inject it manually.
I also found another bug, which after using ?inline, the assets are still emitted, so that seems like a bug.
Update: I took another look at the issue today. The index.css (or main.css in the repro) is not included in the bundled CSS anymore, but the font assets are still emitted. It's because even though the import is unused, we would still transform it and it'll unconditionally emit the assets when transforming.
I'm not sure if there's a way for Rollup to skip transforming imports in dead code, I assume it doesn't treeshake after finished transforming/parsing a module. (Only during rendering?). So there isn't an easy fix on our side at the meantime, other that manually analyzing all assets emitted against the rendered chunks and delete them.
About the ?inline issue, I found that it's the same cause as above.
I'm running into same result but the import is coming from JS file and not CSS. @bluwy do you think this is a different issue and has different root cause?
https://stackblitz.com/edit/vite-unused-assets-tree-shake?file=main.js
Here the asset.png ends up in build even when main.js doesn't import the JS file that imports asset.png. Final build has no references to asset.png on JS or HTML side.
However when the process.env.NODE_ENV check is modified a bit, the dead code elimination seems to work better and asset.png is no longer included in build:
async function enableMocking() {
+ if (process.env.NODE_ENV === 'development') {
- if (process.env.NODE_ENV !== 'development') {
- return;
- }
const devTools = await import('./development-only');
console.log(devTools);
+ }
}
The initial pattern is suggested by MSW documentation. I have MSW serving .png files in development mode, and these are now ending up in production build as well.