bun-wasm fixes & improvements (#4126)

* automate Bun.version & revision polyfills

* polyfill Bun.gc

* bun:jsc module initial polyfills

* fixes & improvements to bun-wasm
This commit is contained in:
jhmaster
2023-08-11 22:20:21 -03:00
committed by GitHub
parent 43ebffedcd
commit 117cee5ca5
14 changed files with 252 additions and 44 deletions

2
.gitignore vendored
View File

@@ -96,6 +96,8 @@ packages/bun-wasm/*.cjs
packages/bun-wasm/*.map
packages/bun-wasm/*.js
packages/bun-wasm/*.d.ts
packages/bun-wasm/*.d.cts
packages/bun-wasm/*.d.mts
*.bc
src/fallback.version

View File

@@ -705,7 +705,7 @@ dev-build-obj-wasm:
dev-wasm: dev-build-obj-wasm
emcc -sEXPORTED_FUNCTIONS="['_bun_free', '_cycleStart', '_cycleEnd', '_bun_malloc', '_scan', '_transform', '_init', '_getTests']" \
-g2 -s ERROR_ON_UNDEFINED_SYMBOLS=0 -DNDEBUG \
$(BUN_DEPS_DIR)/libmimalloc.a.wasm \
$(BUN_DEPS_DIR)/$(MIMALLOC_FILE).wasm \
packages/debug-bun-freestanding-wasm32/bun-wasm.o --no-entry --allow-undefined -s ASSERTIONS=0 -s ALLOW_MEMORY_GROWTH=1 -s WASM_BIGINT=1 \
-o packages/debug-bun-freestanding-wasm32/bun-wasm.wasm
cp packages/debug-bun-freestanding-wasm32/bun-wasm.wasm packages/bun-wasm/bun.wasm
@@ -715,7 +715,7 @@ build-obj-wasm:
$(ZIG) build bun-wasm -Doptimize=ReleaseFast -Dtarget=wasm32-freestanding
emcc -sEXPORTED_FUNCTIONS="['_bun_free', '_cycleStart', '_cycleEnd', '_bun_malloc', '_scan', '_transform', '_init', '_getTests']" \
-s ERROR_ON_UNDEFINED_SYMBOLS=0 -DNDEBUG \
$(BUN_DEPS_DIR)/libmimalloc.a.wasm \
$(BUN_DEPS_DIR)/$(MIMALLOC_FILE).wasm \
packages/bun-freestanding-wasm32/bun-wasm.o $(OPTIMIZATION_LEVEL) --no-entry --allow-undefined -s ASSERTIONS=0 -s ALLOW_MEMORY_GROWTH=1 -s WASM_BIGINT=1 \
-o packages/bun-freestanding-wasm32/bun-wasm.wasm
cp packages/bun-freestanding-wasm32/bun-wasm.wasm packages/bun-wasm/bun.wasm
@@ -725,18 +725,20 @@ build-obj-wasm-small:
$(ZIG) build bun-wasm -Doptimize=ReleaseFast -Dtarget=wasm32-freestanding
emcc -sEXPORTED_FUNCTIONS="['_bun_free', '_cycleStart', '_cycleEnd', '_bun_malloc', '_scan', '_transform', '_init', '_getTests']" \
-Oz -s ERROR_ON_UNDEFINED_SYMBOLS=0 -DNDEBUG \
$(BUN_DEPS_DIR)/libmimalloc.a.wasm \
$(BUN_DEPS_DIR)/$(MIMALLOC_FILE).wasm \
packages/bun-freestanding-wasm32/bun-wasm.o -Oz --no-entry --allow-undefined -s ASSERTIONS=0 -s ALLOW_MEMORY_GROWTH=1 -s WASM_BIGINT=1 \
-o packages/bun-freestanding-wasm32/bun-wasm.wasm
cp packages/bun-freestanding-wasm32/bun-wasm.wasm packages/bun-wasm/bun.wasm
.PHONY: wasm
wasm: api mimalloc-wasm build-obj-wasm-small
@rm -rf packages/bun-wasm/*.{d.ts,js,wasm,cjs,mjs,tsbuildinfo}
@rm -rf packages/bun-wasm/*.{d.ts,d.cts,d.mts,js,wasm,cjs,mjs,tsbuildinfo}
@cp packages/bun-freestanding-wasm32/bun-wasm.wasm packages/bun-wasm/bun.wasm
@cp src/api/schema.d.ts packages/bun-wasm/schema.d.ts
@cp src/api/schema.js packages/bun-wasm/schema.js
@cd packages/bun-wasm && $(NPM_CLIENT) run tsc -- -p .
@cp packages/bun-wasm/index.d.ts packages/bun-wasm/index.d.cts
@mv packages/bun-wasm/index.d.ts packages/bun-wasm/index.d.mts
@bun build --sourcemap=external --external=fs --outdir=packages/bun-wasm --target=browser --minify ./packages/bun-wasm/index.ts
@mv packages/bun-wasm/index.js packages/bun-wasm/index.mjs
@mv packages/bun-wasm/index.js.map packages/bun-wasm/index.mjs.map

View File

@@ -14,7 +14,8 @@
"scripts": {
"node": "node --enable-source-maps --import ./dist/src/repl.js",
"clean": "rm -rf dist",
"build": "bun run clean && bunx tsc && bunx copyfiles \"./**/*.wasm\" dist",
"preprocess": "bun tools/updateversions.ts",
"build": "bun run clean && bun run preprocess && bunx tsc && bunx copyfiles \"./**/*.wasm\" dist",
"build/wasm": "bun run build/zighash",
"build/zighash": "cd lib/zighash && bun run build && cd ../.."
},

