UNPKG

vite-plugin-react-server

Version:
143 lines (129 loc) 4.61 kB
import type { EnvironmentModuleGraph, EnvironmentModuleNode, ModuleNode, ViteDevServer, Logger, } from "vite"; export const CSS_EXT = /\.(css|scss|sass|less|styl|stylus|pcss|postcss)(\?|$)/; export type CollectedCss = { id: string; code: string }; /** * Walks the Vite server environment's module graph from `pagePath` and * returns the raw CSS code of every CSS module reachable from it. * * Replaces the rsc-worker's Node ESM CSS loader for runner mode: the * runner bypasses Node's import resolver, so the loader never fires. * The main thread has visibility into the same module graph and can * collect CSS deterministically once `runner.import(pagePath)` has * caused Vite to transform the page and its deps. */ export async function collectRunnerCss( server: ViteDevServer, pagePath: string, projectRoot: string, logger: Logger, verbose = false ): Promise<CollectedCss[]> { const env = server.environments?.["server"]; if (!env) { if (verbose) logger.warn(`[collectRunnerCss] no server environment`); return []; } const moduleGraph: EnvironmentModuleGraph = env.moduleGraph; const candidates = [ pagePath, pagePath.startsWith("/") ? pagePath : `/${pagePath}`, pagePath.startsWith(projectRoot) ? pagePath : `${projectRoot}/${pagePath}`, ]; let pageModule: EnvironmentModuleNode | ModuleNode | undefined; for (const url of candidates) { pageModule = await moduleGraph.getModuleByUrl(url); if (pageModule) break; } // Fallback: scan idToModuleMap for an entry whose id endsWith the pagePath. // Vite keys server-env modules by full absolute path with optional query. if (!pageModule) { const idMap = (moduleGraph as any).idToModuleMap as | Map<string, EnvironmentModuleNode> | undefined; if (idMap) { for (const [id, node] of idMap.entries()) { if (id.endsWith(pagePath) || id.endsWith(`/${pagePath}`)) { pageModule = node; break; } } } } if (!pageModule) { if (verbose) { logger.warn( `[collectRunnerCss] no module for pagePath: ${pagePath} (tried: ${candidates.join(", ")}; graph size: ${ (moduleGraph as any).idToModuleMap?.size ?? "?" })` ); } return []; } if (verbose) { logger.info(`[collectRunnerCss] resolved page module: ${pageModule.id}`); } const seen = new Set<string>(); const out: CollectedCss[] = []; const walk = async (mod: EnvironmentModuleNode | ModuleNode) => { if (!mod?.id || seen.has(mod.id)) return; seen.add(mod.id); if (CSS_EXT.test(mod.id)) { try { const transformed = await env.transformRequest(`${mod.id}?inline`); if (transformed?.code) { const code = transformed.code; // Vite transforms `.css?inline` modules with different framings: // - SSR runner output: `const __vite_ssr_export_default__ = "...";` // - plain ESM output: `export default "...";` // - legacy module API: `__vite__cssModules.default = "...";` const exportMatch = code.match(/__vite_ssr_export_default__\s*=\s*("(?:\\.|[^"\\])*")/) ?? code.match(/export\s+default\s+("(?:\\.|[^"\\])*")/) ?? code.match(/__vite__cssModules\.default\s*=\s*("(?:\\.|[^"\\])*")/); if (exportMatch) { const raw = JSON.parse(exportMatch[1]); if (typeof raw === "string" && raw.length > 0) { out.push({ id: mod.id, code: raw }); } else if (verbose) { logger.warn(`[collectRunnerCss] inline default empty for ${mod.id}`); } } else if (verbose) { logger.warn( `[collectRunnerCss] could not find inline export in transform for ${mod.id} (codeLen=${code.length})` ); } } else if (verbose) { logger.warn(`[collectRunnerCss] no transformed code for ${mod.id}?inline`); } } catch (err) { if (verbose) { logger.warn( `[collectRunnerCss] failed to inline ${mod.id}: ${String(err)}` ); } } } if (mod.importedModules) { for (const imported of mod.importedModules as Iterable< EnvironmentModuleNode | ModuleNode >) { if (imported && typeof imported === "object" && "id" in imported) { await walk(imported); } } } }; await walk(pageModule); if (verbose) { logger.info( `[collectRunnerCss] collected ${out.length} CSS file(s) for ${pagePath}` ); } return out; }