Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/zhengqbbb/x-satori
🌠 Using Astro or Vue file to generate cover, card, Open Graph ... image SVG, powered by Satori and Vite
https://github.com/zhengqbbb/x-satori
astro opengragh opengraph opengraph-images satori satori-html vite vue
Last synced: 2 days ago
JSON representation
🌠 Using Astro or Vue file to generate cover, card, Open Graph ... image SVG, powered by Satori and Vite
- Host: GitHub
- URL: https://github.com/zhengqbbb/x-satori
- Owner: Zhengqbbb
- License: mit
- Created: 2023-03-14T14:33:52.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2024-07-27T16:58:34.000Z (5 months ago)
- Last Synced: 2024-12-25T19:58:34.399Z (12 days ago)
- Topics: astro, opengragh, opengraph, opengraph-images, satori, satori-html, vite, vue
- Language: TypeScript
- Homepage: https://stackblitz.com/@Zhengqbbb/collections/x-satori-demo
- Size: 27.9 MB
- Stars: 47
- Watchers: 3
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
x-satori
Use Vue and Astro file to generate SVG image by Satori.
The image can be generated by running ESM script or CLI.
---
> Local running example demo
```sh
npx degit Zhengqbbb/x-satori/playground/vue # Vue
npx degit Zhengqbbb/x-satori/playground/astro # Astrocd
pnpm install# Development Model
pnpm dev:og# [Generate] SVG
pnpm gen:svg# [Generate] PNG
pnpm gen:png
```## Usage
```sh
npm install -D x-satori
```### ⭐ Vue
Using Vitepress buildEnd hook
- **Example**: [examples/vue-vitepress](./examples/vue-vitepress/) ⭐⭐⭐
Using CLI
**Example**: [playground/vue](./playground/vue/)
- Dependency: **Vue | Vite**
```sh
$ npx x-satori --helpSYNOPSIS:
x-satori --template --config [--props ]
x-satori --template --config [--props ] --output
x-satori --template --config [--props ] --dev [--host --port ]OPTIONS:
-d|--dev Turn on Dev mode
--host Expose host in Dev mode
--port Specify port in Dev mode
-t|--template The Vue or Astro template file path
-c|--config The export satori configure file path
-o|--output Target output SVG path
--props Overwrite and use props in configEXAMPLES:
x-satori --config "./satori.ts" --template "./Template.vue" --dev --host
x-satori --config "./satori.ts" --template "./Template.vue"
x-satori --config "./satori.js" --template "./Template.vue" --props '{"title": "Hello World"}'
x-satori --config "./satori.js" --template "./Template.vue" -o image.svg
```#### Configure
- Extends Satori options and add Vue file props option
```mjs
import { defineSatoriConfig } from 'x-satori/vue'export default defineSatoriConfig({
// ... Satori options
props: {
// ...Vue SFC props options
// title: "Hello world"
},
})
```#### Vue template file
- **Only the template syntax is used**, and props are only used for hint completion
- [→ Satori supports common CSS features](https://github.com/vercel/satori#css)
- [→ Tailwindcss documentation](https://tailwindcss.com/docs/customizing-colors)```html
const props = defineProps({
title: String,
})
{{ title }} 👋
```
Using ESM script
- Dependency: **Vue**
```mjs
import { defineSatoriConfig, satoriVue } from 'x-satori/vue'function main() {
const _DIRNAME = typeof __dirname !== 'undefined'
? __dirname
: dirname(fileURLToPath(import.meta.url))
const _OUTPUT = resolve(_DIRNAME, './image/og.png')const templateStr = await readFile(resolve(_DIRNAME, './Template.vue'), 'utf8')
const opt = defineSatoriConfig({
// ... Satori options
props: {
// ...Vue SFC props options
// title: "Hello world"
},
})
const strSVG = await satoriVue(opt, templateStr)
console.log(strSVG)
}
main()
```- **Example**: [examples/vue-run-esm-script](./examples/vue-run-esm-script/)
```sh
npm run gen:svg
npm run gen:png
```### ⭐ Astro
Using Astro file-endpoints
- **Example**: [examples/astro-file-endpoint](./examples/astro-file-endpoint/) ⭐⭐⭐
- **Example**: [Repo - Zhengqbbb/qbb.sh](https://github.com/Zhengqbbb/qbb.sh/blob/astro/src/pages/og/%5Bslug%5D.png.ts)---
#### 1. Install Dependencies
```sh
npm install -D x-satori @resvg/resvg-js # Convert SVG to PNG
```#### 2. Create Astro [file-endpoints](https://docs.astro.build/en/guides/endpoints/)
> If target is generate `dist/og/*.png`.
> So that touch a file `src/pages/og/[slug].png.ts````ts
import { readFile } from 'node:fs/promises'
import { type SatoriOptions, satoriAstro } from 'x-satori/astro'
import { Resvg } from '@resvg/resvg-js'
import type { APIRoute } from 'astro'
import { type CollectionEntry, getCollection } from 'astro:content';export async function getStaticPaths() {
const posts = await getCollection('blog');return posts
.map(post => ({
params: { slug: post.slug },
props: { ...post },
}));
}async function getPostImageBuffer(props) {
const template = await readFile(/** .astro template file */, 'utf-8')
const config: SatoriOptions = {
//... satori options,
props: {
//...astro template file props
...props.data,
}
}
const svg = await satoriAstro(config, template)
const resvg = new Resvg(svg)
const pngData = resvg.render()
return pngData.asPng()
}export const GET: APIRoute = async ({ props }) =>
new Response(
await getPostImageBuffer(props as CollectionEntry<'blog'>),
{
headers: { 'Content-Type': 'image/png' },
},
)
```Using ESM script
- Dependency: **Astro**
```mjs
import { defineSatoriConfig, satoriAstro } from 'x-satori/astro'function main() {
const _DIRNAME = typeof __dirname !== 'undefined'
? __dirname
: dirname(fileURLToPath(import.meta.url))
const _OUTPUT = resolve(_DIRNAME, './image/og.png')const templateStr = await readFile(resolve(_DIRNAME, './Template.vue'), 'utf8')
const opt = defineSatoriConfig({
// ... Satori options
props: {
// ...Vue SFC props options
// title: "Hello world"
},
})
const strSVG = await satoriAstro(opt, templateStr)
console.log(strSVG)
}
main()
```- **Example**: [examples/astro-run-esm-script](./examples/astro-run-esm-script/) ⭐⭐⭐
```sh
npm run gen:svg
npm run gen:png
```- **Example**: [Repo - Zhengqbbb/[email protected]/.x-cmd/og/main.ts](https://github.com/Zhengqbbb/qbb.sh/blob/v2.1.1/.x-cmd/og/main.ts)
Using CLI
- Dependency: **Astro** | **Vite** (for dev mode)
```sh
$ npx x-satori --helpSYNOPSIS:
x-satori --template --config [--props ]
x-satori --template --config [--props ] --output
x-satori --template --config [--props ] --dev [--host --port ]OPTIONS:
-d|--dev Turn on Dev mode
--host Expose host in Dev mode
--port Specify port in Dev mode
-t|--template The Vue or Astro template file path
-c|--config The export satori configure file path
-o|--output Target output SVG path
--props Overwrite and use props in configEXAMPLES:
x-satori --config "./satori.ts" --template "./Template.astro" --dev --host
x-satori --config "./satori.ts" --template "./Template.astro"
x-satori --config "./satori.js" --template "./Template.astro" --props '{"title": "Hello World"}'
x-satori --config "./satori.js" --template "./Template.astro" -o image.svg
```#### Configure
- Extends Satori options and add Vue file props option
```mjs
import { defineSatoriConfig } from 'x-satori/astro'export default defineSatoriConfig({
// ... Satori options
props: {
// ...astro file props options
// title: "Hello world"
},
})
```#### Astro template file
- **Only the template syntax is used**, and props are only used for hint completion
- [→ Satori supports common CSS features](https://github.com/vercel/satori#css)
- [→ Tailwindcss documentation](https://tailwindcss.com/docs/customizing-colors)```astro
---
interface Props {
title: string
};const { title = Hello world } = Astro.props;
---
{title}
```
- **Example**: [playground/astro](./playground/astro/) ⭐⭐⭐
- **Example (Advanced)** - Using `Shell Scripts` to batch image generation with `resvg-cli`:
[Repo - Zhengqbbb/qbb.sh/.x-cmd/og](https://github.com/Zhengqbbb/qbb.sh/blob/6fe03b051c1468417682da6e329be0e0b77c6468/.x-cmd/og#L37-L45)### 🎼 Command-line Advanced Usage
pipeline generate png with
resvg-cli
> TIP: You can install it globally or use `bunx` for replacement startup
```sh
npx x-satori --config "./satori.ts" --template "./Template.vue" --props '{"title": "Hello World"}' | \
npx resvg-cli - image.png
```pipeline generate webp or more edit with
resvg-cli
andimagemagick
> TIP: You can install it globally or use `bunx` for replacement startup
```sh
npx x-satori --config "./satori.ts" --template "./Template.vue" --props '{"title": "Hello World"}' | \
npx resvg-cli - | \
magick - webp:image.webp
```## How it works
1. ▲ [Satori](https://github.com/vercel/satori) is an amazing library for generating SVG strings from pure HTML and CSS.
2. Unfortunately, it is built on top of React's JSX and expects ["React-elements-like objects"](https://github.com/vercel/satori#use-without-jsx).
3. Thanks an library [natemoo-re/satori-html](https://github.com/natemoo-re/satori-html) can to generate the necessary VDOM object from a string of HTML.
4. So the key is to **convert the Vue SFC file to an HTML string**, and here I used transform so that I could generate it via script (Only the template syntax is used)
- `@vue/compiler-sfc`: to parse Vue SFC file
- `vue - createSSRApp` and `vue/server-renderer`: transform HTML string
5. Astro: a similar method:
- `@astrojs/compiler`: to transform `.astro` to `ts`
- `AstroContainer`: renderToString to obtain HTML string## Why developed
> My Weekend Pilot Project
1. This processing logic, **initially used in my Vite-SSG person website** [qbb.sh](https://github.com/Zhengqbbb/qbb.sh/blob/790c47026cb1baac34dee8642150ec1729fb0f39/package.json#L18), I prefer to run the script to generate e.g `tsx gen-og.mts` at my building time rather than the edge Fn
2. And personally, I think Vue SFC File would be better in expressing this SVG structure, but I only use the template syntax and props, and the css would use tailwindcss.
3. I did a experiment this weekend, using Vite HRM to improve DX, and developed a CLI so that I could run command and generated the SVG directly.I'm happy that I finally finished this series of experiments and results this weekend.
Vue
Vitepress buildEnd example:
examples/vue-vitepress/
ESM script code example:
qbb.sh@vitesse/node/og/main.mts
Astro
File endpoints example:
https://satori-astro.vercel.app
ESM script code example:
qbb.sh@astro/.x-cmd/og/main.ts
## Related Links
- [nuxt-modules/og-image](https://github.com/nuxt-modules/og-image) - Nuxt or want to use edge Fn
- [Vercel / Open Graph (OG) Image Generation](https://vercel.com/docs/functions/og-image-generation)## FAQ
CJS support ?
**Not supported**, waiting for upstream library [natemoo-re/ultrahtml](https://github.com/natemoo-re/ultrahtml/tree/main)
## Contributing
> I did it step by step according to the documentation of Astro, Vue and Vite, if you are interested, PR welcome 🤗
```sh
pnpm install
pnpm dev # dev mode
pnpm x --help # start up the CLI and development
```## LICENSE
MIT
Copyright (c) 2023-2024 [Q.Ben Zheng](https://github.com/Zhengqbbb)
I just try my best to make thing well.
Could you give a star ⭐ to encourage me 🤗
If possible, can to be my 💖 Sponsor 💖 to support my work