Skip to content

Commit f24d911

Browse files
authored
feat: Standalone build to support providing WASM binary manually (#705)
This will unblock issues like #693.
1 parent 4a7d0bc commit f24d911

18 files changed

+477
-37
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ coverage
1313

1414
playground/public/yoga.wasm
1515
playground/tsconfig.tsbuildinfo
16+
17+
# Vendor files
18+
yoga.wasm

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,29 @@ await satori(
402402

403403
### Runtime Support
404404

405-
Satori can be used in browser, Node.js (>= 16), and Web Workers.
405+
Satori can be directly used in browser, Node.js (>= 16), and Web Workers. It bundles its underlying WASM dependencies as base64-encoded strings and loads them at runtime.
406+
407+
If there is a limitation on dynamically loading WASM (e.g. Cloudflare Workers), you can use the Standalone Build which is mentioned below.
408+
409+
#### Standalone Build of Satori
410+
411+
Satori's standalone build doesn't include Yoga's WASM binary by default, and you need to load it manually before using Satori.
412+
413+
First, you need to download the `yoga.wasm` binary from (Satori build)[https://unpkg.com/satori/] and provide it yourself. Let's use `fetch` to load it directly from the CDN as an example:
414+
415+
```jsx
416+
import satori, { init } from 'satori/standalone'
417+
418+
const res = await fetch('https://unpkg.com/satori/yoga.wasm')
419+
const yogaWasm = await res.arrayBuffer()
420+
421+
await init(yogaWasm)
422+
423+
// Now you can use satori as usual
424+
const svg = await satori(...)
425+
```
426+
427+
Of course, you can also load the `yoga.wasm` file from your local disk via `fs.readFile` in Node.js or other methods.
406428

407429
### Font Embedding
408430

package.json

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,34 @@
1616
"license": "MPL-2.0",
1717
"files": [
1818
"dist/**",
19-
"wasm.js"
19+
"yoga.wasm"
2020
],
2121
"exports": {
2222
"./package.json": "./package.json",
2323
".": {
2424
"import": "./dist/index.js",
2525
"require": "./dist/index.cjs"
2626
},
27-
"./wasm": {
27+
"./standalone": {
2828
"import": {
29-
"types": "./dist/index.d.ts",
30-
"default": "./dist/index.js"
29+
"types": "./dist/standalone.d.ts",
30+
"default": "./dist/standalone.js"
3131
},
3232
"require": {
33-
"types": "./dist/index.d.cts",
34-
"default": "./dist/index.cjs"
33+
"types": "./dist/standalone.d.cts",
34+
"default": "./dist/standalone.cjs"
3535
}
3636
}
3737
},
3838
"scripts": {
39-
"prepare": "husky install",
39+
"prepare": "husky install && pnpm run vendor",
4040
"dev": "pnpm run dev:default",
4141
"dev:default": "NODE_ENV=development tsup src/index.ts --watch --ignore-watch playground",
4242
"dev:playground": "turbo dev --filter=satori-playground...",
43-
"build": "NODE_ENV=production pnpm run build:default",
44-
"build:default": "tsup",
43+
"vendor": "cp node_modules/yoga-layout/dist/binaries/yoga.wasm .",
44+
"build": "pnpm run build:default && pnpm run build:standalone",
45+
"build:default": "NODE_ENV=production tsup",
46+
"build:standalone": "NODE_ENV=production SATORI_STANDALONE=1 tsup",
4547
"test": "NODE_ENV=test vitest run",
4648
"test:ui": "NODE_ENV=test vitest --ui --coverage.enabled",
4749
"test-type": "tsc -p tsconfig.json --noEmit && tsc -p playground/tsconfig.json --noEmit",
@@ -115,5 +117,10 @@
115117
"packageManager": "pnpm@8.7.0",
116118
"engines": {
117119
"node": ">=16"
120+
},
121+
"pnpm": {
122+
"patchedDependencies": {
123+
"yoga-layout@3.2.1": "patches/yoga-layout@3.2.1.patch"
124+
}
118125
}
119126
}

patches/yoga-layout@3.2.1.patch

Lines changed: 362 additions & 0 deletions
Large diffs are not rendered by default.

pnpm-lock.yaml

Lines changed: 9 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/handler/compute.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ import expand, { SerializedStyle } from './expand.js'
1010
import {
1111
asPointAutoPercentageLength,
1212
asPointPercentageLength,
13-
getYoga,
1413
lengthToNumber,
1514
parseViewBox,
1615
v,
17-
YogaNode,
1816
} from '../utils.js'
17+
import { getYoga, YogaNode } from '../yoga.js'
1918
import { resolveImageData } from './image.js'
2019

2120
type SatoriElement = keyof typeof presets

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export type { Locale } from './language.js'
77

88
export * from './satori.js'
99
export { default } from './satori.js'
10+
export { init } from './yoga.js'

src/layout.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@ import {
99
buildXMLString,
1010
normalizeChildren,
1111
hasDangerouslySetInnerHTMLProp,
12-
getYoga,
13-
YogaNode,
1412
} from './utils.js'
13+
import { getYoga, YogaNode } from './yoga.js'
1514
import { SVGNodeToImage } from './handler/preprocess.js'
1615
import computeStyle from './handler/compute.js'
1716
import FontLoader from './font.js'

src/satori.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import type { SatoriNode } from './layout.js'
55
import layout from './layout.js'
66
import FontLoader, { FontOptions } from './font.js'
77
import svg from './builder/svg.js'
8-
import { getYoga, segment, TYoga } from './utils.js'
8+
import { getYoga, TYoga } from './yoga.js'
99
import { detectLanguageCode, LangCode, Locale } from './language.js'
1010
import getTw from './handler/tailwind.js'
1111
import { preProcessNode } from './handler/preprocess.js'
1212
import { cache, inflightRequests } from './handler/image.js'
13+
import { segment } from './utils.js'
1314

1415
// We don't need to initialize the opentype instances every time.
1516
const fontCache = new WeakMap()

src/text/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@ import {
1111
isUndefined,
1212
isString,
1313
lengthToNumber,
14-
getYoga,
15-
TYoga,
16-
YogaNode,
1714
} from '../utils.js'
15+
import { getYoga, TYoga, YogaNode } from '../yoga.js'
1816
import buildText, { container } from '../builder/text.js'
1917
import { buildDropShadow } from '../builder/shadow.js'
2018
import buildDecoration from '../builder/text-decoration.js'

0 commit comments

Comments
 (0)