wasm-bindgen-lite 0.1.0
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/Cargo.lock +27 -0
- package/Cargo.toml +23 -0
- package/LICENSE +21 -0
- package/README.md +313 -0
- package/bin/wasm-bindgen-lite.js +101 -0
- package/package.json +59 -0
- package/scripts/build.js +91 -0
- package/scripts/test-examples.sh +25 -0
- package/scripts/test.js +48 -0
- package/src/cli/build.js +83 -0
- package/src/cli/config.js +211 -0
- package/src/cli/emit.js +423 -0
- package/src/cli/index.js +79 -0
- package/src/cli/pkg.js +52 -0
- package/src/js/browser-inline.js +19 -0
- package/src/js/browser.js +30 -0
- package/src/js/core.js +57 -0
- package/src/js/node-inline.js +19 -0
- package/src/js/node.js +26 -0
- package/src/js/util.js +14 -0
- package/src/lib.rs +72 -0
package/src/cli/emit.js
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
|
|
5
|
+
const UTIL_PATH = fileURLToPath(new URL('../js/util.js', import.meta.url))
|
|
6
|
+
|
|
7
|
+
function createCore({ exportsList, autoInit, stream }) {
|
|
8
|
+
const needsEnsure = autoInit === 'lazy'
|
|
9
|
+
const toBytesHelper = `function toBytes(input) {
|
|
10
|
+
if (input instanceof Uint8Array) return input;
|
|
11
|
+
if (ArrayBuffer.isView(input)) {
|
|
12
|
+
return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
|
|
13
|
+
}
|
|
14
|
+
if (input instanceof ArrayBuffer) return new Uint8Array(input);
|
|
15
|
+
throw new TypeError("Expected a TypedArray or ArrayBuffer");
|
|
16
|
+
}
|
|
17
|
+
`
|
|
18
|
+
const scalarSizeHelper = `function scalarSize(type) {
|
|
19
|
+
switch (type) {
|
|
20
|
+
case "f64": return 8;
|
|
21
|
+
case "f32":
|
|
22
|
+
case "i32":
|
|
23
|
+
case "u32": return 4;
|
|
24
|
+
case "i16":
|
|
25
|
+
case "u16": return 2;
|
|
26
|
+
case "i8":
|
|
27
|
+
case "u8": return 1;
|
|
28
|
+
case "u32_array":
|
|
29
|
+
case "i32_array":
|
|
30
|
+
case "f32_array": return 1024 * 1024; // Default large buffer for arrays, or we can improve this
|
|
31
|
+
default: return 0;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
`
|
|
35
|
+
const decodeHelper = `function decodeReturn(view, type) {
|
|
36
|
+
switch (type) {
|
|
37
|
+
case "f32": return view.getFloat32(0, true);
|
|
38
|
+
case "f64": return view.getFloat64(0, true);
|
|
39
|
+
case "i32": return view.getInt32(0, true);
|
|
40
|
+
case "u32": return view.getUint32(0, true);
|
|
41
|
+
case "i16": return view.getInt16(0, true);
|
|
42
|
+
case "u16": return view.getUint16(0, true);
|
|
43
|
+
case "i8": return view.getInt8(0);
|
|
44
|
+
case "u8": return view.getUint8(0);
|
|
45
|
+
case "u32_array": return new Uint32Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
|
|
46
|
+
case "i32_array": return new Int32Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
|
|
47
|
+
case "f32_array": return new Float32Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
|
|
48
|
+
default: return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
`
|
|
52
|
+
|
|
53
|
+
const wrappers = exportsList
|
|
54
|
+
.map(({ abi, name, return: retType, reuseBuffer }) => {
|
|
55
|
+
const returnType = retType || 'bytes'
|
|
56
|
+
const fnName = name || abi
|
|
57
|
+
const outSizeExpr =
|
|
58
|
+
returnType === 'bytes'
|
|
59
|
+
? 'Math.max(len, 4)'
|
|
60
|
+
: `(scalarSize('${returnType}') || 4)`
|
|
61
|
+
|
|
62
|
+
const stateVars = reuseBuffer
|
|
63
|
+
? `let _${fnName}_in = { ptr: 0, len: 0 };
|
|
64
|
+
let _${fnName}_out = { ptr: 0, len: 0 };`
|
|
65
|
+
: ''
|
|
66
|
+
|
|
67
|
+
const allocIn = reuseBuffer
|
|
68
|
+
? `if (_${fnName}_in.len < len) {
|
|
69
|
+
if (_${fnName}_in.ptr) free(_${fnName}_in.ptr, _${fnName}_in.len);
|
|
70
|
+
_${fnName}_in.ptr = alloc(len);
|
|
71
|
+
_${fnName}_in.len = len;
|
|
72
|
+
}
|
|
73
|
+
const inPtr = _${fnName}_in.ptr;`
|
|
74
|
+
: `const inPtr = alloc(len);`
|
|
75
|
+
|
|
76
|
+
const allocOut = reuseBuffer
|
|
77
|
+
? `if (_${fnName}_out.len < outLen) {
|
|
78
|
+
if (_${fnName}_out.ptr) free(_${fnName}_out.ptr, _${fnName}_out.len);
|
|
79
|
+
_${fnName}_out.ptr = alloc(outLen);
|
|
80
|
+
_${fnName}_out.len = outLen;
|
|
81
|
+
}
|
|
82
|
+
const outPtr = _${fnName}_out.ptr;`
|
|
83
|
+
: `const outPtr = alloc(outLen);`
|
|
84
|
+
|
|
85
|
+
const freeIn = reuseBuffer ? '' : `free(inPtr, len);`
|
|
86
|
+
const freeOut = reuseBuffer ? '' : `free(outPtr, outLen);`
|
|
87
|
+
|
|
88
|
+
const body = `
|
|
89
|
+
if (!_inst) throw new Error("WASM instance not initialized");
|
|
90
|
+
const view = toBytes(input);
|
|
91
|
+
const len = view.byteLength;
|
|
92
|
+
${allocIn}
|
|
93
|
+
memoryU8().set(view, inPtr);
|
|
94
|
+
const outLen = ${outSizeExpr};
|
|
95
|
+
${allocOut}
|
|
96
|
+
const written = _inst.exports.${abi}(
|
|
97
|
+
inPtr, len,
|
|
98
|
+
outPtr, outLen
|
|
99
|
+
);
|
|
100
|
+
if (written < 0) {
|
|
101
|
+
${reuseBuffer ? '' : `free(inPtr, len); free(outPtr, outLen);`}
|
|
102
|
+
throw new Error("${abi} failed: " + written);
|
|
103
|
+
}
|
|
104
|
+
${
|
|
105
|
+
returnType === 'bytes'
|
|
106
|
+
? `const result = memoryU8().slice(outPtr, outPtr + written);`
|
|
107
|
+
: `const retView = new DataView(memoryU8().buffer, outPtr, written);
|
|
108
|
+
const ret = decodeReturn(retView, "${returnType}");`
|
|
109
|
+
}
|
|
110
|
+
${freeIn}
|
|
111
|
+
${freeOut}
|
|
112
|
+
${returnType === 'bytes' ? 'return result;' : 'return ret;'}`
|
|
113
|
+
|
|
114
|
+
const wrapper = needsEnsure
|
|
115
|
+
? `async function ${fnName}(input) { await ensureReady(); ${body} }`
|
|
116
|
+
: `function ${fnName}(input) { ${body} }`
|
|
117
|
+
|
|
118
|
+
return `${stateVars}\n${wrapper}\nexport { ${fnName} };`
|
|
119
|
+
})
|
|
120
|
+
.join('\n\n')
|
|
121
|
+
|
|
122
|
+
const streamHelper = stream?.enable
|
|
123
|
+
? `
|
|
124
|
+
const __exports = { ${exportsList.map(({ name, abi }) => `${name || abi}: ${name || abi}`).join(', ')} };
|
|
125
|
+
|
|
126
|
+
export function createTransformStream(fnName = "${stream.export}") {
|
|
127
|
+
const fn = __exports[fnName];
|
|
128
|
+
if (!fn) throw new Error("Unknown export for streaming: " + fnName);
|
|
129
|
+
|
|
130
|
+
${
|
|
131
|
+
stream.delimiter !== null
|
|
132
|
+
? `let buffer = new Uint8Array(0);
|
|
133
|
+
const delimiter = ${stream.delimiter};`
|
|
134
|
+
: ''
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return new TransformStream({
|
|
138
|
+
async transform(chunk, controller) {
|
|
139
|
+
const bytes = toBytes(chunk);
|
|
140
|
+
const processed = ${needsEnsure ? 'await fn(bytes)' : 'fn(bytes)'};
|
|
141
|
+
|
|
142
|
+
${
|
|
143
|
+
stream.delimiter !== null
|
|
144
|
+
? `// Split and buffer
|
|
145
|
+
const combined = new Uint8Array(buffer.length + processed.length);
|
|
146
|
+
combined.set(buffer, 0);
|
|
147
|
+
combined.set(processed, buffer.length);
|
|
148
|
+
|
|
149
|
+
let start = 0;
|
|
150
|
+
for (let i = 0; i < combined.length; i += 1) {
|
|
151
|
+
if (combined[i] === delimiter) {
|
|
152
|
+
controller.enqueue(combined.subarray(start, i));
|
|
153
|
+
start = i + 1;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
buffer = combined.slice(start);`
|
|
157
|
+
: 'controller.enqueue(processed);'
|
|
158
|
+
}
|
|
159
|
+
}${
|
|
160
|
+
stream.delimiter !== null
|
|
161
|
+
? `,
|
|
162
|
+
flush(controller) {
|
|
163
|
+
if (buffer.length) controller.enqueue(buffer);
|
|
164
|
+
}`
|
|
165
|
+
: ''
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
`
|
|
170
|
+
: ''
|
|
171
|
+
|
|
172
|
+
const ensure = needsEnsure
|
|
173
|
+
? `
|
|
174
|
+
let _ready = null;
|
|
175
|
+
export function registerInit(fn) {
|
|
176
|
+
_initFn = fn;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function ensureReady() {
|
|
180
|
+
if (_ready) return _ready;
|
|
181
|
+
if (!_initFn) throw new Error("init not registered");
|
|
182
|
+
_ready = _initFn();
|
|
183
|
+
return _ready;
|
|
184
|
+
}
|
|
185
|
+
`
|
|
186
|
+
: `
|
|
187
|
+
export function registerInit(fn) {
|
|
188
|
+
_initFn = fn;
|
|
189
|
+
}
|
|
190
|
+
`
|
|
191
|
+
|
|
192
|
+
return `let _inst = null;
|
|
193
|
+
let _memU8 = null;
|
|
194
|
+
let _initFn = null;
|
|
195
|
+
|
|
196
|
+
function refreshViews() {
|
|
197
|
+
_memU8 = new Uint8Array(_inst.exports.memory.buffer);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function setInstance(instance) {
|
|
201
|
+
_inst = instance;
|
|
202
|
+
refreshViews();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function wasmExports() {
|
|
206
|
+
return _inst.exports;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
${ensure}
|
|
210
|
+
|
|
211
|
+
export function memoryU8() {
|
|
212
|
+
if (_memU8 && _memU8.buffer !== _inst.exports.memory.buffer) refreshViews();
|
|
213
|
+
return _memU8;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function alloc(len) {
|
|
217
|
+
return _inst.exports.alloc_bytes(len) >>> 0;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function free(ptr, len) {
|
|
221
|
+
_inst.exports.free_bytes(ptr >>> 0, len >>> 0);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
${wrappers}
|
|
225
|
+
${scalarSizeHelper}
|
|
226
|
+
${decodeHelper}
|
|
227
|
+
${toBytesHelper}
|
|
228
|
+
${streamHelper}
|
|
229
|
+
`
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function createBrowserLoader({ name, autoInit, customJs, wasmDelivery }) {
|
|
233
|
+
const eager =
|
|
234
|
+
autoInit === 'eager'
|
|
235
|
+
? '\nregisterInit(init);\ninit();'
|
|
236
|
+
: '\nregisterInit(init);'
|
|
237
|
+
const exportFrom = customJs ? './custom.js' : './core.js'
|
|
238
|
+
|
|
239
|
+
let simdUrl, baseUrl
|
|
240
|
+
if (wasmDelivery.type === 'jsdelivr') {
|
|
241
|
+
const pkg = wasmDelivery.package
|
|
242
|
+
const ver = wasmDelivery.version
|
|
243
|
+
simdUrl = `https://cdn.jsdelivr.net/npm/${pkg}@${ver}/dist/wasm/${name}.simd.wasm`
|
|
244
|
+
baseUrl = `https://cdn.jsdelivr.net/npm/${pkg}@${ver}/dist/wasm/${name}.base.wasm`
|
|
245
|
+
} else {
|
|
246
|
+
simdUrl = `new URL("./wasm/${name}.simd.wasm", import.meta.url)`
|
|
247
|
+
baseUrl = `new URL("./wasm/${name}.base.wasm", import.meta.url)`
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return `import { setInstance, registerInit } from "./core.js";
|
|
251
|
+
import { instantiateWithFallback } from "./util.js";
|
|
252
|
+
|
|
253
|
+
const simdUrl = ${wasmDelivery.type === 'jsdelivr' ? `"${simdUrl}"` : simdUrl};
|
|
254
|
+
const baseUrl = ${wasmDelivery.type === 'jsdelivr' ? `"${baseUrl}"` : baseUrl};
|
|
255
|
+
|
|
256
|
+
let _ready = null;
|
|
257
|
+
|
|
258
|
+
export function init(imports = {}) {
|
|
259
|
+
return (_ready ??= (async () => {
|
|
260
|
+
const [simdRes, baseRes] = await Promise.all([
|
|
261
|
+
fetch(simdUrl),
|
|
262
|
+
fetch(baseUrl)
|
|
263
|
+
]);
|
|
264
|
+
|
|
265
|
+
const [simdBytes, baseBytes] = await Promise.all([
|
|
266
|
+
simdRes.arrayBuffer(),
|
|
267
|
+
baseRes.arrayBuffer()
|
|
268
|
+
]);
|
|
269
|
+
|
|
270
|
+
const { instance } = await instantiateWithFallback(simdBytes, baseBytes, imports);
|
|
271
|
+
setInstance(instance);
|
|
272
|
+
})());
|
|
273
|
+
}
|
|
274
|
+
${eager}
|
|
275
|
+
export * from "${exportFrom}";
|
|
276
|
+
`
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function createNodeLoader({ name, autoInit, customJs }) {
|
|
280
|
+
const eager =
|
|
281
|
+
autoInit === 'eager'
|
|
282
|
+
? '\nregisterInit(init);\ninit();'
|
|
283
|
+
: '\nregisterInit(init);'
|
|
284
|
+
const exportFrom = customJs ? './custom.js' : './core.js'
|
|
285
|
+
return `import { readFile } from "node:fs/promises";
|
|
286
|
+
import { fileURLToPath } from "node:url";
|
|
287
|
+
import { setInstance, registerInit } from "./core.js";
|
|
288
|
+
import { instantiateWithFallback } from "./util.js";
|
|
289
|
+
|
|
290
|
+
const simdPath = fileURLToPath(new URL("./wasm/${name}.simd.wasm", import.meta.url));
|
|
291
|
+
const basePath = fileURLToPath(new URL("./wasm/${name}.base.wasm", import.meta.url));
|
|
292
|
+
|
|
293
|
+
let _ready = null;
|
|
294
|
+
|
|
295
|
+
export function init(imports = {}) {
|
|
296
|
+
return (_ready ??= (async () => {
|
|
297
|
+
const [simdBytes, baseBytes] = await Promise.all([
|
|
298
|
+
readFile(simdPath),
|
|
299
|
+
readFile(basePath)
|
|
300
|
+
]);
|
|
301
|
+
const { instance } = await instantiateWithFallback(simdBytes, baseBytes, imports);
|
|
302
|
+
setInstance(instance);
|
|
303
|
+
})());
|
|
304
|
+
}
|
|
305
|
+
${eager}
|
|
306
|
+
export * from "${exportFrom}";
|
|
307
|
+
`
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function createInlineLoader({ name, autoInit, customJs }) {
|
|
311
|
+
const eager =
|
|
312
|
+
autoInit === 'eager'
|
|
313
|
+
? '\nregisterInit(init);\ninit();'
|
|
314
|
+
: '\nregisterInit(init);'
|
|
315
|
+
const exportFrom = customJs ? './custom.js' : './core.js'
|
|
316
|
+
return `import { wasmBytes as simdBytes } from "./wasm-inline/${name}.simd.wasm.js";
|
|
317
|
+
import { wasmBytes as baseBytes } from "./wasm-inline/${name}.base.wasm.js";
|
|
318
|
+
import { setInstance, registerInit } from "./core.js";
|
|
319
|
+
import { instantiateWithFallback } from "./util.js";
|
|
320
|
+
|
|
321
|
+
let _ready = null;
|
|
322
|
+
|
|
323
|
+
export function init(imports = {}) {
|
|
324
|
+
return (_ready ??= (async () => {
|
|
325
|
+
const { instance } = await instantiateWithFallback(simdBytes, baseBytes, imports);
|
|
326
|
+
setInstance(instance);
|
|
327
|
+
})());
|
|
328
|
+
}
|
|
329
|
+
${eager}
|
|
330
|
+
export * from "${exportFrom}";
|
|
331
|
+
`
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function createInlineModule(bytes) {
|
|
335
|
+
return `// auto-generated
|
|
336
|
+
export const wasmBytes = new Uint8Array([${bytes.join(',')}]);
|
|
337
|
+
`
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function writeInlineModules({
|
|
341
|
+
outDir,
|
|
342
|
+
artifactBaseName,
|
|
343
|
+
baselinePath,
|
|
344
|
+
simdPath,
|
|
345
|
+
}) {
|
|
346
|
+
const inlineDir = join(outDir, 'wasm-inline')
|
|
347
|
+
mkdirSync(inlineDir, { recursive: true })
|
|
348
|
+
|
|
349
|
+
const baseBytes = readFileSync(baselinePath)
|
|
350
|
+
const simdBytes = simdPath ? readFileSync(simdPath) : baseBytes
|
|
351
|
+
|
|
352
|
+
writeFileSync(
|
|
353
|
+
join(inlineDir, `${artifactBaseName}.base.wasm.js`),
|
|
354
|
+
createInlineModule(baseBytes)
|
|
355
|
+
)
|
|
356
|
+
writeFileSync(
|
|
357
|
+
join(inlineDir, `${artifactBaseName}.simd.wasm.js`),
|
|
358
|
+
createInlineModule(simdBytes)
|
|
359
|
+
)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function emitRuntime({
|
|
363
|
+
outDir,
|
|
364
|
+
artifactBaseName,
|
|
365
|
+
emitNode,
|
|
366
|
+
emitBrowser,
|
|
367
|
+
emitInline,
|
|
368
|
+
wasmPaths,
|
|
369
|
+
exportsList,
|
|
370
|
+
autoInit,
|
|
371
|
+
stream,
|
|
372
|
+
customJs,
|
|
373
|
+
wasmDelivery,
|
|
374
|
+
}) {
|
|
375
|
+
mkdirSync(outDir, { recursive: true })
|
|
376
|
+
|
|
377
|
+
if (customJs) {
|
|
378
|
+
const customJsContent = readFileSync(join(process.cwd(), customJs), 'utf8')
|
|
379
|
+
writeFileSync(join(outDir, 'custom.js'), customJsContent)
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
writeFileSync(
|
|
383
|
+
join(outDir, 'core.js'),
|
|
384
|
+
createCore({ exportsList, autoInit, stream })
|
|
385
|
+
)
|
|
386
|
+
writeFileSync(join(outDir, 'util.js'), readFileSync(UTIL_PATH, 'utf8'))
|
|
387
|
+
|
|
388
|
+
if (emitBrowser) {
|
|
389
|
+
writeFileSync(
|
|
390
|
+
join(outDir, 'browser.js'),
|
|
391
|
+
createBrowserLoader({
|
|
392
|
+
name: artifactBaseName,
|
|
393
|
+
autoInit,
|
|
394
|
+
customJs,
|
|
395
|
+
wasmDelivery,
|
|
396
|
+
})
|
|
397
|
+
)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (emitNode) {
|
|
401
|
+
writeFileSync(
|
|
402
|
+
join(outDir, 'node.js'),
|
|
403
|
+
createNodeLoader({ name: artifactBaseName, autoInit, customJs })
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (emitInline && wasmPaths.baselinePath) {
|
|
408
|
+
writeFileSync(
|
|
409
|
+
join(outDir, 'browser-inline.js'),
|
|
410
|
+
createInlineLoader({ name: artifactBaseName, autoInit, customJs })
|
|
411
|
+
)
|
|
412
|
+
writeFileSync(
|
|
413
|
+
join(outDir, 'node-inline.js'),
|
|
414
|
+
createInlineLoader({ name: artifactBaseName, autoInit, customJs })
|
|
415
|
+
)
|
|
416
|
+
writeInlineModules({
|
|
417
|
+
outDir,
|
|
418
|
+
artifactBaseName,
|
|
419
|
+
baselinePath: wasmPaths.baselinePath,
|
|
420
|
+
simdPath: wasmPaths.simdPath,
|
|
421
|
+
})
|
|
422
|
+
}
|
|
423
|
+
}
|
package/src/cli/index.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { rmSync, existsSync } from 'node:fs'
|
|
2
|
+
import { loadConfigFromCli, summarizeConfig } from './config.js'
|
|
3
|
+
import { buildArtifacts } from './build.js'
|
|
4
|
+
import { emitRuntime } from './emit.js'
|
|
5
|
+
import { updatePackageJson } from './pkg.js'
|
|
6
|
+
|
|
7
|
+
export async function runBuild(cliOpts) {
|
|
8
|
+
const cfg = loadConfigFromCli(cliOpts)
|
|
9
|
+
console.log('Configuration:', summarizeConfig(cfg))
|
|
10
|
+
|
|
11
|
+
const wasmPaths = buildArtifacts({
|
|
12
|
+
crateDir: cfg.crateDir,
|
|
13
|
+
wasmFileStem: cfg.wasmFileStem,
|
|
14
|
+
artifactBaseName: cfg.artifactBaseName,
|
|
15
|
+
outDir: cfg.outDir,
|
|
16
|
+
targets: cfg.targets,
|
|
17
|
+
release: cfg.release,
|
|
18
|
+
wasmOpt: cfg.wasmOpt,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
emitRuntime({
|
|
22
|
+
outDir: cfg.outDir,
|
|
23
|
+
artifactBaseName: cfg.artifactBaseName,
|
|
24
|
+
emitNode: cfg.js.emit.node,
|
|
25
|
+
emitBrowser: cfg.js.emit.browser,
|
|
26
|
+
emitInline: cfg.inline && cfg.js.emit.inline,
|
|
27
|
+
wasmPaths,
|
|
28
|
+
exportsList: cfg.exports,
|
|
29
|
+
autoInit: cfg.autoInit,
|
|
30
|
+
stream: cfg.stream,
|
|
31
|
+
customJs: cfg.js.custom,
|
|
32
|
+
wasmDelivery: cfg.wasmDelivery,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
if (cliOpts.updatePackageJson !== false) {
|
|
36
|
+
updatePackageJson({
|
|
37
|
+
crateDir: cfg.crateDir,
|
|
38
|
+
outDir: cfg.outDir,
|
|
39
|
+
artifactBaseName: cfg.artifactBaseName,
|
|
40
|
+
js: cfg.js,
|
|
41
|
+
inline: cfg.inline,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log('Build complete:', cfg.outDir)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function runClean(cliOpts) {
|
|
49
|
+
const cfg = loadConfigFromCli(cliOpts)
|
|
50
|
+
if (existsSync(cfg.outDir)) {
|
|
51
|
+
rmSync(cfg.outDir, { recursive: true, force: true })
|
|
52
|
+
console.log(`Removed ${cfg.outDir}`)
|
|
53
|
+
} else {
|
|
54
|
+
console.log(`Nothing to clean at ${cfg.outDir}`)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function printHelp() {
|
|
59
|
+
const help = `
|
|
60
|
+
wasm-bindgen-lite <command> [options]
|
|
61
|
+
|
|
62
|
+
Commands:
|
|
63
|
+
build Build wasm artifacts and emit JS loaders (default release)
|
|
64
|
+
clean Remove the configured output directory
|
|
65
|
+
help Show this message
|
|
66
|
+
|
|
67
|
+
Options (for build):
|
|
68
|
+
--crate <path> Crate root (default: .)
|
|
69
|
+
--out <path> Output dir (default: dist-wasm-bindgen-lite)
|
|
70
|
+
--config <path> Path to config JSON (default: wasm-bindgen-lite.config.json)
|
|
71
|
+
--release | --debug Toggle cargo profile (default: release)
|
|
72
|
+
--inline | --no-inline Emit inline loaders and byte modules (default: inline)
|
|
73
|
+
--simd | --no-simd Build SIMD variant (default: simd on)
|
|
74
|
+
--wasm-opt | --no-wasm-opt Force enable/disable wasm-opt (default: auto detect)
|
|
75
|
+
--wasm-opt-args "<args>" Extra args, default "-Oz"
|
|
76
|
+
--no-update-package-json Do not modify package.json exports (default: updates if package.json exists)
|
|
77
|
+
`
|
|
78
|
+
console.log(help)
|
|
79
|
+
}
|
package/src/cli/pkg.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs'
|
|
2
|
+
import { join, relative } from 'node:path'
|
|
3
|
+
|
|
4
|
+
export function updatePackageJson({
|
|
5
|
+
crateDir,
|
|
6
|
+
outDir,
|
|
7
|
+
artifactBaseName,
|
|
8
|
+
js,
|
|
9
|
+
inline,
|
|
10
|
+
}) {
|
|
11
|
+
const pkgPath = join(crateDir, 'package.json')
|
|
12
|
+
if (!existsSync(pkgPath)) return
|
|
13
|
+
|
|
14
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
|
|
15
|
+
const relOutDir = './' + relative(crateDir, outDir)
|
|
16
|
+
|
|
17
|
+
if (!pkg.exports) pkg.exports = {}
|
|
18
|
+
|
|
19
|
+
const mainExports = {}
|
|
20
|
+
if (js.emit.browser) {
|
|
21
|
+
mainExports.browser = `${relOutDir}/browser.js`
|
|
22
|
+
}
|
|
23
|
+
if (js.emit.node) {
|
|
24
|
+
mainExports.node = `${relOutDir}/node.js`
|
|
25
|
+
if (!mainExports.default) mainExports.default = `${relOutDir}/node.js`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (Object.keys(mainExports).length > 0) {
|
|
29
|
+
pkg.exports['.'] = mainExports
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (inline && js.emit.inline) {
|
|
33
|
+
const inlineExports = {}
|
|
34
|
+
if (js.emit.browser) {
|
|
35
|
+
inlineExports.browser = `${relOutDir}/browser-inline.js`
|
|
36
|
+
}
|
|
37
|
+
if (js.emit.node) {
|
|
38
|
+
inlineExports.node = `${relOutDir}/node-inline.js`
|
|
39
|
+
if (!inlineExports.default)
|
|
40
|
+
inlineExports.default = `${relOutDir}/node-inline.js`
|
|
41
|
+
}
|
|
42
|
+
if (Object.keys(inlineExports).length > 0) {
|
|
43
|
+
pkg.exports['./inline'] = inlineExports
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Also update main/module/types if they are missing?
|
|
48
|
+
// For now let's just focus on exports as it's the modern way.
|
|
49
|
+
|
|
50
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
|
|
51
|
+
console.log('Updated package.json exports')
|
|
52
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { wasmBytes as simdBytes } from './wasm-inline/mod.simd.wasm.js'
|
|
2
|
+
import { wasmBytes as baseBytes } from './wasm-inline/mod.base.wasm.js'
|
|
3
|
+
import { setInstance } from './core.js'
|
|
4
|
+
import { instantiateWithFallback } from './util.js'
|
|
5
|
+
|
|
6
|
+
let _ready = null
|
|
7
|
+
|
|
8
|
+
export function init(imports = {}) {
|
|
9
|
+
return (_ready ??= (async () => {
|
|
10
|
+
const { instance } = await instantiateWithFallback(
|
|
11
|
+
simdBytes,
|
|
12
|
+
baseBytes,
|
|
13
|
+
imports
|
|
14
|
+
)
|
|
15
|
+
setInstance(instance)
|
|
16
|
+
})())
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export * from './core.js'
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { setInstance } from './core.js'
|
|
2
|
+
import { instantiateWithFallback } from './util.js'
|
|
3
|
+
|
|
4
|
+
const simdUrl = new URL('./wasm/mod.simd.wasm', import.meta.url)
|
|
5
|
+
const baseUrl = new URL('./wasm/mod.base.wasm', import.meta.url)
|
|
6
|
+
|
|
7
|
+
let _ready = null
|
|
8
|
+
|
|
9
|
+
export function init(imports = {}) {
|
|
10
|
+
return (_ready ??= (async () => {
|
|
11
|
+
const [simdRes, baseRes] = await Promise.all([
|
|
12
|
+
fetch(simdUrl),
|
|
13
|
+
fetch(baseUrl),
|
|
14
|
+
])
|
|
15
|
+
|
|
16
|
+
const [simdBytes, baseBytes] = await Promise.all([
|
|
17
|
+
simdRes.arrayBuffer(),
|
|
18
|
+
baseRes.arrayBuffer(),
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
const { instance } = await instantiateWithFallback(
|
|
22
|
+
simdBytes,
|
|
23
|
+
baseBytes,
|
|
24
|
+
imports
|
|
25
|
+
)
|
|
26
|
+
setInstance(instance)
|
|
27
|
+
})())
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export * from './core.js'
|
package/src/js/core.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
let _inst = null
|
|
2
|
+
let _memU8 = null
|
|
3
|
+
|
|
4
|
+
function refreshViews() {
|
|
5
|
+
_memU8 = new Uint8Array(_inst.exports.memory.buffer)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function setInstance(instance) {
|
|
9
|
+
_inst = instance
|
|
10
|
+
refreshViews()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function memoryU8() {
|
|
14
|
+
// memory can grow, so refresh if needed:
|
|
15
|
+
if (_memU8.buffer !== _inst.exports.memory.buffer) refreshViews()
|
|
16
|
+
return _memU8
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function alloc(len) {
|
|
20
|
+
return _inst.exports.alloc_bytes(len) >>> 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function free(ptr, len) {
|
|
24
|
+
_inst.exports.free_bytes(ptr >>> 0, len >>> 0)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Example wrapper around the raw ABI
|
|
29
|
+
*/
|
|
30
|
+
export function process(inputU8) {
|
|
31
|
+
const inPtr = alloc(inputU8.length)
|
|
32
|
+
memoryU8().set(inputU8, inPtr)
|
|
33
|
+
|
|
34
|
+
const outPtr = alloc(inputU8.length)
|
|
35
|
+
// No need to set outU8 yet, but memory might have grown again
|
|
36
|
+
|
|
37
|
+
const written = _inst.exports.process_bytes(
|
|
38
|
+
inPtr,
|
|
39
|
+
inputU8.length,
|
|
40
|
+
outPtr,
|
|
41
|
+
inputU8.length
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if (written < 0) {
|
|
45
|
+
free(inPtr, inputU8.length)
|
|
46
|
+
free(outPtr, inputU8.length)
|
|
47
|
+
throw new Error(`process_bytes failed: ${written}`)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Refresh view again because memory might have grown during alloc
|
|
51
|
+
const result = memoryU8().slice(outPtr, outPtr + written)
|
|
52
|
+
|
|
53
|
+
free(inPtr, inputU8.length)
|
|
54
|
+
free(outPtr, inputU8.length)
|
|
55
|
+
|
|
56
|
+
return result
|
|
57
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { wasmBytes as simdBytes } from './wasm-inline/mod.simd.wasm.js'
|
|
2
|
+
import { wasmBytes as baseBytes } from './wasm-inline/mod.base.wasm.js'
|
|
3
|
+
import { setInstance } from './core.js'
|
|
4
|
+
import { instantiateWithFallback } from './util.js'
|
|
5
|
+
|
|
6
|
+
let _ready = null
|
|
7
|
+
|
|
8
|
+
export function init(imports = {}) {
|
|
9
|
+
return (_ready ??= (async () => {
|
|
10
|
+
const { instance } = await instantiateWithFallback(
|
|
11
|
+
simdBytes,
|
|
12
|
+
baseBytes,
|
|
13
|
+
imports
|
|
14
|
+
)
|
|
15
|
+
setInstance(instance)
|
|
16
|
+
})())
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export * from './core.js'
|
package/src/js/node.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { setInstance } from './core.js'
|
|
4
|
+
import { instantiateWithFallback } from './util.js'
|
|
5
|
+
|
|
6
|
+
const simdPath = fileURLToPath(new URL('./wasm/mod.simd.wasm', import.meta.url))
|
|
7
|
+
const basePath = fileURLToPath(new URL('./wasm/mod.base.wasm', import.meta.url))
|
|
8
|
+
|
|
9
|
+
let _ready = null
|
|
10
|
+
|
|
11
|
+
export function init(imports = {}) {
|
|
12
|
+
return (_ready ??= (async () => {
|
|
13
|
+
const [simdBytes, baseBytes] = await Promise.all([
|
|
14
|
+
readFile(simdPath),
|
|
15
|
+
readFile(basePath),
|
|
16
|
+
])
|
|
17
|
+
const { instance } = await instantiateWithFallback(
|
|
18
|
+
simdBytes,
|
|
19
|
+
baseBytes,
|
|
20
|
+
imports
|
|
21
|
+
)
|
|
22
|
+
setInstance(instance)
|
|
23
|
+
})())
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export * from './core.js'
|