Remove unused CSS for client:load
components (#4566)
This commit is contained in:
parent
6f1fbd8be0
commit
9ad307a9fc
6 changed files with 48 additions and 9 deletions
5
.changeset/tough-pandas-sneeze.md
Normal file
5
.changeset/tough-pandas-sneeze.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Remove unused CSS for `client:load` components
|
|
@ -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>(),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
7
packages/astro/test/fixtures/0-css/src/components/SvelteDynamic.svelte
vendored
Normal file
7
packages/astro/test/fixtures/0-css/src/components/SvelteDynamic.svelte
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
<h1 id="svelte-dynamic" class="svelte-dynamic">Svelte Dynamic</h1>
|
||||
|
||||
<style>
|
||||
.svelte-dynamic {
|
||||
font-family: Courier, monospace;
|
||||
}
|
||||
</style>
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue