@effing/satori 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,7 @@ npm install @effing/satori
15
15
  For worker pool support (optional):
16
16
 
17
17
  ```bash
18
- npm install @effing/satori react tinypool
18
+ npm install @effing/satori react
19
19
  ```
20
20
 
21
21
  ## Quick Start
@@ -100,7 +100,7 @@ The package automatically loads emoji SVGs from CDNs. Supported styles:
100
100
 
101
101
  ### Worker Pool
102
102
 
103
- When rendering many frames (e.g. for animations), you can parallelize rendering across CPU cores using the worker pool. This can provide up to 4x speedups depending on render complexity.
103
+ When rendering many frames (e.g. for animations), you can parallelize rendering across CPU cores using the worker pool. This can provide up to 5x speedups depending on render complexity.
104
104
 
105
105
  The pool handles React element serialization automatically — you pass JSX in and get PNG/SVG buffers out, just like the single-threaded API.
106
106
 
@@ -118,7 +118,7 @@ const png = await pool.renderToPng(
118
118
  await pool.destroy();
119
119
  ```
120
120
 
121
- **Peer dependencies:** The pool and serde sub-paths require `react` and `tinypool` to be installed. They are listed as optional peer dependencies so the main `@effing/satori` entry works without them.
121
+ **Peer dependencies:** The pool and serde sub-paths require `react` to be installed. It is listed as an optional peer dependency so the main `@effing/satori` entry works without it.
122
122
 
123
123
  ### Vite Plugin (SSR)
124
124
 
@@ -8,14 +8,171 @@ import {
8
8
  import os from "os";
9
9
  import { fileURLToPath } from "url";
10
10
  import path from "path";
11
- import Tinypool from "tinypool";
11
+
12
+ // src/pool/worker-pool.ts
13
+ import { Worker } from "worker_threads";
14
+ import { pathToFileURL } from "url";
15
+ var BOOTSTRAP = `
16
+ "use strict";
17
+ const { parentPort, workerData } = require("node:worker_threads");
18
+
19
+ const modPromise = import(workerData.workerUrl)
20
+ .catch((err) => {
21
+ parentPort.postMessage({
22
+ id: -1,
23
+ error: { name: "InitError", message: err.message, stack: err.stack },
24
+ });
25
+ process.exit(1);
26
+ });
27
+
28
+ parentPort.on("message", async (msg) => {
29
+ const { id, name, args } = msg;
30
+ try {
31
+ const mod = await modPromise;
32
+ const result = await mod[name](...args);
33
+ parentPort.postMessage({ id, result });
34
+ } catch (err) {
35
+ parentPort.postMessage({
36
+ id,
37
+ error: {
38
+ name: err?.name ?? "Error",
39
+ message: err?.message ?? String(err),
40
+ stack: err?.stack,
41
+ code: err?.code,
42
+ },
43
+ });
44
+ }
45
+ });
46
+ `.trim();
47
+ var WorkerPool = class {
48
+ workerUrl;
49
+ workerOpts;
50
+ minThreads;
51
+ maxThreads;
52
+ workers = [];
53
+ queue = [];
54
+ nextId = 0;
55
+ destroyed = false;
56
+ constructor(options) {
57
+ this.workerUrl = pathToFileURL(options.workerFile).href;
58
+ this.minThreads = options.minThreads ?? 1;
59
+ this.maxThreads = options.maxThreads ?? 1;
60
+ this.workerOpts = {
61
+ eval: true,
62
+ workerData: { workerUrl: this.workerUrl }
63
+ };
64
+ for (let i = 0; i < this.minThreads; i++) {
65
+ this.workers.push(this.spawnWorker());
66
+ }
67
+ }
68
+ run(args, options) {
69
+ if (this.destroyed) {
70
+ return Promise.reject(new Error("WorkerPool has been destroyed"));
71
+ }
72
+ return new Promise((resolve, reject) => {
73
+ const task = {
74
+ id: this.nextId++,
75
+ name: options.name,
76
+ args: [args],
77
+ resolve,
78
+ reject
79
+ };
80
+ this.dispatch(task);
81
+ });
82
+ }
83
+ async destroy() {
84
+ this.destroyed = true;
85
+ for (const task of this.queue) {
86
+ task.reject(new Error("WorkerPool has been destroyed"));
87
+ }
88
+ this.queue.length = 0;
89
+ await Promise.all(this.workers.map((entry) => entry.worker.terminate()));
90
+ this.workers.length = 0;
91
+ }
92
+ dispatch(task) {
93
+ const idle = this.workers.find((w) => !w.busy);
94
+ if (idle) {
95
+ this.sendToWorker(idle, task);
96
+ return;
97
+ }
98
+ if (this.workers.length < this.maxThreads) {
99
+ const entry = this.spawnWorker();
100
+ this.workers.push(entry);
101
+ this.sendToWorker(entry, task);
102
+ return;
103
+ }
104
+ this.queue.push(task);
105
+ }
106
+ sendToWorker(entry, task) {
107
+ entry.busy = true;
108
+ const onMessage = (msg) => {
109
+ if (msg.id !== task.id) return;
110
+ entry.worker.off("message", onMessage);
111
+ entry.worker.off("error", onError);
112
+ entry.busy = false;
113
+ if (msg.error) {
114
+ const err = new Error(msg.error.message);
115
+ err.name = msg.error.name;
116
+ if (msg.error.stack) err.stack = msg.error.stack;
117
+ if (msg.error.code)
118
+ err.code = msg.error.code;
119
+ task.reject(err);
120
+ } else {
121
+ task.resolve(msg.result);
122
+ }
123
+ this.processQueue();
124
+ };
125
+ const onError = (err) => {
126
+ entry.worker.off("message", onMessage);
127
+ entry.worker.off("error", onError);
128
+ entry.busy = false;
129
+ task.reject(err);
130
+ this.processQueue();
131
+ };
132
+ entry.worker.on("message", onMessage);
133
+ entry.worker.on("error", onError);
134
+ entry.worker.postMessage({ id: task.id, name: task.name, args: task.args });
135
+ }
136
+ processQueue() {
137
+ if (this.queue.length === 0) return;
138
+ const idle = this.workers.find((w) => !w.busy);
139
+ if (idle) {
140
+ const task = this.queue.shift();
141
+ this.sendToWorker(idle, task);
142
+ }
143
+ }
144
+ spawnWorker() {
145
+ const worker = new Worker(BOOTSTRAP, this.workerOpts);
146
+ const entry = { worker, busy: false };
147
+ worker.on("exit", (code) => {
148
+ const idx = this.workers.indexOf(entry);
149
+ if (idx !== -1) this.workers.splice(idx, 1);
150
+ if (!this.destroyed && code !== 0) {
151
+ this.workers.push(this.spawnWorker());
152
+ }
153
+ });
154
+ worker.on(
155
+ "message",
156
+ (msg) => {
157
+ if (msg.id === -1 && msg.error) {
158
+ const err = new Error(msg.error.message);
159
+ err.name = msg.error.name;
160
+ if (msg.error.stack) err.stack = msg.error.stack;
161
+ }
162
+ }
163
+ );
164
+ return entry;
165
+ }
166
+ };
167
+
168
+ // src/pool/index.ts
12
169
  function createSatoriPool(options) {
13
170
  const workerFile = options?.workerFile ?? path.resolve(
14
171
  path.dirname(fileURLToPath(import.meta.url)),
15
172
  "../worker/index.js"
16
173
  );
17
- const pool = new Tinypool({
18
- filename: workerFile,
174
+ const pool = new WorkerPool({
175
+ workerFile,
19
176
  minThreads: options?.minThreads ?? 1,
20
177
  maxThreads: options?.maxThreads ?? os.cpus().length
21
178
  });
@@ -40,7 +197,7 @@ function createSatoriPool(options) {
40
197
  const serialized = serializeElement(
41
198
  ensureSingleElement(expandElement(element))
42
199
  );
43
- return pool.run(
200
+ return await pool.run(
44
201
  {
45
202
  element: serialized,
46
203
  width: opts.width,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pool/index.ts"],"sourcesContent":["import os from \"os\";\nimport { fileURLToPath } from \"url\";\nimport path from \"path\";\n\nimport type { ReactNode } from \"react\";\nimport type satori from \"satori\";\nimport Tinypool from \"tinypool\";\n\nimport type { EmojiStyle } from \"../emoji.ts\";\nimport {\n ensureSingleElement,\n expandElement,\n serializeElement,\n} from \"../serde/index.ts\";\n\nexport type SatoriPoolOptions = {\n /** Minimum number of worker threads (default: 1) */\n minThreads?: number;\n /** Maximum number of worker threads (default: os.cpus().length) */\n maxThreads?: number;\n /** Absolute path to a bundled worker file (set automatically by the Vite plugin) */\n workerFile?: string;\n};\n\nexport type SatoriPool = {\n /** Render a serialized React element to PNG via the worker pool */\n renderToPng(\n element: Parameters<typeof satori>[0],\n options: {\n width: number;\n height: number;\n fonts: Array<{\n name: string;\n data: Buffer | ArrayBuffer;\n weight: number;\n style: string;\n }>;\n emoji?: EmojiStyle;\n },\n ): Promise<Buffer>;\n\n /** Render a serialized React element to SVG via the worker pool */\n renderToSvg(\n element: Parameters<typeof satori>[0],\n options: {\n width: number;\n height: number;\n fonts: Array<{\n name: string;\n data: Buffer | ArrayBuffer;\n weight: number;\n style: string;\n }>;\n emoji?: EmojiStyle;\n },\n ): Promise<string>;\n\n /** Rasterize an SVG string to PNG via the worker pool */\n rasterizeSvgToPng(\n svg: string,\n options?: {\n fitTo?:\n | { mode: \"original\" }\n | { mode: \"width\"; value: number }\n | { mode: \"height\"; value: number }\n | { mode: \"zoom\"; value: number };\n crop?: {\n left: number;\n top: number;\n right?: number;\n bottom?: number;\n };\n },\n ): Promise<Buffer>;\n\n /** Shut down the worker pool */\n destroy(): Promise<void>;\n};\n\n/**\n * Create a worker pool for parallelized satori rendering.\n *\n * @param options Pool configuration\n * @returns A `SatoriPool` with `renderToPng`, `renderToSvg`, `rasterizeSvgToPng`, and `destroy`\n */\nexport function createSatoriPool(options?: SatoriPoolOptions): SatoriPool {\n const workerFile =\n options?.workerFile ??\n path.resolve(\n path.dirname(fileURLToPath(import.meta.url)),\n \"../worker/index.js\",\n );\n\n const pool = new Tinypool({\n filename: workerFile,\n minThreads: options?.minThreads ?? 1,\n maxThreads: options?.maxThreads ?? os.cpus().length,\n });\n\n return {\n async renderToPng(element, opts) {\n const serialized = serializeElement(\n ensureSingleElement(expandElement(element as ReactNode)),\n );\n const result = await pool.run(\n {\n element: serialized,\n width: opts.width,\n height: opts.height,\n fonts: opts.fonts,\n emoji: opts.emoji,\n },\n { name: \"renderToPng\" },\n );\n return Buffer.from(result);\n },\n\n async renderToSvg(element, opts) {\n const serialized = serializeElement(\n ensureSingleElement(expandElement(element as ReactNode)),\n );\n return pool.run(\n {\n element: serialized,\n width: opts.width,\n height: opts.height,\n fonts: opts.fonts,\n emoji: opts.emoji,\n },\n { name: \"renderToSvg\" },\n );\n },\n\n async rasterizeSvgToPng(svg, opts) {\n const result = await pool.run(\n { svg, options: opts },\n { name: \"rasterizeSvgToPng\" },\n );\n return Buffer.from(result);\n },\n\n async destroy() {\n await pool.destroy();\n },\n };\n}\n"],"mappings":";;;;;;;AAAA,OAAO,QAAQ;AACf,SAAS,qBAAqB;AAC9B,OAAO,UAAU;AAIjB,OAAO,cAAc;AA+Ed,SAAS,iBAAiB,SAAyC;AACxE,QAAM,aACJ,SAAS,cACT,KAAK;AAAA,IACH,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAAA,IAC3C;AAAA,EACF;AAEF,QAAM,OAAO,IAAI,SAAS;AAAA,IACxB,UAAU;AAAA,IACV,YAAY,SAAS,cAAc;AAAA,IACnC,YAAY,SAAS,cAAc,GAAG,KAAK,EAAE;AAAA,EAC/C,CAAC;AAED,SAAO;AAAA,IACL,MAAM,YAAY,SAAS,MAAM;AAC/B,YAAM,aAAa;AAAA,QACjB,oBAAoB,cAAc,OAAoB,CAAC;AAAA,MACzD;AACA,YAAM,SAAS,MAAM,KAAK;AAAA,QACxB;AAAA,UACE,SAAS;AAAA,UACT,OAAO,KAAK;AAAA,UACZ,QAAQ,KAAK;AAAA,UACb,OAAO,KAAK;AAAA,UACZ,OAAO,KAAK;AAAA,QACd;AAAA,QACA,EAAE,MAAM,cAAc;AAAA,MACxB;AACA,aAAO,OAAO,KAAK,MAAM;AAAA,IAC3B;AAAA,IAEA,MAAM,YAAY,SAAS,MAAM;AAC/B,YAAM,aAAa;AAAA,QACjB,oBAAoB,cAAc,OAAoB,CAAC;AAAA,MACzD;AACA,aAAO,KAAK;AAAA,QACV;AAAA,UACE,SAAS;AAAA,UACT,OAAO,KAAK;AAAA,UACZ,QAAQ,KAAK;AAAA,UACb,OAAO,KAAK;AAAA,UACZ,OAAO,KAAK;AAAA,QACd;AAAA,QACA,EAAE,MAAM,cAAc;AAAA,MACxB;AAAA,IACF;AAAA,IAEA,MAAM,kBAAkB,KAAK,MAAM;AACjC,YAAM,SAAS,MAAM,KAAK;AAAA,QACxB,EAAE,KAAK,SAAS,KAAK;AAAA,QACrB,EAAE,MAAM,oBAAoB;AAAA,MAC9B;AACA,aAAO,OAAO,KAAK,MAAM;AAAA,IAC3B;AAAA,IAEA,MAAM,UAAU;AACd,YAAM,KAAK,QAAQ;AAAA,IACrB;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/pool/index.ts","../../src/pool/worker-pool.ts"],"sourcesContent":["import os from \"os\";\nimport { fileURLToPath } from \"url\";\nimport path from \"path\";\n\nimport type { ReactNode } from \"react\";\nimport type satori from \"satori\";\n\nimport type { EmojiStyle } from \"../emoji.ts\";\nimport {\n ensureSingleElement,\n expandElement,\n serializeElement,\n} from \"../serde/index.ts\";\nimport { WorkerPool } from \"./worker-pool.ts\";\n\nexport type SatoriPoolOptions = {\n /** Minimum number of worker threads (default: 1) */\n minThreads?: number;\n /** Maximum number of worker threads (default: os.cpus().length) */\n maxThreads?: number;\n /** Absolute path to a bundled worker file (set automatically by the Vite plugin) */\n workerFile?: string;\n};\n\nexport type SatoriPool = {\n /** Render a serialized React element to PNG via the worker pool */\n renderToPng(\n element: Parameters<typeof satori>[0],\n options: {\n width: number;\n height: number;\n fonts: Array<{\n name: string;\n data: Buffer | ArrayBuffer;\n weight: number;\n style: string;\n }>;\n emoji?: EmojiStyle;\n },\n ): Promise<Buffer>;\n\n /** Render a serialized React element to SVG via the worker pool */\n renderToSvg(\n element: Parameters<typeof satori>[0],\n options: {\n width: number;\n height: number;\n fonts: Array<{\n name: string;\n data: Buffer | ArrayBuffer;\n weight: number;\n style: string;\n }>;\n emoji?: EmojiStyle;\n },\n ): Promise<string>;\n\n /** Rasterize an SVG string to PNG via the worker pool */\n rasterizeSvgToPng(\n svg: string,\n options?: {\n fitTo?:\n | { mode: \"original\" }\n | { mode: \"width\"; value: number }\n | { mode: \"height\"; value: number }\n | { mode: \"zoom\"; value: number };\n crop?: {\n left: number;\n top: number;\n right?: number;\n bottom?: number;\n };\n },\n ): Promise<Buffer>;\n\n /** Shut down the worker pool */\n destroy(): Promise<void>;\n};\n\n/**\n * Create a worker pool for parallelized satori rendering.\n *\n * @param options Pool configuration\n * @returns A `SatoriPool` with `renderToPng`, `renderToSvg`, `rasterizeSvgToPng`, and `destroy`\n */\nexport function createSatoriPool(options?: SatoriPoolOptions): SatoriPool {\n const workerFile =\n options?.workerFile ??\n path.resolve(\n path.dirname(fileURLToPath(import.meta.url)),\n \"../worker/index.js\",\n );\n\n const pool = new WorkerPool({\n workerFile,\n minThreads: options?.minThreads ?? 1,\n maxThreads: options?.maxThreads ?? os.cpus().length,\n });\n\n return {\n async renderToPng(element, opts) {\n const serialized = serializeElement(\n ensureSingleElement(expandElement(element as ReactNode)),\n );\n const result = (await pool.run(\n {\n element: serialized,\n width: opts.width,\n height: opts.height,\n fonts: opts.fonts,\n emoji: opts.emoji,\n },\n { name: \"renderToPng\" },\n )) as Uint8Array;\n return Buffer.from(result);\n },\n\n async renderToSvg(element, opts) {\n const serialized = serializeElement(\n ensureSingleElement(expandElement(element as ReactNode)),\n );\n return (await pool.run(\n {\n element: serialized,\n width: opts.width,\n height: opts.height,\n fonts: opts.fonts,\n emoji: opts.emoji,\n },\n { name: \"renderToSvg\" },\n )) as string;\n },\n\n async rasterizeSvgToPng(svg, opts) {\n const result = (await pool.run(\n { svg, options: opts },\n { name: \"rasterizeSvgToPng\" },\n )) as Uint8Array;\n return Buffer.from(result);\n },\n\n async destroy() {\n await pool.destroy();\n },\n };\n}\n","import { Worker, type WorkerOptions } from \"node:worker_threads\";\nimport { pathToFileURL } from \"node:url\";\n\ninterface Task {\n id: number;\n name: string;\n args: unknown[];\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n}\n\ninterface WorkerEntry {\n worker: Worker;\n busy: boolean;\n}\n\nexport interface WorkerPoolOptions {\n workerFile: string;\n minThreads?: number;\n maxThreads?: number;\n}\n\n// CJS bootstrap that dynamically imports the ESM worker module, then\n// wires up parentPort message handling for { id, name, args } calls.\nconst BOOTSTRAP = `\n\"use strict\";\nconst { parentPort, workerData } = require(\"node:worker_threads\");\n\nconst modPromise = import(workerData.workerUrl)\n .catch((err) => {\n parentPort.postMessage({\n id: -1,\n error: { name: \"InitError\", message: err.message, stack: err.stack },\n });\n process.exit(1);\n });\n\nparentPort.on(\"message\", async (msg) => {\n const { id, name, args } = msg;\n try {\n const mod = await modPromise;\n const result = await mod[name](...args);\n parentPort.postMessage({ id, result });\n } catch (err) {\n parentPort.postMessage({\n id,\n error: {\n name: err?.name ?? \"Error\",\n message: err?.message ?? String(err),\n stack: err?.stack,\n code: err?.code,\n },\n });\n }\n});\n`.trim();\n\nexport class WorkerPool {\n private readonly workerUrl: string;\n private readonly workerOpts: WorkerOptions;\n private readonly minThreads: number;\n private readonly maxThreads: number;\n private readonly workers: WorkerEntry[] = [];\n private readonly queue: Task[] = [];\n private nextId = 0;\n private destroyed = false;\n\n constructor(options: WorkerPoolOptions) {\n this.workerUrl = pathToFileURL(options.workerFile).href;\n this.minThreads = options.minThreads ?? 1;\n this.maxThreads = options.maxThreads ?? 1;\n this.workerOpts = {\n eval: true,\n workerData: { workerUrl: this.workerUrl },\n };\n\n // Pre-spawn minimum workers\n for (let i = 0; i < this.minThreads; i++) {\n this.workers.push(this.spawnWorker());\n }\n }\n\n run(args: unknown, options: { name: string }): Promise<unknown> {\n if (this.destroyed) {\n return Promise.reject(new Error(\"WorkerPool has been destroyed\"));\n }\n\n return new Promise<unknown>((resolve, reject) => {\n const task: Task = {\n id: this.nextId++,\n name: options.name,\n args: [args],\n resolve,\n reject,\n };\n this.dispatch(task);\n });\n }\n\n async destroy(): Promise<void> {\n this.destroyed = true;\n\n // Reject all queued tasks\n for (const task of this.queue) {\n task.reject(new Error(\"WorkerPool has been destroyed\"));\n }\n this.queue.length = 0;\n\n // Terminate all workers\n await Promise.all(this.workers.map((entry) => entry.worker.terminate()));\n this.workers.length = 0;\n }\n\n private dispatch(task: Task): void {\n // Find an idle worker\n const idle = this.workers.find((w) => !w.busy);\n if (idle) {\n this.sendToWorker(idle, task);\n return;\n }\n\n // Spawn a new worker if under the max\n if (this.workers.length < this.maxThreads) {\n const entry = this.spawnWorker();\n this.workers.push(entry);\n this.sendToWorker(entry, task);\n return;\n }\n\n // All workers busy, queue the task\n this.queue.push(task);\n }\n\n private sendToWorker(entry: WorkerEntry, task: Task): void {\n entry.busy = true;\n\n const onMessage = (msg: {\n id: number;\n result?: unknown;\n error?: { name: string; message: string; stack?: string; code?: string };\n }) => {\n if (msg.id !== task.id) return;\n entry.worker.off(\"message\", onMessage);\n entry.worker.off(\"error\", onError);\n entry.busy = false;\n\n if (msg.error) {\n const err = new Error(msg.error.message);\n err.name = msg.error.name;\n if (msg.error.stack) err.stack = msg.error.stack;\n if (msg.error.code)\n (err as NodeJS.ErrnoException).code = msg.error.code;\n task.reject(err);\n } else {\n task.resolve(msg.result);\n }\n\n this.processQueue();\n };\n\n const onError = (err: Error) => {\n entry.worker.off(\"message\", onMessage);\n entry.worker.off(\"error\", onError);\n entry.busy = false;\n task.reject(err);\n this.processQueue();\n };\n\n entry.worker.on(\"message\", onMessage);\n entry.worker.on(\"error\", onError);\n\n entry.worker.postMessage({ id: task.id, name: task.name, args: task.args });\n }\n\n private processQueue(): void {\n if (this.queue.length === 0) return;\n const idle = this.workers.find((w) => !w.busy);\n if (idle) {\n const task = this.queue.shift()!;\n this.sendToWorker(idle, task);\n }\n }\n\n private spawnWorker(): WorkerEntry {\n const worker = new Worker(BOOTSTRAP, this.workerOpts);\n const entry: WorkerEntry = { worker, busy: false };\n\n worker.on(\"exit\", (code) => {\n const idx = this.workers.indexOf(entry);\n if (idx !== -1) this.workers.splice(idx, 1);\n\n // Auto-restart on unexpected exit (not from destroy)\n if (!this.destroyed && code !== 0) {\n this.workers.push(this.spawnWorker());\n }\n });\n\n // Handle init errors (id === -1 from bootstrap)\n worker.on(\n \"message\",\n (msg: {\n id: number;\n error?: { name: string; message: string; stack?: string };\n }) => {\n if (msg.id === -1 && msg.error) {\n const err = new Error(msg.error.message);\n err.name = msg.error.name;\n if (msg.error.stack) err.stack = msg.error.stack;\n // Worker will exit(1) after this, triggering auto-restart\n }\n },\n );\n\n return entry;\n }\n}\n"],"mappings":"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","names":[]}
