Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/three-cooks-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Replaces internal CSS chunking behavior for Astro components' scoped styles to use Vite's `cssScopeTo` feature. The feature is a port of Astro's implementation so this should not change the behavior.
53 changes: 2 additions & 51 deletions packages/astro/src/core/build/plugins/plugin-css.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import type { GetModuleInfo } from 'rollup';
import type { BuildOptions, ResolvedConfig, Rollup, Plugin as VitePlugin } from 'vite';
import type { BuildOptions, ResolvedConfig, Plugin as VitePlugin } from 'vite';
import { isBuildableCSSRequest } from '../../../vite-plugin-astro-server/util.js';
import type { BuildInternals } from '../internal.js';
import type { AstroBuildPlugin, BuildTarget } from '../plugin.js';
import type { PageBuildData, StaticBuildOptions, StylesheetAsset } from '../types.js';

import { hasAssetPropagationFlag } from '../../../content/index.js';
import type { AstroPluginCssMetadata } from '../../../vite-plugin-astro/index.js';
import * as assetName from '../css-asset-name.js';
import {
getParentExtendedModuleInfos,
Expand Down Expand Up @@ -156,32 +155,6 @@ function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] {
},
};

/**
* This plugin is a port of https://github.com/vitejs/vite/pull/16058. It enables removing unused
* scoped CSS from the bundle if the scoped target (e.g. Astro files) were not bundled.
* Once/If that PR is merged, we can refactor this away, renaming `meta.astroCss` to `meta.vite`.
*/
const cssScopeToPlugin: VitePlugin = {
name: 'astro:rollup-plugin-css-scope-to',
renderChunk(_, chunk, __, meta) {
for (const id in chunk.modules) {
// If this CSS is scoped to its importers exports, check if those importers exports
// are rendered in the chunks. If they are not, we can skip bundling this CSS.
const modMeta = this.getModuleInfo(id)?.meta as AstroPluginCssMetadata | undefined;
const cssScopeTo = modMeta?.astroCss?.cssScopeTo;
if (cssScopeTo && !isCssScopeToRendered(cssScopeTo, Object.values(meta.chunks))) {
// If this CSS is not used, delete it from the chunk modules so that Vite is unable
// to trace that it's used
delete chunk.modules[id];
const moduleIdsIndex = chunk.moduleIds.indexOf(id);
if (moduleIdsIndex > -1) {
chunk.moduleIds.splice(moduleIdsIndex, 1);
}
}
}
},
};

const singleCssPlugin: VitePlugin = {
name: 'astro:rollup-plugin-single-css',
enforce: 'post',
Expand Down Expand Up @@ -273,7 +246,7 @@ function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] {
},
};

return [cssBuildPlugin, cssScopeToPlugin, singleCssPlugin, inlineStylesheetsPlugin];
return [cssBuildPlugin, singleCssPlugin, inlineStylesheetsPlugin];
}

/***** UTILITY FUNCTIONS *****/
Expand Down Expand Up @@ -321,25 +294,3 @@ function appendCSSToPage(
}
}
}

/**
* `cssScopeTo` is a map of `importer`s to its `export`s. This function iterate each `cssScopeTo` entries
* and check if the `importer` and its `export`s exists in the final chunks. If at least one matches,
* `cssScopeTo` is considered "rendered" by Rollup and we return true.
*/
function isCssScopeToRendered(
cssScopeTo: Record<string, string[]>,
chunks: Rollup.RenderedChunk[],
) {
for (const moduleId in cssScopeTo) {
const exports = cssScopeTo[moduleId];
// Find the chunk that renders this `moduleId` and get the rendered module
const renderedModule = chunks.find((c) => c.moduleIds.includes(moduleId))?.modules[moduleId];
// Return true if `renderedModule` exists and one of its exports is rendered
if (renderedModule?.renderedExports.some((e) => exports.includes(e))) {
return true;
}
}

return false;
}
22 changes: 8 additions & 14 deletions packages/astro/src/vite-plugin-astro/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ import type { SourceDescription } from 'rollup';
import type * as vite from 'vite';
import type { Logger } from '../core/logger/core.js';
import type { AstroSettings } from '../types/astro.js';
import type {
PluginCssMetadata as AstroPluginCssMetadata,
PluginMetadata as AstroPluginMetadata,
CompileMetadata,
} from './types.js';
import type { PluginMetadata as AstroPluginMetadata, CompileMetadata } from './types.js';

import { defaultClientConditions, defaultServerConditions, normalizePath } from 'vite';
import type { AstroConfig } from '../types/public/config.js';
Expand All @@ -16,7 +12,7 @@ import { handleHotUpdate } from './hmr.js';
import { parseAstroRequest } from './query.js';
import { loadId } from './utils.js';
export { getAstroMetadata } from './metadata.js';
export type { AstroPluginMetadata, AstroPluginCssMetadata };
export type { AstroPluginMetadata };

interface AstroPluginOptions {
settings: AstroSettings;
Expand Down Expand Up @@ -138,17 +134,15 @@ export default function astro({ settings, logger }: AstroPluginOptions): vite.Pl

return {
code: result.code,
// This metadata is used by `cssScopeToPlugin` to remove this module from the bundle
// if the `filename` default export (the Astro component) is unused.
// `vite.cssScopeTo` is a Vite feature that allows this CSS to be treeshaken
// if the Astro component's default export is not used
meta: result.isGlobal
? undefined
: ({
astroCss: {
cssScopeTo: {
[filename]: ['default'],
},
: {
vite: {
cssScopeTo: [filename, 'default'],
},
} satisfies AstroPluginCssMetadata),
},
};
}
case 'script': {
Expand Down
21 changes: 0 additions & 21 deletions packages/astro/src/vite-plugin-astro/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,6 @@ export interface PluginMetadata {
};
}

export interface PluginCssMetadata {
astroCss: {
/**
* For Astro CSS virtual modules, it can scope to the main Astro module's default export
* so that if those exports are treeshaken away, the CSS module will also be treeshaken.
*
* Example config if the CSS id is `/src/Foo.astro?astro&type=style&lang.css`:
* ```js
* cssScopeTo: {
* '/src/Foo.astro': ['default']
* }
* ```
*
* The above is the only config we use today, but we're exposing as a `Record` to follow the
* upstream Vite implementation: https://github.com/vitejs/vite/pull/16058. When/If that lands,
* we can also remove our custom implementation.
*/
cssScopeTo: Record<string, string[]>;
};
}

export interface CompileMetadata {
/** Used for HMR to compare code changes */
originalCode: string;
Expand Down