View File

@@ -30,8 +30,16 @@ import openEditor from 'open-editor';
export const main = path.resolve(process.cwd(), process.argv[1] ?? 'repl') satisfies typeof Bun.main;
export const version = '0.7.1' satisfies typeof Bun.version; // TODO: This can probably be fetched from somewhere in the repo
export const revision = '0'.repeat(39) + '1' satisfies typeof Bun.revision;
//? These are automatically updated on build by tools/updateversions.ts, do not edit manually.
export const version = '0.7.4' satisfies typeof Bun.version;
export const revision = '7088d7e182635a58a50860302da0b1abc42c7ce7' satisfies typeof Bun.revision;
export const gc = (globalThis.gc ? (() => (globalThis.gc!(), process.memoryUsage().heapUsed)) : (() => {
const err = new Error('[bun-polyfills] Garbage collection polyfills are only available when Node.js is ran with the --expose-gc flag.');
Error.captureStackTrace(err, gc);
throw err;
})) satisfies typeof Bun.gc;
//getter(bun, 'cwd', proc.cwd); //! Can't named export a getter
export const origin = '' satisfies typeof Bun.origin;
// @ts-expect-error ---

View File

@@ -0,0 +1,111 @@
import type jsc from 'bun:jsc';
import v8 from 'node:v8';
//import { setRandomSeed, getRandomSeed } from './mathrandom.js';
import { NotImplementedError, getCallSites } from '../utils/errors.js';
import { gc } from './bun.js';
const STUB = () => void 0;
function jscSerialize(value: any, options?: { binaryType: 'nodebuffer'; }): Buffer;
function jscSerialize(value: any, options?: { binaryType?: 'arraybuffer'; }): SharedArrayBuffer;
function jscSerialize(value: any, options?: { binaryType?: string }): Buffer | SharedArrayBuffer {
const serialized = v8.serialize(value);
if (options?.binaryType === 'nodebuffer') return serialized;
else return new SharedArrayBuffer(serialized.byteLength);
}
// TODO: Investigate ways of making these the actual JSC serialization format (probably Bun WASM)
// TODO: whilst this works for common use-cases like Node <-> Node it still does not make it
// TODO: possible for Node <-> Bun transfers of this kind of data, which might be interesting to have.
export const serialize = jscSerialize satisfies typeof jsc.serialize;
export const deserialize = (value => {
if (value instanceof ArrayBuffer || value instanceof SharedArrayBuffer) return v8.deserialize(Buffer.from(value));
else return v8.deserialize(value);
}) satisfies typeof jsc.deserialize;
export const setTimeZone = ((timeZone: string) => {
const resolvedTZ = Intl.DateTimeFormat(undefined, { timeZone }).resolvedOptions().timeZone;
return process.env.TZ = resolvedTZ;
}) satisfies typeof jsc.setTimeZone;
export const callerSourceOrigin = (() => {
const callsites: NodeJS.CallSite[] = getCallSites(2);
// This may be inaccurate with async code. Needs more testing.
let lastSeenURL = '';
for (const callsite of callsites) {
const sourceURL = callsite.getScriptNameOrSourceURL();
if (sourceURL.startsWith('file://')) lastSeenURL = sourceURL;
}
return lastSeenURL;
}) satisfies typeof jsc.callerSourceOrigin;
// TODO: Like with jsc.serialize/deserialize, these may be possible with Bun WASM.
export const jscDescribe = (() => { throw new NotImplementedError('jsc.jscDescribe', STUB); }) satisfies typeof jsc.jscDescribe;
export const jscDescribeArray = (() => { throw new NotImplementedError('jsc.jscDescribeArray', STUB); }) satisfies typeof jsc.jscDescribeArray;
// These are no longer documented but still exist.
export const describe = jscDescribe;
export const describeArray = jscDescribeArray;
// Node.js only provides a singular non-configurable global GC function, so we have to make do with that.
export const edenGC = gc satisfies typeof jsc.edenGC;
export const fullGC = gc satisfies typeof jsc.fullGC;
export const gcAndSweep = gc satisfies typeof jsc.gcAndSweep;
export const drainMicrotasks = STUB satisfies typeof jsc.drainMicrotasks; // no-op
export const releaseWeakRefs = STUB satisfies typeof jsc.releaseWeakRefs; // no-op
export const startSamplingProfiler = STUB satisfies typeof jsc.startSamplingProfiler; // no-op
//! likely broken but needs more testing
export const startRemoteDebugger = STUB satisfies typeof jsc.startRemoteDebugger; // no-op
//! this is a really poor polyfill but it's better than nothing
export const getProtectedObjects = (() => { return [globalThis]; }) satisfies typeof jsc.getProtectedObjects;
export const getRandomSeed = 0; // TODO
export const setRandomSeed = 0; // TODO
export const heapSize = (() => { return v8.getHeapStatistics().used_heap_size; }) satisfies typeof jsc.heapSize;
export const heapStats = (() => {
const stats = v8.getHeapStatistics();
return {
heapSize: stats.used_heap_size,
heapCapacity: stats.total_available_size,
extraMemorySize: stats.external_memory ?? 0,
objectCount: 1, // TODO: how to get this in node?
protectedObjectCount: getProtectedObjects().length,
globalObjectCount: 2, // TODO: this one is probably fine hardcoded but is there a way to get this in node?
protectedGlobalObjectCount: 1, // TODO: ^
objectTypeCounts: {}, //! can't really throw an error here, so just return an empty object (TODO: how to get this in node?)
protectedObjectTypeCounts: {} //! can't really throw an error here, so just return an empty object (TODO: how to get this in node?)
};
}) satisfies typeof jsc.heapStats;
//! doubtful anyone relies on the return of this for anything besides debugging
export const isRope = (() => false) satisfies typeof jsc.isRope;
export const memoryUsage = (() => {
const stats = v8.getHeapStatistics();
const resUse = process.resourceUsage();
return {
current: stats.malloced_memory,
peak: stats.peak_malloced_memory,
currentCommit: stats.malloced_memory,
peakCommit: stats.malloced_memory,
pageFaults: resUse.minorPageFault + resUse.majorPageFault
};
}) satisfies typeof jsc.memoryUsage;
//! these are likely broken, seemingly always returning undefined which does not match the documented return types
export const noFTL = (() => { return void 0 as unknown as Function; }) satisfies typeof jsc.noFTL;
export const noOSRExitFuzzing = (() => { return void 0 as unknown as Function; }) satisfies typeof jsc.noOSRExitFuzzing;
//! likely broken, seems to always returns zero
export const totalCompileTime = (() => 0) satisfies typeof jsc.totalCompileTime;
//! likely broken, seem to always returns 0 if any arguments are passed, undefined otherwise
export const numberOfDFGCompiles = ((...args) => args.length ? 0 : void 0 as unknown as number) satisfies typeof jsc.numberOfDFGCompiles;
export const reoptimizationRetryCount = ((...args) => args.length ? 0 : void 0 as unknown as number) satisfies typeof jsc.reoptimizationRetryCount;
//! The following are very likely impossible to ever polyfill.
export const profile = (() => {
throw new NotImplementedError('jsc.profile is not polyfillable', STUB, true);
}) satisfies typeof jsc.profile;
export const optimizeNextInvocation = (() => {
throw new NotImplementedError('jsc.optimizeNextInvocation is not polyfillable', STUB, true);
}) satisfies typeof jsc.optimizeNextInvocation;