@@ -311,9 +311,6 @@ function satoriPoolPlugin() {
311
311
  return {
312
312
  name: "@effing/satori:worker",
313
313
  apply: "build",
314
- config() {
315
- return { ssr: { external: ["tinypool"] } };
316
- },
317
314
  configResolved(config) {
318
315
  resolvedConfig = config;
319
316
  },
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/vite/index.ts"],"sourcesContent":["import { posix } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { Plugin, ResolvedConfig } from \"vite\";\n\ninterface EstreeNode {\n type: string;\n start: number;\n end: number;\n [key: string]: unknown;\n}\n\ninterface TextEdit {\n start: number;\n end: number;\n replacement: string;\n}\n\nconst TARGET_MODULES = new Set([\"@effing/satori/pool\", \"@effing/satori\"]);\n\nconst SCOPE_TYPES = new Set([\n \"FunctionDeclaration\",\n \"FunctionExpression\",\n \"ArrowFunctionExpression\",\n \"ClassExpression\",\n \"BlockStatement\",\n \"ForStatement\",\n \"ForInStatement\",\n \"ForOfStatement\",\n \"SwitchStatement\",\n \"CatchClause\",\n]);\n\nfunction isEstreeNode(value: unknown): value is EstreeNode {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as EstreeNode).type === \"string\" &&\n typeof (value as EstreeNode).start === \"number\" &&\n typeof (value as EstreeNode).end === \"number\"\n );\n}\n\nfunction walk(\n node: EstreeNode,\n enter: (node: EstreeNode, parent: EstreeNode | null) => void,\n leave?: (node: EstreeNode, parent: EstreeNode | null) => void,\n parent: EstreeNode | null = null,\n): void {\n enter(node, parent);\n for (const key of Object.keys(node)) {\n if (key === \"type\" || key === \"start\" || key === \"end\") continue;\n const val = node[key];\n if (isEstreeNode(val)) {\n walk(val, enter, leave, node);\n } else if (Array.isArray(val)) {\n for (const item of val) {\n if (isEstreeNode(item)) {\n walk(item, enter, leave, node);\n }\n }\n }\n }\n leave?.(node, parent);\n}\n\nfunction findImportedLocalNames(ast: EstreeNode): Set<string> {\n const names = new Set<string>();\n const body = ast.body as EstreeNode[] | undefined;\n if (!Array.isArray(body)) return names;\n\n for (const node of body) {\n if (\n node.type !== \"ImportDeclaration\" ||\n !isEstreeNode(node.source) ||\n !TARGET_MODULES.has(node.source.value as string)\n )\n continue;\n if ((node.importKind as string | undefined) === \"type\") continue;\n\n const specifiers = node.specifiers as EstreeNode[] | undefined;\n if (!Array.isArray(specifiers)) continue;\n\n for (const spec of specifiers) {\n if (spec.type !== \"ImportSpecifier\") continue;\n if ((spec.importKind as string | undefined) === \"type\") continue;\n const imported = spec.imported as EstreeNode | undefined;\n const local = spec.local as EstreeNode | undefined;\n if (imported && (imported.name as string) === \"createSatoriPool\" && local)\n names.add(local.name as string);\n }\n }\n return names;\n}\n\nfunction collectPatternNames(\n pattern: EstreeNode,\n tracked: Set<string>,\n into: Set<string>,\n): void {\n switch (pattern.type) {\n case \"Identifier\":\n if (tracked.has(pattern.name as string)) into.add(pattern.name as string);\n break;\n case \"ObjectPattern\": {\n const props = pattern.properties as EstreeNode[] | undefined;\n if (Array.isArray(props)) {\n for (const prop of props) {\n if (prop.type === \"RestElement\") {\n const arg = prop.argument as EstreeNode | undefined;\n if (isEstreeNode(arg)) collectPatternNames(arg, tracked, into);\n } else {\n // Property node — use value if present, fall back to key for\n // robustness across parser node shapes (shorthand properties)\n const target = (prop.value ?? prop.key) as EstreeNode | undefined;\n if (isEstreeNode(target))\n collectPatternNames(target, tracked, into);\n }\n }\n }\n break;\n }\n case \"ArrayPattern\": {\n const elems = pattern.elements as (EstreeNode | null)[] | undefined;\n if (Array.isArray(elems)) {\n for (const elem of elems) {\n if (isEstreeNode(elem)) collectPatternNames(elem, tracked, into);\n }\n }\n break;\n }\n case \"RestElement\": {\n const arg = pattern.argument as EstreeNode | undefined;\n if (isEstreeNode(arg)) collectPatternNames(arg, tracked, into);\n break;\n }\n case \"AssignmentPattern\": {\n const left = pattern.left as EstreeNode | undefined;\n if (isEstreeNode(left)) collectPatternNames(left, tracked, into);\n break;\n }\n }\n}\n\nfunction collectHoistedVarNames(\n body: EstreeNode[],\n tracked: Set<string>,\n): Set<string> {\n const found = new Set<string>();\n\n function scanStatements(stmts: EstreeNode[]): void {\n for (const stmt of stmts) {\n if (\n stmt.type === \"VariableDeclaration\" &&\n (stmt.kind as string) === \"var\"\n ) {\n const decls = stmt.declarations as EstreeNode[] | undefined;\n if (!Array.isArray(decls)) continue;\n for (const d of decls) {\n const id = d.id as EstreeNode | undefined;\n if (isEstreeNode(id)) collectPatternNames(id, tracked, found);\n }\n }\n // Recurse into blocks but NOT into nested functions\n if (stmt.type === \"BlockStatement\" || stmt.type === \"SwitchStatement\") {\n const inner = (stmt.body ?? stmt.cases) as EstreeNode[] | undefined;\n if (Array.isArray(inner)) scanStatements(inner);\n }\n if (stmt.type === \"IfStatement\") {\n if (isEstreeNode(stmt.consequent)) scanStatements([stmt.consequent]);\n if (isEstreeNode(stmt.alternate)) scanStatements([stmt.alternate]);\n }\n if (\n stmt.type === \"ForStatement\" ||\n stmt.type === \"ForInStatement\" ||\n stmt.type === \"ForOfStatement\" ||\n stmt.type === \"WhileStatement\" ||\n stmt.type === \"DoWhileStatement\"\n ) {\n if (isEstreeNode(stmt.body)) scanStatements([stmt.body]);\n if (isEstreeNode(stmt.init)) scanStatements([stmt.init]);\n if (isEstreeNode(stmt.left)) scanStatements([stmt.left]);\n }\n if (stmt.type === \"TryStatement\") {\n if (isEstreeNode(stmt.block)) scanStatements([stmt.block]);\n if (isEstreeNode(stmt.handler)) {\n const handlerBody = stmt.handler.body as EstreeNode | undefined;\n if (isEstreeNode(handlerBody)) scanStatements([handlerBody]);\n }\n if (isEstreeNode(stmt.finalizer)) scanStatements([stmt.finalizer]);\n }\n if (stmt.type === \"LabeledStatement\" && isEstreeNode(stmt.body)) {\n scanStatements([stmt.body]);\n }\n if (stmt.type === \"SwitchCase\") {\n const consequent = stmt.consequent as EstreeNode[] | undefined;\n if (Array.isArray(consequent)) scanStatements(consequent);\n }\n }\n }\n\n scanStatements(body);\n return found;\n}\n\nfunction collectBlockScopedNames(\n body: EstreeNode[],\n tracked: Set<string>,\n): Set<string> {\n const found = new Set<string>();\n for (const stmt of body) {\n if (\n stmt.type === \"FunctionDeclaration\" ||\n stmt.type === \"ClassDeclaration\"\n ) {\n const id = stmt.id as EstreeNode | undefined;\n if (id?.type === \"Identifier\" && tracked.has(id.name as string))\n found.add(id.name as string);\n }\n if (\n stmt.type === \"VariableDeclaration\" &&\n ((stmt.kind as string) === \"let\" || (stmt.kind as string) === \"const\")\n ) {\n const decls = stmt.declarations as EstreeNode[] | undefined;\n if (Array.isArray(decls)) {\n for (const d of decls) {\n const id = d.id as EstreeNode | undefined;\n if (isEstreeNode(id)) collectPatternNames(id, tracked, found);\n }\n }\n }\n }\n return found;\n}\n\nfunction collectEdits(\n ast: EstreeNode,\n localNames: Set<string>,\n warn: (msg: string) => void,\n): TextEdit[] {\n const edits: TextEdit[] = [];\n const scopeStack: { names: Set<string>; isFunction: boolean }[] = [\n { names: new Set(), isFunction: true },\n ];\n\n function isShadowed(name: string): boolean {\n for (let i = scopeStack.length - 1; i >= 1; i--) {\n if (scopeStack[i].names.has(name)) return true;\n }\n return false;\n }\n\n function nearestFunctionScope(): { names: Set<string>; isFunction: boolean } {\n for (let i = scopeStack.length - 1; i >= 0; i--) {\n if (scopeStack[i].isFunction) return scopeStack[i];\n }\n return scopeStack[0];\n }\n\n function getFunctionBody(node: EstreeNode): EstreeNode[] | null {\n const body = node.body as EstreeNode | EstreeNode[] | undefined;\n if (isEstreeNode(body) && body.type === \"BlockStatement\") {\n const stmts = body.body as EstreeNode[] | undefined;\n return Array.isArray(stmts) ? stmts : null;\n }\n return null;\n }\n\n walk(\n ast,\n (node, parent) => {\n // Push scope\n if (SCOPE_TYPES.has(node.type)) {\n const isFunction =\n node.type === \"FunctionDeclaration\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"ArrowFunctionExpression\";\n\n const scope = { names: new Set<string>(), isFunction };\n scopeStack.push(scope);\n\n // Prescan for hoisting\n if (isFunction) {\n const body = getFunctionBody(node);\n if (body) {\n const hoisted = collectHoistedVarNames(body, localNames);\n for (const n of hoisted) scope.names.add(n);\n }\n // Add function params\n const params = node.params as EstreeNode[] | undefined;\n if (Array.isArray(params)) {\n for (const p of params) {\n collectPatternNames(p, localNames, scope.names);\n }\n }\n // Named FunctionExpression id is only visible inside its own body\n if (node.type === \"FunctionExpression\") {\n const id = node.id as EstreeNode | undefined;\n if (id?.type === \"Identifier\" && localNames.has(id.name as string))\n scope.names.add(id.name as string);\n }\n }\n\n // Named ClassExpression id is only visible inside the class body\n if (node.type === \"ClassExpression\") {\n const id = node.id as EstreeNode | undefined;\n if (id?.type === \"Identifier\" && localNames.has(id.name as string))\n scope.names.add(id.name as string);\n }\n\n if (node.type === \"BlockStatement\") {\n const body = node.body as EstreeNode[] | undefined;\n if (Array.isArray(body)) {\n const scoped = collectBlockScopedNames(body, localNames);\n for (const n of scoped) scope.names.add(n);\n }\n }\n\n if (node.type === \"SwitchStatement\") {\n const cases = node.cases as EstreeNode[] | undefined;\n if (Array.isArray(cases)) {\n const stmts: EstreeNode[] = [];\n for (const c of cases) {\n const consequent = c.consequent as EstreeNode[] | undefined;\n if (Array.isArray(consequent)) stmts.push(...consequent);\n }\n const scoped = collectBlockScopedNames(stmts, localNames);\n for (const n of scoped) scope.names.add(n);\n }\n }\n\n if (node.type === \"CatchClause\") {\n const param = node.param as EstreeNode | undefined;\n if (isEstreeNode(param))\n collectPatternNames(param, localNames, scope.names);\n }\n }\n\n // Track variable declarations\n if (node.type === \"VariableDeclarator\" && parent) {\n const id = node.id as EstreeNode | undefined;\n if (isEstreeNode(id)) {\n const kind = (parent as EstreeNode).kind as string | undefined;\n const target =\n kind === \"var\"\n ? nearestFunctionScope().names\n : kind === \"let\" || kind === \"const\"\n ? scopeStack[scopeStack.length - 1].names\n : null;\n if (target) collectPatternNames(id, localNames, target);\n }\n }\n\n // Track function declarations (name goes to enclosing scope)\n if (node.type === \"FunctionDeclaration\") {\n const id = node.id as EstreeNode | undefined;\n if (id?.type === \"Identifier\" && localNames.has(id.name as string)) {\n const target =\n scopeStack.length >= 2\n ? scopeStack[scopeStack.length - 2]\n : scopeStack[0];\n target.names.add(id.name as string);\n }\n }\n\n // Track class declarations (block-scoped, name goes to current scope)\n if (node.type === \"ClassDeclaration\") {\n const id = node.id as EstreeNode | undefined;\n if (id?.type === \"Identifier\" && localNames.has(id.name as string)) {\n scopeStack[scopeStack.length - 1].names.add(id.name as string);\n }\n }\n\n // Detect call sites\n if (node.type === \"CallExpression\") {\n const callee = node.callee as EstreeNode | undefined;\n if (\n callee?.type === \"Identifier\" &&\n localNames.has(callee.name as string) &&\n !isShadowed(callee.name as string)\n ) {\n if (typeof callee.end !== \"number\" || typeof node.end !== \"number\")\n return;\n\n const args = node.arguments as EstreeNode[] | undefined;\n if (!Array.isArray(args) || args.length === 0) {\n // No args: replace entire arg list\n edits.push({\n start: callee.end,\n end: node.end,\n replacement: '({ workerFile: \"__SATORI_WORKER_FILE__\" })',\n });\n } else if (args[0].type === \"ObjectExpression\") {\n if (typeof args[0].start !== \"number\") return;\n edits.push({\n start: args[0].start + 1,\n end: args[0].start + 1,\n replacement: ' workerFile: \"__SATORI_WORKER_FILE__\",',\n });\n } else {\n warn(\n `satoriPoolPlugin: unexpected first argument type \"${args[0].type}\" in createSatoriPool() call`,\n );\n }\n }\n }\n },\n (node) => {\n if (SCOPE_TYPES.has(node.type)) {\n scopeStack.pop();\n }\n },\n );\n\n return edits;\n}\n\nfunction applyEdits(code: string, edits: TextEdit[]): string {\n const sorted = [...edits].sort((a, b) => b.start - a.start);\n let result = code;\n for (const { start, end, replacement } of sorted) {\n result = result.slice(0, start) + replacement + result.slice(end);\n }\n return result;\n}\n\n/**\n * Vite plugin that bundles the `@effing/satori` worker into the SSR output and\n * rewrites `createSatoriPool()` calls to point at it.\n *\n * **This plugin is required for production SSR builds.** Without it the worker\n * path resolved via `import.meta.url` breaks after Vite bundles the pool code,\n * because the URL points at the build output directory instead of `node_modules`.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { satoriPoolPlugin } from \"@effing/satori/vite\";\n *\n * export default defineConfig({\n * plugins: [satoriPoolPlugin()],\n * });\n * ```\n */\nexport function satoriPoolPlugin(): Plugin {\n let resolvedConfig: ResolvedConfig;\n\n return {\n name: \"@effing/satori:worker\",\n apply: \"build\",\n\n config() {\n return { ssr: { external: [\"tinypool\"] } };\n },\n\n configResolved(config) {\n resolvedConfig = config;\n },\n\n transform(code, id, options) {\n if (!options?.ssr || !code.includes(\"createSatoriPool\")) return;\n\n let ast: EstreeNode;\n try {\n ast = this.parse(code) as unknown as EstreeNode;\n } catch {\n if (!id.startsWith(\"\\0\")) {\n this.warn(`satoriPoolPlugin: skipped AST transform for ${id}`);\n }\n return;\n }\n\n const localNames = findImportedLocalNames(ast);\n if (localNames.size === 0) return;\n\n const edits = collectEdits(ast, localNames, (msg) =>\n this.warn(`${msg} (${id})`),\n );\n if (edits.length === 0) return;\n return { code: applyEdits(code, edits), map: null };\n },\n\n renderChunk(code, chunk) {\n const placeholder = '\"__SATORI_WORKER_FILE__\"';\n if (!code.includes(placeholder)) return;\n\n const chunkDir = posix.dirname(chunk.fileName);\n const relToRoot = chunkDir === \".\" ? \".\" : posix.relative(chunkDir, \".\");\n const workerRel = posix.join(relToRoot, \"satori-worker.js\");\n const expr = `import.meta.dirname + ${JSON.stringify(\"/\" + workerRel)}`;\n\n return {\n code: code.replaceAll(placeholder, expr),\n map: null,\n };\n },\n\n async writeBundle(outputOptions) {\n if (!resolvedConfig.build.ssr) return;\n\n const workerEntry = fileURLToPath(\n new URL(\"../worker/index.js\", import.meta.url),\n );\n const outDir = outputOptions.dir ?? resolvedConfig.build.outDir;\n\n const { build } = await import(\"vite\");\n await build({\n configFile: false,\n logLevel: \"silent\",\n build: {\n write: true,\n emptyOutDir: false,\n outDir,\n lib: {\n entry: workerEntry,\n formats: [\"es\"],\n fileName: () => \"satori-worker.js\",\n },\n rollupOptions: {\n external: [\"@resvg/resvg-js\"],\n },\n },\n });\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/vite/index.ts"],"sourcesContent":["import { posix } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { Plugin, ResolvedConfig } from \"vite\";\n\ninterface EstreeNode {\n type: string;\n start: number;\n end: number;\n [key: string]: unknown;\n}\n\ninterface TextEdit {\n start: number;\n end: number;\n replacement: string;\n}\n\nconst TARGET_MODULES = new Set([\"@effing/satori/pool\", \"@effing/satori\"]);\n\nconst SCOPE_TYPES = new Set([\n \"FunctionDeclaration\",\n \"FunctionExpression\",\n \"ArrowFunctionExpression\",\n \"ClassExpression\",\n \"BlockStatement\",\n \"ForStatement\",\n \"ForInStatement\",\n \"ForOfStatement\",\n \"SwitchStatement\",\n \"CatchClause\",\n]);\n\nfunction isEstreeNode(value: unknown): value is EstreeNode {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as EstreeNode).type === \"string\" &&\n typeof (value as EstreeNode).start === \"number\" &&\n typeof (value as EstreeNode).end === \"number\"\n );\n}\n\nfunction walk(\n node: EstreeNode,\n enter: (node: EstreeNode, parent: EstreeNode | null) => void,\n leave?: (node: EstreeNode, parent: EstreeNode | null) => void,\n parent: EstreeNode | null = null,\n): void {\n enter(node, parent);\n for (const key of Object.keys(node)) {\n if (key === \"type\" || key === \"start\" || key === \"end\") continue;\n const val = node[key];\n if (isEstreeNode(val)) {\n walk(val, enter, leave, node);\n } else if (Array.isArray(val)) {\n for (const item of val) {\n if (isEstreeNode(item)) {\n walk(item, enter, leave, node);\n }\n }\n }\n }\n leave?.(node, parent);\n}\n\nfunction findImportedLocalNames(ast: EstreeNode): Set<string> {\n const names = new Set<string>();\n const body = ast.body as EstreeNode[] | undefined;\n if (!Array.isArray(body)) return names;\n\n for (const node of body) {\n if (\n node.type !== \"ImportDeclaration\" ||\n !isEstreeNode(node.source) ||\n !TARGET_MODULES.has(node.source.value as string)\n )\n continue;\n if ((node.importKind as string | undefined) === \"type\") continue;\n\n const specifiers = node.specifiers as EstreeNode[] | undefined;\n if (!Array.isArray(specifiers)) continue;\n\n for (const spec of specifiers) {\n if (spec.type !== \"ImportSpecifier\") continue;\n if ((spec.importKind as string | undefined) === \"type\") continue;\n const imported = spec.imported as EstreeNode | undefined;\n const local = spec.local as EstreeNode | undefined;\n if (imported && (imported.name as string) === \"createSatoriPool\" && local)\n names.add(local.name as string);\n }\n }\n return names;\n}\n\nfunction collectPatternNames(\n pattern: EstreeNode,\n tracked: Set<string>,\n into: Set<string>,\n): void {\n switch (pattern.type) {\n case \"Identifier\":\n if (tracked.has(pattern.name as string)) into.add(pattern.name as string);\n break;\n case \"ObjectPattern\": {\n const props = pattern.properties as EstreeNode[] | undefined;\n if (Array.isArray(props)) {\n for (const prop of props) {\n if (prop.type === \"RestElement\") {\n const arg = prop.argument as EstreeNode | undefined;\n if (isEstreeNode(arg)) collectPatternNames(arg, tracked, into);\n } else {\n // Property node — use value if present, fall back to key for\n // robustness across parser node shapes (shorthand properties)\n const target = (prop.value ?? prop.key) as EstreeNode | undefined;\n if (isEstreeNode(target))\n collectPatternNames(target, tracked, into);\n }\n }\n }\n break;\n }\n case \"ArrayPattern\": {\n const elems = pattern.elements as (EstreeNode | null)[] | undefined;\n if (Array.isArray(elems)) {\n for (const elem of elems) {\n if (isEstreeNode(elem)) collectPatternNames(elem, tracked, into);\n }\n }\n break;\n }\n case \"RestElement\": {\n const arg = pattern.argument as EstreeNode | undefined;\n if (isEstreeNode(arg)) collectPatternNames(arg, tracked, into);\n break;\n }\n case \"AssignmentPattern\": {\n const left = pattern.left as EstreeNode | undefined;\n if (isEstreeNode(left)) collectPatternNames(left, tracked, into);\n break;\n }\n }\n}\n\nfunction collectHoistedVarNames(\n body: EstreeNode[],\n tracked: Set<string>,\n): Set<string> {\n const found = new Set<string>();\n\n function scanStatements(stmts: EstreeNode[]): void {\n for (const stmt of stmts) {\n if (\n stmt.type === \"VariableDeclaration\" &&\n (stmt.kind as string) === \"var\"\n ) {\n const decls = stmt.declarations as EstreeNode[] | undefined;\n if (!Array.isArray(decls)) continue;\n for (const d of decls) {\n const id = d.id as EstreeNode | undefined;\n if (isEstreeNode(id)) collectPatternNames(id, tracked, found);\n }\n }\n // Recurse into blocks but NOT into nested functions\n if (stmt.type === \"BlockStatement\" || stmt.type === \"SwitchStatement\") {\n const inner = (stmt.body ?? stmt.cases) as EstreeNode[] | undefined;\n if (Array.isArray(inner)) scanStatements(inner);\n }\n if (stmt.type === \"IfStatement\") {\n if (isEstreeNode(stmt.consequent)) scanStatements([stmt.consequent]);\n if (isEstreeNode(stmt.alternate)) scanStatements([stmt.alternate]);\n }\n if (\n stmt.type === \"ForStatement\" ||\n stmt.type === \"ForInStatement\" ||\n stmt.type === \"ForOfStatement\" ||\n stmt.type === \"WhileStatement\" ||\n stmt.type === \"DoWhileStatement\"\n ) {\n if (isEstreeNode(stmt.body)) scanStatements([stmt.body]);\n if (isEstreeNode(stmt.init)) scanStatements([stmt.init]);\n if (isEstreeNode(stmt.left)) scanStatements([stmt.left]);\n }\n if (stmt.type === \"TryStatement\") {\n if (isEstreeNode(stmt.block)) scanStatements([stmt.block]);\n if (isEstreeNode(stmt.handler)) {\n const handlerBody = stmt.handler.body as EstreeNode | undefined;\n if (isEstreeNode(handlerBody)) scanStatements([handlerBody]);\n }\n if (isEstreeNode(stmt.finalizer)) scanStatements([stmt.finalizer]);\n }\n if (stmt.type === \"LabeledStatement\" && isEstreeNode(stmt.body)) {\n scanStatements([stmt.body]);\n }\n if (stmt.type === \"SwitchCase\") {\n const consequent = stmt.consequent as EstreeNode[] | undefined;\n if (Array.isArray(consequent)) scanStatements(consequent);\n }\n }\n }\n\n scanStatements(body);\n return found;\n}\n\nfunction collectBlockScopedNames(\n body: EstreeNode[],\n tracked: Set<string>,\n): Set<string> {\n const found = new Set<string>();\n for (const stmt of body) {\n if (\n stmt.type === \"FunctionDeclaration\" ||\n stmt.type === \"ClassDeclaration\"\n ) {\n const id = stmt.id as EstreeNode | undefined;\n if (id?.type === \"Identifier\" && tracked.has(id.name as string))\n found.add(id.name as string);\n }\n if (\n stmt.type === \"VariableDeclaration\" &&\n ((stmt.kind as string) === \"let\" || (stmt.kind as string) === \"const\")\n ) {\n const decls = stmt.declarations as EstreeNode[] | undefined;\n if (Array.isArray(decls)) {\n for (const d of decls) {\n const id = d.id as EstreeNode | undefined;\n if (isEstreeNode(id)) collectPatternNames(id, tracked, found);\n }\n }\n }\n }\n return found;\n}\n\nfunction collectEdits(\n ast: EstreeNode,\n localNames: Set<string>,\n warn: (msg: string) => void,\n): TextEdit[] {\n const edits: TextEdit[] = [];\n const scopeStack: { names: Set<string>; isFunction: boolean }[] = [\n { names: new Set(), isFunction: true },\n ];\n\n function isShadowed(name: string): boolean {\n for (let i = scopeStack.length - 1; i >= 1; i--) {\n if (scopeStack[i].names.has(name)) return true;\n }\n return false;\n }\n\n function nearestFunctionScope(): { names: Set<string>; isFunction: boolean } {\n for (let i = scopeStack.length - 1; i >= 0; i--) {\n if (scopeStack[i].isFunction) return scopeStack[i];\n }\n return scopeStack[0];\n }\n\n function getFunctionBody(node: EstreeNode): EstreeNode[] | null {\n const body = node.body as EstreeNode | EstreeNode[] | undefined;\n if (isEstreeNode(body) && body.type === \"BlockStatement\") {\n const stmts = body.body as EstreeNode[] | undefined;\n return Array.isArray(stmts) ? stmts : null;\n }\n return null;\n }\n\n walk(\n ast,\n (node, parent) => {\n // Push scope\n if (SCOPE_TYPES.has(node.type)) {\n const isFunction =\n node.type === \"FunctionDeclaration\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"ArrowFunctionExpression\";\n\n const scope = { names: new Set<string>(), isFunction };\n scopeStack.push(scope);\n\n // Prescan for hoisting\n if (isFunction) {\n const body = getFunctionBody(node);\n if (body) {\n const hoisted = collectHoistedVarNames(body, localNames);\n for (const n of hoisted) scope.names.add(n);\n }\n // Add function params\n const params = node.params as EstreeNode[] | undefined;\n if (Array.isArray(params)) {\n for (const p of params) {\n collectPatternNames(p, localNames, scope.names);\n }\n }\n // Named FunctionExpression id is only visible inside its own body\n if (node.type === \"FunctionExpression\") {\n const id = node.id as EstreeNode | undefined;\n if (id?.type === \"Identifier\" && localNames.has(id.name as string))\n scope.names.add(id.name as string);\n }\n }\n\n // Named ClassExpression id is only visible inside the class body\n if (node.type === \"ClassExpression\") {\n const id = node.id as EstreeNode | undefined;\n if (id?.type === \"Identifier\" && localNames.has(id.name as string))\n scope.names.add(id.name as string);\n }\n\n if (node.type === \"BlockStatement\") {\n const body = node.body as EstreeNode[] | undefined;\n if (Array.isArray(body)) {\n const scoped = collectBlockScopedNames(body, localNames);\n for (const n of scoped) scope.names.add(n);\n }\n }\n\n if (node.type === \"SwitchStatement\") {\n const cases = node.cases as EstreeNode[] | undefined;\n if (Array.isArray(cases)) {\n const stmts: EstreeNode[] = [];\n for (const c of cases) {\n const consequent = c.consequent as EstreeNode[] | undefined;\n if (Array.isArray(consequent)) stmts.push(...consequent);\n }\n const scoped = collectBlockScopedNames(stmts, localNames);\n for (const n of scoped) scope.names.add(n);\n }\n }\n\n if (node.type === \"CatchClause\") {\n const param = node.param as EstreeNode | undefined;\n if (isEstreeNode(param))\n collectPatternNames(param, localNames, scope.names);\n }\n }\n\n // Track variable declarations\n if (node.type === \"VariableDeclarator\" && parent) {\n const id = node.id as EstreeNode | undefined;\n if (isEstreeNode(id)) {\n const kind = (parent as EstreeNode).kind as string | undefined;\n const target =\n kind === \"var\"\n ? nearestFunctionScope().names\n : kind === \"let\" || kind === \"const\"\n ? scopeStack[scopeStack.length - 1].names\n : null;\n if (target) collectPatternNames(id, localNames, target);\n }\n }\n\n // Track function declarations (name goes to enclosing scope)\n if (node.type === \"FunctionDeclaration\") {\n const id = node.id as EstreeNode | undefined;\n if (id?.type === \"Identifier\" && localNames.has(id.name as string)) {\n const target =\n scopeStack.length >= 2\n ? scopeStack[scopeStack.length - 2]\n : scopeStack[0];\n target.names.add(id.name as string);\n }\n }\n\n // Track class declarations (block-scoped, name goes to current scope)\n if (node.type === \"ClassDeclaration\") {\n const id = node.id as EstreeNode | undefined;\n if (id?.type === \"Identifier\" && localNames.has(id.name as string)) {\n scopeStack[scopeStack.length - 1].names.add(id.name as string);\n }\n }\n\n // Detect call sites\n if (node.type === \"CallExpression\") {\n const callee = node.callee as EstreeNode | undefined;\n if (\n callee?.type === \"Identifier\" &&\n localNames.has(callee.name as string) &&\n !isShadowed(callee.name as string)\n ) {\n if (typeof callee.end !== \"number\" || typeof node.end !== \"number\")\n return;\n\n const args = node.arguments as EstreeNode[] | undefined;\n if (!Array.isArray(args) || args.length === 0) {\n // No args: replace entire arg list\n edits.push({\n start: callee.end,\n end: node.end,\n replacement: '({ workerFile: \"__SATORI_WORKER_FILE__\" })',\n });\n } else if (args[0].type === \"ObjectExpression\") {\n if (typeof args[0].start !== \"number\") return;\n edits.push({\n start: args[0].start + 1,\n end: args[0].start + 1,\n replacement: ' workerFile: \"__SATORI_WORKER_FILE__\",',\n });\n } else {\n warn(\n `satoriPoolPlugin: unexpected first argument type \"${args[0].type}\" in createSatoriPool() call`,\n );\n }\n }\n }\n },\n (node) => {\n if (SCOPE_TYPES.has(node.type)) {\n scopeStack.pop();\n }\n },\n );\n\n return edits;\n}\n\nfunction applyEdits(code: string, edits: TextEdit[]): string {\n const sorted = [...edits].sort((a, b) => b.start - a.start);\n let result = code;\n for (const { start, end, replacement } of sorted) {\n result = result.slice(0, start) + replacement + result.slice(end);\n }\n return result;\n}\n\n/**\n * Vite plugin that bundles the `@effing/satori` worker into the SSR output and\n * rewrites `createSatoriPool()` calls to point at it.\n *\n * **This plugin is required for production SSR builds.** Without it the worker\n * path resolved via `import.meta.url` breaks after Vite bundles the pool code,\n * because the URL points at the build output directory instead of `node_modules`.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { satoriPoolPlugin } from \"@effing/satori/vite\";\n *\n * export default defineConfig({\n * plugins: [satoriPoolPlugin()],\n * });\n * ```\n */\nexport function satoriPoolPlugin(): Plugin {\n let resolvedConfig: ResolvedConfig;\n\n return {\n name: \"@effing/satori:worker\",\n apply: \"build\",\n\n configResolved(config) {\n resolvedConfig = config;\n },\n\n transform(code, id, options) {\n if (!options?.ssr || !code.includes(\"createSatoriPool\")) return;\n\n let ast: EstreeNode;\n try {\n ast = this.parse(code) as unknown as EstreeNode;\n } catch {\n if (!id.startsWith(\"\\0\")) {\n this.warn(`satoriPoolPlugin: skipped AST transform for ${id}`);\n }\n return;\n }\n\n const localNames = findImportedLocalNames(ast);\n if (localNames.size === 0) return;\n\n const edits = collectEdits(ast, localNames, (msg) =>\n this.warn(`${msg} (${id})`),\n );\n if (edits.length === 0) return;\n return { code: applyEdits(code, edits), map: null };\n },\n\n renderChunk(code, chunk) {\n const placeholder = '\"__SATORI_WORKER_FILE__\"';\n if (!code.includes(placeholder)) return;\n\n const chunkDir = posix.dirname(chunk.fileName);\n const relToRoot = chunkDir === \".\" ? \".\" : posix.relative(chunkDir, \".\");\n const workerRel = posix.join(relToRoot, \"satori-worker.js\");\n const expr = `import.meta.dirname + ${JSON.stringify(\"/\" + workerRel)}`;\n\n return {\n code: code.replaceAll(placeholder, expr),\n map: null,\n };\n },\n\n async writeBundle(outputOptions) {\n if (!resolvedConfig.build.ssr) return;\n\n const workerEntry = fileURLToPath(\n new URL(\"../worker/index.js\", import.meta.url),\n );\n const outDir = outputOptions.dir ?? resolvedConfig.build.outDir;\n\n const { build } = await import(\"vite\");\n await build({\n configFile: false,\n logLevel: \"silent\",\n build: {\n write: true,\n emptyOutDir: false,\n outDir,\n lib: {\n entry: workerEntry,\n formats: [\"es\"],\n fileName: () => \"satori-worker.js\",\n },\n rollupOptions: {\n external: [\"@resvg/resvg-js\"],\n },\n },\n });\n },\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effing/satori",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "description": "Render JSX to PNG using Satori with emoji support",
5
5
  "type": "module",
6
6
  "exports": {
@@ -25,8 +25,7 @@
25
25
  "dist"
26
26
  ],
27
27
  "dependencies": {
28
- "satori": ">=0.10.0 <1.0.0",
29
- "tinypool": "^2.0.0"
28
+ "satori": ">=0.10.0 <1.0.0"
30
29
  },
31
30
  "devDependencies": {
32
31
  "@types/react": "^19.0.0",