|
| 1 | +#!/usr/bin/env node |
| 2 | +/* |
| 3 | + Bundle the wasm-bindgen JS glue and WASM binary into a single ESM file. |
| 4 | + - Input: pkg/wasm_sdk.js, pkg/wasm_sdk_bg.wasm, pkg/wasm_sdk.d.ts |
| 5 | + - Output: dist/sdk.js (single-file with embedded WASM), dist/sdk.d.ts |
| 6 | +
|
| 7 | + Notes: |
| 8 | + - We keep the exported API identical to wasm_bindgen output, including default export (init) and initSync. |
| 9 | + - We replace the default loader path with inlined bytes so no network or file access is required at runtime. |
| 10 | +*/ |
| 11 | + |
| 12 | +const fs = require('fs'); |
| 13 | +const path = require('path'); |
| 14 | + |
| 15 | +const root = process.cwd(); |
| 16 | +const pkgDir = path.join(root, 'pkg'); |
| 17 | +const distDir = path.join(root, 'dist'); |
| 18 | +const rawDir = path.join(distDir, 'raw'); |
| 19 | + |
| 20 | +const jsPath = path.join(pkgDir, 'wasm_sdk.js'); |
| 21 | +const wasmPath = path.join(pkgDir, 'wasm_sdk_bg.wasm'); |
| 22 | +const dtsPath = path.join(pkgDir, 'wasm_sdk.d.ts'); |
| 23 | +const wasmDtsPath = path.join(pkgDir, 'wasm_sdk_bg.wasm.d.ts'); |
| 24 | + |
| 25 | +if (!fs.existsSync(jsPath) || !fs.existsSync(wasmPath) || !fs.existsSync(dtsPath)) { |
| 26 | + console.error('Missing build artifacts in pkg/. Run build first.'); |
| 27 | + process.exit(1); |
| 28 | +} |
| 29 | + |
| 30 | +const js = fs.readFileSync(jsPath, 'utf8'); |
| 31 | +const wasmBase64 = fs.readFileSync(wasmPath).toString('base64'); |
| 32 | + |
| 33 | +// Helper injected to decode base64 → Uint8Array in both Node and browser |
| 34 | +const injectHeader = ` |
| 35 | +// Inlined WASM bytes (base64) |
| 36 | +const __WASM_BASE64 = '${wasmBase64}'; |
| 37 | +function __wasmBytes() { |
| 38 | + if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') { |
| 39 | + return Buffer.from(__WASM_BASE64, 'base64'); |
| 40 | + } |
| 41 | + const atobFn = (typeof atob === 'function') ? atob : (s) => globalThis.atob(s); |
| 42 | + const bin = atobFn(__WASM_BASE64); |
| 43 | + const len = bin.length; |
| 44 | + const bytes = new Uint8Array(len); |
| 45 | + for (let i = 0; i < len; i++) bytes[i] = bin.charCodeAt(i); |
| 46 | + return bytes; |
| 47 | +} |
| 48 | +`; |
| 49 | + |
| 50 | +// Patch 1: default init path resolution to use inlined bytes |
| 51 | +const initDefaultSearch = /if \(typeof module_or_path === 'undefined'\) {\s*\n\s*module_or_path = new URL\('wasm_sdk_bg\.wasm', import\.meta\.url\);\s*\n\s*}/; |
| 52 | +const initDefaultReplace = `if (typeof module_or_path === 'undefined') {\n module_or_path = __wasmBytes();\n }`; |
| 53 | + |
| 54 | +// Patch 2: initSync to use inlined bytes when module is not provided |
| 55 | +const initSyncSearch = /(__wbg_init_memory\(imports\);\s*\n\s*if \(!\(module instanceof WebAssembly\.Module\)\) {)/; |
| 56 | +const initSyncReplace = `__wbg_init_memory(imports);\n\n if (typeof module === 'undefined') {\n module = __wasmBytes();\n }\n\n if (!(module instanceof WebAssembly.Module)) {`; |
| 57 | + |
| 58 | +let patched = js; |
| 59 | + |
| 60 | +if (!initDefaultSearch.test(patched)) { |
| 61 | + console.error('Failed to find default init block to patch'); |
| 62 | + process.exit(1); |
| 63 | +} |
| 64 | +patched = patched.replace(initDefaultSearch, initDefaultReplace); |
| 65 | + |
| 66 | +if (!initSyncSearch.test(patched)) { |
| 67 | + console.error('Failed to find initSync block to patch'); |
| 68 | + process.exit(1); |
| 69 | +} |
| 70 | +patched = patched.replace(initSyncSearch, initSyncReplace); |
| 71 | + |
| 72 | +// Prepend helper header |
| 73 | +patched = injectHeader + '\n' + patched; |
| 74 | + |
| 75 | +// Ensure dist directories and write outputs |
| 76 | +fs.mkdirSync(distDir, { recursive: true }); |
| 77 | +fs.mkdirSync(rawDir, { recursive: true }); |
| 78 | +fs.writeFileSync(path.join(distDir, 'sdk.js'), patched); |
| 79 | +fs.copyFileSync(dtsPath, path.join(distDir, 'sdk.d.ts')); |
| 80 | + |
| 81 | +// Also ship non-bundled artifacts for advanced/asset-pipeline users |
| 82 | +fs.copyFileSync(jsPath, path.join(rawDir, 'wasm_sdk.js')); |
| 83 | +fs.copyFileSync(wasmPath, path.join(rawDir, 'wasm_sdk_bg.wasm')); |
| 84 | +fs.copyFileSync(dtsPath, path.join(rawDir, 'wasm_sdk.d.ts')); |
| 85 | +if (fs.existsSync(wasmDtsPath)) { |
| 86 | + fs.copyFileSync(wasmDtsPath, path.join(rawDir, 'wasm_sdk_bg.wasm.d.ts')); |
| 87 | +} |
| 88 | + |
| 89 | +// Basic report |
| 90 | +const outStat = fs.statSync(path.join(distDir, 'sdk.js')); |
| 91 | +console.log(`Wrote dist/sdk.js (${outStat.size} bytes) with inlined WASM (${Math.round(Buffer.byteLength(wasmBase64, 'utf8')/1024)} KB base64)`); |
| 92 | +console.log('Wrote dist/sdk.d.ts'); |
| 93 | +console.log('Wrote dist/raw/* (separate JS + WASM)'); |
| 94 | + |
| 95 | +// Clean up: remove pkg directory after bundling to avoid publishing it |
| 96 | +try { |
| 97 | + fs.rmSync(pkgDir, { recursive: true, force: true }); |
| 98 | + console.log('Removed pkg/ directory after bundling'); |
| 99 | +} catch (e) { |
| 100 | + console.warn('Warning: failed to remove pkg/ directory:', e?.message || e); |
| 101 | +} |
0 commit comments