View File

@@ -1,4 +1,5 @@
import bun from './index.js';
import * as jsc from './modules/jsc.js';
// This file serves two purposes:
// 1. It is the entry point for using the Bun global in the REPL. (--import this file)
@@ -12,7 +13,6 @@ globalThis.Bun = bun as typeof bun & {
deepMatch: typeof import('bun').deepMatch;
build: typeof import('bun').build;
mmap: typeof import('bun').mmap;
gc: typeof import('bun').gc;
connect: typeof import('bun').connect;
listen: typeof import('bun').listen;
Transpiler: typeof import('bun').Transpiler;
@@ -27,3 +27,5 @@ globalThis.Bun = bun as typeof bun & {
stderr: typeof import('bun').stderr;
stdin: typeof import('bun').stdin;
};
Reflect.set(globalThis, 'jsc', jsc);

View File

@@ -12,4 +12,19 @@ declare module 'stream/web' {
declare global {
var performance: typeof import('perf_hooks').performance;
// TODO: These should be contributed to @types/node upstream
namespace NodeJS {
interface CallSite {
getScriptNameOrSourceURL(): string;
getEnclosingColumnNumber(): number;
getEnclosingLineNumber(): number;
getPosition(): number;
getPromiseIndex(): number;
getScriptHash(): string;
isAsync(): boolean;
isPromiseAll(): boolean;
toString(): string;
}
}
}

View File

@@ -1,6 +1,15 @@
type PosixErrNo = MapKeysType<ReturnType<typeof getPosixSystemErrorMap>>;
type Win32ErrNo = MapKeysType<ReturnType<typeof getWin32SystemErrorMap>>;
export function getCallSites(sliceOff = 1) {
const originalPST = Error.prepareStackTrace;
Error.prepareStackTrace = (error, stack) => stack;
const { stack } = new Error();
if (stack?.constructor.name !== 'Array') throw new Error('Failed to acquire structured JS stack trace');
Error.prepareStackTrace = originalPST;
return (stack as unknown as NodeJS.CallSite[]).slice(sliceOff);
}
export function getPosixSystemErrorMap() {
return new Map([
[ -7, [ 'E2BIG', 'argument list too long' ] ],

View File

@@ -0,0 +1,41 @@
import path from 'path';
const abort = (...msg: string[]): never => (console.error(...msg), process.exit(1));
const makefilePath = path.resolve(import.meta.dir, '../../../Makefile');
const makefile = Bun.file(makefilePath);
if (!await makefile.exists()) abort('Makefile not found at', makefilePath);
const makefileContent = await makefile.text();
const matched = makefileContent.match(/^BUN_BASE_VERSION\s*=\s*(\d+.\d+)/m);
if (!matched) abort('Could not find BUN_BASE_VERSION in Makefile');
const buildidPath = path.resolve(import.meta.dir, '../../../src/build-id');
const buildid = Bun.file(buildidPath);
if (!await buildid.exists()) abort('Build ID file not found at', buildidPath);
const [, BUN_BASE_VERSION] = matched!;
const BUN_VERSION = `${BUN_BASE_VERSION}.${await buildid.text()}`.trim();
const bunTsPath = path.resolve(import.meta.dir, '../src/modules/bun.ts');
const bunTs = Bun.file(bunTsPath);
if (!await bunTs.exists()) abort('bun.ts source file not found at', bunTsPath);
const bunTsContent = await bunTs.text();
const bunTsContentNew = bunTsContent.replace(
/^export const version = '.+' satisfies typeof Bun.version;$/m,
`export const version = '${BUN_VERSION}' satisfies typeof Bun.version;`
);
if (bunTsContentNew !== bunTsContent) console.info('Updated Bun.version polyfill to', BUN_VERSION);
const git = Bun.spawnSync({ cmd: ['git', 'rev-parse', 'HEAD'] });
if (!git.success) abort('Could not get git HEAD commit hash');
const BUN_REVISION = git.stdout.toString('utf8').trim();
const bunTsContentNewer = bunTsContentNew.replace(
/^export const revision = '.+' satisfies typeof Bun.revision;$/m,
`export const revision = '${BUN_REVISION}' satisfies typeof Bun.revision;`
);
if (bunTsContentNewer !== bunTsContentNew) console.info('Updated Bun.revision polyfill to', BUN_REVISION);
Bun.write(bunTs, bunTsContentNewer);

Binary file not shown.

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
import { ByteBuffer } from "peechy/bb";
import {
Loader as BunLoader,
@@ -12,20 +11,28 @@ import {
type ScanResult,
type TransformResponse,
} from "./schema";
export enum Loader {
jsx = BunLoader.jsx,
js = BunLoader.js,
tsx = BunLoader.tsx,
ts = BunLoader.ts,
}
export interface TestReference {
name: string,
byteOffset: number,
kind: 'test' | 'describe',
}
export type { ScanResult, TransformResponse };
const testKindMap = {
[TestKind.describe_fn]: "describe",
[TestKind.test_fn]: "test",
};
const capturedErrors = [];
const capturedErrors: string[] = [];
let captureErrors = false;
export type { ScanResult, TransformResponse };
function normalizeLoader(file_name: string, loader?: Loader): BunLoader {
function normalizeLoader(file_name: string, loader?: keyof typeof Loader): BunLoader {
return (
(loader
? {
@@ -46,11 +53,12 @@ function normalizeLoader(file_name: string, loader?: Loader): BunLoader {
}
interface WebAssemblyModule {
init(): number;
init(heapSize: number): number;
transform(a: number): number;
bun_malloc(a: number): number;
bun_free(a: number): number;
scan(a: number): number;
getTests(a: number): number;
}
const ptr_converter = new ArrayBuffer(16);
@@ -58,28 +66,28 @@ const ptr_float = new BigUint64Array(ptr_converter);
const slice = new Uint32Array(ptr_converter);
const Wasi = {
clock_time_get(clk_id, tp) {
clock_time_get(clk_id: unknown, tp: unknown) {
return Date.now();
},
environ_sizes_get() {
debugger;
return 0;
},
environ_get(__environ, environ_buf) {
environ_get(__environ: unknown, environ_buf: unknown) {
debugger;
return 0;
},
fd_close(fd) {
fd_close(fd: number) {
debugger;
return 0;
},
proc_exit() {},
fd_seek(fd, offset_bigint, whence, newOffset) {
fd_seek(fd: number, offset_bigint: bigint, whence: unknown, newOffset: unknown) {
debugger;
},
fd_write(fd, iov, iovcnt, pnum) {
fd_write(fd: unknown, iov: unknown, iovcnt: unknown, pnum: unknown) {
debugger;
},
};
@@ -89,16 +97,16 @@ var scratch2: Uint8Array;
const env = {
console_log(slice: number) {
// @ts-expect-error
const text = Bun._wasmPtrLenToString(slice);
if (captureErrors) {
capturedErrors.push(text);
return;
}
//@ts-ignore
console.log(text);
},
console_error(slice: number) {
//@ts-ignore
// @ts-expect-error
const text = Bun._wasmPtrLenToString(slice);
if (captureErrors) {
capturedErrors.push(text);
@@ -107,19 +115,17 @@ const env = {
console.error(text);
},
console_warn(slice: number) {
//@ts-ignore
// @ts-expect-error
console.warn(Bun._wasmPtrLenToString(slice));
},
console_info(slice: number) {
//@ts-ignore
// @ts-expect-error
console.info(Bun._wasmPtrLenToString(slice));
},
// @ts-ignore-line
__indirect_function_table: new WebAssembly.Table({
initial: 0,
element: "anyfunc",
}),
// @ts-ignore-line
__stack_pointer: new WebAssembly.Global({
mutable: true,
value: "i32",
@@ -131,11 +137,11 @@ const env = {
return one % two;
},
memset(ptr: number, value: number, len: number) {
//@ts-ignore
// @ts-expect-error
Bun.memory_array.fill(value, ptr, ptr + len);
},
memcpy(ptr: number, value: number, len: number) {
//@ts-ignore
// @ts-expect-error
Bun.memory_array.copyWithin(ptr, value, value + len);
},
// These functions convert a to an unsigned long long, rounding toward zero. Negative values all become zero.
@@ -167,14 +173,13 @@ const env = {
};
export class Bun {
private static has_initialized = false;
// @ts-ignore-line
private static wasm_source: WebAssembly.WebAssemblyInstantiatedSource = null;
private static wasm_source: WebAssembly.WebAssemblyInstantiatedSource;
private static get wasm_exports(): WebAssemblyModule {
return Bun.wasm_source.instance.exports as any;
return Bun.wasm_source.instance.exports as unknown as WebAssemblyModule;
}
// @ts-ignore-line
private static get memory(): WebAssembly.Memory {
return Bun.wasm_source.instance.exports.memory as any;
return Bun.wasm_source.instance.exports.memory as WebAssembly.Memory;
}
private static memory_array: Uint8Array;
@@ -195,7 +200,8 @@ export class Bun {
return Bun._decoder.decode(region);
}
static async init(url, heapSize = 64_000_000, fetch = globalThis.fetch) {
static async init(url?: URL | string | null, heapSize = 64_000_000, fetch = globalThis.fetch) {
url ??= new URL("./bun.wasm", import.meta.url);
scratch = new Uint8Array(8096);
if (Bun.has_initialized) {
@@ -216,9 +222,12 @@ export class Bun {
// is it node?
}
} else {
//@ts-ignore
const fs = await import("fs");
if (typeof url === 'string' && url.startsWith('file://')) {
url = new URL(url); // fs.readFileSync cannot consume URL strings, only URL objects
}
Bun.wasm_source = await globalThis.WebAssembly.instantiate(fs.readFileSync(url), {
env: env,
wasi_snapshot_preview1: Wasi,
@@ -234,7 +243,7 @@ export class Bun {
Bun.has_initialized = true;
}
static getTests(content: Uint8Array | string, filename = "my.test.tsx") {
static getTests(content: Uint8Array, filename = "my.test.tsx") {
const bb = new ByteBuffer(scratch);
bb.length = 0;
bb.index = 0;
@@ -283,7 +292,7 @@ export class Bun {
var _bb = new ByteBuffer(Bun._wasmPtrToSlice(resp_ptr));
const response = decodeGetTestsResponse(_bb);
var tests = new Array(response.tests.length);
var tests: TestReference[] = new Array(response.tests.length);
for (var i = 0; i < response.tests.length; i++) {
tests[i] = {
@@ -294,7 +303,7 @@ export class Bun {
),
),
byteOffset: response.tests[i].byteOffset,
kind: testKindMap[response.tests[i].kind],
kind: testKindMap[response.tests[i].kind] as 'test' | 'describe',
};
}
@@ -303,7 +312,7 @@ export class Bun {
return tests;
}
static transformSync(content: Uint8Array | string, file_name: string, loader?: Loader): TransformResponse {
static transformSync(content: Uint8Array | string, file_name: string, loader?: keyof typeof Loader): TransformResponse {
const bb = new ByteBuffer(scratch);
bb.length = 0;
bb.index = 0;
@@ -330,7 +339,6 @@ export class Bun {
{
contents: contents_buffer,
path: file_name,
// @ts-ignore
loader: normalizeLoader(file_name, loader),
},
bb,
@@ -349,7 +357,7 @@ export class Bun {
return response;
}
static scan(content: Uint8Array | string, file_name: string, loader?: Loader): ScanResult {
static scan(content: Uint8Array | string, file_name: string, loader?: keyof typeof Loader): ScanResult {
const bb = new ByteBuffer(scratch);
bb.length = 0;
bb.index = 0;
@@ -368,7 +376,6 @@ export class Bun {
{
contents: contents_buffer,
path: file_name,
// @ts-ignore
loader: normalizeLoader(file_name, loader),
},
bb,

View File

@@ -1,7 +1,12 @@
{
"name": "bun-wasm",
"version": "0.0.79",
"scripts": {
"build": "cd ../.. && make wasm",
"dev": "cd ../.. && make dev-wasm"
},
"devDependencies": {
"@types/node": "^20.4.10",
"peechy": "0.4.32",
"typescript": "latest"
},
@@ -16,7 +21,7 @@
"schema.js"
],
"type": "module",
"types": "index.d.ts",
"types": "index.d.mts",
"exports": {
".": {
"import": "./index.mjs",

View File

@@ -1,7 +1,10 @@
import { readFileSync } from "fs";
import { init, getTests } from "../index.mjs";
const buf = (process.argv.length > 2 ? readFileSync(process.argv.at(-1)) : "") || readFileSync(import.meta.url);
await init(new URL("../bun.wasm", import.meta.url));
const filePath = process.argv[2];
if (!filePath) throw new Error("Usage: node node.mjs <file>");
const buf = readFileSync(filePath);
await init();
console.log(getTests(buf));

View File

@@ -8,8 +8,10 @@
"isolatedModules": false,
"skipLibCheck": true,
"emitDeclarationOnly": true,
"strict": true,
"outDir": ".",
"baseUrl": "."
"baseUrl": ".",
"types": ["node"]
},
"include": [
"./node_modules/peechy",