diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 1948c169b83774..707345a55bfb21 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -600,7 +600,9 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { js = `import "${modulePreloadPolyfillId}";\n${js}` } - return js + // Force rollup to keep this module from being shared between other entry points. + // If the resulting chunk is empty, it will be removed in generateBundle. + return { code: js, moduleSideEffects: 'no-treeshake' } } }, diff --git a/playground/css-codesplit/__tests__/css-codesplit.spec.ts b/playground/css-codesplit/__tests__/css-codesplit.spec.ts index 99d1f2d7040506..2f7d5ab5fc5fba 100644 --- a/playground/css-codesplit/__tests__/css-codesplit.spec.ts +++ b/playground/css-codesplit/__tests__/css-codesplit.spec.ts @@ -43,6 +43,14 @@ describe.runIf(isBuild)('build', () => { expect(findAssetFile(/async.*\.js$/)).toBe('') }) + test('should remove empty chunk, HTML without JS', async () => { + const sharedCSSWithJSChunk = findAssetFile('shared-css-with-js.*.js$') + expect(sharedCSSWithJSChunk).toMatch(`/* empty css`) + // there are functions and modules in the src code that should be tree-shaken + expect(sharedCSSWithJSChunk).not.toMatch('function') + expect(sharedCSSWithJSChunk).not.toMatch(/import(?!".\/modulepreload)/) + }) + test('should generate correct manifest', async () => { const manifest = readManifest() expect(manifest['index.html'].css.length).toBe(2) diff --git a/playground/css-codesplit/shared-css-empty-1.js b/playground/css-codesplit/shared-css-empty-1.js new file mode 100644 index 00000000000000..80636d362c52d5 --- /dev/null +++ b/playground/css-codesplit/shared-css-empty-1.js @@ -0,0 +1,4 @@ +function shouldBeTreeshaken_1() { + // This function should be treeshaken, even if { moduleSideEffects: 'no-treeshake' } + // was used in the JS corresponding to the HTML entrypoint. +} diff --git a/playground/css-codesplit/shared-css-empty-2.js b/playground/css-codesplit/shared-css-empty-2.js new file mode 100644 index 00000000000000..7ce6d30628268d --- /dev/null +++ b/playground/css-codesplit/shared-css-empty-2.js @@ -0,0 +1,4 @@ +export default function shouldBeTreeshaken_2() { + // This function should be treeshaken, even if { moduleSideEffects: 'no-treeshake' } + // was used in the JS corresponding to the HTML entrypoint. +} diff --git a/playground/css-codesplit/shared-css-main.js b/playground/css-codesplit/shared-css-main.js new file mode 100644 index 00000000000000..639861b66321f9 --- /dev/null +++ b/playground/css-codesplit/shared-css-main.js @@ -0,0 +1,10 @@ +import shouldTreeshake from './shared-css-empty-2.js' +document.querySelector('#app').innerHTML = ` +
+

Shared CSS, with JS

+
+` +function shouldBeTreeshaken_0() { + // This function should be treeshaken, even if { moduleSideEffects: 'no-treeshake' } + // was used in the JS corresponding to the HTML entrypoint. +} diff --git a/playground/css-codesplit/shared-css-no-js.html b/playground/css-codesplit/shared-css-no-js.html new file mode 100644 index 00000000000000..27c666af881f15 --- /dev/null +++ b/playground/css-codesplit/shared-css-no-js.html @@ -0,0 +1,4 @@ + + +

Share CSS, no JS

+ diff --git a/playground/css-codesplit/shared-css-theme.css b/playground/css-codesplit/shared-css-theme.css new file mode 100644 index 00000000000000..adc68fa6a4dfa0 --- /dev/null +++ b/playground/css-codesplit/shared-css-theme.css @@ -0,0 +1,3 @@ +h1 { + color: red; +} diff --git a/playground/css-codesplit/shared-css-with-js.html b/playground/css-codesplit/shared-css-with-js.html new file mode 100644 index 00000000000000..aaa856f2c6c5ef --- /dev/null +++ b/playground/css-codesplit/shared-css-with-js.html @@ -0,0 +1,6 @@ + + + + +

Replaced by shared-css-main.js

+ diff --git a/playground/css-codesplit/vite.config.js b/playground/css-codesplit/vite.config.js index 3e0d3ac3540be5..5042b6d9b9cab7 100644 --- a/playground/css-codesplit/vite.config.js +++ b/playground/css-codesplit/vite.config.js @@ -9,6 +9,8 @@ export default defineConfig({ main: resolve(__dirname, './index.html'), other: resolve(__dirname, './other.js'), style2: resolve(__dirname, './style2.js'), + 'shared-css-with-js': resolve(__dirname, 'shared-css-with-js.html'), + 'shared-css-no-js': resolve(__dirname, 'shared-css-no-js.html'), }, output: { manualChunks(id) {