diff --git a/.changeset/tiny-lemons-sit.md b/.changeset/tiny-lemons-sit.md new file mode 100644 index 000000000000..8e43429f7f42 --- /dev/null +++ b/.changeset/tiny-lemons-sit.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Prevent accidental inclusion of page CSS in dev mode diff --git a/packages/astro/src/core/module-loader/loader.ts b/packages/astro/src/core/module-loader/loader.ts index 84bb862d0fa4..39c24253ed26 100644 --- a/packages/astro/src/core/module-loader/loader.ts +++ b/packages/astro/src/core/module-loader/loader.ts @@ -42,6 +42,10 @@ export interface ModuleNode { id: string | null; url: string; ssrModule: Record | null; + ssrTransformResult: { + deps?: string[]; + dynamicDeps?: string[]; + } | null; ssrError: Error | null; importedModules: Set; } diff --git a/packages/astro/src/core/render/dev/vite.ts b/packages/astro/src/core/render/dev/vite.ts index fe4d3f7916b2..5cfea6e49fd9 100644 --- a/packages/astro/src/core/render/dev/vite.ts +++ b/packages/astro/src/core/render/dev/vite.ts @@ -41,6 +41,7 @@ export async function* crawlGraph( continue; } if (id === entry.id) { + const urlDeps = getDepsFromEntry(entry); scanned.add(id); const entryIsStyle = isCSSRequest(id); @@ -82,7 +83,7 @@ export async function* crawlGraph( } } } - if (!isPropagationStoppingPoint) { + if (urlDeps.includes(urlId(importedModule.url)) && !isPropagationStoppingPoint) { importedModules.add(importedModule); } } @@ -100,3 +101,19 @@ export async function* crawlGraph( yield* crawlGraph(loader, importedModule.id, false, scanned); } } + +// Virtual modules URL should start with /@id/ but do not +function urlId(url: string) { + if (url.startsWith('astro:scripts')) { + return '/@id/' + url; + } + return url; +} + +function getDepsFromEntry(entry: ModuleNode) { + let deps = entry.ssrTransformResult?.deps ?? []; + if(entry.ssrTransformResult?.dynamicDeps) { + return deps.concat(entry.ssrTransformResult.dynamicDeps); + } + return deps; +} diff --git a/packages/astro/test/units/dev/styles.test.js b/packages/astro/test/units/dev/styles.test.js new file mode 100644 index 000000000000..44dbc3be5fde --- /dev/null +++ b/packages/astro/test/units/dev/styles.test.js @@ -0,0 +1,63 @@ +import { expect } from 'chai'; +import { fileURLToPath } from 'url'; + +import { + getStylesForURL +} from '../../../dist/core/render/dev/css.js'; +import { viteID } from '../../../dist/core/util.js'; + +const root = new URL('../../fixtures/alias/', import.meta.url); + +class TestLoader { + constructor(modules) { + this.modules = new Map(modules.map(m => [m.id, m])) + } + getModuleById(id) { + return this.modules.get(id); + } + getModulesByFile(id) { + return this.modules.has(id) ? [this.modules.get(id)] : []; + } +} + +describe('Crawling graph for CSS', () => { + let loader; + before(() => { + const indexId = viteID(new URL('./src/pages/index.astro', root)); + const aboutId = viteID(new URL('./src/pages/about.astro', root)); + loader = new TestLoader([ + { + id: indexId, + importedModules: [{ + id: aboutId, + url: aboutId, + }, { + id: indexId + '?astro&style.css', + url: indexId + '?astro&style.css', + ssrModule: {} + }], + ssrTransformResult: { + deps: [indexId + '?astro&style.css'] + } + }, + { + id: aboutId, + importedModules: [{ + id: aboutId + '?astro&style.css', + url: aboutId + '?astro&style.css', + ssrModule: {} + }], + ssrTransformResult: { + deps: [aboutId + '?astro&style.css'] + } + } + ]); + }) + + it('importedModules is checked against the child\'s importers', async () => { + // In dev mode, HMR modules tracked are added to importedModules. We use `importers` + // to verify that they are true importers. + const res = await getStylesForURL(new URL('./src/pages/index.astro', root), loader, 'development') + expect(res.urls.size).to.equal(1); + }) +})