From 9ad307a9fca064dcd9b2f27c3243d09d9154a5dc Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Wed, 31 Aug 2022 22:36:41 +0800 Subject: [PATCH] Remove unused CSS for `client:load` components (#4566) --- .changeset/tough-pandas-sneeze.md | 5 ++++ packages/astro/src/core/build/internal.ts | 13 +++++------ .../astro/src/core/build/vite-plugin-css.ts | 23 +++++++++++++++++-- packages/astro/test/0-css.test.js | 7 ++++++ .../0-css/src/components/SvelteDynamic.svelte | 7 ++++++ .../test/fixtures/0-css/src/pages/index.astro | 2 ++ 6 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 .changeset/tough-pandas-sneeze.md create mode 100644 packages/astro/test/fixtures/0-css/src/components/SvelteDynamic.svelte diff --git a/.changeset/tough-pandas-sneeze.md b/.changeset/tough-pandas-sneeze.md new file mode 100644 index 000000000..4b926766c --- /dev/null +++ b/.changeset/tough-pandas-sneeze.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Remove unused CSS for `client:load` components diff --git a/packages/astro/src/core/build/internal.ts b/packages/astro/src/core/build/internal.ts index ca24e0d90..acc2230cd 100644 --- a/packages/astro/src/core/build/internal.ts +++ b/packages/astro/src/core/build/internal.ts @@ -5,8 +5,11 @@ import { prependForwardSlash } from '../path.js'; import { viteID } from '../util.js'; export interface BuildInternals { - // Pure CSS chunks are chunks that only contain CSS. - pureCSSChunks: Set; + /** + * The module ids of all CSS chunks, used to deduplicate CSS assets between + * SSR build and client build in vite-plugin-css. + */ + cssChunkModuleIds: Set; // A mapping of hoisted script ids back to the exact hoisted scripts it references hoistedScriptIdToHoistedMap: Map>; @@ -59,10 +62,6 @@ export interface BuildInternals { * @returns {BuildInternals} */ export function createBuildInternals(): BuildInternals { - // Pure CSS chunks are chunks that only contain CSS. - // This is all of them, and chunkToReferenceIdMap maps them to a hash id used to find the final file. - const pureCSSChunks = new Set(); - // These are for tracking hoisted script bundling const hoistedScriptIdToHoistedMap = new Map>(); @@ -70,7 +69,7 @@ export function createBuildInternals(): BuildInternals { const hoistedScriptIdToPagesMap = new Map>(); return { - pureCSSChunks, + cssChunkModuleIds: new Set(), hoistedScriptIdToHoistedMap, hoistedScriptIdToPagesMap, entrySpecifierToBundleMap: new Map(), diff --git a/packages/astro/src/core/build/vite-plugin-css.ts b/packages/astro/src/core/build/vite-plugin-css.ts index 4833f3547..76f19c8f3 100644 --- a/packages/astro/src/core/build/vite-plugin-css.ts +++ b/packages/astro/src/core/build/vite-plugin-css.ts @@ -134,11 +134,30 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] // Chunks that have the viteMetadata.importedCss are CSS chunks if (meta.importedCss.size) { + // In the SSR build, keep track of all CSS chunks' modules as the client build may + // duplicate them, e.g. for `client:load` components that render in SSR and client + // for hydation. + if (options.target === 'server') { + for (const id of Object.keys(c.modules)) { + internals.cssChunkModuleIds.add(id); + } + } + // In the client build, we bail if the chunk is a duplicated CSS chunk tracked from + // above. We remove all the importedCss to prevent emitting the CSS asset. + if (options.target === 'client') { + if (Object.keys(c.modules).every((id) => internals.cssChunkModuleIds.has(id))) { + for (const importedCssImport of meta.importedCss) { + delete bundle[importedCssImport]; + } + return; + } + } + // For the client build, client:only styles need to be mapped // over to their page. For this chunk, determine if it's a child of a // client:only component and if so, add its CSS to the page it belongs to. if (options.target === 'client') { - for (const [id] of Object.entries(c.modules)) { + for (const id of Object.keys(c.modules)) { for (const pageData of getParentClientOnlys(id, this)) { for (const importedCssImport of meta.importedCss) { pageData.css.set(importedCssImport, { depth: -1 }); @@ -148,7 +167,7 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] } // For this CSS chunk, walk parents until you find a page. Add the CSS to that page. - for (const [id] of Object.entries(c.modules)) { + for (const id of Object.keys(c.modules)) { for (const [pageInfo, depth] of walkParentInfos(id, this)) { if (moduleIsTopLevelPage(pageInfo)) { const pageViteID = pageInfo.id; diff --git a/packages/astro/test/0-css.test.js b/packages/astro/test/0-css.test.js index 81f3354af..5a32b3ad6 100644 --- a/packages/astro/test/0-css.test.js +++ b/packages/astro/test/0-css.test.js @@ -354,5 +354,12 @@ describe('CSS', function () { expect(allInjectedStyles).to.contain('.vue-scss{'); expect(allInjectedStyles).to.contain('.vue-scoped[data-v-'); }); + + it('remove unused styles from client:load components', async () => { + const bundledAssets = await fixture.readdir('./assets'); + // SvelteDynamic styles is already included in the main page css asset + const unusedCssAsset = bundledAssets.find((asset) => /SvelteDynamic\..*\.css/.test(asset)); + expect(unusedCssAsset, 'Found unused style ' + unusedCssAsset).to.be.undefined; + }); }); }); diff --git a/packages/astro/test/fixtures/0-css/src/components/SvelteDynamic.svelte b/packages/astro/test/fixtures/0-css/src/components/SvelteDynamic.svelte new file mode 100644 index 000000000..bf9b6abe8 --- /dev/null +++ b/packages/astro/test/fixtures/0-css/src/components/SvelteDynamic.svelte @@ -0,0 +1,7 @@ +

Svelte Dynamic

+ + diff --git a/packages/astro/test/fixtures/0-css/src/pages/index.astro b/packages/astro/test/fixtures/0-css/src/pages/index.astro index 6c68e9107..7da2dbcb1 100644 --- a/packages/astro/test/fixtures/0-css/src/pages/index.astro +++ b/packages/astro/test/fixtures/0-css/src/pages/index.astro @@ -18,6 +18,7 @@ import VueSass from '../components/VueSass.vue'; import VueScoped from '../components/VueScoped.vue'; import VueScss from '../components/VueScss.vue'; import ReactDynamic from '../components/ReactDynamic.jsx'; +import SvelteDynamic from '../components/SvelteDynamic.svelte'; import '../styles/imported-url.css'; import '../styles/imported.sass'; @@ -69,6 +70,7 @@ import '../styles/imported.scss'; +