Satori for Edge Scripting for bunny.net 🐰

NPM Version

Satori with resvg

Satori is a library to convert HTML and CSS to SVG.

resvg is a high-performance SVG renderer and toolkit.

Quickstart

Installation

mkdir satori-examplecd satori-examplenpm init -ynpm install @bunny.net/edgescript-sdk bunny-satori bunny-resvg react react-domnpm install --save-dev esbuild esbuild-plugin-node-protocol-imports tsx @tsconfig/node20 @types/react @types/react-dom satori yoga-wasm-web

Add a build script to your package.json file:

{  "scripts": {    "build": "npx tsx src/esbuild"  }}

Example

Add TypeScript support with React:

tsconfig.json

{  "extends": "@tsconfig/node20/tsconfig.json",  "include": ["src"],  "compilerOptions": {    "noEmit": true,    "jsx": "react-jsx",    "jsxImportSource": "react",    "resolvePackageJsonExports": true  }}

Write the edge script:

src/index.tsx

import * as BunnySDK from '@bunny.net/edgescript-sdk'import { initSatori } from 'bunny-satori'import { initResvg } from 'bunny-resvg'let satori: Awaited<ReturnType<typeof initSatori>>let resvg: Awaited<ReturnType<typeof initResvg>>BunnySDK.net.http.serve(async (request: Request): Promise<Response> => {  // keep script startup time under 500ms limit  if (!satori || !resvg) {    ;[satori, resvg] = await Promise.all([initSatori(), initResvg()])  }  // jsx elements  const element = <div style={{ color: 'black' }}>hello, world</div>  // generate svg image  const svg = await satori(element, {    width: 600,    height: 400,    fonts: [      // if text is rendered, at least one font must be loaded      {        name: 'ABeeZee',        weight: 400,        data: await fetch(          'https://cdn.jsdelivr.net/gh/google/fonts@main/ofl/abeezee/ABeeZee-Regular.ttf',        ).then((res) => res.arrayBuffer()),        style: 'normal',      },    ],  })  // generate png image  const resvgJS = new resvg.Resvg(svg, {})  const pngData = resvgJS.render()  const pngBuffer = pngData.asPng()  // return image response  return new Response(pngBuffer, {    headers: {      'Content-Type': 'image/png',    },  })})

Create a build script:

src/esbuild.ts

import fs from 'node:fs/promises'import { builtinModules } from 'node:module'import { build } from 'esbuild'import esbuildPluginNodeProtocolImports from 'esbuild-plugin-node-protocol-imports'const MINIFY = process.env.MINIFY === 'false' ? false : trueconst allBuiltinModules = [  ...builtinModules,  ...builtinModules.map((builtinModule) => `node:${builtinModule}`),]await fs.rm('dist', {  recursive: true,  force: true,})await build({  alias: {    '@bunny.net/edgescript-sdk':      './node_modules/@bunny.net/edgescript-sdk/esm-bunny/lib.mjs',  },  banner: {    js: `import * as process from "node:process";import { Buffer } from "node:buffer";globalThis.process ??= process;globalThis.Buffer ??= Buffer;globalThis.global ??= globalThis;`,  },  bundle: true,  define: { 'process.env.NODE_ENV': '"production"' },  entryPoints: ['src/index.tsx'],  external: allBuiltinModules,  format: 'esm',  keepNames: !MINIFY,  minify: MINIFY,  outdir: 'dist',  packages: 'bundle',  platform: 'node',  sourcemap: !MINIFY,  target: 'node20.17.0',  plugins: [esbuildPluginNodeProtocolImports],})

For more information on bundling for edge scripts, please read the guide.

Deployment

Build

npm run build

Deploy

npx --yes bunny-launcher@latest --interactive