diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index f656c8c8aed4fd..102fdcea96f9c2 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -542,7 +542,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { map: { mappings: '' }, // avoid the css module from being tree-shaken so that we can retrieve // it in renderChunk() - moduleSideEffects: inlined ? false : 'no-treeshake', + moduleSideEffects: modulesCode || inlined ? false : 'no-treeshake', } }, diff --git a/playground/css/__tests__/css.spec.ts b/playground/css/__tests__/css.spec.ts index 89226a8fbd5ba1..c0efb130bf00f9 100644 --- a/playground/css/__tests__/css.spec.ts +++ b/playground/css/__tests__/css.spec.ts @@ -533,3 +533,8 @@ test.runIf(isBuild)('manual chunk path', async () => { findAssetFile(/dir\/dir2\/manual-chunk-[-\w]{8}\.css$/), ).not.toBeUndefined() }) + +test.runIf(isBuild)('CSS modules should be treeshaken if not used', () => { + const css = findAssetFile(/\.css$/, undefined, undefined, true) + expect(css).not.toContain('treeshake-module-b') +}) diff --git a/playground/css/index.html b/playground/css/index.html index 508744160526de..a0e92b205e79f6 100644 --- a/playground/css/index.html +++ b/playground/css/index.html @@ -105,6 +105,8 @@

CSS

Imported SASS module:


 
+  

CSS modules should treeshake in build

+

Imported compose/from CSS/SASS module:

CSS modules composes path resolving: this should be turquoise diff --git a/playground/css/main.js b/playground/css/main.js index 8b3eb488fe813b..05a9c426f3419c 100644 --- a/playground/css/main.js +++ b/playground/css/main.js @@ -20,6 +20,11 @@ import sassMod from './mod.module.scss' document.querySelector('.modules-sass').classList.add(sassMod['apply-color']) text('.modules-sass-code', JSON.stringify(sassMod, null, 2)) +import { a as treeshakeMod } from './treeshake-module/index.js' +document + .querySelector('.modules-treeshake') + .classList.add(treeshakeMod()['treeshake-module-a']) + import composesPathResolvingMod from './composes-path-resolving.module.css' document .querySelector('.path-resolved-modules-css') diff --git a/playground/css/treeshake-module/a.js b/playground/css/treeshake-module/a.js new file mode 100644 index 00000000000000..7272fa1dc1d9c1 --- /dev/null +++ b/playground/css/treeshake-module/a.js @@ -0,0 +1,5 @@ +import style from './a.module.css' + +export function a() { + return style +} diff --git a/playground/css/treeshake-module/a.module.css b/playground/css/treeshake-module/a.module.css new file mode 100644 index 00000000000000..72ab1a9fdb001a --- /dev/null +++ b/playground/css/treeshake-module/a.module.css @@ -0,0 +1,3 @@ +.treeshake-module-a { + color: red; +} diff --git a/playground/css/treeshake-module/b.js b/playground/css/treeshake-module/b.js new file mode 100644 index 00000000000000..b3db996f7f64cd --- /dev/null +++ b/playground/css/treeshake-module/b.js @@ -0,0 +1,5 @@ +import style from './b.module.css' + +export function b() { + return style +} diff --git a/playground/css/treeshake-module/b.module.css b/playground/css/treeshake-module/b.module.css new file mode 100644 index 00000000000000..5ad402ef7353e8 --- /dev/null +++ b/playground/css/treeshake-module/b.module.css @@ -0,0 +1,3 @@ +.treeshake-module-b { + color: red; +} diff --git a/playground/css/treeshake-module/index.js b/playground/css/treeshake-module/index.js new file mode 100644 index 00000000000000..67332c5a21eb3d --- /dev/null +++ b/playground/css/treeshake-module/index.js @@ -0,0 +1,2 @@ +export { a } from './a.js' +export { b } from './b.js' diff --git a/playground/test-utils.ts b/playground/test-utils.ts index b5fe29d2a24ae8..2916c350d12f5f 100644 --- a/playground/test-utils.ts +++ b/playground/test-utils.ts @@ -156,6 +156,7 @@ export function findAssetFile( match: string | RegExp, base = '', assets = 'assets', + matchAll = false, ): string { const assetsDir = path.join(testDir, 'dist', base, assets) let files: string[] @@ -167,10 +168,21 @@ export function findAssetFile( } throw e } - const file = files.find((file) => { - return file.match(match) - }) - return file ? fs.readFileSync(path.resolve(assetsDir, file), 'utf-8') : '' + if (matchAll) { + const matchedFiles = files.filter((file) => file.match(match)) + return matchedFiles.length + ? matchedFiles + .map((file) => + fs.readFileSync(path.resolve(assetsDir, file), 'utf-8'), + ) + .join('') + : '' + } else { + const matchedFile = files.find((file) => file.match(match)) + return matchedFile + ? fs.readFileSync(path.resolve(assetsDir, matchedFile), 'utf-8') + : '' + } } export function readManifest(base = ''): Manifest {