UNPKG

astro

Version:

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

171 lines (168 loc) • 6.07 kB
import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../constants.js"; import { AstroError, AstroErrorData } from "../errors/index.js"; const SERVER_ISLAND_MANIFEST = "virtual:astro:server-island-manifest"; const RESOLVED_SERVER_ISLAND_MANIFEST = "\0" + SERVER_ISLAND_MANIFEST; const serverIslandPlaceholderMap = "'$$server-islands-map$$'"; const serverIslandPlaceholderNameMap = "'$$server-islands-name-map$$'"; const SERVER_ISLAND_MAP_MARKER = "$$server-islands-map$$"; const serverIslandMapReplaceExp = /['"]\$\$server-islands-map\$\$['"]/g; const serverIslandNameMapReplaceExp = /['"]\$\$server-islands-name-map\$\$['"]/g; function vitePluginServerIslands({ settings, serverIslandsState }) { let command = "serve"; let serverEnvironments = []; function ensureServerIslandReferenceIds(ctx) { for (const [resolvedPath, island] of serverIslandsState.getDiscoveredIslandEntries()) { if (serverIslandsState.hasReferenceId(resolvedPath)) continue; const referenceId = ctx.emitFile({ type: "chunk", id: island.specifier, importer: island.importer, name: island.islandName }); serverIslandsState.setReferenceId(resolvedPath, referenceId); } } return { name: "astro:server-islands", enforce: "post", config(_config, { command: _command }) { command = _command; }, buildStart() { if (command !== "build" || this.environment?.name !== ASTRO_VITE_ENVIRONMENT_NAMES.ssr) { return; } ensureServerIslandReferenceIds(this); }, configureServer(server) { serverEnvironments = []; for (const name of [ ASTRO_VITE_ENVIRONMENT_NAMES.ssr, ASTRO_VITE_ENVIRONMENT_NAMES.prerender, ASTRO_VITE_ENVIRONMENT_NAMES.astro ]) { const env = server.environments[name]; if (env) { serverEnvironments.push(env); } } }, resolveId: { filter: { id: new RegExp(`^${SERVER_ISLAND_MANIFEST}$`) }, handler() { return RESOLVED_SERVER_ISLAND_MANIFEST; } }, load: { filter: { id: new RegExp(`^${RESOLVED_SERVER_ISLAND_MANIFEST}$`) }, handler() { return { code: `export const serverIslandMap = ${serverIslandPlaceholderMap}; export const serverIslandNameMap = ${serverIslandPlaceholderNameMap};` }; } }, transform: { filter: { id: { include: [/\.(astro|mdx)$/, new RegExp(`^${RESOLVED_SERVER_ISLAND_MANIFEST}$`)] } }, async handler(_code, id) { const info = this.getModuleInfo(id); const astro = info ? info.meta.astro : void 0; const isBuildSsr = command === "build" && this.environment?.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr; if (astro) { for (const comp of astro.serverComponents) { if (!settings.adapter) { throw new AstroError(AstroErrorData.NoAdapterInstalledServerIslands); } const island = serverIslandsState.discover({ resolvedPath: comp.resolvedPath, localName: comp.localName, specifier: comp.specifier ?? comp.resolvedPath, importer: id }); if (isBuildSsr && !serverIslandsState.hasReferenceId(comp.resolvedPath)) { const referenceId = this.emitFile({ type: "chunk", id: island.specifier, importer: island.importer, name: island.islandName }); serverIslandsState.setReferenceId(comp.resolvedPath, referenceId); } } } if (serverIslandsState.hasIslands()) { for (const env of serverEnvironments) { const mod = env.moduleGraph.getModuleById(RESOLVED_SERVER_ISLAND_MANIFEST); if (mod) { env.moduleGraph.invalidateModule(mod); } } } if (id === RESOLVED_SERVER_ISLAND_MANIFEST) { if (command === "build" && settings.buildOutput) { const hasServerIslands = serverIslandsState.hasIslands(); if (hasServerIslands && settings.buildOutput !== "server") { throw new AstroError(AstroErrorData.NoAdapterInstalledServerIslands); } } if (command !== "build" && serverIslandsState.hasIslands()) { const mapSource = serverIslandsState.createImportMapSourceFromDiscovered( (fileName) => fileName ); const nameMapSource = serverIslandsState.createNameMapSource(); return { code: ` export const serverIslandMap = ${mapSource}; export const serverIslandNameMap = ${nameMapSource}; ` }; } } } }, renderChunk(code, chunk) { if (!code.includes(SERVER_ISLAND_MAP_MARKER)) return; if (command === "build") { const envName = this.environment?.name; let mapSource; if (envName === ASTRO_VITE_ENVIRONMENT_NAMES.ssr) { const isRelativeChunk = !chunk.isEntry; const dots = isRelativeChunk ? ".." : "."; mapSource = serverIslandsState.createImportMapSourceFromReferences( (referenceId) => this.getFileName(referenceId), (fileName) => `${dots}/${fileName}` ); } else { mapSource = serverIslandsState.createImportMapSourceFromDiscovered( (fileName) => fileName ); } const nameMapSource = serverIslandsState.createNameMapSource(); return { code: code.replace(serverIslandMapReplaceExp, mapSource).replace(serverIslandNameMapReplaceExp, nameMapSource), map: null }; } return { code: code.replace(serverIslandMapReplaceExp, "new Map();").replace(serverIslandNameMapReplaceExp, "new Map()"), map: null }; } }; } export { SERVER_ISLAND_MANIFEST, SERVER_ISLAND_MAP_MARKER, vitePluginServerIslands };