FMHYedit/.vitepress/hooks/opengraph.ts

97 lines
2.5 KiB
TypeScript
Raw Normal View History

2024-01-25 16:32:45 +00:00
import { mkdir, readFile, writeFile } from 'node:fs/promises'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { createContentLoader } from 'vitepress'
import type { ContentData, SiteConfig } from 'vitepress'
import { type SatoriOptions, satoriVue } from 'x-satori/vue'
import { renderAsync } from '@resvg/resvg-js'
import consola from 'consola'
2023-11-12 16:42:57 +00:00
2024-01-25 16:32:45 +00:00
const __dirname = dirname(fileURLToPath(import.meta.url))
const __fonts = resolve(__dirname, '../fonts')
2023-11-12 16:42:57 +00:00
2023-12-31 06:57:10 +00:00
export async function generateImages(config: SiteConfig): Promise<void> {
2024-01-25 16:32:45 +00:00
const pages = await createContentLoader('**/*.md', { excerpt: true }).load()
const template = await readFile(resolve(__dirname, './Template.vue'), 'utf-8')
2023-11-12 16:42:57 +00:00
2024-01-25 16:32:45 +00:00
const fonts: SatoriOptions['fonts'] = [
2023-11-12 16:42:57 +00:00
{
2024-01-25 16:32:45 +00:00
name: 'Inter',
data: await readFile(resolve(__fonts, 'Inter-Regular.otf')),
2023-11-12 16:42:57 +00:00
weight: 400,
2024-01-25 16:32:45 +00:00
style: 'normal'
2023-11-12 16:42:57 +00:00
},
{
2024-01-25 16:32:45 +00:00
name: 'Inter',
data: await readFile(resolve(__fonts, 'Inter-Medium.otf')),
2023-11-12 16:42:57 +00:00
weight: 500,
2024-01-25 16:32:45 +00:00
style: 'normal'
2023-11-12 16:42:57 +00:00
},
{
2024-01-25 16:32:45 +00:00
name: 'Inter',
data: await readFile(resolve(__fonts, 'Inter-SemiBold.otf')),
2023-11-12 16:42:57 +00:00
weight: 600,
2024-01-25 16:32:45 +00:00
style: 'normal'
2023-11-12 16:42:57 +00:00
},
{
2024-01-25 16:32:45 +00:00
name: 'Inter',
data: await readFile(resolve(__fonts, 'Inter-Bold.otf')),
2023-11-12 16:42:57 +00:00
weight: 700,
2024-01-25 16:32:45 +00:00
style: 'normal'
}
]
2023-11-12 16:42:57 +00:00
for (const page of pages) {
await generateImage({
page,
template,
outDir: config.outDir,
2024-01-25 16:32:45 +00:00
fonts
})
2023-11-12 16:42:57 +00:00
}
2024-01-25 16:32:45 +00:00
return consola.info('Generated opengraph images.')
2023-11-12 16:42:57 +00:00
}
interface GenerateImagesOptions {
2024-01-25 16:32:45 +00:00
page: ContentData
template: string
outDir: string
fonts: SatoriOptions['fonts']
2023-11-12 16:42:57 +00:00
}
2023-12-31 06:57:10 +00:00
async function generateImage({
page,
template,
outDir,
2024-01-25 16:32:45 +00:00
fonts
2023-12-31 06:57:10 +00:00
}: GenerateImagesOptions): Promise<void> {
2024-01-25 16:32:45 +00:00
const { frontmatter, url } = page
2023-11-12 16:42:57 +00:00
const options: SatoriOptions = {
width: 1200,
height: 628,
fonts,
props: {
title:
2024-01-25 16:32:45 +00:00
frontmatter.layout === 'home'
2023-11-12 16:42:57 +00:00
? frontmatter.hero.name ?? frontmatter.title
: frontmatter.title,
description:
2024-01-25 16:32:45 +00:00
frontmatter.layout === 'home'
2023-11-12 16:42:57 +00:00
? frontmatter.hero.tagline ?? frontmatter.description
2024-01-25 16:32:45 +00:00
: frontmatter.description
}
}
2023-11-12 16:42:57 +00:00
2024-01-25 16:32:45 +00:00
const svg = await satoriVue(options, template)
2023-11-12 16:42:57 +00:00
2024-01-25 16:32:45 +00:00
const render = await renderAsync(svg)
2023-11-12 16:42:57 +00:00
2024-01-25 16:32:45 +00:00
const outputFolder = resolve(outDir, url.slice(1), '__og_image__')
const outputFile = resolve(outputFolder, 'og.png')
2023-11-12 16:42:57 +00:00
2024-01-25 16:32:45 +00:00
await mkdir(outputFolder, { recursive: true })
2023-11-12 16:42:57 +00:00
2024-01-25 16:32:45 +00:00
await writeFile(outputFile, render.asPng())
2023-11-12 16:42:57 +00:00
}