astro
Version:
Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
219 lines (218 loc) • 8.65 kB
JavaScript
import { isBuildableCSSRequest } from "../../../vite-plugin-astro-server/util.js";
import { hasAssetPropagationFlag } from "../../../content/index.js";
import * as assetName from "../css-asset-name.js";
import {
getParentExtendedModuleInfos,
getParentModuleInfos,
moduleIsTopLevelPage
} from "../graph.js";
import { getPageDataByViteID, getPageDatasByClientOnlyID } from "../internal.js";
import { extendManualChunks, shouldInlineAsset } from "./util.js";
function pluginCSS(options, internals) {
return {
targets: ["client", "server"],
hooks: {
"build:before": ({ target }) => {
let plugins = rollupPluginAstroBuildCSS({
buildOptions: options,
internals,
target
});
return {
vitePlugin: plugins
};
}
}
};
}
function rollupPluginAstroBuildCSS(options) {
const { internals, buildOptions } = options;
const { settings } = buildOptions;
let resolvedConfig;
const pagesToCss = {};
const moduleIdToPropagatedCss = {};
const cssBuildPlugin = {
name: "astro:rollup-plugin-build-css",
outputOptions(outputOptions) {
const assetFileNames = outputOptions.assetFileNames;
const namingIncludesHash = assetFileNames?.toString().includes("[hash]");
const createNameForParentPages = namingIncludesHash ? assetName.shortHashedName(settings) : assetName.createSlugger(settings);
extendManualChunks(outputOptions, {
after(id, meta) {
if (isBuildableCSSRequest(id)) {
if (options.target === "client") {
return internals.cssModuleToChunkIdMap.get(id);
}
const ctx = { getModuleInfo: meta.getModuleInfo };
for (const pageInfo of getParentModuleInfos(id, ctx)) {
if (hasAssetPropagationFlag(pageInfo.id)) {
const chunkId2 = assetName.createNameHash(id, [id], settings);
internals.cssModuleToChunkIdMap.set(id, chunkId2);
return chunkId2;
}
}
const chunkId = createNameForParentPages(id, meta);
internals.cssModuleToChunkIdMap.set(id, chunkId);
return chunkId;
}
}
});
},
async generateBundle(_outputOptions, bundle) {
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 (options.target === "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 parentModuleInfos = getParentExtendedModuleInfos(id, this, hasAssetPropagationFlag);
for (const { info: pageInfo, depth, order } of parentModuleInfos) {
if (hasAssetPropagationFlag(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);
}
} else if (options.target === "client") {
const pageDatas = internals.pagesByScriptId.get(pageInfo.id);
if (pageDatas) {
for (const pageData of pageDatas) {
appendCSSToPage(pageData, meta, pagesToCss, -1, order);
}
}
}
}
}
}
}
};
const cssScopeToPlugin = {
name: "astro:rollup-plugin-css-scope-to",
renderChunk(_, chunk, __, meta) {
for (const id in chunk.modules) {
const modMeta = this.getModuleInfo(id)?.meta;
const cssScopeTo = modMeta?.astroCss?.cssScopeTo;
if (cssScopeTo && !isCssScopeToRendered(cssScopeTo, Object.values(meta.chunks))) {
delete chunk.modules[id];
const moduleIdsIndex = chunk.moduleIds.indexOf(id);
if (moduleIdsIndex > -1) {
chunk.moduleIds.splice(moduleIdsIndex, 1);
}
}
}
}
};
const singleCssPlugin = {
name: "astro:rollup-plugin-single-css",
enforce: "post",
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",
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;
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) {
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;
}
if (toBeInlined && sheetAddedToPage) {
delete bundle[id];
for (const chunk of Object.values(bundle)) {
if (chunk.type === "chunk") {
chunk.viteMetadata?.importedCss?.delete(id);
}
}
}
});
}
};
return [cssBuildPlugin, cssScopeToPlugin, singleCssPlugin, inlineStylesheetsPlugin];
}
function* getParentClientOnlys(id, ctx, internals) {
for (const info of getParentModuleInfos(id, ctx)) {
yield* getPageDatasByClientOnlyID(internals, info.id);
}
}
function appendCSSToPage(pageData, meta, pagesToCss, depth, order) {
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 };
}
}
}
function isCssScopeToRendered(cssScopeTo, chunks) {
for (const moduleId in cssScopeTo) {
const exports = cssScopeTo[moduleId];
const renderedModule = chunks.find((c) => c.moduleIds.includes(moduleId))?.modules[moduleId];
if (renderedModule?.renderedExports.some((e) => exports.includes(e))) {
return true;
}
}
return false;
}
export {
pluginCSS
};