npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

x-satori

v0.2.0

Published

use Vue or Astro file to generate SVG image using Satori

Downloads

698

Readme


Local running example demo

npx degit Zhengqbbb/x-satori/playground/vue   <file_name>     # Vue
npx degit Zhengqbbb/x-satori/playground/astro <file_name>     # Astro

cd <file_name>
pnpm install

# Development Model
pnpm dev:og

# [Generate] SVG
pnpm gen:svg

# [Generate] PNG
pnpm gen:png

Usage

npm install -D x-satori

⭐ Vue

Example: playground/vue

  • Dependency: Vue | Vite
$ npx x-satori --help

SYNOPSIS:
    x-satori --template <template_file_path> --config <satori_config_path> [--props <JSON>]
    x-satori --template <template_file_path> --config <satori_config_path> [--props <JSON>] --output <svg_path>
    x-satori --template <template_file_path> --config <satori_config_path> [--props <JSON>] --dev [--host --port <num>]

OPTIONS:
    -d|--dev                   Turn on Dev mode
       --host                  Expose  host in Dev mode
       --port     <num>        Specify port in Dev mode
    -t|--template <path>       The Vue or Astro template file path
    -c|--config   <path>       The export satori configure file path
    -o|--output   <path>       Target output SVG path
    --props       <JSON_str>   Overwrite and use props in config

EXAMPLES:
    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
import { defineSatoriConfig } from 'x-satori/vue'

export default defineSatoriConfig({
    // ... Satori options
    props: {
        // ...Vue SFC props options
        // title: "Hello world"
    },
})

Vue template file

<script setup lang="ts">
const props = defineProps({
  title: String,
})
</script>
<template>
  <div class="w-full h-full flex text-white bg-blue-500 items-center justify-center">
    <h1 :style="{ fontSize: '70px' }">
      {{ title }} 👋
    </h1>
  </div>
</template>
  • Dependency: Vue
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()

⭐ Astro


1. Install Dependencies

npm install -D x-satori @resvg/resvg-js # Convert SVG to PNG

2. Create Astro file-endpoints

If target is generate dist/og/*.png. So that touch a file src/pages/og/[slug].png.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' },
        },
    )
  • Dependency: Astro
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()
  • Dependency: Astro | Vite (for dev mode)
$ npx x-satori --help

SYNOPSIS:
    x-satori --template <template_file_path> --config <satori_config_path> [--props <JSON>]
    x-satori --template <template_file_path> --config <satori_config_path> [--props <JSON>] --output <svg_path>
    x-satori --template <template_file_path> --config <satori_config_path> [--props <JSON>] --dev [--host --port <num>]

OPTIONS:
    -d|--dev                   Turn on Dev mode
       --host                  Expose  host in Dev mode
       --port     <num>        Specify port in Dev mode
    -t|--template <path>       The Vue or Astro template file path
    -c|--config   <path>       The export satori configure file path
    -o|--output   <path>       Target output SVG path
    --props       <JSON_str>   Overwrite and use props in config

EXAMPLES:
    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
import { defineSatoriConfig } from 'x-satori/astro'

export default defineSatoriConfig({
    // ... Satori options
    props: {
        // ...astro file props options
        // title: "Hello world"
    },
})

Astro template file

---
interface Props {
    title: string
};

const { title = Hello world } = Astro.props;
---
<div class="w-full h-full text-1.4rem text-white flex flex-col items-center justify-between">
    <h2 >
        {title}
    </h2>
</div>

🎼 Command-line Advanced Usage

TIP: You can install it globally or use bunx for replacement startup

npx x-satori --config "./satori.ts" --template "./Template.vue" --props '{"title": "Hello World"}' | \
    npx resvg-cli - image.png

TIP: You can install it globally or use bunx for replacement startup

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 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".
  3. Thanks an library 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, 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.

Related Links

FAQ

Not supported, waiting for upstream library natemoo-re/ultrahtml

Contributing

I did it step by step according to the documentation of Astro, Vue and Vite, if you are interested, PR welcome 🤗

pnpm install
pnpm dev        # dev mode
pnpm x --help   # start up the CLI and development

LICENSE

MIT Copyright (c) 2023-2024 Q.Ben Zheng