UNPKG

nuxt-og-image

Version:

Enlightened OG Image generation for Nuxt.

121 lines (120 loc) 4.15 kB
import { fontCache } from "#og-image-cache"; import { theme } from "#og-image-virtual/unocss-config.mjs"; import compatibility from "#og-image/compatibility"; import { defu } from "defu"; import { sendError } from "h3"; import { normaliseFontInput, useOgImageRuntimeConfig } from "../../../shared.js"; import { loadFont } from "./font.js"; import { useResvg, useSatori, useSharp } from "./instances.js"; import { createVNodes } from "./vnodes.js"; const fontPromises = {}; async function resolveFonts(event) { const { fonts } = useOgImageRuntimeConfig(); const normalisedFonts = normaliseFontInput([...event.options.fonts || [], ...fonts]); const localFontPromises = []; const preloadedFonts = []; if (fontCache) { for (const font of normalisedFonts) { if (await fontCache.hasItem(font.cacheKey)) { font.data = await fontCache.getItemRaw(font.cacheKey); preloadedFonts.push(font); } else { if (!fontPromises[font.cacheKey]) { fontPromises[font.cacheKey] = loadFont(event, font).then(async (_font) => { if (_font?.data) await fontCache?.setItemRaw(_font.cacheKey, _font.data); return _font; }); } localFontPromises.push(fontPromises[font.cacheKey]); } } } const awaitedFonts = await Promise.all(localFontPromises); return [...preloadedFonts, ...awaitedFonts].map((_f) => { return { name: _f.name, data: _f.data, style: _f.style, weight: Number(_f.weight) }; }); } export async function createSvg(event) { const { options } = event; const { satoriOptions: _satoriOptions } = useOgImageRuntimeConfig(); const [satori, vnodes, fonts] = await Promise.all([ useSatori(), createVNodes(event), resolveFonts(event) ]); await event._nitro.hooks.callHook("nuxt-og-image:satori:vnodes", vnodes, event); const satoriOptions = defu(options.satori, _satoriOptions, { fonts, tailwindConfig: { theme }, embedFont: true, width: options.width, height: options.height }); return satori(vnodes, satoriOptions).catch((err) => { return sendError(event.e, err, import.meta.dev); }); } async function createPng(event) { const { resvgOptions } = useOgImageRuntimeConfig(); const svg = await createSvg(event); const Resvg = await useResvg(); const resvg = new Resvg(svg, defu( event.options.resvg, resvgOptions )); const pngData = resvg.render(); return pngData.asPng(); } async function createJpeg(event) { const { sharpOptions } = useOgImageRuntimeConfig(); if (compatibility.sharp === false) { if (import.meta.dev) { throw new Error("Sharp dependency is not accessible. Please check you have it installed and are using a compatible runtime."); } else { console.error("Sharp dependency is not accessible. Please check you have it installed and are using a compatible runtime. Falling back to png."); } return createPng(event); } const svg = await createSvg(event); if (!svg) { throw new Error("Failed to create SVG for JPEG rendering."); } const svgBuffer = Buffer.from(svg); const sharp = await useSharp().catch(() => { if (import.meta.dev) { throw new Error("Sharp dependency could not be loaded. Please check you have it installed and are using a compatible runtime."); } return null; }); if (!sharp) { console.error("Sharp dependency is not accessible. Please check you have it installed and are using a compatible runtime. Falling back to png."); return createPng(event); } const options = defu(event.options.sharp, sharpOptions); return sharp(svgBuffer, options).jpeg(options).toBuffer(); } const SatoriRenderer = { name: "satori", supportedFormats: ["png", "jpeg", "jpg", "json"], async createImage(e) { switch (e.extension) { case "png": return createPng(e); case "jpeg": case "jpg": return createJpeg(e); } }, async debug(e) { const [vnodes, svg] = await Promise.all([ createVNodes(e), createSvg(e) ]); return { vnodes, svg }; } }; export default SatoriRenderer;