From 7cc066eb39ba9fc14c31eb940d625eeb7413924c Mon Sep 17 00:00:00 2001 From: HiDeoo <494699+HiDeoo@users.noreply.github.com> Date: Sat, 10 Jan 2026 17:18:12 +0100 Subject: [PATCH 1/3] fix: stop css collection traversal on propagation stopping point in dev --- .changeset/big-forks-lead.md | 5 ++++ packages/astro/src/vite-plugin-css/index.ts | 5 ++++ .../test/content-collections-render.test.js | 20 ++++++++++++++++ .../content/src/components/AboutLayout.astro | 9 ++++++++ .../content/src/components/BlogLayout.astro | 23 +++++++++++++++++++ .../content/src/components/RedButton.astro | 8 +++++++ .../content/src/content/blog/5-big-news.mdx | 10 ++++++++ .../test/fixtures/content/src/libs/routes.ts | 7 ++++++ .../content/src/pages/blog/[...slug].astro | 23 +++++++++++++++++++ .../content/src/pages/blog/about.astro | 7 ++++++ 10 files changed, 117 insertions(+) create mode 100644 .changeset/big-forks-lead.md create mode 100644 packages/astro/test/fixtures/content/src/components/AboutLayout.astro create mode 100644 packages/astro/test/fixtures/content/src/components/BlogLayout.astro create mode 100644 packages/astro/test/fixtures/content/src/components/RedButton.astro create mode 100644 packages/astro/test/fixtures/content/src/content/blog/5-big-news.mdx create mode 100644 packages/astro/test/fixtures/content/src/libs/routes.ts create mode 100644 packages/astro/test/fixtures/content/src/pages/blog/[...slug].astro create mode 100644 packages/astro/test/fixtures/content/src/pages/blog/about.astro diff --git a/.changeset/big-forks-lead.md b/.changeset/big-forks-lead.md new file mode 100644 index 000000000000..15c24ed26da1 --- /dev/null +++ b/.changeset/big-forks-lead.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes an issue where CSS from unused components, when using content collections, could be incorrectly included between page navigations in development mode. diff --git a/packages/astro/src/vite-plugin-css/index.ts b/packages/astro/src/vite-plugin-css/index.ts index 54925c522d26..41cb3486fed5 100644 --- a/packages/astro/src/vite-plugin-css/index.ts +++ b/packages/astro/src/vite-plugin-css/index.ts @@ -44,6 +44,11 @@ function* collectCSSWithOrder( ): Generator { seen.add(id); + // Stop traversing if we reach a propagation stopping point. + if (id.includes('?astroPropagatedAssets')) { + return; + } + // Keep all of the imported modules into an array so we can go through them one at a time const imported = Array.from(mod.importedModules); diff --git a/packages/astro/test/content-collections-render.test.js b/packages/astro/test/content-collections-render.test.js index 2338b04e0dc3..75f47bc4eade 100644 --- a/packages/astro/test/content-collections-render.test.js +++ b/packages/astro/test/content-collections-render.test.js @@ -218,5 +218,25 @@ describe('Content Collections - render()', () => { assert.equal(h2.length, 1); assert.equal(h2.attr('data-components-export-applied'), 'true'); }); + + it('Stops collecting CSS when reaching a propagation stopping point', async () => { + let response = await fixture.fetch('/blog/5-big-news', { method: 'GET' }); + assert.equal(response.status, 200); + + let html = await response.text(); + let $ = cheerio.load(html); + + // Includes the red button styles used in the MDX blog post + assert.ok($('head > style').text().includes('background-color:red;')); + + response = await fixture.fetch('/blog/about', { method: 'GET' }); + assert.equal(response.status, 200); + + html = await response.text(); + $ = cheerio.load(html); + + // Does not include the red button styles not used in this page + assert.equal($('head > style').text().includes('background-color:red;'), false); + }); }); }); diff --git a/packages/astro/test/fixtures/content/src/components/AboutLayout.astro b/packages/astro/test/fixtures/content/src/components/AboutLayout.astro new file mode 100644 index 000000000000..244ce2026e35 --- /dev/null +++ b/packages/astro/test/fixtures/content/src/components/AboutLayout.astro @@ -0,0 +1,9 @@ +--- +import BlogLayout from './BlogLayout.astro'; +import { doSomething } from '../libs/routes'; + +// Calling a random function from a file importing `astro:content`. +doSomething() +--- + + diff --git a/packages/astro/test/fixtures/content/src/components/BlogLayout.astro b/packages/astro/test/fixtures/content/src/components/BlogLayout.astro new file mode 100644 index 000000000000..7dd76accf03a --- /dev/null +++ b/packages/astro/test/fixtures/content/src/components/BlogLayout.astro @@ -0,0 +1,23 @@ +--- +interface Props { + title: string; +} + +const { title } = Astro.props; +--- + + + + + + + + Astro + + +
+

{title}

+ +
+ + diff --git a/packages/astro/test/fixtures/content/src/components/RedButton.astro b/packages/astro/test/fixtures/content/src/components/RedButton.astro new file mode 100644 index 000000000000..1592fc872d6e --- /dev/null +++ b/packages/astro/test/fixtures/content/src/components/RedButton.astro @@ -0,0 +1,8 @@ + + + diff --git a/packages/astro/test/fixtures/content/src/content/blog/5-big-news.mdx b/packages/astro/test/fixtures/content/src/content/blog/5-big-news.mdx new file mode 100644 index 000000000000..a6a349b91327 --- /dev/null +++ b/packages/astro/test/fixtures/content/src/content/blog/5-big-news.mdx @@ -0,0 +1,10 @@ +--- +title: Big News +description: Just some big news. +--- + +This is the content of a blog post with big news. + +import RedButton from "../../components/RedButton.astro"; + + diff --git a/packages/astro/test/fixtures/content/src/libs/routes.ts b/packages/astro/test/fixtures/content/src/libs/routes.ts new file mode 100644 index 000000000000..27cfa433ef85 --- /dev/null +++ b/packages/astro/test/fixtures/content/src/libs/routes.ts @@ -0,0 +1,7 @@ +import { getCollection } from "astro:content"; + +export const routes = await getCollection("blog"); + +export function doSomething() { + // … +} diff --git a/packages/astro/test/fixtures/content/src/pages/blog/[...slug].astro b/packages/astro/test/fixtures/content/src/pages/blog/[...slug].astro new file mode 100644 index 000000000000..4889bd7f7d20 --- /dev/null +++ b/packages/astro/test/fixtures/content/src/pages/blog/[...slug].astro @@ -0,0 +1,23 @@ +--- +import { getEntry, render } from "astro:content"; +import BlogLayout from "../../components/BlogLayout.astro"; +import { routes } from "../../libs/routes"; + +export function getStaticPaths() { + return [routes.map((route) => ({ params: { slug: route.id } }))].flat(); +} + +const { slug } = Astro.params; + +const entry = await getEntry("blog", slug); + +if (!entry) { + throw new Error(`Entry not found for slug: ${slug}`); +} + +const { Content } = await render(entry) +--- + + + + diff --git a/packages/astro/test/fixtures/content/src/pages/blog/about.astro b/packages/astro/test/fixtures/content/src/pages/blog/about.astro new file mode 100644 index 000000000000..e4ef1d255e60 --- /dev/null +++ b/packages/astro/test/fixtures/content/src/pages/blog/about.astro @@ -0,0 +1,7 @@ +--- +import AboutLayout from "../../components/AboutLayout.astro"; +--- + + +

This is the content of the about page.

+
From 1b19bd937a63300bfca091f8d967a13522be537c Mon Sep 17 00:00:00 2001 From: HiDeoo <494699+HiDeoo@users.noreply.github.com> Date: Mon, 12 Jan 2026 10:50:14 +0100 Subject: [PATCH 2/3] chore: try to improve comment --- packages/astro/src/vite-plugin-css/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/vite-plugin-css/index.ts b/packages/astro/src/vite-plugin-css/index.ts index 41cb3486fed5..92c14a08eae2 100644 --- a/packages/astro/src/vite-plugin-css/index.ts +++ b/packages/astro/src/vite-plugin-css/index.ts @@ -44,7 +44,12 @@ function* collectCSSWithOrder( ): Generator { seen.add(id); - // Stop traversing if we reach a propagation stopping point. + // Stop traversing if we reach an asset propagation stopping point to ensure we only collect CSS + // relevant to a content collection entry, if any. Not doing so could cause CSS from other + // entries to potentially be collected and bleed into the CSS included on the page, causing + // unexpected styles, for example when a module shared between 2 pages would import + // `astro:content` and thus potentially adding multiple content collection entry assets to the + // module graph. if (id.includes('?astroPropagatedAssets')) { return; } From 047e56f35de4f2def46bd9c47e06421843562f73 Mon Sep 17 00:00:00 2001 From: HiDeoo <494699+HiDeoo@users.noreply.github.com> Date: Mon, 12 Jan 2026 11:10:12 +0100 Subject: [PATCH 3/3] refactor: use `PROPAGATED_ASSET_FLAG` constant --- packages/astro/src/content/consts.ts | 1 + packages/astro/src/vite-plugin-astro-server/vite.ts | 3 ++- packages/astro/src/vite-plugin-css/index.ts | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/content/consts.ts b/packages/astro/src/content/consts.ts index c2902cb70b13..b3aebada997c 100644 --- a/packages/astro/src/content/consts.ts +++ b/packages/astro/src/content/consts.ts @@ -1,4 +1,5 @@ export const PROPAGATED_ASSET_FLAG = 'astroPropagatedAssets'; +export const PROPAGATED_ASSET_QUERY_PARAM = `?${PROPAGATED_ASSET_FLAG}`; export const CONTENT_RENDER_FLAG = 'astroRenderContent'; export const CONTENT_FLAG = 'astroContentCollectionEntry'; export const DATA_FLAG = 'astroDataCollectionEntry'; diff --git a/packages/astro/src/vite-plugin-astro-server/vite.ts b/packages/astro/src/vite-plugin-astro-server/vite.ts index dfff85927def..ad997a19b6cb 100644 --- a/packages/astro/src/vite-plugin-astro-server/vite.ts +++ b/packages/astro/src/vite-plugin-astro-server/vite.ts @@ -3,6 +3,7 @@ import { type EnvironmentModuleNode, isCSSRequest, type RunnableDevEnvironment } import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from '../core/constants.js'; import { unwrapId } from '../core/util.js'; import { hasSpecialQueries } from '../vite-plugin-utils/index.js'; +import { PROPAGATED_ASSET_QUERY_PARAM } from '../content/consts.js'; /** * List of file extensions signalling we can (and should) SSR ahead-of-time @@ -76,7 +77,7 @@ export async function* crawlGraph( const isFileTypeNeedingSSR = fileExtensionsToSSR.has(npath.extname(importedModulePathname)); // A propagation stopping point is a module with the ?astroPropagatedAssets flag. // When we encounter one of these modules we don't want to continue traversing. - const isPropagationStoppingPoint = importedModule.id.includes('?astroPropagatedAssets'); + const isPropagationStoppingPoint = importedModule.id.includes(PROPAGATED_ASSET_QUERY_PARAM); if ( isFileTypeNeedingSSR && // Should not SSR a module with ?astroPropagatedAssets diff --git a/packages/astro/src/vite-plugin-css/index.ts b/packages/astro/src/vite-plugin-css/index.ts index 92c14a08eae2..fd689040eb34 100644 --- a/packages/astro/src/vite-plugin-css/index.ts +++ b/packages/astro/src/vite-plugin-css/index.ts @@ -8,6 +8,7 @@ import { inlineRE, isBuildableCSSRequest, rawRE } from '../vite-plugin-astro-ser import { getVirtualModulePageNameForComponent } from '../vite-plugin-pages/util.js'; import { getDevCSSModuleName } from './util.js'; import { CSS_LANGS_RE } from '../core/viteUtils.js'; +import { PROPAGATED_ASSET_QUERY_PARAM } from '../content/consts.js'; interface AstroVitePluginOptions { routesList: RoutesList; @@ -50,7 +51,7 @@ function* collectCSSWithOrder( // unexpected styles, for example when a module shared between 2 pages would import // `astro:content` and thus potentially adding multiple content collection entry assets to the // module graph. - if (id.includes('?astroPropagatedAssets')) { + if (id.includes(PROPAGATED_ASSET_QUERY_PARAM)) { return; }