-
Notifications
You must be signed in to change notification settings - Fork 7
/
module.ts
120 lines (110 loc) · 4.09 KB
/
module.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
* Utility functions for working with JavaScript modules.
* @module
*/
import { isDeno, isNodeLike } from "./env.ts";
import { createReadableStream, readFile } from "./fs.ts";
import { isFileUrl, isUrl, resolve, toFsPath } from "./path.ts";
import { importWasm as webImportWasm } from "./module/web.ts";
export * from "./module/web.ts";
const wasmCache = new Map<string | WebAssembly.Module, Promise<WebAssembly.Exports>>();
/**
* Imports a WebAssembly module from a URL or file path (relative to the current
* working directory if not absolute), or from a {@link WebAssembly.Module}
* object, returns the symbols exported by the module.
*
* This function is available in both the browser and server runtimes such as
* Node.js, Deno, Bun and Cloudflare Workers.
*
* This function uses cache to avoid loading the same module multiple times.
*
* @param imports An object containing the values to be imported by the
* WebAssembly module, allowing passing values from JavaScript to WebAssembly.
*
* ```ts
* import { importWasm } from "@ayonli/jsext/module";
*
* const { timestamp } = await importWasm<{
* timestamp: () => number; // function exported by the WebAssembly module
* }>("./examples/convert.wasm", {
* // JavaScript module and function passed into the WebAssembly module
* Date: { now: Date.now },
* });
*
* console.log("The current timestamp is:", timestamp());
* ```
*
* The corresponding WebAssembly module `convert.wasm` in text format looks like
* this:
*
* ```wat
* ;; examples/convert.wat
* (module
* (import "Date" "now" (func $now (result i32)))
* (func (export "timestamp") (result i32)
* call $now
* ;; Convert to seconds
* i32.const 1000
* i32.div_u
* )
* )
* ```
*
* NOTE: In Cloudflare Workers, this function cannot access the file system, we
* need to import the WebAssembly module with a `import` statement or with the
* `import()` function before we can use it. For example:
*
* ```ts
* // In Cloudflare Workers
* import { importWasm } from "@ayonli/jsext/module";
* import wasmModule from "./examples/convert.wasm";
*
* const { timestamp } = await importWasm<{
* timestamp: () => number; // function exported by the WebAssembly module
* }>(wasmModule, {
* // JavaScript module and function passed into the WebAssembly module
* Date: { now: Date.now },
* });
*
* console.log("The current timestamp is:", timestamp());
* ```
*
* NOTE: Deno v2.1+ has built-in support for importing WebAssembly modules as
* JavaScript modules, it's recommended to use the built-in `import` statement
* to import WebAssembly modules directly for Deno programs.
*/
export async function importWasm<T extends WebAssembly.Exports>(
module: string | URL | WebAssembly.Module,
imports: WebAssembly.Imports | undefined = undefined
): Promise<T> {
if ((!isNodeLike && !isDeno) ||
module instanceof WebAssembly.Module ||
(module instanceof URL && module.protocol !== "file:") ||
(typeof module === "string" && isUrl(module) && !isFileUrl(module))
) {
return await webImportWasm<T>(module, imports);
}
const src = typeof module === "string" ? module : module.href;
const filename = isFileUrl(src) ? toFsPath(src) : resolve(src);
if (typeof WebAssembly.instantiateStreaming === "function") {
let cache = wasmCache.get(filename);
if (!cache) {
const stream = createReadableStream(filename);
cache = Promise.resolve(new Response(stream, {
headers: { "Content-Type": "application/wasm" },
})).then(res => WebAssembly.instantiateStreaming(res, imports))
.then(ins => ins.instance.exports);
wasmCache.set(filename, cache);
}
return await cache as T;
} else {
let cache = wasmCache.get(filename);
if (!cache) {
cache = readFile(filename)
.then(bytes => WebAssembly.instantiate(bytes, imports))
.then(ins => ins.instance.exports);
wasmCache.set(filename, cache);
}
return await cache as T;
}
}