diff options
-rw-r--r-- | .changeset/fast-oranges-collect.md | 5 | ||||
-rw-r--r-- | packages/astro/src/core/build/graph.ts | 11 | ||||
-rw-r--r-- | packages/astro/src/core/build/internal.ts | 28 | ||||
-rw-r--r-- | packages/astro/src/core/build/vite-plugin-css.ts | 45 | ||||
-rw-r--r-- | packages/astro/test/astro-scripts.test.js | 7 | ||||
-rw-r--r-- | packages/astro/test/fixtures/astro-scripts/src/pages/with-styles.astro | 15 | ||||
-rw-r--r-- | packages/astro/test/fixtures/astro-scripts/src/styles/one.css | 3 |
7 files changed, 87 insertions, 27 deletions
diff --git a/.changeset/fast-oranges-collect.md b/.changeset/fast-oranges-collect.md new file mode 100644 index 000000000..a24e8f54a --- /dev/null +++ b/.changeset/fast-oranges-collect.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Include styles imported by hoisted scripts diff --git a/packages/astro/src/core/build/graph.ts b/packages/astro/src/core/build/graph.ts index beef79563..b911253d5 100644 --- a/packages/astro/src/core/build/graph.ts +++ b/packages/astro/src/core/build/graph.ts @@ -1,4 +1,5 @@ import type { GetModuleInfo, ModuleInfo } from 'rollup'; + import { resolvedPagesVirtualModuleId } from '../app/index.js'; // This walks up the dependency graph and yields out each ModuleInfo object. @@ -22,14 +23,20 @@ export function* walkParentInfos( } } +// Returns true if a module is a top-level page. We determine this based on whether +// it is imported by the top-level virtual module. +export function moduleIsTopLevelPage(info: ModuleInfo): boolean { + return info.importers[0] === resolvedPagesVirtualModuleId; +} + // This function walks the dependency graph, going up until it finds a page component. // This could be a .astro page or a .md page. export function* getTopLevelPages( id: string, - ctx: { getModuleInfo: GetModuleInfo } + ctx: { getModuleInfo: GetModuleInfo }, ): Generator<[ModuleInfo, number], void, unknown> { for (const res of walkParentInfos(id, ctx)) { - if (res[0]?.importers[0] === resolvedPagesVirtualModuleId) { + if (moduleIsTopLevelPage(res[0])) { yield res; } } diff --git a/packages/astro/src/core/build/internal.ts b/packages/astro/src/core/build/internal.ts index e8704a282..dfebc3052 100644 --- a/packages/astro/src/core/build/internal.ts +++ b/packages/astro/src/core/build/internal.ts @@ -1,4 +1,4 @@ -import type { OutputChunk, RenderedChunk } from 'rollup'; +import type { OutputChunk, RenderedChunk, ModuleInfo, GetModuleInfo } from 'rollup'; import type { PageBuildData, ViteID } from './types'; import { prependForwardSlash } from '../path.js'; @@ -62,13 +62,6 @@ 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>(); - const chunkToReferenceIdMap = new Map<string, string>(); - - // This is a mapping of pathname to the string source of all collected - // inline <style> for a page. - const astroStyleMap = new Map<string, string>(); - // This is a virtual JS module that imports all dependent styles for a page. - const astroPageStyleMap = new Map<string, string>(); // These are for tracking hoisted script bundling const hoistedScriptIdToHoistedMap = new Map<string, Set<string>>(); @@ -204,3 +197,22 @@ export function sortedCSS(pageData: PageBuildData) { }) .map(([id]) => id); } + +export function isHoistedScript(internals: BuildInternals, id: string): boolean { + return internals.hoistedScriptIdToPagesMap.has(id); +} + +export function* getPageDatasByHoistedScriptId( + internals: BuildInternals, + id: string +): Generator<PageBuildData, void, unknown> { + const set = internals.hoistedScriptIdToPagesMap.get(id); + if(set) { + for(const pageId of set) { + const pageData = getPageDataByComponent(internals, pageId.slice(1)); + if(pageData) { + yield pageData; + } + } + } +} diff --git a/packages/astro/src/core/build/vite-plugin-css.ts b/packages/astro/src/core/build/vite-plugin-css.ts index b01868034..f6ae3f5a2 100644 --- a/packages/astro/src/core/build/vite-plugin-css.ts +++ b/packages/astro/src/core/build/vite-plugin-css.ts @@ -9,8 +9,8 @@ import npath from 'path'; import { Plugin as VitePlugin, ResolvedConfig } from 'vite'; import { isCSSRequest } from '../render/util.js'; import { relativeToSrcDir } from '../util.js'; -import { getTopLevelPages, walkParentInfos } from './graph.js'; -import { eachPageData, getPageDataByViteID, getPageDatasByClientOnlyID } from './internal.js'; +import { getTopLevelPages, walkParentInfos, moduleIsTopLevelPage } from './graph.js'; +import { eachPageData, getPageDataByViteID, getPageDatasByClientOnlyID, getPageDatasByHoistedScriptId, isHoistedScript } from './internal.js'; interface PluginOptions { internals: BuildInternals; @@ -104,6 +104,22 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] importedCss: Set<string>; }; + const appendCSSToPage = (pageData: PageBuildData, meta: ViteMetadata, depth: number) => { + for (const importedCssImport of meta.importedCss) { + // CSS is prioritized based on depth. Shared CSS has a higher depth due to being imported by multiple pages. + // Depth info is used when sorting the links on the page. + if (pageData?.css.has(importedCssImport)) { + // eslint-disable-next-line + const cssInfo = pageData?.css.get(importedCssImport)!; + if (depth < cssInfo.depth) { + cssInfo.depth = depth; + } + } else { + pageData?.css.set(importedCssImport, { depth }); + } + } + } + for (const [_, chunk] of Object.entries(bundle)) { if (chunk.type === 'chunk') { const c = chunk; @@ -127,21 +143,16 @@ 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 [pageInfo, depth] of getTopLevelPages(id, this)) { - const pageViteID = pageInfo.id; - const pageData = getPageDataByViteID(internals, pageViteID); - - for (const importedCssImport of meta.importedCss) { - // CSS is prioritized based on depth. Shared CSS has a higher depth due to being imported by multiple pages. - // Depth info is used when sorting the links on the page. - if (pageData?.css.has(importedCssImport)) { - // eslint-disable-next-line - const cssInfo = pageData?.css.get(importedCssImport)!; - if (depth < cssInfo.depth) { - cssInfo.depth = depth; - } - } else { - pageData?.css.set(importedCssImport, { depth }); + for (const [pageInfo, depth] of walkParentInfos(id, this)) { + if(moduleIsTopLevelPage(pageInfo)) { + const pageViteID = pageInfo.id; + const pageData = getPageDataByViteID(internals, pageViteID); + if(pageData) { + appendCSSToPage(pageData, meta, depth); + } + } else if(options.target === 'client' && isHoistedScript(internals, pageInfo.id)) { + for(const pageData of getPageDatasByHoistedScriptId(internals, pageInfo.id)) { + appendCSSToPage(pageData, meta, -1); } } } diff --git a/packages/astro/test/astro-scripts.test.js b/packages/astro/test/astro-scripts.test.js index f182dee20..1229de5f5 100644 --- a/packages/astro/test/astro-scripts.test.js +++ b/packages/astro/test/astro-scripts.test.js @@ -114,6 +114,13 @@ describe('Scripts (hoisted and not)', () => { expect($('script[type="module"]').length).to.be.greaterThan(0); }); + + it('Styles imported by hoisted scripts are included on the page', async () => { + let html = await fixture.readFile('/with-styles/index.html'); + let $ = cheerio.load(html); + + expect($('link[rel=stylesheet]')).to.have.a.lengthOf(1); + }); }); describe('Dev', () => { diff --git a/packages/astro/test/fixtures/astro-scripts/src/pages/with-styles.astro b/packages/astro/test/fixtures/astro-scripts/src/pages/with-styles.astro new file mode 100644 index 000000000..af984e7b3 --- /dev/null +++ b/packages/astro/test/fixtures/astro-scripts/src/pages/with-styles.astro @@ -0,0 +1,15 @@ + +<html> + <head> + <title>Testing</title> + </head> + <body> + <h1>Testing</h1> + <script> + import "../styles/one.css"; + console.log("This component imports styles."); + </script> + </body> +</html> + + diff --git a/packages/astro/test/fixtures/astro-scripts/src/styles/one.css b/packages/astro/test/fixtures/astro-scripts/src/styles/one.css new file mode 100644 index 000000000..fb68e0f63 --- /dev/null +++ b/packages/astro/test/fixtures/astro-scripts/src/styles/one.css @@ -0,0 +1,3 @@ +body { + background: rebeccapurple; +} |