summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/tough-pandas-sneeze.md5
-rw-r--r--packages/astro/src/core/build/internal.ts13
-rw-r--r--packages/astro/src/core/build/vite-plugin-css.ts23
-rw-r--r--packages/astro/test/0-css.test.js7
-rw-r--r--packages/astro/test/fixtures/0-css/src/components/SvelteDynamic.svelte7
-rw-r--r--packages/astro/test/fixtures/0-css/src/pages/index.astro2
6 files changed, 48 insertions, 9 deletions
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<RenderedChunk>;
+ /**
+ * The module ids of all CSS chunks, used to deduplicate CSS assets between
+ * SSR build and client build in vite-plugin-css.
+ */
+ cssChunkModuleIds: Set<string>;
// A mapping of hoisted script ids back to the exact hoisted scripts it references
hoistedScriptIdToHoistedMap: Map<string, Set<string>>;
@@ -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<RenderedChunk>();
-
// These are for tracking hoisted script bundling
const hoistedScriptIdToHoistedMap = new Map<string, Set<string>>();
@@ -70,7 +69,7 @@ export function createBuildInternals(): BuildInternals {
const hoistedScriptIdToPagesMap = new Map<string, Set<string>>();
return {
- pureCSSChunks,
+ cssChunkModuleIds: new Set(),
hoistedScriptIdToHoistedMap,
hoistedScriptIdToPagesMap,
entrySpecifierToBundleMap: new Map<string, string>(),
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 @@
+<h1 id="svelte-dynamic" class="svelte-dynamic">Svelte Dynamic</h1>
+
+<style>
+ .svelte-dynamic {
+ font-family: Courier, monospace;
+ }
+</style>
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';
<VueScoped />
<VueScss />
<ReactDynamic client:load />
+ <SvelteDynamic client:load />
</div>
</body>
</html>