UNPKG

astro

Version:

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

115 lines (114 loc) • 3.93 kB
import fs from "node:fs"; import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../constants.js"; import { getHandles, resetHandles } from "../../assets/utils/assets.js"; import { pathToDirectoryURL } from "./common.js"; import { getOrCreateSSRAssets } from "./internal.js"; /** * Vite plugin that tracks emitted assets. * This plugin coordinates with emitClientAsset() to track which assets should * be moved to the client directory. The resolved filenames are stored in * BuildInternals.ssrAssetsPerEnvironment during generateBundle. */ export function vitePluginSSRAssets(internals) { return { name: "astro:ssr-assets", applyToEnvironment(environment) { return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client || environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr || environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender; }, configEnvironment(name) { // Enable manifest generation for SSR and prerender environments // so we can read CSS files from the manifest if (name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr || name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender) { return { build: { manifest: true } }; } }, buildStart() { // Reset tracking for this environment at the start of each build resetHandles(this.environment); }, generateBundle() { const env = this.environment; const handles = getHandles(env); const filenames = getOrCreateSSRAssets(internals, env.name); // Resolve handles to filenames and store in internals if (handles) { for (const handle of handles) { try { const filename = this.getFileName(handle); filenames.add(filename); } catch {} } } }, writeBundle: { sequential: true, order: "post", async handler() { const env = this.environment; // Add CSS and assets from manifest (these are always client assets) // Must be done in writeBundle because the manifest is written during the bundle write phase const manifestDir = pathToDirectoryURL(env.config.build.outDir); const manifest = loadViteManifest(manifestDir); if (manifest) { const manifestAssets = collectAssetsFromManifest(manifest); if (manifestAssets.size > 0) { // Append manifest assets to internals.ssrAssetsPerEnvironment for this environment const filenames = getOrCreateSSRAssets(internals, env.name); for (const asset of manifestAssets) { filenames.add(asset); } } } } } }; } /** * Loads a Vite manifest from a directory if it exists. */ function loadViteManifest(directory) { const manifestPath = new URL(".vite/manifest.json", directory); if (!fs.existsSync(manifestPath)) { return null; } const contents = fs.readFileSync(manifestPath, "utf-8"); return JSON.parse(contents); } /** * Collects client asset filenames from a Vite manifest. * This includes CSS files and other assets (fonts, images) that need to be * moved to the client directory. */ function collectAssetsFromManifest(manifest) { const assets = new Set(); for (const chunk of Object.values(manifest)) { // Add CSS files listed in the css array if (chunk.css) { for (const css of chunk.css) { assets.add(css); } } // Add assets (fonts, images referenced from CSS, etc.) if (chunk.assets) { for (const asset of chunk.assets) { assets.add(asset); } } // Add CSS files that are the primary output of a chunk // This happens when a JS module only imports CSS and has no JS output if (chunk.file.endsWith(".css")) { assets.add(chunk.file); } } return assets; } /** * Deletes the .vite folder from a directory if it exists. */ export async function removeViteManifestDirectories(directories) { const viteFolders = new Set(Array.from(directories, (directory) => new URL(".vite/", directory).href)); await Promise.all(Array.from(viteFolders, (viteFolder) => fs.promises.rm(new URL(viteFolder), { recursive: true, force: true }))); }