Remove unused CSS for client:load components (#4566)

This commit is contained in:
Bjorn Lu 2022-08-31 22:36:41 +08:00 committed by GitHub
parent 6f1fbd8be0
commit 9ad307a9fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 48 additions and 9 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Remove unused CSS for `client:load` components

View file

@ -5,8 +5,11 @@ import { prependForwardSlash } from '../path.js';
import { viteID } from '../util.js'; import { viteID } from '../util.js';
export interface BuildInternals { 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 // A mapping of hoisted script ids back to the exact hoisted scripts it references
hoistedScriptIdToHoistedMap: Map<string, Set<string>>; hoistedScriptIdToHoistedMap: Map<string, Set<string>>;
@ -59,10 +62,6 @@ export interface BuildInternals {
* @returns {BuildInternals} * @returns {BuildInternals}
*/ */
export function createBuildInternals(): 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 // These are for tracking hoisted script bundling
const hoistedScriptIdToHoistedMap = new Map<string, Set<string>>(); const hoistedScriptIdToHoistedMap = new Map<string, Set<string>>();
@ -70,7 +69,7 @@ export function createBuildInternals(): BuildInternals {
const hoistedScriptIdToPagesMap = new Map<string, Set<string>>(); const hoistedScriptIdToPagesMap = new Map<string, Set<string>>();
return { return {
pureCSSChunks, cssChunkModuleIds: new Set(),
hoistedScriptIdToHoistedMap, hoistedScriptIdToHoistedMap,
hoistedScriptIdToPagesMap, hoistedScriptIdToPagesMap,
entrySpecifierToBundleMap: new Map<string, string>(), entrySpecifierToBundleMap: new Map<string, string>(),

View file

@ -134,11 +134,30 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
// Chunks that have the viteMetadata.importedCss are CSS chunks // Chunks that have the viteMetadata.importedCss are CSS chunks
if (meta.importedCss.size) { 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 // 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 // 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. // client:only component and if so, add its CSS to the page it belongs to.
if (options.target === 'client') { 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 pageData of getParentClientOnlys(id, this)) {
for (const importedCssImport of meta.importedCss) { for (const importedCssImport of meta.importedCss) {
pageData.css.set(importedCssImport, { depth: -1 }); 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 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)) { for (const [pageInfo, depth] of walkParentInfos(id, this)) {
if (moduleIsTopLevelPage(pageInfo)) { if (moduleIsTopLevelPage(pageInfo)) {
const pageViteID = pageInfo.id; const pageViteID = pageInfo.id;

View file

@ -354,5 +354,12 @@ describe('CSS', function () {
expect(allInjectedStyles).to.contain('.vue-scss{'); expect(allInjectedStyles).to.contain('.vue-scss{');
expect(allInjectedStyles).to.contain('.vue-scoped[data-v-'); 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;
});
}); });
}); });

View file

@ -0,0 +1,7 @@
<h1 id="svelte-dynamic" class="svelte-dynamic">Svelte Dynamic</h1>
<style>
.svelte-dynamic {
font-family: Courier, monospace;
}
</style>

View file

@ -18,6 +18,7 @@ import VueSass from '../components/VueSass.vue';
import VueScoped from '../components/VueScoped.vue'; import VueScoped from '../components/VueScoped.vue';
import VueScss from '../components/VueScss.vue'; import VueScss from '../components/VueScss.vue';
import ReactDynamic from '../components/ReactDynamic.jsx'; import ReactDynamic from '../components/ReactDynamic.jsx';
import SvelteDynamic from '../components/SvelteDynamic.svelte';
import '../styles/imported-url.css'; import '../styles/imported-url.css';
import '../styles/imported.sass'; import '../styles/imported.sass';
@ -69,6 +70,7 @@ import '../styles/imported.scss';
<VueScoped /> <VueScoped />
<VueScss /> <VueScss />
<ReactDynamic client:load /> <ReactDynamic client:load />
<SvelteDynamic client:load />
</div> </div>
</body> </body>
</html> </html>