"A hot air balloon floating in the sky over a desert landscape"

Generated with flux-latentpop and seedance-1-pro-fast.

❯ cd ~/code/

Satori, Vite, Remix, Cloudflare, og my!

Overview
How to handle bundling Satori (Yoga) and Resvg WASM files for both Vite and Cloudflare.
Last Updated
26/05/2024
Tags
vite, cloudflare, satori

This has not been updated in the last three months, so this information miiiiiight be out of date. Here be dragons, etc.

NOTE

Update, November 2026: You probably don't need this anymore. Cloudflare's official Vite plugin imports .wasm files as a WebAssembly.Module out of the box, and Satori's bring-your-own-yoga build is now satori/standalone. Import the .wasm files in your Worker entry, pass them to initResvg and initSatori, and you're set. I've archived vite-plugin-wasm-module-workers.

On Friday night I decided to add Satori to code.charliegleason.com to automatically generate og:image's for each post.

I'm hosting the site on Cloudflare Pages, which makes it super fast at the edge™. I installed the Satori WASM requirements, added Resvg to save it as a PNG, and then assumed the pose of a man awaiting success.

I got a lot of errors.

It turns out, Vite handles WASM files in a specific way, and Cloudflare expects WASM files in a certain way. The result was a weekend spent noodling on regular expressions. And a library that will do it for you.

👉 vite-plugin-wasm-module-workers

It will, in essence, take this:

import satori, { init as initSatori } from 'satori/wasm'
import { Resvg, initWasm as initResvg } from '@resvg/resvg-wasm'
import initYoga from 'yoga-wasm-web'

import YOGA_WASM from 'yoga-wasm-web/dist/yoga.wasm?url'
import RESVG_WASM from '@resvg/resvg-wasm/index_bg.wasm?url'
Then, in our default function:

export async function createOGImage(title: string, requestUrl: string) {
const { default: resvgwasm } = await import(
/_ @vite-ignore _/ `${RESVG_WASM}?module`
)
const { default: yogawasm } = await import(
/_ @vite-ignore _/ `${YOGA_WASM}?module`
)

  try {
    if (!initialised) {
      await initResvg(resvgwasm)
      await initSatori(await initYoga(yogawasm))
      initialised = true
    }
  } catch (e) {
    initialised = true
  }

  // more fancy code

And turn it into this, on build:

import YOGA_WASM from './assets/yoga-CP4IUfLV.wasm'
import RESVG_WASM from './assets/index_bg-Blvrv-U2.wasm'
let initialised = false

async function createOGImage(title, requestUrl) {
  const resvgwasm = RESVG_WASM
  const yogawasm = YOGA_WASM
  try {
    if (!initialised) {
      await initWasm(resvgwasm)
      await init(await initYoga(yogawasm))
      initialised = true
    }
  } catch (e) {
    initialised = true
  }

  // more fancy build code
Edit on GitHub
Links