@shopify/react-native-skia/headless for Edge Scripting for bunny.net 🐰
React Skia
React Skia is a high-performance React graphics library using Skia.
Quickstart
Installation
mkdir react-skia-examplecd react-skia-examplenpm init -ynpm install @bunny.net/edgescript-sdk bunny-react-skia react react-domnpm install --save-dev esbuild esbuild-plugin-node-protocol-imports tsx @tsconfig/node20 @types/react @types/react-dom
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 { locateCompressedFile, LoadSkiaWeb, makeOffscreenSurface, drawOffscreen, getSkiaExports, Circle, Group,} from 'bunny-react-skia'enum ImageFormat { JPEG = 3, PNG = 4, WEBP = 6,}let hasLoaded = falseBunnySDK.net.http.serve(async (request: Request): Promise<Response> => { // keep script startup time under 500ms limit if (!hasLoaded) { const file = await locateCompressedFile() await LoadSkiaWeb({ locateFile() { return file }, }) hasLoaded = true } // generate image const width = 256 const height = 256 const size = 50 const r = size * 0.33 const { Skia } = getSkiaExports() const surface = makeOffscreenSurface(width, height) const image = drawOffscreen( surface, <Group blendMode="multiply"> <Circle cx={r} cy={r} r={r} color="cyan" /> <Circle cx={size - r} cy={r} r={r} color="magenta" /> <Circle cx={size / 2} cy={size - r} r={r} color="yellow" /> </Group>, ) // save to image const bytes = image.encodeToBytes(ImageFormat.WEBP) // clean up resources image.dispose() surface.dispose() // return image response return new Response(bytes, { headers: { 'Content-Type': 'image/webp', }, })})
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