UNPKG

astro

Version:

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

182 lines (181 loc) • 6.67 kB
import { ASTRO_ACTIONS_INTERNAL_MODULE_ID } from "../../../actions/consts.js"; import { MIDDLEWARE_MODULE_ID } from "../../middleware/vite-plugin.js"; import { routeIsRedirect } from "../../redirects/index.js"; import { VIRTUAL_ISLAND_MAP_ID } from "../../server-islands/vite-plugin-server-islands.js"; import { addRollupInput } from "../add-rollup-input.js"; import { SSR_MANIFEST_VIRTUAL_MODULE_ID } from "./plugin-manifest.js"; import { ASTRO_PAGE_MODULE_ID } from "./plugin-pages.js"; import { RENDERERS_MODULE_ID } from "./plugin-renderers.js"; import { getVirtualModulePageName } from "./util.js"; const SSR_VIRTUAL_MODULE_ID = "@astrojs-ssr-virtual-entry"; const RESOLVED_SSR_VIRTUAL_MODULE_ID = "\0" + SSR_VIRTUAL_MODULE_ID; const ADAPTER_VIRTUAL_MODULE_ID = "@astrojs-ssr-adapter"; const RESOLVED_ADAPTER_VIRTUAL_MODULE_ID = "\0" + ADAPTER_VIRTUAL_MODULE_ID; function vitePluginAdapter(adapter) { return { name: "@astrojs/vite-plugin-astro-adapter", enforce: "post", resolveId(id) { if (id === ADAPTER_VIRTUAL_MODULE_ID) { return RESOLVED_ADAPTER_VIRTUAL_MODULE_ID; } }, async load(id) { if (id === RESOLVED_ADAPTER_VIRTUAL_MODULE_ID) { return { code: `export * from ${JSON.stringify(adapter.serverEntrypoint)};` }; } } }; } function vitePluginSSR(internals, adapter, options) { return { name: "@astrojs/vite-plugin-astro-ssr-server", enforce: "post", options(opts) { const inputs = /* @__PURE__ */ new Set(); for (const pageData of Object.values(options.allPages)) { if (routeIsRedirect(pageData.route)) { continue; } inputs.add(getVirtualModulePageName(ASTRO_PAGE_MODULE_ID, pageData.component)); } const adapterServerEntrypoint = options.settings.adapter?.serverEntrypoint; if (adapterServerEntrypoint) { inputs.add(ADAPTER_VIRTUAL_MODULE_ID); } inputs.add(SSR_VIRTUAL_MODULE_ID); return addRollupInput(opts, Array.from(inputs)); }, resolveId(id) { if (id === SSR_VIRTUAL_MODULE_ID) { return RESOLVED_SSR_VIRTUAL_MODULE_ID; } }, async load(id) { if (id === RESOLVED_SSR_VIRTUAL_MODULE_ID) { const { allPages } = options; const imports = []; const contents = []; const exports = []; let i = 0; const pageMap = []; for (const pageData of Object.values(allPages)) { if (routeIsRedirect(pageData.route)) { continue; } const virtualModuleName = getVirtualModulePageName( ASTRO_PAGE_MODULE_ID, pageData.component ); let module = await this.resolve(virtualModuleName); if (module) { const variable = `_page${i}`; imports.push(`const ${variable} = () => import("${virtualModuleName}");`); const pageData2 = internals.pagesByKeys.get(pageData.key); if (pageData2) { pageMap.push(`[${JSON.stringify(pageData2.component)}, ${variable}]`); } i++; } } contents.push(`const pageMap = new Map([ ${pageMap.join(",\n ")} ]);`); exports.push(`export { pageMap }`); const middleware = await this.resolve(MIDDLEWARE_MODULE_ID); const ssrCode = generateSSRCode(adapter, middleware.id); imports.push(...ssrCode.imports); contents.push(...ssrCode.contents); return { code: [...imports, ...contents, ...exports].join("\n") }; } }, async generateBundle(_opts, bundle) { for (const [, chunk] of Object.entries(bundle)) { if (chunk.type === "asset") { internals.staticFiles.add(chunk.fileName); } } for (const [, chunk] of Object.entries(bundle)) { if (chunk.type === "asset") { continue; } if (chunk.modules[RESOLVED_SSR_VIRTUAL_MODULE_ID]) { internals.ssrEntryChunk = chunk; } } } }; } function pluginSSR(options, internals) { const ssr = options.settings.buildOutput === "server"; return { targets: ["server"], hooks: { "build:before": () => { const adapter = options.settings.adapter; const ssrPlugin = ssr && vitePluginSSR(internals, adapter, options); const vitePlugin = [vitePluginAdapter(adapter)]; if (ssrPlugin) { vitePlugin.unshift(ssrPlugin); } return { enforce: "after-user-plugins", vitePlugin }; }, "build:post": async () => { if (!ssr) { return; } if (!internals.ssrEntryChunk) { throw new Error(`Did not generate an entry chunk for SSR`); } internals.ssrEntryChunk.fileName = options.settings.config.build.serverEntry; } } }; } function generateSSRCode(adapter, middlewareId) { const edgeMiddleware = adapter?.adapterFeatures?.edgeMiddleware ?? false; const imports = [ `import { renderers } from '${RENDERERS_MODULE_ID}';`, `import * as serverEntrypointModule from '${ADAPTER_VIRTUAL_MODULE_ID}';`, `import { manifest as defaultManifest } from '${SSR_MANIFEST_VIRTUAL_MODULE_ID}';`, `import { serverIslandMap } from '${VIRTUAL_ISLAND_MAP_ID}';` ]; const contents = [ edgeMiddleware ? `const middleware = (_, next) => next()` : "", `const _manifest = Object.assign(defaultManifest, {`, ` pageMap,`, ` serverIslandMap,`, ` renderers,`, ` actions: () => import("${ASTRO_ACTIONS_INTERNAL_MODULE_ID}"),`, ` middleware: ${edgeMiddleware ? "undefined" : `() => import("${middlewareId}")`}`, `});`, `const _args = ${adapter.args ? JSON.stringify(adapter.args, null, 4) : "undefined"};`, adapter.exports ? `const _exports = serverEntrypointModule.createExports(_manifest, _args);` : "", ...adapter.exports?.map((name) => { if (name === "default") { return `export default _exports.default;`; } else { return `export const ${name} = _exports['${name}'];`; } }) ?? [], // NOTE: This is intentionally obfuscated! // Do NOT simplify this to something like `serverEntrypointModule.start?.(_manifest, _args)` // They are NOT equivalent! Some bundlers will throw if `start` is not exported, but we // only want to silently ignore it... hence the dynamic, obfuscated weirdness. `const _start = 'start'; if (_start in serverEntrypointModule) { serverEntrypointModule[_start](_manifest, _args); }` ]; return { imports, contents }; } export { RESOLVED_SSR_VIRTUAL_MODULE_ID, pluginSSR };