UNPKG

astro

Version:

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

100 lines (99 loc) 3.48 kB
import MagicString from "magic-string"; const VIRTUAL_ISLAND_MAP_ID = "@astro-server-islands"; const RESOLVED_VIRTUAL_ISLAND_MAP_ID = "\0" + VIRTUAL_ISLAND_MAP_ID; const serverIslandPlaceholder = "'$$server-islands$$'"; function vitePluginServerIslands({ settings, logger }) { let command = "serve"; let viteServer = null; const referenceIdMap = /* @__PURE__ */ new Map(); return { name: "astro:server-islands", enforce: "post", config(_config, { command: _command }) { command = _command; }, configureServer(_server) { viteServer = _server; }, resolveId(name) { if (name === VIRTUAL_ISLAND_MAP_ID) { return RESOLVED_VIRTUAL_ISLAND_MAP_ID; } }, load(id) { if (id === RESOLVED_VIRTUAL_ISLAND_MAP_ID) { return { code: `export const serverIslandMap = ${serverIslandPlaceholder};` }; } }, transform(_code, id) { if (id.endsWith(".astro")) { const info = this.getModuleInfo(id); if (info?.meta) { const astro = info.meta.astro; if (astro?.serverComponents.length) { for (const comp of astro.serverComponents) { if (!settings.serverIslandNameMap.has(comp.resolvedPath)) { if (!settings.adapter) { logger.error( "islands", "You tried to render a server island without an adapter added to your project. An adapter is required to use the `server:defer` attribute on any component. Your project will fail to build unless you add an adapter or remove the attribute." ); } let name = comp.localName; let idx = 1; while (true) { if (!settings.serverIslandMap.has(name)) { break; } name += idx++; } settings.serverIslandNameMap.set(comp.resolvedPath, name); settings.serverIslandMap.set(name, () => { return viteServer?.ssrLoadModule(comp.resolvedPath); }); if (command === "build") { let referenceId = this.emitFile({ type: "chunk", id: comp.specifier, importer: id, name: comp.localName }); referenceIdMap.set(comp.resolvedPath, referenceId); } } } } } } }, renderChunk(code) { if (code.includes(serverIslandPlaceholder)) { if (referenceIdMap.size === 0) { return { code: code.replace(serverIslandPlaceholder, "new Map();"), map: null }; } let mapSource = "new Map(["; for (let [resolvedPath, referenceId] of referenceIdMap) { const fileName = this.getFileName(referenceId); const islandName = settings.serverIslandNameMap.get(resolvedPath); mapSource += ` ['${islandName}', () => import('./${fileName}')],`; } mapSource += "\n]);"; referenceIdMap.clear(); const ms = new MagicString(code); ms.replace(serverIslandPlaceholder, mapSource); return { code: ms.toString(), map: ms.generateMap({ hires: "boundary" }) }; } } }; } export { VIRTUAL_ISLAND_MAP_ID, vitePluginServerIslands };