98 lines
4.1 KiB
TypeScript
98 lines
4.1 KiB
TypeScript
import { plugin, $, type BunPlugin } from "bun";
|
|
import path from 'path';
|
|
import asc from 'assemblyscript/asc';
|
|
|
|
interface WasmLoaderConfig {
|
|
production?: boolean;
|
|
portable?: boolean;
|
|
}
|
|
|
|
const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin => {
|
|
const p: BunPlugin = {
|
|
name: "WASM loader",
|
|
async setup(build) {
|
|
build.onLoad({ filter: /\.(c(pp)?|wasm(\.ts)?)$/ }, async (args) => {
|
|
let wasmPath = path.resolve(import.meta.dir, '..', 'dist', 'tmp.wasm');
|
|
let jsContent: string = `
|
|
async function instantiate(url) {
|
|
const memory = new WebAssembly.Memory({
|
|
initial: 32,
|
|
});
|
|
let data = new DataView(memory.buffer);
|
|
const { instance } = await WebAssembly.instantiateStreaming(fetch(url), {
|
|
env: {
|
|
memory,
|
|
log(...args) {
|
|
console.log('[wasm]', ...args);
|
|
},
|
|
grow(blocks) {
|
|
if (blocks > 0) {
|
|
memory.grow(blocks);
|
|
data = new DataView(memory.buffer);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
return {
|
|
...instance.exports,
|
|
memory,
|
|
get data() { return data; },
|
|
};
|
|
}
|
|
const module = await instantiate(new URL($WASM$));
|
|
if (typeof module.__srand === 'function') module.__srand(BigInt(Date.now()));
|
|
|
|
export default module;
|
|
`;
|
|
if (args.path.endsWith('.ts')) {
|
|
if (portable) {
|
|
const contents = await Bun.file(args.path).text();
|
|
return {
|
|
contents: `import "assemblyscript/std/portable/index.js";\n${contents}`,
|
|
loader: 'tsx',
|
|
}
|
|
}
|
|
const jsPath = wasmPath.replace(/\.wasm$/, '.js');
|
|
const ascArgs = [
|
|
args.path,
|
|
'--outFile', wasmPath,
|
|
'--bindings', 'esm',
|
|
'-Ospeed'
|
|
];
|
|
|
|
const { error, stderr } = await asc.main(ascArgs);
|
|
if (error) {
|
|
console.error(stderr.toString(), error.message);
|
|
throw error;
|
|
}
|
|
jsContent = await Bun.file(jsPath).text();
|
|
} else if (args.path.endsWith('.wasm')) {
|
|
wasmPath = args.path;
|
|
} else {
|
|
const buildAssets = path.resolve(import.meta.dir, 'assets');
|
|
const stdlib = `${buildAssets}/stdlib.c`;
|
|
const opt = production ? '-O3': '-O0';
|
|
const result = await $`clang --target=wasm32 ${opt} -flto -fno-builtin --no-standard-libraries -I ${buildAssets} -Wall -Wextra -Wpedantic -Werror -Wl,--lto-O3 -Wl,--no-entry -Wl,--import-memory -o ${wasmPath} ${args.path} ${stdlib}`;
|
|
|
|
if (result.exitCode !== 0) {
|
|
throw new Error('Compile failed, check output');
|
|
}
|
|
}
|
|
|
|
const wasmContent = await Bun.file(wasmPath).arrayBuffer();
|
|
const wasmBuffer = Buffer.from(wasmContent).toString('base64');
|
|
const wasmURL = `data:application/wasm;base64,${wasmBuffer}`;
|
|
|
|
return {
|
|
loader: 'js',
|
|
contents: jsContent.replace(/new URL\([^)]*\)/, `new URL(${JSON.stringify(wasmURL)})`),
|
|
};
|
|
});
|
|
}
|
|
};
|
|
plugin(p);
|
|
return p;
|
|
};
|
|
|
|
export default wasmPlugin; |