UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

342 lines (341 loc) • 14.6 kB
import { isCSSRequest } from "vite"; import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../../constants.js"; import { isPropagatedAssetBoundary } from "../../head-propagation/boundary.js"; import { VIRTUAL_PAGE_RESOLVED_MODULE_ID } from "../../../vite-plugin-pages/const.js"; import { getParentExtendedModuleInfos, getParentModuleInfos, moduleIsTopLevelPage } from "../graph.js"; import { getPageDataByViteID, getPageDatasByClientOnlyID } from "../internal.js"; import { normalizeEntryId } from "./plugin-component-entry.js"; import { shouldInlineAsset } from "./util.js"; function pluginCSS(options, internals) { return rollupPluginAstroBuildCSS({ buildOptions: options, internals }); } function isBuildCssBoundary(id, ctx) { if (isPropagatedAssetBoundary(id)) return true; const info = ctx.getModuleInfo(id); if (!info || !moduleIsTopLevelPage(info)) return false; const allImporters = info.importers.concat(info.dynamicImporters); const hasNonVirtualPageImporter = allImporters.some( (importer) => !importer.includes(VIRTUAL_PAGE_RESOLVED_MODULE_ID) ); return !hasNonVirtualPageImporter; } function rollupPluginAstroBuildCSS(options) { const { internals, buildOptions } = options; const { settings } = buildOptions; let resolvedConfig; const pagesToCss = {}; const moduleIdToPropagatedCss = {}; const cssBuildPlugin = { name: "astro:rollup-plugin-build-css", applyToEnvironment(environment) { return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client || environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr || environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender; }, async generateBundle(_outputOptions, bundle) { if (this.environment?.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr || this.environment?.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender) { for (const [, chunk] of Object.entries(bundle)) { if (chunk.type !== "chunk") continue; for (const moduleId of Object.keys(chunk.modules || {})) { if (isCSSRequest(moduleId)) { internals.cssModuleToChunkIdMap.set(moduleId, chunk.fileName); } } } } const renderedComponentExports = /* @__PURE__ */ new Map(); const componentToPages = /* @__PURE__ */ new Map(); if (this.environment?.name === ASTRO_VITE_ENVIRONMENT_NAMES.client) { for (const [, item] of Object.entries(bundle)) { if (item.type !== "chunk") continue; for (const [moduleId, moduleRenderedInfo] of Object.entries(item.modules)) { if (moduleRenderedInfo.renderedExports.length > 0) { renderedComponentExports.set(moduleId, moduleRenderedInfo.renderedExports); if (item.facadeModuleId) { let pages = componentToPages.get(moduleId); if (!pages) { pages = /* @__PURE__ */ new Set(); componentToPages.set(moduleId, pages); } pages.add(item.facadeModuleId); } } } if ("viteMetadata" in item === false) continue; const meta = item.viteMetadata; const allModules = Object.keys(item.modules || {}); const cssModules = allModules.filter((m) => isCSSRequest(m)); if (cssModules.length > 0) { const allCssInSSR = cssModules.every( (moduleId) => internals.cssModuleToChunkIdMap.has(moduleId) ); if (allCssInSSR && shouldDeleteCSSChunk(allModules, internals)) { for (const cssId of meta.importedCss) { delete bundle[cssId]; } } } } } for (const [, chunk] of Object.entries(bundle)) { if (chunk.type !== "chunk") continue; if ("viteMetadata" in chunk === false) continue; const meta = chunk.viteMetadata; if (meta.importedCss.size < 1) continue; if (this.environment?.name === ASTRO_VITE_ENVIRONMENT_NAMES.client) { for (const id of Object.keys(chunk.modules)) { for (const pageData of getParentClientOnlys(id, this, internals)) { for (const importedCssImport of meta.importedCss) { const cssToInfoRecord = pagesToCss[pageData.moduleSpecifier] ??= {}; cssToInfoRecord[importedCssImport] = { depth: -1, order: -1 }; } } } for (const id of Object.keys(chunk.modules)) { const moduleInfo = this.getModuleInfo(id); const cssScopeTo = moduleInfo?.meta?.vite?.cssScopeTo; if (cssScopeTo) { const [scopedToModule, scopedToExport] = cssScopeTo; const renderedExports = renderedComponentExports.get(scopedToModule); if (renderedExports?.includes(scopedToExport)) { const parentModuleInfos = getParentExtendedModuleInfos( scopedToModule, this, (moduleId) => isBuildCssBoundary(moduleId, this) ); for (const { info: pageInfo, depth, order } of parentModuleInfos) { if (moduleIsTopLevelPage(pageInfo)) { const pageData = getPageDataByViteID(internals, pageInfo.id); if (pageData) { appendCSSToPage( pageData, meta, pagesToCss, depth, order, this.environment?.name ); } } const pageDatas = internals.pagesByScriptId.get(pageInfo.id); if (pageDatas) { for (const pageData of pageDatas) { appendCSSToPage( pageData, meta, pagesToCss, -1, order, this.environment?.name ); } } } let addedToAnyPage = false; for (const importedCssImport of meta.importedCss) { for (const pageData of internals.pagesByKeys.values()) { const cssToInfoRecord = pagesToCss[pageData.moduleSpecifier]; if (cssToInfoRecord && importedCssImport in cssToInfoRecord) { addedToAnyPage = true; break; } } } if (!addedToAnyPage) { for (const { info: parentInfo } of parentModuleInfos) { const normalizedParent = normalizeEntryId(parentInfo.id); const pages = internals.pagesByHydratedComponent.get(normalizedParent); if (pages) { for (const pageData of pages) { appendCSSToPage(pageData, meta, pagesToCss, -1, -1, this.environment?.name); } } } } } } } } for (const id of Object.keys(chunk.modules)) { if (!isCSSRequest(id)) continue; const parentModuleInfos = getParentExtendedModuleInfos( id, this, (importer) => isBuildCssBoundary(importer, this) ); for (const { info: pageInfo, depth, order } of parentModuleInfos) { if (isPropagatedAssetBoundary(pageInfo.id)) { const propagatedCss = moduleIdToPropagatedCss[pageInfo.id] ??= /* @__PURE__ */ new Set(); for (const css of meta.importedCss) { propagatedCss.add(css); } } else if (moduleIsTopLevelPage(pageInfo)) { const pageViteID = pageInfo.id; const pageData = getPageDataByViteID(internals, pageViteID); if (pageData) { appendCSSToPage(pageData, meta, pagesToCss, depth, order, this.environment?.name); } } else if (this.environment?.name === ASTRO_VITE_ENVIRONMENT_NAMES.client) { const pageDatas = internals.pagesByScriptId.get(pageInfo.id); if (pageDatas) { for (const pageData of pageDatas) { appendCSSToPage(pageData, meta, pagesToCss, -1, order, this.environment?.name); } } } } } } } }; const singleCssPlugin = { name: "astro:rollup-plugin-single-css", enforce: "post", applyToEnvironment(environment) { return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client || environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr || environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender; }, configResolved(config) { resolvedConfig = config; }, generateBundle(_, bundle) { if (resolvedConfig.build.cssCodeSplit) return; const cssChunk = Object.values(bundle).find( (chunk) => chunk.type === "asset" && chunk.name === "style.css" ); if (cssChunk === void 0) return; for (const pageData of internals.pagesByKeys.values()) { const cssToInfoMap = pagesToCss[pageData.moduleSpecifier] ??= {}; cssToInfoMap[cssChunk.fileName] = { depth: -1, order: -1 }; } } }; let assetsInlineLimit; const inlineStylesheetsPlugin = { name: "astro:rollup-plugin-inline-stylesheets", enforce: "post", applyToEnvironment(environment) { return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client || environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr || environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender; }, configResolved(config) { assetsInlineLimit = config.build.assetsInlineLimit; }, async generateBundle(_outputOptions, bundle) { const inlineConfig = settings.config.build.inlineStylesheets; Object.entries(bundle).forEach(([id, stylesheet]) => { if (stylesheet.type !== "asset" || stylesheet.name?.endsWith(".css") !== true || typeof stylesheet.source !== "string") return; if (stylesheet.source.length === 0) { delete bundle[id]; return; } const toBeInlined = inlineConfig === "always" ? true : inlineConfig === "never" ? false : shouldInlineAsset(stylesheet.source, stylesheet.fileName, assetsInlineLimit); const sheet = toBeInlined ? { type: "inline", content: stylesheet.source } : { type: "external", src: stylesheet.fileName }; let sheetAddedToPage = false; internals.pagesByKeys.forEach((pageData) => { const orderingInfo = pagesToCss[pageData.moduleSpecifier]?.[stylesheet.fileName]; if (orderingInfo !== void 0) { const alreadyAdded = pageData.styles.some((s) => { if (s.sheet.type === "external" && sheet.type === "external") { return s.sheet.src === sheet.src; } if (s.sheet.type === "inline" && sheet.type === "inline") { return s.sheet.content === sheet.content; } return false; }); if (!alreadyAdded) { pageData.styles.push({ ...orderingInfo, sheet }); } sheetAddedToPage = true; } }); for (const moduleId in moduleIdToPropagatedCss) { if (!moduleIdToPropagatedCss[moduleId].has(stylesheet.fileName)) continue; let propagatedStyles = internals.propagatedStylesMap.get(moduleId); if (!propagatedStyles) { propagatedStyles = /* @__PURE__ */ new Set(); internals.propagatedStylesMap.set(moduleId, propagatedStyles); } propagatedStyles.add(sheet); sheetAddedToPage = true; } const wasInlined = toBeInlined && sheetAddedToPage; const wasAddedToChunk = Object.values(bundle).some( (chunk) => chunk.type === "chunk" && chunk.viteMetadata?.importedAssets?.has(id) ); const isOrphaned = !sheetAddedToPage && !wasAddedToChunk; if (wasInlined || isOrphaned) { delete bundle[id]; for (const chunk of Object.values(bundle)) { if (chunk.type === "chunk") { chunk.viteMetadata?.importedCss?.delete(id); } } } }); } }; return [cssBuildPlugin, singleCssPlugin, inlineStylesheetsPlugin]; } function shouldDeleteCSSChunk(allModules, internals) { const componentPaths = /* @__PURE__ */ new Set(); for (const componentPath of internals.discoveredClientOnlyComponents.keys()) { if (allModules.some((m) => m.includes(componentPath))) { componentPaths.add(componentPath); } } for (const componentPath of internals.discoveredHydratedComponents.keys()) { if (allModules.some((m) => m.includes(componentPath))) { componentPaths.add(componentPath); } } if (componentPaths.size === 0) return false; for (const componentPath of componentPaths) { const pagesUsingClientOnly = internals.pagesByClientOnly.get(componentPath); if (pagesUsingClientOnly) { for (const pageData of internals.pagesByKeys.values()) { if (!pagesUsingClientOnly.has(pageData)) { return false; } } } } return true; } function* getParentClientOnlys(id, ctx, internals) { for (const info of getParentModuleInfos(id, ctx)) { yield* getPageDatasByClientOnlyID(internals, info.id); } } function appendCSSToPage(pageData, meta, pagesToCss, depth, order, environmentName) { if (environmentName === ASTRO_VITE_ENVIRONMENT_NAMES.ssr && pageData.route.prerender) { return; } if (environmentName === ASTRO_VITE_ENVIRONMENT_NAMES.prerender && !pageData.route.prerender) { return; } for (const importedCssImport of meta.importedCss) { const cssInfo = pagesToCss[pageData.moduleSpecifier]?.[importedCssImport]; if (cssInfo !== void 0) { if (depth < cssInfo.depth) { cssInfo.depth = depth; } if (cssInfo.order === -1) { cssInfo.order = order; } else if (order < cssInfo.order && order > -1) { cssInfo.order = order; } } else { const cssToInfoRecord = pagesToCss[pageData.moduleSpecifier] ??= {}; cssToInfoRecord[importedCssImport] = { depth, order }; } } } export { pluginCSS };