aboutsummaryrefslogtreecommitdiff
path: root/test/bun.js
diff options
context:
space:
mode:
Diffstat (limited to 'test/bun.js')
-rw-r--r--test/bun.js/atob.test.js77
-rw-r--r--test/bun.js/baz.js2
-rw-r--r--test/bun.js/buffer.test.js304
-rw-r--r--test/bun.js/bun-jsc.test.js98
-rwxr-xr-xtest/bun.js/bun.lockbbin0 -> 109914 bytes
l---------test/bun.js/bundled/always-bundled-module/always-bundled-module1
-rw-r--r--test/bun.js/bundled/always-bundled-module/cjs.js10
-rw-r--r--test/bun.js/bundled/always-bundled-module/esm.js5
-rw-r--r--test/bun.js/bundled/always-bundled-module/package.json4
-rw-r--r--test/bun.js/bundled/entrypoint.ts13
-rw-r--r--test/bun.js/bundled/package.json12
-rw-r--r--test/bun.js/bundled/to_bundle_node_modules/i-am-bundled/cjs.js10
-rw-r--r--test/bun.js/bundled/to_bundle_node_modules/i-am-bundled/esm.js5
-rw-r--r--test/bun.js/bundled/to_bundle_node_modules/i-am-bundled/package.json4
-rw-r--r--test/bun.js/bundled/tsconfig.json6
-rw-r--r--test/bun.js/concat.test.js46
-rw-r--r--test/bun.js/console-log.js58
-rw-r--r--test/bun.js/crypto.test.js70
-rw-r--r--test/bun.js/dirname.test.js9
-rw-r--r--test/bun.js/escapeHTML.test.js105
-rw-r--r--test/bun.js/esm/first.mjs8
-rw-r--r--test/bun.js/esm/second-child.mjs5
-rw-r--r--test/bun.js/esm/second.mjs7
-rw-r--r--test/bun.js/esm/startEnd.mjs6
-rw-r--r--test/bun.js/esm/third.mjs4
-rw-r--r--test/bun.js/exit.js2
-rw-r--r--test/bun.js/fetch.js.txt46
-rw-r--r--test/bun.js/fetch.test.js440
-rw-r--r--test/bun.js/ffi-test.c129
-rw-r--r--test/bun.js/ffi.test.fixture.callback.c270
-rw-r--r--test/bun.js/ffi.test.fixture.receiver.c268
-rw-r--r--test/bun.js/ffi.test.js543
-rw-r--r--test/bun.js/fs-stream.js23
-rw-r--r--test/bun.js/fs.test.js244
-rw-r--r--test/bun.js/gc.js15
-rw-r--r--test/bun.js/globals.test.js39
-rw-r--r--test/bun.js/hash.test.js36
-rw-r--r--test/bun.js/html-rewriter.test.js293
-rw-r--r--test/bun.js/import-meta.test.js33
-rw-r--r--test/bun.js/inline.macro.js3
-rw-r--r--test/bun.js/inspect.test.js96
-rw-r--r--test/bun.js/macro-check.js7
-rw-r--r--test/bun.js/microtask.test.js80
-rw-r--r--test/bun.js/mmap.test.js69
-rw-r--r--test/bun.js/node-builtins.test.js16
-rw-r--r--test/bun.js/path.test.js457
-rw-r--r--test/bun.js/performance.test.js18
-rw-r--r--test/bun.js/process-nexttick.js91
-rw-r--r--test/bun.js/process-nexttick.test.js93
-rw-r--r--test/bun.js/process.test.js54
-rw-r--r--test/bun.js/readFileSync.txt1
-rw-r--r--test/bun.js/readdir.js9
-rw-r--r--test/bun.js/reportError.test.js25
-rw-r--r--test/bun.js/require-json.json3
-rw-r--r--test/bun.js/resolve-typescript-file.tsx1
-rw-r--r--test/bun.js/resolve.test.js100
-rw-r--r--test/bun.js/response.file.test.js218
-rw-r--r--test/bun.js/serve.test.ts82
-rw-r--r--test/bun.js/setInterval.test.js35
-rw-r--r--test/bun.js/setTimeout.test.js39
-rw-r--r--test/bun.js/shadow.test.js10
-rw-r--r--test/bun.js/sleep.js10
-rw-r--r--test/bun.js/solid-dom-fixtures/SVG/code.js74
-rw-r--r--test/bun.js/solid-dom-fixtures/SVG/output.bun.js33
-rw-r--r--test/bun.js/solid-dom-fixtures/SVG/output.js108
-rw-r--r--test/bun.js/solid-dom-fixtures/attributeExpressions/code.js115
-rw-r--r--test/bun.js/solid-dom-fixtures/attributeExpressions/output.bun.js155
-rw-r--r--test/bun.js/solid-dom-fixtures/attributeExpressions/output.js241
-rw-r--r--test/bun.js/solid-dom-fixtures/components/code.js161
-rw-r--r--test/bun.js/solid-dom-fixtures/components/output.bun.js205
-rw-r--r--test/bun.js/solid-dom-fixtures/components/output.js443
-rw-r--r--test/bun.js/solid-dom-fixtures/conditionalExpressions/code.js71
-rw-r--r--test/bun.js/solid-dom-fixtures/conditionalExpressions/output.bun.js144
-rw-r--r--test/bun.js/solid-dom-fixtures/conditionalExpressions/output.js319
-rw-r--r--test/bun.js/solid-dom-fixtures/customElements/code.js29
-rw-r--r--test/bun.js/solid-dom-fixtures/customElements/output.bun.js27
-rw-r--r--test/bun.js/solid-dom-fixtures/customElements/output.js66
-rw-r--r--test/bun.js/solid-dom-fixtures/eventExpressions/code.js32
-rw-r--r--test/bun.js/solid-dom-fixtures/eventExpressions/output.bun.js57
-rw-r--r--test/bun.js/solid-dom-fixtures/eventExpressions/output.js63
-rw-r--r--test/bun.js/solid-dom-fixtures/fragments/code.js83
-rw-r--r--test/bun.js/solid-dom-fixtures/fragments/output.bun.js13
-rw-r--r--test/bun.js/solid-dom-fixtures/fragments/output.js66
-rw-r--r--test/bun.js/solid-dom-fixtures/insertChildren/code.js36
-rw-r--r--test/bun.js/solid-dom-fixtures/insertChildren/output.js185
-rw-r--r--test/bun.js/solid-dom-fixtures/namespaceElements/code.js6
-rw-r--r--test/bun.js/solid-dom-fixtures/namespaceElements/output.js16
-rw-r--r--test/bun.js/solid-dom-fixtures/simpleElements/code.js9
-rw-r--r--test/bun.js/solid-dom-fixtures/simpleElements/output.bun.js5
-rw-r--r--test/bun.js/solid-dom-fixtures/simpleElements/output.js8
-rw-r--r--test/bun.js/solid-dom-fixtures/textInterpolation/code.js72
-rw-r--r--test/bun.js/solid-dom-fixtures/textInterpolation/output.bun.js71
-rw-r--r--test/bun.js/solid-dom-fixtures/textInterpolation/output.js144
-rw-r--r--test/bun.js/some-fs.js51
-rw-r--r--test/bun.js/sql-raw.test.js71
-rw-r--r--test/bun.js/sqlite.test.js430
-rw-r--r--test/bun.js/streams.test.js317
-rw-r--r--test/bun.js/text-encoder.test.js212
-rw-r--r--test/bun.js/toml-fixture.toml39
-rw-r--r--test/bun.js/toml.test.js30
-rw-r--r--test/bun.js/transpiler.test.js1675
-rw-r--r--test/bun.js/tsconfig.json16
-rw-r--r--test/bun.js/unsafe.test.js51
-rw-r--r--test/bun.js/url.test.ts102
-rw-r--r--test/bun.js/wasm-return-1-test.zig5
-rw-r--r--test/bun.js/wasm.js1
-rw-r--r--test/bun.js/wasm.test.js20
-rw-r--r--test/bun.js/web-globals.test.js46
-rw-r--r--test/bun.js/websocket.test.js79
-rw-r--r--test/bun.js/writeFileSync.txt1
-rw-r--r--test/bun.js/zlib.test.js18
111 files changed, 10988 insertions, 0 deletions
diff --git a/test/bun.js/atob.test.js b/test/bun.js/atob.test.js
new file mode 100644
index 000000000..4945829e1
--- /dev/null
+++ b/test/bun.js/atob.test.js
@@ -0,0 +1,77 @@
+import { expect, it } from "bun:test";
+
+function expectInvalidCharacters(val) {
+ try {
+ atob(val);
+ throw new Error("Expected error");
+ } catch (error) {
+ expect(error.message).toBe("The string contains invalid characters.");
+ }
+}
+
+it("atob", () => {
+ expect(atob("YQ==")).toBe("a");
+ expect(atob("YWI=")).toBe("ab");
+ expect(atob("YWJj")).toBe("abc");
+ expect(atob("YWJjZA==")).toBe("abcd");
+ expect(atob("YWJjZGU=")).toBe("abcde");
+ expect(atob("YWJjZGVm")).toBe("abcdef");
+ expect(atob("zzzz")).toBe("Ï<ó");
+ expect(atob("")).toBe("");
+ expect(atob(null)).toBe("žée");
+ expect(atob("6ek=")).toBe("éé");
+ expect(atob("6ek")).toBe("éé");
+ expect(atob("gIE=")).toBe("€");
+ expect(atob("zz")).toBe("Ï");
+ expect(atob("zzz")).toBe("Ï<");
+ expect(atob("zzz=")).toBe("Ï<");
+ expect(atob(" YQ==")).toBe("a");
+ expect(atob("YQ==\u000a")).toBe("a");
+
+ try {
+ atob();
+ } catch (error) {
+ expect(error.name).toBe("TypeError");
+ }
+ expectInvalidCharacters(undefined);
+ expectInvalidCharacters(" abcd===");
+ expectInvalidCharacters("abcd=== ");
+ expectInvalidCharacters("abcd ===");
+ expectInvalidCharacters("тест");
+ expectInvalidCharacters("z");
+ expectInvalidCharacters("zzz==");
+ expectInvalidCharacters("zzz===");
+ expectInvalidCharacters("zzz====");
+ expectInvalidCharacters("zzz=====");
+ expectInvalidCharacters("zzzzz");
+ expectInvalidCharacters("z=zz");
+ expectInvalidCharacters("=");
+ expectInvalidCharacters("==");
+ expectInvalidCharacters("===");
+ expectInvalidCharacters("====");
+ expectInvalidCharacters("=====");
+});
+
+it("btoa", () => {
+ expect(btoa("a")).toBe("YQ==");
+ expect(btoa("ab")).toBe("YWI=");
+ expect(btoa("abc")).toBe("YWJj");
+ expect(btoa("abcd")).toBe("YWJjZA==");
+ expect(btoa("abcde")).toBe("YWJjZGU=");
+ expect(btoa("abcdef")).toBe("YWJjZGVm");
+ expect(typeof btoa).toBe("function");
+ try {
+ btoa();
+ throw new Error("Expected error");
+ } catch (error) {
+ expect(error.name).toBe("TypeError");
+ }
+ var window = "[object Window]";
+ expect(btoa("")).toBe("");
+ expect(btoa(null)).toBe("bnVsbA==");
+ expect(btoa(undefined)).toBe("dW5kZWZpbmVk");
+ expect(btoa(window)).toBe("W29iamVjdCBXaW5kb3dd");
+ expect(btoa("éé")).toBe("6ek=");
+ expect(btoa("\u0080\u0081")).toBe("gIE=");
+ expect(btoa(Bun)).toBe(btoa("[object Bun]"));
+});
diff --git a/test/bun.js/baz.js b/test/bun.js/baz.js
new file mode 100644
index 000000000..5837bb3bb
--- /dev/null
+++ b/test/bun.js/baz.js
@@ -0,0 +1,2 @@
+// this file is used in resolve.test.js
+export default {};
diff --git a/test/bun.js/buffer.test.js b/test/bun.js/buffer.test.js
new file mode 100644
index 000000000..6e9a3c6b4
--- /dev/null
+++ b/test/bun.js/buffer.test.js
@@ -0,0 +1,304 @@
+// import { describe, it, expect, beforeEach, afterEach } from "bun:test";
+// import { gc } from "./gc";
+
+// beforeEach(() => gc());
+// afterEach(() => gc());
+
+// it("buffer", () => {
+// var buf = new Buffer(20);
+// gc();
+// // if this fails or infinitely loops, it means there is a memory issue with the JSC::Structure object
+// expect(Object.keys(buf).length > 0).toBe(true);
+// gc();
+// expect(buf.write("hello world ")).toBe(12);
+// expect(buf.write("hello world ", "utf8")).toBe(12);
+
+// gc();
+// expect(buf.toString("utf8", 0, "hello world ".length)).toBe("hello world ");
+// gc();
+// expect(buf.toString("base64url", 0, "hello world ".length)).toBe(
+// btoa("hello world ")
+// );
+// gc();
+// expect(buf instanceof Uint8Array).toBe(true);
+// gc();
+// expect(buf instanceof Buffer).toBe(true);
+// gc();
+// expect(buf.slice() instanceof Uint8Array).toBe(true);
+// gc();
+// expect(buf.slice(0, 1) instanceof Buffer).toBe(true);
+// gc();
+// expect(buf.slice(0, 1) instanceof Uint8Array).toBe(true);
+// gc();
+// expect(buf.slice(0, 1) instanceof Buffer).toBe(true);
+// gc();
+// });
+
+// it("Buffer", () => {
+// var inputs = [
+// "hello world",
+// "hello world".repeat(100),
+// `😋 Get Emoji — All Emojis to ✂️ Copy and 📋 Paste 👌`,
+// ];
+// var good = inputs.map((a) => new TextEncoder().encode(a));
+// for (let i = 0; i < inputs.length; i++) {
+// var input = inputs[i];
+// expect(new Buffer(input).toString("utf8")).toBe(inputs[i]);
+// gc();
+// expect(Array.from(new Buffer(input)).join(",")).toBe(good[i].join(","));
+// gc();
+// expect(Buffer.byteLength(input)).toBe(good[i].length);
+// gc();
+// expect(Buffer.from(input).byteLength).toBe(Buffer.byteLength(input));
+// }
+// });
+
+// it("Buffer.byteLength", () => {
+// expect(Buffer.byteLength("😀😃😄😁😆😅😂🤣☺️😊😊😇")).toBe(
+// new TextEncoder().encode("😀😃😄😁😆😅😂🤣☺️😊😊😇").byteLength
+// );
+// });
+
+// it("Buffer.isBuffer", () => {
+// expect(Buffer.isBuffer(new Buffer(1))).toBe(true);
+// gc();
+// expect(Buffer.isBuffer(new Buffer(0))).toBe(true);
+// gc();
+// expect(Buffer.isBuffer(new Uint8Array(0))).toBe(false);
+// gc();
+// expect(Buffer.isBuffer(new Uint8Array(1))).toBe(false);
+// gc();
+// var a = new Uint8Array(1);
+// gc();
+// expect(Buffer.isBuffer(a)).toBe(false);
+// gc();
+// Buffer.toBuffer(a);
+// gc();
+// expect(Buffer.isBuffer(a)).toBe(true);
+// gc();
+// });
+
+// it("Buffer.toBuffer throws", () => {
+// const checks = [
+// [],
+// {},
+// "foo",
+// new Uint16Array(),
+// new DataView(new Uint8Array(14).buffer),
+// ];
+// for (let i = 0; i < checks.length; i++) {
+// try {
+// Buffer.toBuffer(checks[i]);
+// expect(false).toBe(true);
+// } catch (exception) {
+// expect(exception.message).toBe("Expected Uint8Array");
+// }
+// }
+// expect(true).toBe(true);
+// });
+
+// it("Buffer.toBuffer works", () => {
+// var array = new Uint8Array(20);
+// expect(array instanceof Buffer).toBe(false);
+// var buf = Buffer.toBuffer(array);
+// expect(array instanceof Buffer).toBe(true);
+// // if this fails or infinitely loops, it means there is a memory issue with the JSC::Structure object
+// expect(Object.keys(buf).length > 0).toBe(true);
+
+// expect(buf.write("hello world ")).toBe(12);
+// gc();
+// expect(buf.toString("utf8", 0, "hello world ".length)).toBe("hello world ");
+// gc();
+// expect(buf.toString("base64url", 0, "hello world ".length)).toBe(
+// btoa("hello world ")
+// );
+// gc();
+
+// expect(buf instanceof Uint8Array).toBe(true);
+// expect(buf instanceof Buffer).toBe(true);
+// expect(buf.slice() instanceof Uint8Array).toBe(true);
+// expect(buf.slice(0, 1) instanceof Buffer).toBe(true);
+// expect(buf.slice(0, 1) instanceof Uint8Array).toBe(true);
+// expect(buf.slice(0, 1) instanceof Buffer).toBe(true);
+// expect(new Buffer(buf) instanceof Buffer).toBe(true);
+// expect(new Buffer(buf.buffer) instanceof Buffer).toBe(true);
+// });
+
+// it("writeInt", () => {
+// var buf = new Buffer(1024);
+// var data = new DataView(buf.buffer);
+// buf.writeInt32BE(100);
+// expect(data.getInt32(0, false)).toBe(100);
+// buf.writeInt32BE(100);
+// expect(data.getInt32(0, false)).toBe(100);
+// var childBuf = buf.subarray(0, 4);
+// expect(data.getInt32(0, false)).toBe(100);
+// expect(childBuf.readInt32BE(0, false)).toBe(100);
+// });
+
+// it("Buffer.from", () => {
+// expect(Buffer.from("hello world").toString("utf8")).toBe("hello world");
+// expect(Buffer.from("hello world", "ascii").toString("utf8")).toBe(
+// "hello world"
+// );
+// expect(Buffer.from("hello world", "latin1").toString("utf8")).toBe(
+// "hello world"
+// );
+// gc();
+// expect(Buffer.from([254]).join(",")).toBe("254");
+// expect(Buffer.from(123).join(",")).toBe(Uint8Array.from(123).join(","));
+// expect(Buffer.from({ length: 124 }).join(",")).toBe(
+// Uint8Array.from({ length: 124 }).join(",")
+// );
+
+// expect(Buffer.from(new ArrayBuffer(1024), 0, 512).join(",")).toBe(
+// new Uint8Array(512).join(",")
+// );
+
+// expect(Buffer.from(new Buffer(new ArrayBuffer(1024), 0, 512)).join(",")).toBe(
+// new Uint8Array(512).join(",")
+// );
+// gc();
+// });
+
+// it("Buffer.equals", () => {
+// var a = new Uint8Array(10);
+// a[2] = 1;
+// var b = new Uint8Array(10);
+// b[2] = 1;
+// Buffer.toBuffer(a);
+// Buffer.toBuffer(b);
+// expect(a.equals(b)).toBe(true);
+// b[2] = 0;
+// expect(a.equals(b)).toBe(false);
+// });
+
+// it("Buffer.compare", () => {
+// var a = new Uint8Array(10);
+// a[2] = 1;
+// var b = new Uint8Array(10);
+// b[2] = 1;
+// Buffer.toBuffer(a);
+// Buffer.toBuffer(b);
+// expect(a.compare(b)).toBe(0);
+// b[2] = 0;
+// expect(a.compare(b)).toBe(1);
+// expect(b.compare(a)).toBe(-1);
+// });
+
+// it("Buffer.copy", () => {
+// var array1 = new Uint8Array(128);
+// array1.fill(100);
+// Buffer.toBuffer(array1);
+// var array2 = new Uint8Array(128);
+// array2.fill(200);
+// Buffer.toBuffer(array2);
+// var array3 = new Uint8Array(128);
+// Buffer.toBuffer(array3);
+// gc();
+// expect(array1.copy(array2)).toBe(128);
+// expect(array1.join("")).toBe(array2.join(""));
+// });
+
+// it("Buffer.concat", () => {
+// var array1 = new Uint8Array(128);
+// array1.fill(100);
+// var array2 = new Uint8Array(128);
+// array2.fill(200);
+// var array3 = new Uint8Array(128);
+// array3.fill(300);
+// gc();
+// expect(Buffer.concat([array1, array2, array3]).join("")).toBe(
+// array1.join("") + array2.join("") + array3.join("")
+// );
+// expect(Buffer.concat([array1, array2, array3], 222).length).toBe(222);
+// expect(
+// Buffer.concat([array1, array2, array3], 222).subarray(0, 128).join("")
+// ).toBe("100".repeat(128));
+// expect(
+// Buffer.concat([array1, array2, array3], 222).subarray(129, 222).join("")
+// ).toBe("200".repeat(222 - 129));
+// });
+
+// it("read", () => {
+// var buf = new Buffer(1024);
+// var data = new DataView(buf.buffer);
+// function reset() {
+// new Uint8Array(buf.buffer).fill(0);
+// }
+// data.setBigInt64(0, BigInt(1000), false);
+// expect(buf.readBigInt64BE(0)).toBe(BigInt(1000));
+// reset();
+
+// data.setBigInt64(0, BigInt(1000), false);
+// expect(buf.readBigInt64LE(0)).toBe(BigInt(1000));
+// reset();
+
+// data.setBigUint64(0, BigInt(1000), false);
+// expect(buf.readBigUInt64BE(0)).toBe(BigInt(1000));
+// reset();
+
+// data.setBigUint64(0, BigInt(1000), false);
+// expect(buf.readBigUInt64LE(0)).toBe(BigInt(1000));
+// reset();
+
+// data.setFloat64(0, 1000, false);
+// expect(buf.readDoubleBE(0)).toBe(1000);
+// reset();
+
+// data.setFloat64(0, 1000, true);
+// expect(buf.readDoubleLE(0)).toBe(1000);
+// reset();
+
+// data.setFloat32(0, 1000, false);
+// expect(buf.readFloatBE(0)).toBe(1000);
+// reset();
+
+// data.setFloat32(0, 1000, true);
+// expect(buf.readFloatLE(0)).toBe(1000);
+// reset();
+
+// data.setInt16(0, 1000, false);
+// expect(buf.readInt16BE(0)).toBe(1000);
+// reset();
+
+// data.setInt16(0, 1000, true);
+// expect(buf.readInt16LE(0)).toBe(1000);
+// reset();
+
+// data.setInt32(0, 1000, false);
+// expect(buf.readInt32BE(0)).toBe(1000);
+// reset();
+
+// data.setInt32(0, 1000, true);
+// expect(buf.readInt32LE(0)).toBe(1000);
+// reset();
+
+// data.setInt8(0, 100, false);
+// expect(buf.readInt8(0)).toBe(100);
+// reset();
+
+// data.setUint16(0, 1000, false);
+// expect(buf.readUInt16BE(0)).toBe(1000);
+// reset();
+
+// data.setUint16(0, 1000, true);
+// expect(buf.readUInt16LE(0)).toBe(1000);
+// reset();
+
+// data.setUint32(0, 1000, false);
+// expect(buf.readUInt32BE(0)).toBe(1000);
+// reset();
+
+// data.setUint32(0, 1000, true);
+// expect(buf.readUInt32LE(0)).toBe(1000);
+// reset();
+
+// data.setUint8(0, 255, false);
+// expect(buf.readUInt8(0)).toBe(255);
+// reset();
+
+// data.setUint8(0, 255, false);
+// expect(buf.readUInt8(0)).toBe(255);
+// reset();
+// });
diff --git a/test/bun.js/bun-jsc.test.js b/test/bun.js/bun-jsc.test.js
new file mode 100644
index 000000000..8ee0decf2
--- /dev/null
+++ b/test/bun.js/bun-jsc.test.js
@@ -0,0 +1,98 @@
+import { describe, expect, it } from "bun:test";
+import {
+ describe as jscDescribe,
+ describeArray,
+ gcAndSweep,
+ fullGC,
+ edenGC,
+ heapSize,
+ heapStats,
+ memoryUsage,
+ getRandomSeed,
+ setRandomSeed,
+ isRope,
+ callerSourceOrigin,
+ noFTL,
+ noOSRExitFuzzing,
+ optimizeNextInvocation,
+ numberOfDFGCompiles,
+ releaseWeakRefs,
+ totalCompileTime,
+ reoptimizationRetryCount,
+ drainMicrotasks,
+ startRemoteDebugger,
+} from "bun:jsc";
+
+describe("bun:jsc", () => {
+ function count() {
+ var j = 0;
+ for (var i = 0; i < 999999; i++) {
+ j += i + 2;
+ }
+
+ return j;
+ }
+
+ it("describe", () => {
+ jscDescribe([]);
+ });
+ it("describeArray", () => {
+ describeArray([1, 2, 3]);
+ });
+ it("gcAndSweep", () => {
+ gcAndSweep();
+ });
+ it("fullGC", () => {
+ fullGC();
+ });
+ it("edenGC", () => {
+ edenGC();
+ });
+ it("heapSize", () => {
+ expect(heapSize() > 0).toBe(true);
+ });
+ it("heapStats", () => {
+ heapStats();
+ });
+ it("memoryUsage", () => {
+ memoryUsage();
+ });
+ it("getRandomSeed", () => {
+ getRandomSeed(2);
+ });
+ it("setRandomSeed", () => {
+ setRandomSeed(2);
+ });
+ it("isRope", () => {
+ expect(isRope("a" + 123 + "b")).toBe(true);
+ expect(isRope("abcdefgh")).toBe(false);
+ });
+ it("callerSourceOrigin", () => {
+ expect(callerSourceOrigin()).toBe(import.meta.url);
+ });
+ it("noFTL", () => {});
+ it("noOSRExitFuzzing", () => {});
+ it("optimizeNextInvocation", () => {
+ count();
+ optimizeNextInvocation(count);
+ count();
+ });
+ it("numberOfDFGCompiles", () => {
+ expect(numberOfDFGCompiles(count) > 0).toBe(true);
+ });
+ it("releaseWeakRefs", () => {
+ releaseWeakRefs();
+ });
+ it("totalCompileTime", () => {
+ totalCompileTime(count);
+ });
+ it("reoptimizationRetryCount", () => {
+ reoptimizationRetryCount(count);
+ });
+ it("drainMicrotasks", () => {
+ drainMicrotasks();
+ });
+ it("startRemoteDebugger", () => {
+ startRemoteDebugger("");
+ });
+});
diff --git a/test/bun.js/bun.lockb b/test/bun.js/bun.lockb
new file mode 100755
index 000000000..cff7a8ddc
--- /dev/null
+++ b/test/bun.js/bun.lockb
Binary files differ
diff --git a/test/bun.js/bundled/always-bundled-module/always-bundled-module b/test/bun.js/bundled/always-bundled-module/always-bundled-module
new file mode 120000
index 000000000..f9a91ac4d
--- /dev/null
+++ b/test/bun.js/bundled/always-bundled-module/always-bundled-module
@@ -0,0 +1 @@
+node_modules/always-bundled-module \ No newline at end of file
diff --git a/test/bun.js/bundled/always-bundled-module/cjs.js b/test/bun.js/bundled/always-bundled-module/cjs.js
new file mode 100644
index 000000000..087697589
--- /dev/null
+++ b/test/bun.js/bundled/always-bundled-module/cjs.js
@@ -0,0 +1,10 @@
+module.exports = {
+ default: 0xdeadbeef,
+ default() {
+ return "ok";
+ },
+ default: true,
+ ok() {
+ return true;
+ },
+};
diff --git a/test/bun.js/bundled/always-bundled-module/esm.js b/test/bun.js/bundled/always-bundled-module/esm.js
new file mode 100644
index 000000000..28e702881
--- /dev/null
+++ b/test/bun.js/bundled/always-bundled-module/esm.js
@@ -0,0 +1,5 @@
+const __esModule = true;
+
+export const foo = () => __esModule;
+
+export { __esModule, foo as default };
diff --git a/test/bun.js/bundled/always-bundled-module/package.json b/test/bun.js/bundled/always-bundled-module/package.json
new file mode 100644
index 000000000..5029c1695
--- /dev/null
+++ b/test/bun.js/bundled/always-bundled-module/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "always-bundled-module",
+ "version": "1.0.0"
+}
diff --git a/test/bun.js/bundled/entrypoint.ts b/test/bun.js/bundled/entrypoint.ts
new file mode 100644
index 000000000..b9a17b538
--- /dev/null
+++ b/test/bun.js/bundled/entrypoint.ts
@@ -0,0 +1,13 @@
+import "i-am-bundled/cjs";
+import "i-am-bundled/esm";
+import "always-bundled-module/esm";
+import "always-bundled-module/cjs";
+import { foo } from "i-am-bundled/esm";
+import { foo as foo2 } from "always-bundled-module/esm";
+import cJS from "always-bundled-module/cjs";
+
+foo();
+foo2();
+cJS();
+
+export default cJS();
diff --git a/test/bun.js/bundled/package.json b/test/bun.js/bundled/package.json
new file mode 100644
index 000000000..cce72af9c
--- /dev/null
+++ b/test/bun.js/bundled/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "to-bundle",
+ "scripts": {
+ "prebundle": "rm -rf node_modules; cp -r to_bundle_node_modules node_modules; ln -s always-bundled-module node_modules/always-bundled-module",
+ "bundle": "${BUN_BIN:-$(which bun)} bun ./entrypoint.ts"
+ },
+ "bun": {
+ "alwaysBundle": [
+ "always-bundled-module"
+ ]
+ }
+}
diff --git a/test/bun.js/bundled/to_bundle_node_modules/i-am-bundled/cjs.js b/test/bun.js/bundled/to_bundle_node_modules/i-am-bundled/cjs.js
new file mode 100644
index 000000000..087697589
--- /dev/null
+++ b/test/bun.js/bundled/to_bundle_node_modules/i-am-bundled/cjs.js
@@ -0,0 +1,10 @@
+module.exports = {
+ default: 0xdeadbeef,
+ default() {
+ return "ok";
+ },
+ default: true,
+ ok() {
+ return true;
+ },
+};
diff --git a/test/bun.js/bundled/to_bundle_node_modules/i-am-bundled/esm.js b/test/bun.js/bundled/to_bundle_node_modules/i-am-bundled/esm.js
new file mode 100644
index 000000000..28e702881
--- /dev/null
+++ b/test/bun.js/bundled/to_bundle_node_modules/i-am-bundled/esm.js
@@ -0,0 +1,5 @@
+const __esModule = true;
+
+export const foo = () => __esModule;
+
+export { __esModule, foo as default };
diff --git a/test/bun.js/bundled/to_bundle_node_modules/i-am-bundled/package.json b/test/bun.js/bundled/to_bundle_node_modules/i-am-bundled/package.json
new file mode 100644
index 000000000..661a80b2d
--- /dev/null
+++ b/test/bun.js/bundled/to_bundle_node_modules/i-am-bundled/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "i-am-bundled",
+ "version": "1.0.0"
+}
diff --git a/test/bun.js/bundled/tsconfig.json b/test/bun.js/bundled/tsconfig.json
new file mode 100644
index 000000000..358cb5526
--- /dev/null
+++ b/test/bun.js/bundled/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "compilerOptions": {
+ "paths": {},
+ "baseUrl": "."
+ }
+}
diff --git a/test/bun.js/concat.test.js b/test/bun.js/concat.test.js
new file mode 100644
index 000000000..a965fdb94
--- /dev/null
+++ b/test/bun.js/concat.test.js
@@ -0,0 +1,46 @@
+import { describe, it, expect } from "bun:test";
+import { gcTick } from "./gc";
+import { concatArrayBuffers } from "bun";
+
+describe("concat", () => {
+ function polyfill(chunks) {
+ var size = 0;
+ for (const chunk of chunks) {
+ size += chunk.byteLength;
+ }
+ var buffer = new ArrayBuffer(size);
+ var view = new Uint8Array(buffer);
+ var offset = 0;
+ for (const chunk of chunks) {
+ view.set(chunk, offset);
+ offset += chunk.byteLength;
+ }
+ return buffer;
+ }
+
+ function concatToString(chunks) {
+ return Array.from(new Uint8Array(concatArrayBuffers(chunks))).join("");
+ }
+
+ function polyfillToString(chunks) {
+ return Array.from(new Uint8Array(polyfill(chunks))).join("");
+ }
+
+ it("works with one element", () => {
+ expect(concatToString([new Uint8Array([123])])).toBe(
+ polyfillToString([new Uint8Array([123])])
+ );
+ });
+
+ it("works with two elements", () => {
+ expect(
+ concatToString([Uint8Array.from([123]), Uint8Array.from([456])])
+ ).toBe(polyfillToString([Uint8Array.from([123]), Uint8Array.from([456])]));
+ });
+
+ it("works with mix of ArrayBuffer and TypedArray elements", () => {
+ expect(
+ concatToString([Uint8Array.from([123]).buffer, Uint8Array.from([456])])
+ ).toBe(polyfillToString([Uint8Array.from([123]), Uint8Array.from([456])]));
+ });
+});
diff --git a/test/bun.js/console-log.js b/test/bun.js/console-log.js
new file mode 100644
index 000000000..e8aa200ac
--- /dev/null
+++ b/test/bun.js/console-log.js
@@ -0,0 +1,58 @@
+console.log("Hello World!");
+console.log(123);
+console.log(-123);
+console.log(123.567);
+console.log(-123.567);
+console.log(true);
+console.log(false);
+console.log(null);
+console.log(undefined);
+console.log(Symbol("Symbol Description"));
+console.log(new Date(2021, 12, 30, 666, 777, 888, 999));
+console.log([123, 456, 789]);
+console.log({ a: 123, b: 456, c: 789 });
+console.log({
+ a: {
+ b: {
+ c: 123,
+ },
+ bacon: true,
+ },
+});
+
+console.log(new Promise(() => {}));
+
+class Foo {}
+
+console.log(() => {});
+console.log(Foo);
+console.log(new Foo());
+console.log(function foooo() {});
+
+console.log(/FooRegex/);
+
+console.error("uh oh");
+console.time("Check");
+
+console.log(
+ "Is it a bug or a feature that formatting numbers like %d is colored",
+ 123
+);
+console.log(globalThis);
+
+console.log(
+ "String %s should be 2nd word, 456 == %s and percent s %s == %s",
+ "123",
+ "456",
+ "%s",
+ "What",
+ "okay"
+);
+
+const infinteLoop = {
+ foo: {},
+ bar: {},
+};
+
+infinteLoop.bar = infinteLoop;
+console.log(infinteLoop, "am");
diff --git a/test/bun.js/crypto.test.js b/test/bun.js/crypto.test.js
new file mode 100644
index 000000000..c489e11c1
--- /dev/null
+++ b/test/bun.js/crypto.test.js
@@ -0,0 +1,70 @@
+import {
+ sha,
+ MD5,
+ MD4,
+ SHA1,
+ SHA256,
+ SHA384,
+ SHA512,
+ SHA512_256,
+ gc,
+} from "bun";
+import { it, expect, describe } from "bun:test";
+import { readFileSync } from "fs";
+
+describe("crypto", () => {
+ for (let Hash of [MD5, MD4, SHA1, SHA256, SHA384, SHA512, SHA512_256]) {
+ for (let [input, label] of [
+ ["hello world", '"hello world"'],
+ ["hello world".repeat(20).slice(), '"hello world" x 20'],
+ ["", "empty string"],
+ ["a", '"a"'],
+ ]) {
+ describe(label, () => {
+ gc(true);
+
+ it(`${Hash.name} base64`, () => {
+ gc(true);
+ const result = new Hash();
+ result.update(input);
+ expect(typeof result.digest("base64")).toBe("string");
+ gc(true);
+ });
+
+ it(`${Hash.name} hash base64`, () => {
+ Hash.hash(input, "base64");
+ gc(true);
+ });
+
+ it(`${Hash.name} hex`, () => {
+ const result = new Hash();
+ result.update(input);
+ expect(typeof result.digest("hex")).toBe("string");
+ gc(true);
+ });
+
+ it(`${Hash.name} hash hex`, () => {
+ expect(typeof Hash.hash(input, "hex")).toBe("string");
+ gc(true);
+ });
+
+ it(`${Hash.name} buffer`, () => {
+ var buf = new Uint8Array(256);
+ const result = new Hash();
+
+ result.update(input);
+ expect(result.digest(buf)).toBe(buf);
+ expect(buf[0] != 0).toBe(true);
+ gc(true);
+ });
+
+ it(`${Hash.name} buffer`, () => {
+ var buf = new Uint8Array(256);
+
+ expect(Hash.hash(input, buf) instanceof Uint8Array).toBe(true);
+ gc(true);
+ });
+ });
+ }
+ }
+});
diff --git a/test/bun.js/dirname.test.js b/test/bun.js/dirname.test.js
new file mode 100644
index 000000000..98292dc49
--- /dev/null
+++ b/test/bun.js/dirname.test.js
@@ -0,0 +1,9 @@
+import { expect, it } from "bun:test";
+
+it("__dirname should work", () => {
+ expect(import.meta.dir).toBe(__dirname);
+});
+
+it("__filename should work", () => {
+ expect(import.meta.path).toBe(__filename);
+});
diff --git a/test/bun.js/escapeHTML.test.js b/test/bun.js/escapeHTML.test.js
new file mode 100644
index 000000000..ecfcc5e7c
--- /dev/null
+++ b/test/bun.js/escapeHTML.test.js
@@ -0,0 +1,105 @@
+import { describe, it, expect } from "bun:test";
+import { gcTick } from "./gc";
+import { escapeHTML } from "bun";
+
+describe("escapeHTML", () => {
+ // The matrix of cases we need to test for:
+ // 1. Works with short strings
+ // 2. Works with long strings
+ // 3. Works with latin1 strings
+ // 4. Works with utf16 strings
+ // 5. Works when the text to escape is somewhere in the middle
+ // 6. Works when the text to escape is in the beginning
+ // 7. Works when the text to escape is in the end
+ // 8. Returns the same string when there's no need to escape
+ it("works", () => {
+ expect(escapeHTML("absolutely nothing to do here")).toBe(
+ "absolutely nothing to do here"
+ );
+ expect(escapeHTML("<script>alert(1)</script>")).toBe(
+ "&lt;script&gt;alert(1)&lt;/script&gt;"
+ );
+ expect(escapeHTML("<")).toBe("&lt;");
+ expect(escapeHTML(">")).toBe("&gt;");
+ expect(escapeHTML("&")).toBe("&amp;");
+ expect(escapeHTML("'")).toBe("&#x27;");
+ expect(escapeHTML('"')).toBe("&quot;");
+ expect(escapeHTML("\n")).toBe("\n");
+ expect(escapeHTML("\r")).toBe("\r");
+ expect(escapeHTML("\t")).toBe("\t");
+ expect(escapeHTML("\f")).toBe("\f");
+ expect(escapeHTML("\v")).toBe("\v");
+ expect(escapeHTML("\b")).toBe("\b");
+ expect(escapeHTML("\u00A0")).toBe("\u00A0");
+ expect(escapeHTML("<script>ab")).toBe("&lt;script&gt;ab");
+ expect(escapeHTML("<script>")).toBe("&lt;script&gt;");
+ expect(escapeHTML("<script><script>")).toBe("&lt;script&gt;&lt;script&gt;");
+
+ expect(escapeHTML("lalala" + "<script>alert(1)</script>" + "lalala")).toBe(
+ "lalala&lt;script&gt;alert(1)&lt;/script&gt;lalala"
+ );
+
+ expect(escapeHTML("<script>alert(1)</script>" + "lalala")).toBe(
+ "&lt;script&gt;alert(1)&lt;/script&gt;lalala"
+ );
+ expect(escapeHTML("lalala" + "<script>alert(1)</script>")).toBe(
+ "lalala" + "&lt;script&gt;alert(1)&lt;/script&gt;"
+ );
+
+ expect(escapeHTML("What does 😊 mean?")).toBe("What does 😊 mean?");
+ const output = escapeHTML("<What does 😊");
+ expect(output).toBe("&lt;What does 😊");
+ expect(escapeHTML("<div>What does 😊 mean in text?")).toBe(
+ "&lt;div&gt;What does 😊 mean in text?"
+ );
+
+ expect(
+ escapeHTML(
+ ("lalala" + "<script>alert(1)</script>" + "lalala").repeat(900)
+ )
+ ).toBe("lalala&lt;script&gt;alert(1)&lt;/script&gt;lalala".repeat(900));
+ expect(
+ escapeHTML(("<script>alert(1)</script>" + "lalala").repeat(900))
+ ).toBe("&lt;script&gt;alert(1)&lt;/script&gt;lalala".repeat(900));
+ expect(
+ escapeHTML(("lalala" + "<script>alert(1)</script>").repeat(900))
+ ).toBe(("lalala" + "&lt;script&gt;alert(1)&lt;/script&gt;").repeat(900));
+
+ // the positions of the unicode codepoint are important
+ // our simd code for U16 is at 8 bytes, so we need to especially check the boundaries
+ expect(
+ escapeHTML("😊lalala" + "<script>alert(1)</script>" + "lalala")
+ ).toBe("😊lalala&lt;script&gt;alert(1)&lt;/script&gt;lalala");
+ expect(escapeHTML("<script>😊alert(1)</script>" + "lalala")).toBe(
+ "&lt;script&gt;😊alert(1)&lt;/script&gt;lalala"
+ );
+ expect(escapeHTML("<script>alert(1)😊</script>" + "lalala")).toBe(
+ "&lt;script&gt;alert(1)😊&lt;/script&gt;lalala"
+ );
+ expect(escapeHTML("<script>alert(1)</script>" + "😊lalala")).toBe(
+ "&lt;script&gt;alert(1)&lt;/script&gt;😊lalala"
+ );
+ expect(escapeHTML("<script>alert(1)</script>" + "lal😊ala")).toBe(
+ "&lt;script&gt;alert(1)&lt;/script&gt;lal😊ala"
+ );
+ expect(
+ escapeHTML("<script>alert(1)</script>" + "lal😊ala".repeat(10))
+ ).toBe("&lt;script&gt;alert(1)&lt;/script&gt;" + "lal😊ala".repeat(10));
+
+ for (let i = 1; i < 10; i++)
+ expect(escapeHTML("<script>alert(1)</script>" + "la😊".repeat(i))).toBe(
+ "&lt;script&gt;alert(1)&lt;/script&gt;" + "la😊".repeat(i)
+ );
+
+ expect(escapeHTML("la😊" + "<script>alert(1)</script>")).toBe(
+ "la😊" + "&lt;script&gt;alert(1)&lt;/script&gt;"
+ );
+ expect(
+ escapeHTML(("lalala" + "<script>alert(1)</script>😊").repeat(1))
+ ).toBe(("lalala" + "&lt;script&gt;alert(1)&lt;/script&gt;😊").repeat(1));
+
+ expect(escapeHTML("😊".repeat(100))).toBe("😊".repeat(100));
+ expect(escapeHTML("😊<".repeat(100))).toBe("😊&lt;".repeat(100));
+ expect(escapeHTML("<😊>".repeat(100))).toBe("&lt;😊&gt;".repeat(100));
+ });
+});
diff --git a/test/bun.js/esm/first.mjs b/test/bun.js/esm/first.mjs
new file mode 100644
index 000000000..17021c623
--- /dev/null
+++ b/test/bun.js/esm/first.mjs
@@ -0,0 +1,8 @@
+import { end, start } from "./startEnd.mjs";
+
+start("First");
+
+import "./second.mjs";
+import "./third.mjs";
+
+end("First");
diff --git a/test/bun.js/esm/second-child.mjs b/test/bun.js/esm/second-child.mjs
new file mode 100644
index 000000000..5fb06ed45
--- /dev/null
+++ b/test/bun.js/esm/second-child.mjs
@@ -0,0 +1,5 @@
+import { start, end } from "./startEnd.mjs";
+
+start("Second (nested import)");
+
+end("Second (nested import)");
diff --git a/test/bun.js/esm/second.mjs b/test/bun.js/esm/second.mjs
new file mode 100644
index 000000000..888eb11b9
--- /dev/null
+++ b/test/bun.js/esm/second.mjs
@@ -0,0 +1,7 @@
+import { start, end } from "./startEnd.mjs";
+
+start("Second");
+
+import "./second-child.mjs";
+
+end("Second");
diff --git a/test/bun.js/esm/startEnd.mjs b/test/bun.js/esm/startEnd.mjs
new file mode 100644
index 000000000..8b5549802
--- /dev/null
+++ b/test/bun.js/esm/startEnd.mjs
@@ -0,0 +1,6 @@
+export function start(name) {
+ console.log(`[start] ${name}`);
+}
+export function end(name) {
+ console.log(`[end] ${name}`);
+}
diff --git a/test/bun.js/esm/third.mjs b/test/bun.js/esm/third.mjs
new file mode 100644
index 000000000..f5ba5cc84
--- /dev/null
+++ b/test/bun.js/esm/third.mjs
@@ -0,0 +1,4 @@
+import { end, start } from "./startEnd.mjs";
+
+start("Third");
+end("Third");
diff --git a/test/bun.js/exit.js b/test/bun.js/exit.js
new file mode 100644
index 000000000..fb28b1fb4
--- /dev/null
+++ b/test/bun.js/exit.js
@@ -0,0 +1,2 @@
+process.exit(0);
+throw new Error("Well that didn't work");
diff --git a/test/bun.js/fetch.js.txt b/test/bun.js/fetch.js.txt
new file mode 100644
index 000000000..5a9b52fcf
--- /dev/null
+++ b/test/bun.js/fetch.js.txt
@@ -0,0 +1,46 @@
+<!doctype html>
+<html>
+<head>
+ <title>Example Domain</title>
+
+ <meta charset="utf-8" />
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <style type="text/css">
+ body {
+ background-color: #f0f0f2;
+ margin: 0;
+ padding: 0;
+ font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+
+ }
+ div {
+ width: 600px;
+ margin: 5em auto;
+ padding: 2em;
+ background-color: #fdfdff;
+ border-radius: 0.5em;
+ box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
+ }
+ a:link, a:visited {
+ color: #38488f;
+ text-decoration: none;
+ }
+ @media (max-width: 700px) {
+ div {
+ margin: 0 auto;
+ width: auto;
+ }
+ }
+ </style>
+</head>
+
+<body>
+<div>
+ <h1>Example Domain</h1>
+ <p>This domain is for use in illustrative examples in documents. You may use this
+ domain in literature without prior coordination or asking for permission.</p>
+ <p><a href="https://www.iana.org/domains/example">More information...</a></p>
+</div>
+</body>
+</html>
diff --git a/test/bun.js/fetch.test.js b/test/bun.js/fetch.test.js
new file mode 100644
index 000000000..9b6093afd
--- /dev/null
+++ b/test/bun.js/fetch.test.js
@@ -0,0 +1,440 @@
+import { it, describe, expect } from "bun:test";
+import fs from "fs";
+import { gc } from "./gc";
+
+describe("fetch", () => {
+ const urls = ["https://example.com", "http://example.com"];
+ for (let url of urls) {
+ gc();
+ it(url, async () => {
+ gc();
+ const response = await fetch(url);
+ gc();
+ const text = await response.text();
+ gc();
+ expect(
+ fs.readFileSync(
+ import.meta.path.substring(0, import.meta.path.lastIndexOf("/")) +
+ "/fetch.js.txt",
+ "utf8"
+ )
+ ).toBe(text);
+ });
+ }
+});
+
+function testBlobInterface(blobbyConstructor, hasBlobFn) {
+ for (let withGC of [false, true]) {
+ for (let jsonObject of [
+ { hello: true },
+ {
+ hello:
+ "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 ☺️ 😊 😇 🙂 🙃 😉 😌 😍 🥰 😘 😗 😙 😚 😋 😛 😝 😜 🤪 🤨 🧐 🤓 😎 🥸 🤩 🥳",
+ },
+ ]) {
+ it(`${jsonObject.hello === true ? "latin1" : "utf16"} json${
+ withGC ? " (with gc) " : ""
+ }`, async () => {
+ if (withGC) gc();
+ var response = blobbyConstructor(JSON.stringify(jsonObject));
+ if (withGC) gc();
+ expect(JSON.stringify(await response.json())).toBe(
+ JSON.stringify(jsonObject)
+ );
+ if (withGC) gc();
+ });
+
+ it(`${
+ jsonObject.hello === true ? "latin1" : "utf16"
+ } arrayBuffer -> json${withGC ? " (with gc) " : ""}`, async () => {
+ if (withGC) gc();
+ var response = blobbyConstructor(
+ new TextEncoder().encode(JSON.stringify(jsonObject))
+ );
+ if (withGC) gc();
+ expect(JSON.stringify(await response.json())).toBe(
+ JSON.stringify(jsonObject)
+ );
+ if (withGC) gc();
+ });
+
+ it(`${jsonObject.hello === true ? "latin1" : "utf16"} text${
+ withGC ? " (with gc) " : ""
+ }`, async () => {
+ if (withGC) gc();
+ var response = blobbyConstructor(JSON.stringify(jsonObject));
+ if (withGC) gc();
+ expect(await response.text()).toBe(JSON.stringify(jsonObject));
+ if (withGC) gc();
+ });
+
+ it(`${
+ jsonObject.hello === true ? "latin1" : "utf16"
+ } arrayBuffer -> text${withGC ? " (with gc) " : ""}`, async () => {
+ if (withGC) gc();
+ var response = blobbyConstructor(
+ new TextEncoder().encode(JSON.stringify(jsonObject))
+ );
+ if (withGC) gc();
+ expect(await response.text()).toBe(JSON.stringify(jsonObject));
+ if (withGC) gc();
+ });
+
+ it(`${jsonObject.hello === true ? "latin1" : "utf16"} arrayBuffer${
+ withGC ? " (with gc) " : ""
+ }`, async () => {
+ if (withGC) gc();
+
+ var response = blobbyConstructor(JSON.stringify(jsonObject));
+ if (withGC) gc();
+
+ const bytes = new TextEncoder().encode(JSON.stringify(jsonObject));
+ if (withGC) gc();
+
+ const compare = new Uint8Array(await response.arrayBuffer());
+ if (withGC) gc();
+
+ for (let i = 0; i < compare.length; i++) {
+ if (withGC) gc();
+
+ expect(compare[i]).toBe(bytes[i]);
+ if (withGC) gc();
+ }
+ if (withGC) gc();
+ });
+
+ it(`${
+ jsonObject.hello === true ? "latin1" : "utf16"
+ } arrayBuffer -> arrayBuffer${withGC ? " (with gc) " : ""}`, async () => {
+ if (withGC) gc();
+
+ var response = blobbyConstructor(
+ new TextEncoder().encode(JSON.stringify(jsonObject))
+ );
+ if (withGC) gc();
+
+ const bytes = new TextEncoder().encode(JSON.stringify(jsonObject));
+ if (withGC) gc();
+
+ const compare = new Uint8Array(await response.arrayBuffer());
+ if (withGC) gc();
+
+ for (let i = 0; i < compare.length; i++) {
+ if (withGC) gc();
+
+ expect(compare[i]).toBe(bytes[i]);
+ if (withGC) gc();
+ }
+ if (withGC) gc();
+ });
+
+ hasBlobFn &&
+ it(`${jsonObject.hello === true ? "latin1" : "utf16"} blob${
+ withGC ? " (with gc) " : ""
+ }`, async () => {
+ if (withGC) gc();
+ const text = JSON.stringify(jsonObject);
+ var response = blobbyConstructor(text);
+ if (withGC) gc();
+ const size = new TextEncoder().encode(text).byteLength;
+ if (withGC) gc();
+ const blobed = await response.blob();
+ if (withGC) gc();
+ expect(blobed instanceof Blob).toBe(true);
+ if (withGC) gc();
+ expect(blobed.size).toBe(size);
+ if (withGC) gc();
+ expect(blobed.type).toBe("");
+ if (withGC) gc();
+ blobed.type = "application/json";
+ if (withGC) gc();
+ expect(blobed.type).toBe("application/json");
+ if (withGC) gc();
+ const out = await blobed.text();
+ expect(out).toBe(text);
+ if (withGC) gc();
+ await new Promise((resolve) => setTimeout(resolve, 1));
+ if (withGC) gc();
+ expect(out).toBe(text);
+ const first = await blobed.arrayBuffer();
+ const initial = first[0];
+ first[0] = 254;
+ const second = await blobed.arrayBuffer();
+ expect(second[0]).toBe(initial);
+ expect(first[0]).toBe(254);
+ });
+ }
+ }
+}
+
+describe("Blob", () => {
+ testBlobInterface((data) => new Blob([data]));
+
+ var blobConstructorValues = [
+ ["123", "456"],
+ ["123", 456],
+ ["123", "456", "789"],
+ ["123", 456, 789],
+ [1, 2, 3, 4, 5, 6, 7, 8, 9],
+ [Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 9])],
+ [Uint8Array.from([1, 2, 3, 4]), "5678", 9],
+ [new Blob([Uint8Array.from([1, 2, 3, 4])]), "5678", 9],
+ [
+ new Blob([
+ new TextEncoder().encode(
+ "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 ☺️ 😊 😇 🙂 🙃 😉 😌 😍 🥰 😘 😗 😙 😚 😋 😛 😝 😜 🤪 🤨 🧐 🤓 😎 🥸 🤩 🥳"
+ ),
+ ]),
+ ],
+ [
+ new TextEncoder().encode(
+ "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 ☺️ 😊 😇 🙂 🙃 😉 😌 😍 🥰 😘 😗 😙 😚 😋 😛 😝 😜 🤪 🤨 🧐 🤓 😎 🥸 🤩 🥳"
+ ),
+ ],
+ ];
+
+ var expected = [
+ "123456",
+ "123456",
+ "123456789",
+ "123456789",
+ "123456789",
+ "\x01\x02\x03\x04\x05\x06\x07\t",
+ "\x01\x02\x03\x0456789",
+ "\x01\x02\x03\x0456789",
+ "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 ☺️ 😊 😇 🙂 🙃 😉 😌 😍 🥰 😘 😗 😙 😚 😋 😛 😝 😜 🤪 🤨 🧐 🤓 😎 🥸 🤩 🥳",
+ "😀 😃 😄 😁 😆 😅 😂 🤣 🥲 ☺️ 😊 😇 🙂 🙃 😉 😌 😍 🥰 😘 😗 😙 😚 😋 😛 😝 😜 🤪 🤨 🧐 🤓 😎 🥸 🤩 🥳",
+ ];
+
+ it(`blobConstructorValues`, async () => {
+ for (let i = 0; i < blobConstructorValues.length; i++) {
+ var response = new Blob(blobConstructorValues[i]);
+ const res = await response.text();
+ if (res !== expected[i]) {
+ throw new Error(
+ `Failed: ${expected[i]
+ .split("")
+ .map((a) => a.charCodeAt(0))}, received: ${res
+ .split("")
+ .map((a) => a.charCodeAt(0))}`
+ );
+ }
+
+ expect(res).toBe(expected[i]);
+ }
+ });
+
+ for (let withGC of [false, true]) {
+ it(`Blob.slice() ${withGC ? " with gc" : ""}`, async () => {
+ var parts = ["hello", " ", "world"];
+ if (withGC) gc();
+ var str = parts.join("");
+ if (withGC) gc();
+ var combined = new Blob(parts);
+ if (withGC) gc();
+ for (let part of parts) {
+ if (withGC) gc();
+ expect(
+ await combined
+ .slice(str.indexOf(part), str.indexOf(part) + part.length)
+ .text()
+ ).toBe(part);
+ if (withGC) gc();
+ }
+ if (withGC) gc();
+ for (let part of parts) {
+ if (withGC) gc();
+ expect(
+ await combined
+ .slice(str.indexOf(part), str.indexOf(part) + part.length)
+ .text()
+ ).toBe(part);
+ if (withGC) gc();
+ }
+ });
+ }
+});
+
+describe("Response", () => {
+ describe("Response.json", () => {
+ it("works", async () => {
+ const inputs = [
+ "hellooo",
+ [[123], 456, 789],
+ { hello: "world" },
+ { ok: "😉 😌 😍 🥰 😘 " },
+ ];
+ for (let input of inputs) {
+ const output = JSON.stringify(input);
+ expect(await Response.json(input).text()).toBe(output);
+ }
+ // JSON.stringify() returns undefined
+ expect(await Response.json().text()).toBe("");
+ // JSON.stringify("") returns '""'
+ expect(await Response.json("").text()).toBe('""');
+ });
+ it("sets the content-type header", () => {
+ let response = Response.json("hello");
+ expect(response.type).toBe("basic");
+ expect(response.headers.get("content-type")).toBe(
+ "application/json;charset=utf-8"
+ );
+ expect(response.status).toBe(200);
+ });
+ it("supports number status code", () => {
+ let response = Response.json("hello", 407);
+ expect(response.type).toBe("basic");
+ expect(response.headers.get("content-type")).toBe(
+ "application/json;charset=utf-8"
+ );
+ expect(response.status).toBe(407);
+ });
+
+ it("supports headers", () => {
+ var response = Response.json("hello", {
+ headers: {
+ "content-type": "potato",
+ "x-hello": "world",
+ },
+ status: 408,
+ });
+
+ expect(response.headers.get("x-hello")).toBe("world");
+ expect(response.status).toBe(408);
+ });
+ });
+ describe("Response.redirect", () => {
+ it("works", () => {
+ const inputs = [
+ "http://example.com",
+ "http://example.com/",
+ "http://example.com/hello",
+ "http://example.com/hello/",
+ "http://example.com/hello/world",
+ "http://example.com/hello/world/",
+ ];
+ for (let input of inputs) {
+ expect(Response.redirect(input).headers.get("Location")).toBe(input);
+ }
+ });
+
+ it("supports headers", () => {
+ var response = Response.redirect("https://example.com", {
+ headers: {
+ "content-type": "potato",
+ "x-hello": "world",
+ Location: "https://wrong.com",
+ },
+ status: 408,
+ });
+ expect(response.headers.get("x-hello")).toBe("world");
+ expect(response.headers.get("Location")).toBe("https://example.com");
+ expect(response.status).toBe(302);
+ expect(response.type).toBe("basic");
+ expect(response.ok).toBe(false);
+ });
+ });
+ describe("Response.error", () => {
+ it("works", () => {
+ expect(Response.error().type).toBe("error");
+ expect(Response.error().ok).toBe(false);
+ expect(Response.error().status).toBe(0);
+ });
+ });
+ it("clone", async () => {
+ gc();
+ var body = new Response("<div>hello</div>", {
+ headers: {
+ "content-type": "text/html; charset=utf-8",
+ },
+ });
+ gc();
+ var clone = body.clone();
+ gc();
+ body.headers.set("content-type", "text/plain");
+ gc();
+ expect(clone.headers.get("content-type")).toBe("text/html; charset=utf-8");
+ gc();
+ expect(body.headers.get("content-type")).toBe("text/plain");
+ gc();
+ expect(await clone.text()).toBe("<div>hello</div>");
+ gc();
+ });
+ it("invalid json", async () => {
+ gc();
+ var body = new Response("<div>hello</div>", {
+ headers: {
+ "content-type": "text/html; charset=utf-8",
+ },
+ });
+ try {
+ await body.json();
+ expect(false).toBe(true);
+ } catch (exception) {
+ expect(exception instanceof SyntaxError);
+ }
+ });
+
+ testBlobInterface((data) => new Response(data), true);
+});
+
+describe("Request", () => {
+ it("clone", async () => {
+ gc();
+ var body = new Request("https://hello.com", {
+ headers: {
+ "content-type": "text/html; charset=utf-8",
+ },
+ body: "<div>hello</div>",
+ });
+ gc();
+ expect(body.headers.get("content-type")).toBe("text/html; charset=utf-8");
+ gc();
+ var clone = body.clone();
+ gc();
+ body.headers.set("content-type", "text/plain");
+ gc();
+ expect(clone.headers.get("content-type")).toBe("text/html; charset=utf-8");
+ gc();
+ expect(body.headers.get("content-type")).toBe("text/plain");
+ gc();
+ expect(await clone.text()).toBe("<div>hello</div>");
+ gc();
+ });
+
+ testBlobInterface(
+ (data) => new Request("https://hello.com", { body: data }),
+ true
+ );
+});
+
+describe("Headers", () => {
+ it("writes", async () => {
+ var headers = new Headers({
+ "content-type": "text/html; charset=utf-8",
+ });
+ gc();
+ expect(headers.get("content-type")).toBe("text/html; charset=utf-8");
+ gc();
+ headers.delete("content-type");
+ gc();
+ expect(headers.get("content-type")).toBe(null);
+ gc();
+ headers.append("content-type", "text/plain");
+ gc();
+ expect(headers.get("content-type")).toBe("text/plain");
+ gc();
+ headers.append("content-type", "text/plain");
+ gc();
+ expect(headers.get("content-type")).toBe("text/plain, text/plain");
+ gc();
+ headers.set("content-type", "text/html; charset=utf-8");
+ gc();
+ expect(headers.get("content-type")).toBe("text/html; charset=utf-8");
+
+ headers.delete("content-type");
+ gc();
+ expect(headers.get("content-type")).toBe(null);
+ gc();
+ });
+});
diff --git a/test/bun.js/ffi-test.c b/test/bun.js/ffi-test.c
new file mode 100644
index 000000000..cc87d0528
--- /dev/null
+++ b/test/bun.js/ffi-test.c
@@ -0,0 +1,129 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+bool returns_true();
+bool returns_false();
+char returns_42_char();
+float returns_42_float();
+double returns_42_double();
+uint8_t returns_42_uint8_t();
+int8_t returns_neg_42_int8_t();
+uint16_t returns_42_uint16_t();
+uint32_t returns_42_uint32_t();
+uint64_t returns_42_uint64_t();
+int16_t returns_neg_42_int16_t();
+int32_t returns_neg_42_int32_t();
+int64_t returns_neg_42_int64_t();
+
+bool cb_identity_true(bool (*cb)());
+bool cb_identity_false(bool (*cb)());
+char cb_identity_42_char(char (*cb)());
+float cb_identity_42_float(float (*cb)());
+double cb_identity_42_double(double (*cb)());
+uint8_t cb_identity_42_uint8_t(uint8_t (*cb)());
+int8_t cb_identity_neg_42_int8_t(int8_t (*cb)());
+uint16_t cb_identity_42_uint16_t(uint16_t (*cb)());
+uint32_t cb_identity_42_uint32_t(uint32_t (*cb)());
+uint64_t cb_identity_42_uint64_t(uint64_t (*cb)());
+int16_t cb_identity_neg_42_int16_t(int16_t (*cb)());
+int32_t cb_identity_neg_42_int32_t(int32_t (*cb)());
+int64_t cb_identity_neg_42_int64_t(int64_t (*cb)());
+
+bool identity_bool_true();
+bool identity_bool_false();
+char identity_char(char a);
+float identity_float(float a);
+bool identity_bool(bool ident);
+double identity_double(double a);
+int8_t identity_int8_t(int8_t a);
+int16_t identity_int16_t(int16_t a);
+int32_t identity_int32_t(int32_t a);
+int64_t identity_int64_t(int64_t a);
+uint8_t identity_uint8_t(uint8_t a);
+uint16_t identity_uint16_t(uint16_t a);
+uint32_t identity_uint32_t(uint32_t a);
+uint64_t identity_uint64_t(uint64_t a);
+
+char add_char(char a, char b);
+float add_float(float a, float b);
+double add_double(double a, double b);
+int8_t add_int8_t(int8_t a, int8_t b);
+int16_t add_int16_t(int16_t a, int16_t b);
+int32_t add_int32_t(int32_t a, int32_t b);
+int64_t add_int64_t(int64_t a, int64_t b);
+uint8_t add_uint8_t(uint8_t a, uint8_t b);
+uint16_t add_uint16_t(uint16_t a, uint16_t b);
+uint32_t add_uint32_t(uint32_t a, uint32_t b);
+uint64_t add_uint64_t(uint64_t a, uint64_t b);
+
+bool returns_false() { return false; }
+bool returns_true() { return true; }
+char returns_42_char() { return '*'; }
+double returns_42_double() { return (double)42.42; }
+float returns_42_float() { return 42.42f; }
+int16_t returns_neg_42_int16_t() { return -42; }
+int32_t returns_neg_42_int32_t() { return -42; }
+int64_t returns_neg_42_int64_t() { return -42; }
+int8_t returns_neg_42_int8_t() { return -42; }
+uint16_t returns_42_uint16_t() { return 42; }
+uint32_t returns_42_uint32_t() { return 42; }
+uint64_t returns_42_uint64_t() { return 42; }
+uint8_t returns_42_uint8_t() { return (uint8_t)42; }
+
+char identity_char(char a) { return a; }
+float identity_float(float a) { return a; }
+double identity_double(double a) { return a; }
+int8_t identity_int8_t(int8_t a) { return a; }
+int16_t identity_int16_t(int16_t a) { return a; }
+int32_t identity_int32_t(int32_t a) { return a; }
+int64_t identity_int64_t(int64_t a) { return a; }
+uint8_t identity_uint8_t(uint8_t a) { return a; }
+uint16_t identity_uint16_t(uint16_t a) { return a; }
+uint32_t identity_uint32_t(uint32_t a) { return a; }
+uint64_t identity_uint64_t(uint64_t a) { return a; }
+bool identity_bool(bool ident) { return ident; }
+void *identity_ptr(void *ident) { return ident; }
+
+char add_char(char a, char b) { return a + b; }
+float add_float(float a, float b) { return a + b; }
+double add_double(double a, double b) { return a + b; }
+int8_t add_int8_t(int8_t a, int8_t b) { return a + b; }
+int16_t add_int16_t(int16_t a, int16_t b) { return a + b; }
+int32_t add_int32_t(int32_t a, int32_t b) { return a + b; }
+int64_t add_int64_t(int64_t a, int64_t b) { return a + b; }
+uint8_t add_uint8_t(uint8_t a, uint8_t b) { return a + b; }
+uint16_t add_uint16_t(uint16_t a, uint16_t b) { return a + b; }
+uint32_t add_uint32_t(uint32_t a, uint32_t b) { return a + b; }
+uint64_t add_uint64_t(uint64_t a, uint64_t b) { return a + b; }
+
+void *ptr_should_point_to_42_as_int32_t();
+void *ptr_should_point_to_42_as_int32_t() {
+ int32_t *ptr = malloc(sizeof(int32_t));
+ *ptr = 42;
+ return ptr;
+}
+
+bool does_pointer_equal_42_as_int32_t(int32_t *ptr);
+bool does_pointer_equal_42_as_int32_t(int32_t *ptr) { return *ptr == 42; }
+
+void *return_a_function_ptr_to_function_that_returns_true();
+void *return_a_function_ptr_to_function_that_returns_true() {
+ return (void *)&returns_true;
+}
+
+bool cb_identity_true(bool (*cb)()) { return cb(); }
+
+bool cb_identity_false(bool (*cb)()) { return cb(); }
+char cb_identity_42_char(char (*cb)()) { return cb(); }
+float cb_identity_42_float(float (*cb)()) { return cb(); }
+double cb_identity_42_double(double (*cb)()) { return cb(); }
+uint8_t cb_identity_42_uint8_t(uint8_t (*cb)()) { return cb(); }
+int8_t cb_identity_neg_42_int8_t(int8_t (*cb)()) { return cb(); }
+uint16_t cb_identity_42_uint16_t(uint16_t (*cb)()) { return cb(); }
+uint32_t cb_identity_42_uint32_t(uint32_t (*cb)()) { return cb(); }
+uint64_t cb_identity_42_uint64_t(uint64_t (*cb)()) { return cb(); }
+int16_t cb_identity_neg_42_int16_t(int16_t (*cb)()) { return cb(); }
+int32_t cb_identity_neg_42_int32_t(int32_t (*cb)()) { return cb(); }
+int64_t cb_identity_neg_42_int64_t(int64_t (*cb)()) { return cb(); } \ No newline at end of file
diff --git a/test/bun.js/ffi.test.fixture.callback.c b/test/bun.js/ffi.test.fixture.callback.c
new file mode 100644
index 000000000..d48ef6753
--- /dev/null
+++ b/test/bun.js/ffi.test.fixture.callback.c
@@ -0,0 +1,270 @@
+#define IS_CALLBACK 1
+// This file is part of Bun!
+// You can find the original source:
+// https://github.com/Jarred-Sumner/bun/blob/main/src/bun.js/api/FFI.h#L2
+//
+// clang-format off
+// This file is only compatible with 64 bit CPUs
+// It must be kept in sync with JSCJSValue.h
+// https://github.com/Jarred-Sumner/WebKit/blob/72c2052b781cbfd4af867ae79ac9de460e392fba/Source/JavaScriptCore/runtime/JSCJSValue.h#L455-L458
+#ifdef IS_CALLBACK
+#define INJECT_BEFORE int c = 500; // This is a callback, so we need to inject code before the call
+#endif
+#define IS_BIG_ENDIAN 0
+#define USE_JSVALUE64 1
+#define USE_JSVALUE32_64 0
+
+
+// /* 7.18.1.1 Exact-width integer types */
+typedef unsigned char uint8_t;
+typedef signed char int8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef long long int64_t;
+typedef unsigned long long uint64_t;
+typedef unsigned long long size_t;
+typedef long intptr_t;
+typedef uint64_t uintptr_t;
+typedef _Bool bool;
+
+#define true 1
+#define false 0
+
+
+#ifdef INJECT_BEFORE
+// #include <stdint.h>
+#endif
+// #include <tcclib.h>
+
+// This value is 2^49, used to encode doubles such that the encoded value will
+// begin with a 15-bit pattern within the range 0x0002..0xFFFC.
+#define DoubleEncodeOffsetBit 49
+#define DoubleEncodeOffset (1ll << DoubleEncodeOffsetBit)
+#define OtherTag 0x2
+#define BoolTag 0x4
+#define UndefinedTag 0x8
+#define TagValueFalse (OtherTag | BoolTag | false)
+#define TagValueTrue (OtherTag | BoolTag | true)
+#define TagValueUndefined (OtherTag | UndefinedTag)
+#define TagValueNull (OtherTag)
+#define NotCellMask NumberTag | OtherTag
+
+#define MAX_INT32 2147483648
+#define MAX_INT52 9007199254740991
+
+// If all bits in the mask are set, this indicates an integer number,
+// if any but not all are set this value is a double precision number.
+#define NumberTag 0xfffe000000000000ll
+
+typedef void* JSCell;
+
+typedef union EncodedJSValue {
+ int64_t asInt64;
+
+#if USE_JSVALUE64
+ JSCell *ptr;
+#endif
+
+#if IS_BIG_ENDIAN
+ struct {
+ int32_t tag;
+ int32_t payload;
+ } asBits;
+#else
+ struct {
+ int32_t payload;
+ int32_t tag;
+ } asBits;
+#endif
+
+ void* asPtr;
+ double asDouble;
+} EncodedJSValue;
+
+EncodedJSValue ValueUndefined = { TagValueUndefined };
+EncodedJSValue ValueTrue = { TagValueTrue };
+
+typedef void* JSContext;
+
+// Bun_FFI_PointerOffsetToArgumentsList is injected into the build
+// The value is generated in `make sizegen`
+// The value is 6.
+// On ARM64_32, the value is something else but it really doesn't matter for our case
+// However, I don't want this to subtly break amidst future upgrades to JavaScriptCore
+#define LOAD_ARGUMENTS_FROM_CALL_FRAME \
+ int64_t *argsPtr = (int64_t*)((size_t*)callFrame + Bun_FFI_PointerOffsetToArgumentsList)
+
+
+#ifdef IS_CALLBACK
+extern int64_t bun_call(JSContext, void* func, void* thisValue, size_t len, const EncodedJSValue args[], void* exception);
+JSContext cachedJSContext;
+void* cachedCallbackFunction;
+#endif
+
+static bool JSVALUE_IS_CELL(EncodedJSValue val) __attribute__((__always_inline__));
+static bool JSVALUE_IS_INT32(EncodedJSValue val) __attribute__((__always_inline__));
+static bool JSVALUE_IS_NUMBER(EncodedJSValue val) __attribute__((__always_inline__));
+
+static uint64_t JSVALUE_TO_UINT64(void* globalObject, EncodedJSValue value) __attribute__((__always_inline__));
+static int64_t JSVALUE_TO_INT64(EncodedJSValue value) __attribute__((__always_inline__));
+uint64_t JSVALUE_TO_UINT64_SLOW(void* globalObject, EncodedJSValue value);
+int64_t JSVALUE_TO_INT64_SLOW(EncodedJSValue value);
+
+EncodedJSValue UINT64_TO_JSVALUE_SLOW(void* globalObject, uint64_t val);
+EncodedJSValue INT64_TO_JSVALUE_SLOW(void* globalObject, int64_t val);
+static EncodedJSValue UINT64_TO_JSVALUE(void* globalObject, uint64_t val) __attribute__((__always_inline__));
+static EncodedJSValue INT64_TO_JSVALUE(void* globalObject, int64_t val) __attribute__((__always_inline__));
+
+
+static EncodedJSValue INT32_TO_JSVALUE(int32_t val) __attribute__((__always_inline__));
+static EncodedJSValue DOUBLE_TO_JSVALUE(double val) __attribute__((__always_inline__));
+static EncodedJSValue FLOAT_TO_JSVALUE(float val) __attribute__((__always_inline__));
+static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) __attribute__((__always_inline__));
+static EncodedJSValue PTR_TO_JSVALUE(void* ptr) __attribute__((__always_inline__));
+
+static void* JSVALUE_TO_PTR(EncodedJSValue val) __attribute__((__always_inline__));
+static int32_t JSVALUE_TO_INT32(EncodedJSValue val) __attribute__((__always_inline__));
+static float JSVALUE_TO_FLOAT(EncodedJSValue val) __attribute__((__always_inline__));
+static double JSVALUE_TO_DOUBLE(EncodedJSValue val) __attribute__((__always_inline__));
+static bool JSVALUE_TO_BOOL(EncodedJSValue val) __attribute__((__always_inline__));
+
+static bool JSVALUE_IS_CELL(EncodedJSValue val) {
+ return !(val.asInt64 & NotCellMask);
+}
+
+static bool JSVALUE_IS_INT32(EncodedJSValue val) {
+ return (val.asInt64 & NumberTag) == NumberTag;
+}
+
+static bool JSVALUE_IS_NUMBER(EncodedJSValue val) {
+ return val.asInt64 & NumberTag;
+}
+
+
+static void* JSVALUE_TO_PTR(EncodedJSValue val) {
+ // must be a double
+ return (void*)(val.asInt64 - DoubleEncodeOffset);
+}
+
+static EncodedJSValue PTR_TO_JSVALUE(void* ptr) {
+ EncodedJSValue val;
+ val.asInt64 = (int64_t)ptr + DoubleEncodeOffset;
+ return val;
+}
+
+static int32_t JSVALUE_TO_INT32(EncodedJSValue val) {
+ return val.asInt64;
+}
+
+static EncodedJSValue INT32_TO_JSVALUE(int32_t val) {
+ EncodedJSValue res;
+ res.asInt64 = NumberTag | (uint32_t)val;
+ return res;
+}
+
+
+static EncodedJSValue DOUBLE_TO_JSVALUE(double val) {
+ EncodedJSValue res;
+ res.asDouble = val;
+ res.asInt64 += DoubleEncodeOffset;
+ return res;
+}
+
+static EncodedJSValue FLOAT_TO_JSVALUE(float val) {
+ return DOUBLE_TO_JSVALUE((double)val);
+}
+
+static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) {
+ EncodedJSValue res;
+ res.asInt64 = val ? TagValueTrue : TagValueFalse;
+ return res;
+}
+
+
+static double JSVALUE_TO_DOUBLE(EncodedJSValue val) {
+ val.asInt64 -= DoubleEncodeOffset;
+ return val.asDouble;
+}
+
+static float JSVALUE_TO_FLOAT(EncodedJSValue val) {
+ return (float)JSVALUE_TO_DOUBLE(val);
+}
+
+static bool JSVALUE_TO_BOOL(EncodedJSValue val) {
+ return val.asInt64 == TagValueTrue;
+}
+
+
+static uint64_t JSVALUE_TO_UINT64(void* globalObject, EncodedJSValue value) {
+ if (JSVALUE_IS_INT32(value)) {
+ return (uint64_t)JSVALUE_TO_INT32(value);
+ }
+
+ if (JSVALUE_IS_NUMBER(value)) {
+ return (uint64_t)JSVALUE_TO_DOUBLE(value);
+ }
+
+ return JSVALUE_TO_UINT64_SLOW(globalObject, value);
+}
+static int64_t JSVALUE_TO_INT64(EncodedJSValue value) {
+ if (JSVALUE_IS_INT32(value)) {
+ return (int64_t)JSVALUE_TO_INT32(value);
+ }
+
+ if (JSVALUE_IS_NUMBER(value)) {
+ return (int64_t)JSVALUE_TO_DOUBLE(value);
+ }
+
+ return JSVALUE_TO_INT64_SLOW(value);
+}
+
+static EncodedJSValue UINT64_TO_JSVALUE(void* globalObject, uint64_t val) {
+ if (val < MAX_INT32) {
+ return INT32_TO_JSVALUE((int32_t)val);
+ }
+
+ if (val < MAX_INT52) {
+ return DOUBLE_TO_JSVALUE((double)val);
+ }
+
+ return UINT64_TO_JSVALUE_SLOW(globalObject, val);
+}
+
+static EncodedJSValue INT64_TO_JSVALUE(void* globalObject, int64_t val) {
+ if (val >= -MAX_INT32 && val <= MAX_INT32) {
+ return INT32_TO_JSVALUE((int32_t)val);
+ }
+
+ if (val >= -MAX_INT52 && val <= MAX_INT52) {
+ return DOUBLE_TO_JSVALUE((double)val);
+ }
+
+ return INT64_TO_JSVALUE_SLOW(globalObject, val);
+}
+
+#ifndef IS_CALLBACK
+void* JSFunctionCall(void* globalObject, void* callFrame);
+
+#endif
+
+
+// --- Generated Code ---
+
+
+/* --- The Callback Function */
+/* --- The Callback Function */
+bool my_callback_function(void* arg0);
+
+bool my_callback_function(void* arg0) {
+#ifdef INJECT_BEFORE
+INJECT_BEFORE;
+#endif
+ EncodedJSValue arguments[1] = {
+ PTR_TO_JSVALUE(arg0)
+ };
+ EncodedJSValue return_value = {bun_call(cachedJSContext, cachedCallbackFunction, (void*)0, 1, &arguments[0], (void*)0)};
+ return JSVALUE_TO_BOOL(return_value);
+}
+
diff --git a/test/bun.js/ffi.test.fixture.receiver.c b/test/bun.js/ffi.test.fixture.receiver.c
new file mode 100644
index 000000000..5bb51bda5
--- /dev/null
+++ b/test/bun.js/ffi.test.fixture.receiver.c
@@ -0,0 +1,268 @@
+#define HAS_ARGUMENTS
+#define USES_FLOAT 1
+// This file is part of Bun!
+// You can find the original source:
+// https://github.com/Jarred-Sumner/bun/blob/main/src/bun.js/api/FFI.h#L2
+//
+// clang-format off
+// This file is only compatible with 64 bit CPUs
+// It must be kept in sync with JSCJSValue.h
+// https://github.com/Jarred-Sumner/WebKit/blob/72c2052b781cbfd4af867ae79ac9de460e392fba/Source/JavaScriptCore/runtime/JSCJSValue.h#L455-L458
+#ifdef IS_CALLBACK
+#define INJECT_BEFORE int c = 500; // This is a callback, so we need to inject code before the call
+#endif
+#define IS_BIG_ENDIAN 0
+#define USE_JSVALUE64 1
+#define USE_JSVALUE32_64 0
+
+
+// /* 7.18.1.1 Exact-width integer types */
+typedef unsigned char uint8_t;
+typedef signed char int8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef long long int64_t;
+typedef unsigned long long uint64_t;
+typedef unsigned long long size_t;
+typedef long intptr_t;
+typedef uint64_t uintptr_t;
+typedef _Bool bool;
+
+#define true 1
+#define false 0
+
+
+#ifdef INJECT_BEFORE
+// #include <stdint.h>
+#endif
+// #include <tcclib.h>
+
+// This value is 2^49, used to encode doubles such that the encoded value will
+// begin with a 15-bit pattern within the range 0x0002..0xFFFC.
+#define DoubleEncodeOffsetBit 49
+#define DoubleEncodeOffset (1ll << DoubleEncodeOffsetBit)
+#define OtherTag 0x2
+#define BoolTag 0x4
+#define UndefinedTag 0x8
+#define TagValueFalse (OtherTag | BoolTag | false)
+#define TagValueTrue (OtherTag | BoolTag | true)
+#define TagValueUndefined (OtherTag | UndefinedTag)
+#define TagValueNull (OtherTag)
+#define NotCellMask NumberTag | OtherTag
+
+#define MAX_INT32 2147483648
+#define MAX_INT52 9007199254740991
+
+// If all bits in the mask are set, this indicates an integer number,
+// if any but not all are set this value is a double precision number.
+#define NumberTag 0xfffe000000000000ll
+
+typedef void* JSCell;
+
+typedef union EncodedJSValue {
+ int64_t asInt64;
+
+#if USE_JSVALUE64
+ JSCell *ptr;
+#endif
+
+#if IS_BIG_ENDIAN
+ struct {
+ int32_t tag;
+ int32_t payload;
+ } asBits;
+#else
+ struct {
+ int32_t payload;
+ int32_t tag;
+ } asBits;
+#endif
+
+ void* asPtr;
+ double asDouble;
+} EncodedJSValue;
+
+EncodedJSValue ValueUndefined = { TagValueUndefined };
+EncodedJSValue ValueTrue = { TagValueTrue };
+
+typedef void* JSContext;
+
+// Bun_FFI_PointerOffsetToArgumentsList is injected into the build
+// The value is generated in `make sizegen`
+// The value is 6.
+// On ARM64_32, the value is something else but it really doesn't matter for our case
+// However, I don't want this to subtly break amidst future upgrades to JavaScriptCore
+#define LOAD_ARGUMENTS_FROM_CALL_FRAME \
+ int64_t *argsPtr = (int64_t*)((size_t*)callFrame + Bun_FFI_PointerOffsetToArgumentsList)
+
+
+#ifdef IS_CALLBACK
+extern int64_t bun_call(JSContext, void* func, void* thisValue, size_t len, const EncodedJSValue args[], void* exception);
+JSContext cachedJSContext;
+void* cachedCallbackFunction;
+#endif
+
+static bool JSVALUE_IS_CELL(EncodedJSValue val) __attribute__((__always_inline__));
+static bool JSVALUE_IS_INT32(EncodedJSValue val) __attribute__((__always_inline__));
+static bool JSVALUE_IS_NUMBER(EncodedJSValue val) __attribute__((__always_inline__));
+
+static uint64_t JSVALUE_TO_UINT64(void* globalObject, EncodedJSValue value) __attribute__((__always_inline__));
+static int64_t JSVALUE_TO_INT64(EncodedJSValue value) __attribute__((__always_inline__));
+uint64_t JSVALUE_TO_UINT64_SLOW(void* globalObject, EncodedJSValue value);
+int64_t JSVALUE_TO_INT64_SLOW(EncodedJSValue value);
+
+EncodedJSValue UINT64_TO_JSVALUE_SLOW(void* globalObject, uint64_t val);
+EncodedJSValue INT64_TO_JSVALUE_SLOW(void* globalObject, int64_t val);
+static EncodedJSValue UINT64_TO_JSVALUE(void* globalObject, uint64_t val) __attribute__((__always_inline__));
+static EncodedJSValue INT64_TO_JSVALUE(void* globalObject, int64_t val) __attribute__((__always_inline__));
+
+
+static EncodedJSValue INT32_TO_JSVALUE(int32_t val) __attribute__((__always_inline__));
+static EncodedJSValue DOUBLE_TO_JSVALUE(double val) __attribute__((__always_inline__));
+static EncodedJSValue FLOAT_TO_JSVALUE(float val) __attribute__((__always_inline__));
+static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) __attribute__((__always_inline__));
+static EncodedJSValue PTR_TO_JSVALUE(void* ptr) __attribute__((__always_inline__));
+
+static void* JSVALUE_TO_PTR(EncodedJSValue val) __attribute__((__always_inline__));
+static int32_t JSVALUE_TO_INT32(EncodedJSValue val) __attribute__((__always_inline__));
+static float JSVALUE_TO_FLOAT(EncodedJSValue val) __attribute__((__always_inline__));
+static double JSVALUE_TO_DOUBLE(EncodedJSValue val) __attribute__((__always_inline__));
+static bool JSVALUE_TO_BOOL(EncodedJSValue val) __attribute__((__always_inline__));
+
+static bool JSVALUE_IS_CELL(EncodedJSValue val) {
+ return !(val.asInt64 & NotCellMask);
+}
+
+static bool JSVALUE_IS_INT32(EncodedJSValue val) {
+ return (val.asInt64 & NumberTag) == NumberTag;
+}
+
+static bool JSVALUE_IS_NUMBER(EncodedJSValue val) {
+ return val.asInt64 & NumberTag;
+}
+
+
+static void* JSVALUE_TO_PTR(EncodedJSValue val) {
+ // must be a double
+ return (void*)(val.asInt64 - DoubleEncodeOffset);
+}
+
+static EncodedJSValue PTR_TO_JSVALUE(void* ptr) {
+ EncodedJSValue val;
+ val.asInt64 = (int64_t)ptr + DoubleEncodeOffset;
+ return val;
+}
+
+static int32_t JSVALUE_TO_INT32(EncodedJSValue val) {
+ return val.asInt64;
+}
+
+static EncodedJSValue INT32_TO_JSVALUE(int32_t val) {
+ EncodedJSValue res;
+ res.asInt64 = NumberTag | (uint32_t)val;
+ return res;
+}
+
+
+static EncodedJSValue DOUBLE_TO_JSVALUE(double val) {
+ EncodedJSValue res;
+ res.asDouble = val;
+ res.asInt64 += DoubleEncodeOffset;
+ return res;
+}
+
+static EncodedJSValue FLOAT_TO_JSVALUE(float val) {
+ return DOUBLE_TO_JSVALUE((double)val);
+}
+
+static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) {
+ EncodedJSValue res;
+ res.asInt64 = val ? TagValueTrue : TagValueFalse;
+ return res;
+}
+
+
+static double JSVALUE_TO_DOUBLE(EncodedJSValue val) {
+ val.asInt64 -= DoubleEncodeOffset;
+ return val.asDouble;
+}
+
+static float JSVALUE_TO_FLOAT(EncodedJSValue val) {
+ return (float)JSVALUE_TO_DOUBLE(val);
+}
+
+static bool JSVALUE_TO_BOOL(EncodedJSValue val) {
+ return val.asInt64 == TagValueTrue;
+}
+
+
+static uint64_t JSVALUE_TO_UINT64(void* globalObject, EncodedJSValue value) {
+ if (JSVALUE_IS_INT32(value)) {
+ return (uint64_t)JSVALUE_TO_INT32(value);
+ }
+
+ if (JSVALUE_IS_NUMBER(value)) {
+ return (uint64_t)JSVALUE_TO_DOUBLE(value);
+ }
+
+ return JSVALUE_TO_UINT64_SLOW(globalObject, value);
+}
+static int64_t JSVALUE_TO_INT64(EncodedJSValue value) {
+ if (JSVALUE_IS_INT32(value)) {
+ return (int64_t)JSVALUE_TO_INT32(value);
+ }
+
+ if (JSVALUE_IS_NUMBER(value)) {
+ return (int64_t)JSVALUE_TO_DOUBLE(value);
+ }
+
+ return JSVALUE_TO_INT64_SLOW(value);
+}
+
+static EncodedJSValue UINT64_TO_JSVALUE(void* globalObject, uint64_t val) {
+ if (val < MAX_INT32) {
+ return INT32_TO_JSVALUE((int32_t)val);
+ }
+
+ if (val < MAX_INT52) {
+ return DOUBLE_TO_JSVALUE((double)val);
+ }
+
+ return UINT64_TO_JSVALUE_SLOW(globalObject, val);
+}
+
+static EncodedJSValue INT64_TO_JSVALUE(void* globalObject, int64_t val) {
+ if (val >= -MAX_INT32 && val <= MAX_INT32) {
+ return INT32_TO_JSVALUE((int32_t)val);
+ }
+
+ if (val >= -MAX_INT52 && val <= MAX_INT52) {
+ return DOUBLE_TO_JSVALUE((double)val);
+ }
+
+ return INT64_TO_JSVALUE_SLOW(globalObject, val);
+}
+
+#ifndef IS_CALLBACK
+void* JSFunctionCall(void* globalObject, void* callFrame);
+
+#endif
+
+
+// --- Generated Code ---
+/* --- The Function To Call */
+float not_a_callback(float arg0);
+
+
+/* ---- Your Wrapper Function ---- */
+void* JSFunctionCall(void* globalObject, void* callFrame) {
+ LOAD_ARGUMENTS_FROM_CALL_FRAME;
+ EncodedJSValue arg0;
+ arg0.asInt64 = *argsPtr;
+ float return_value = not_a_callback( JSVALUE_TO_FLOAT(arg0));
+
+ return FLOAT_TO_JSVALUE(return_value).asPtr;
+}
+
diff --git a/test/bun.js/ffi.test.js b/test/bun.js/ffi.test.js
new file mode 100644
index 000000000..db2cfb6d4
--- /dev/null
+++ b/test/bun.js/ffi.test.js
@@ -0,0 +1,543 @@
+// import { describe, it, expect } from "bun:test";
+// import { unsafe } from "bun";
+// //
+// import {
+// native,
+// viewSource,
+// dlopen,
+// CString,
+// ptr,
+// toBuffer,
+// toArrayBuffer,
+// FFIType,
+// callback,
+// CFunction,
+// } from "bun:ffi";
+
+// it("ffi print", async () => {
+// await Bun.write(
+// import.meta.dir + "/ffi.test.fixture.callback.c",
+// viewSource(
+// {
+// returns: "bool",
+// args: ["ptr"],
+// },
+// true
+// )
+// );
+// await Bun.write(
+// import.meta.dir + "/ffi.test.fixture.receiver.c",
+// viewSource(
+// {
+// not_a_callback: {
+// returns: "float",
+// args: ["float"],
+// },
+// },
+// false
+// )[0]
+// );
+// expect(
+// viewSource(
+// {
+// returns: "int8_t",
+// args: [],
+// },
+// true
+// ).length > 0
+// ).toBe(true);
+// expect(
+// viewSource(
+// {
+// a: {
+// returns: "int8_t",
+// args: [],
+// },
+// },
+// false
+// ).length > 0
+// ).toBe(true);
+// });
+
+// function getTypes(fast) {
+// const int64_t = fast ? "i64_fast" : "int64_t";
+// const uint64_t = fast ? "u64_fast" : "uint64_t";
+// return {
+// returns_true: {
+// returns: "bool",
+// args: [],
+// },
+// returns_false: {
+// returns: "bool",
+// args: [],
+// },
+// returns_42_char: {
+// returns: "char",
+// args: [],
+// },
+// returns_42_float: {
+// returns: "float",
+// args: [],
+// },
+// returns_42_double: {
+// returns: "double",
+// args: [],
+// },
+// returns_42_uint8_t: {
+// returns: "uint8_t",
+// args: [],
+// },
+// returns_neg_42_int8_t: {
+// returns: "int8_t",
+// args: [],
+// },
+// returns_42_uint16_t: {
+// returns: "uint16_t",
+// args: [],
+// },
+// returns_42_uint32_t: {
+// returns: "uint32_t",
+// args: [],
+// },
+// returns_42_uint64_t: {
+// returns: uint64_t,
+// args: [],
+// },
+// returns_neg_42_int16_t: {
+// returns: "int16_t",
+// args: [],
+// },
+// returns_neg_42_int32_t: {
+// returns: "int32_t",
+// args: [],
+// },
+// returns_neg_42_int64_t: {
+// returns: int64_t,
+// args: [],
+// },
+
+// identity_char: {
+// returns: "char",
+// args: ["char"],
+// },
+// identity_float: {
+// returns: "float",
+// args: ["float"],
+// },
+// identity_bool: {
+// returns: "bool",
+// args: ["bool"],
+// },
+// identity_double: {
+// returns: "double",
+// args: ["double"],
+// },
+// identity_int8_t: {
+// returns: "int8_t",
+// args: ["int8_t"],
+// },
+// identity_int16_t: {
+// returns: "int16_t",
+// args: ["int16_t"],
+// },
+// identity_int32_t: {
+// returns: "int32_t",
+// args: ["int32_t"],
+// },
+// identity_int64_t: {
+// returns: int64_t,
+// args: [int64_t],
+// },
+// identity_uint8_t: {
+// returns: "uint8_t",
+// args: ["uint8_t"],
+// },
+// identity_uint16_t: {
+// returns: "uint16_t",
+// args: ["uint16_t"],
+// },
+// identity_uint32_t: {
+// returns: "uint32_t",
+// args: ["uint32_t"],
+// },
+// identity_uint64_t: {
+// returns: uint64_t,
+// args: [uint64_t],
+// },
+
+// add_char: {
+// returns: "char",
+// args: ["char", "char"],
+// },
+// add_float: {
+// returns: "float",
+// args: ["float", "float"],
+// },
+// add_double: {
+// returns: "double",
+// args: ["double", "double"],
+// },
+// add_int8_t: {
+// returns: "int8_t",
+// args: ["int8_t", "int8_t"],
+// },
+// add_int16_t: {
+// returns: "int16_t",
+// args: ["int16_t", "int16_t"],
+// },
+// add_int32_t: {
+// returns: "int32_t",
+// args: ["int32_t", "int32_t"],
+// },
+// add_int64_t: {
+// returns: int64_t,
+// args: [int64_t, int64_t],
+// },
+// add_uint8_t: {
+// returns: "uint8_t",
+// args: ["uint8_t", "uint8_t"],
+// },
+// add_uint16_t: {
+// returns: "uint16_t",
+// args: ["uint16_t", "uint16_t"],
+// },
+// add_uint32_t: {
+// returns: "uint32_t",
+// args: ["uint32_t", "uint32_t"],
+// },
+
+// does_pointer_equal_42_as_int32_t: {
+// returns: "bool",
+// args: ["ptr"],
+// },
+
+// ptr_should_point_to_42_as_int32_t: {
+// returns: "ptr",
+// args: [],
+// },
+// identity_ptr: {
+// returns: "ptr",
+// args: ["ptr"],
+// },
+// add_uint64_t: {
+// returns: uint64_t,
+// args: [uint64_t, uint64_t],
+// },
+
+// cb_identity_true: {
+// returns: "bool",
+// args: ["ptr"],
+// },
+// cb_identity_false: {
+// returns: "bool",
+// args: ["ptr"],
+// },
+// cb_identity_42_char: {
+// returns: "char",
+// args: ["ptr"],
+// },
+// cb_identity_42_float: {
+// returns: "float",
+// args: ["ptr"],
+// },
+// cb_identity_42_double: {
+// returns: "double",
+// args: ["ptr"],
+// },
+// cb_identity_42_uint8_t: {
+// returns: "uint8_t",
+// args: ["ptr"],
+// },
+// cb_identity_neg_42_int8_t: {
+// returns: "int8_t",
+// args: ["ptr"],
+// },
+// cb_identity_42_uint16_t: {
+// returns: "uint16_t",
+// args: ["ptr"],
+// },
+// cb_identity_42_uint32_t: {
+// returns: "uint32_t",
+// args: ["ptr"],
+// },
+// cb_identity_42_uint64_t: {
+// returns: uint64_t,
+// args: ["ptr"],
+// },
+// cb_identity_neg_42_int16_t: {
+// returns: "int16_t",
+// args: ["ptr"],
+// },
+// cb_identity_neg_42_int32_t: {
+// returns: "int32_t",
+// args: ["ptr"],
+// },
+// cb_identity_neg_42_int64_t: {
+// returns: int64_t,
+// args: ["ptr"],
+// },
+
+// return_a_function_ptr_to_function_that_returns_true: {
+// returns: "ptr",
+// args: [],
+// },
+// };
+// }
+
+// function ffiRunner(types) {
+// const {
+// symbols: {
+// returns_true,
+// returns_false,
+// return_a_function_ptr_to_function_that_returns_true,
+// returns_42_char,
+// returns_42_float,
+// returns_42_double,
+// returns_42_uint8_t,
+// returns_neg_42_int8_t,
+// returns_42_uint16_t,
+// returns_42_uint32_t,
+// returns_42_uint64_t,
+// returns_neg_42_int16_t,
+// returns_neg_42_int32_t,
+// returns_neg_42_int64_t,
+// identity_char,
+// identity_float,
+// identity_bool,
+// identity_double,
+// identity_int8_t,
+// identity_int16_t,
+// identity_int32_t,
+// identity_int64_t,
+// identity_uint8_t,
+// identity_uint16_t,
+// identity_uint32_t,
+// identity_uint64_t,
+// add_char,
+// add_float,
+// add_double,
+// add_int8_t,
+// add_int16_t,
+// add_int32_t,
+// add_int64_t,
+// add_uint8_t,
+// add_uint16_t,
+// identity_ptr,
+// add_uint32_t,
+// add_uint64_t,
+// does_pointer_equal_42_as_int32_t,
+// ptr_should_point_to_42_as_int32_t,
+// cb_identity_true,
+// cb_identity_false,
+// cb_identity_42_char,
+// cb_identity_42_float,
+// cb_identity_42_double,
+// cb_identity_42_uint8_t,
+// cb_identity_neg_42_int8_t,
+// cb_identity_42_uint16_t,
+// cb_identity_42_uint32_t,
+// cb_identity_42_uint64_t,
+// cb_identity_neg_42_int16_t,
+// cb_identity_neg_42_int32_t,
+// cb_identity_neg_42_int64_t,
+// },
+// close,
+// } = dlopen("/tmp/bun-ffi-test.dylib", types);
+
+// expect(returns_true()).toBe(true);
+
+// expect(returns_false()).toBe(false);
+
+// expect(returns_42_char()).toBe(42);
+// console.log(
+// returns_42_uint64_t().valueOf(),
+// returns_42_uint64_t(),
+// returns_42_uint64_t().valueOf() === returns_42_uint64_t()
+// );
+// expect(returns_42_uint64_t().valueOf()).toBe(42);
+
+// expect(Math.fround(returns_42_float())).toBe(Math.fround(42.41999804973602));
+// expect(returns_42_double()).toBe(42.42);
+// expect(returns_42_uint8_t()).toBe(42);
+// expect(returns_neg_42_int8_t()).toBe(-42);
+// expect(returns_42_uint16_t()).toBe(42);
+// expect(returns_42_uint32_t()).toBe(42);
+// expect(returns_42_uint64_t()).toBe(42);
+// expect(returns_neg_42_int16_t()).toBe(-42);
+// expect(returns_neg_42_int32_t()).toBe(-42);
+// expect(identity_int32_t(10)).toBe(10);
+// expect(returns_neg_42_int64_t()).toBe(-42);
+
+// expect(identity_char(10)).toBe(10);
+
+// expect(identity_float(10.199999809265137)).toBe(10.199999809265137);
+
+// expect(identity_bool(true)).toBe(true);
+
+// expect(identity_bool(false)).toBe(false);
+// expect(identity_double(10.100000000000364)).toBe(10.100000000000364);
+
+// expect(identity_int8_t(10)).toBe(10);
+// expect(identity_int16_t(10)).toBe(10);
+// expect(identity_int64_t(10)).toBe(10);
+// expect(identity_uint8_t(10)).toBe(10);
+// expect(identity_uint16_t(10)).toBe(10);
+// expect(identity_uint32_t(10)).toBe(10);
+// expect(identity_uint64_t(10)).toBe(10);
+
+// var bigArray = new BigUint64Array(8);
+// new Uint8Array(bigArray.buffer).fill(255);
+// var bigIntArray = new BigInt64Array(bigArray.buffer);
+// expect(identity_uint64_t(bigArray[0])).toBe(bigArray[0]);
+// expect(identity_uint64_t(bigArray[0] - BigInt(1))).toBe(
+// bigArray[0] - BigInt(1)
+// );
+
+// expect(add_uint64_t(BigInt(-1) * bigArray[0], bigArray[0])).toBe(0);
+// expect(add_uint64_t(BigInt(-1) * bigArray[0] + BigInt(10), bigArray[0])).toBe(
+// 10
+// );
+// expect(identity_uint64_t(0)).toBe(0);
+// expect(identity_uint64_t(100)).toBe(100);
+// expect(identity_uint64_t(BigInt(100))).toBe(100);
+// expect(identity_int64_t(bigIntArray[0])).toBe(bigIntArray[0]);
+// expect(identity_int64_t(bigIntArray[0] - BigInt(1))).toBe(
+// bigIntArray[0] - BigInt(1)
+// );
+
+// expect(add_char(1, 1)).toBe(2);
+// expect(add_float(2.4, 2.8)).toBe(Math.fround(5.2));
+// expect(add_double(4.2, 0.1)).toBe(4.3);
+// expect(add_int8_t(1, 1)).toBe(2);
+// expect(add_int16_t(1, 1)).toBe(2);
+// expect(add_int32_t(1, 1)).toBe(2);
+// expect(add_int64_t(1, 1)).toBe(2);
+// expect(add_uint8_t(1, 1)).toBe(2);
+// expect(add_uint16_t(1, 1)).toBe(2);
+// expect(add_uint32_t(1, 1)).toBe(2);
+
+// const cptr = ptr_should_point_to_42_as_int32_t();
+// expect(cptr != 0).toBe(true);
+// expect(typeof cptr === "number").toBe(true);
+// expect(does_pointer_equal_42_as_int32_t(cptr)).toBe(true);
+// const buffer = toBuffer(cptr, 0, 4);
+// expect(buffer.readInt32(0)).toBe(42);
+// expect(new DataView(toArrayBuffer(cptr, 0, 4), 0, 4).getInt32(0, true)).toBe(
+// 42
+// );
+// expect(ptr(buffer)).toBe(cptr);
+// expect(new CString(cptr, 0, 1).toString()).toBe("*");
+// expect(identity_ptr(cptr)).toBe(cptr);
+// const second_ptr = ptr(new Buffer(8));
+// expect(identity_ptr(second_ptr)).toBe(second_ptr);
+
+// var myCFunction = new CFunction({
+// ptr: return_a_function_ptr_to_function_that_returns_true(),
+// returns: "bool",
+// });
+// expect(myCFunction()).toBe(true);
+
+// // function identityBool() {
+// // return true;
+// // }
+// // globalThis.identityBool = identityBool;
+
+// // const first = native.callback(
+// // {
+// // returns: "bool",
+// // },
+// // identityBool
+// // );
+// // expect(
+// // cb_identity_true()
+// // ).toBe(true);
+
+// // expect(cb_identity_true(first)).toBe(true);
+
+// // expect(
+// // cb_identity_false(
+// // callback(
+// // {
+// // returns: "bool",
+// // },
+// // () => false
+// // )
+// // )
+// // ).toBe(false);
+
+// // expect(
+// // cb_identity_42_char(
+// // callback(
+// // {
+// // returns: "char",
+// // },
+// // () => 42
+// // )
+// // )
+// // ).toBe(42);
+// // expect(
+// // cb_identity_42_uint8_t(
+// // callback(
+// // {
+// // returns: "uint8_t",
+// // },
+// // () => 42
+// // )
+// // )
+// // ).toBe(42);
+
+// // cb_identity_neg_42_int8_t(
+// // callback(
+// // {
+// // returns: "int8_t",
+// // },
+// // () => -42
+// // )
+// // ).toBe(-42);
+
+// // cb_identity_42_uint16_t(
+// // callback(
+// // {
+// // returns: "uint16_t",
+// // },
+// // () => 42
+// // )
+// // ).toBe(42);
+
+// // cb_identity_42_uint32_t(
+// // callback(
+// // {
+// // returns: "uint32_t",
+// // },
+// // () => 42
+// // )
+// // ).toBe(42);
+
+// // cb_identity_neg_42_int16_t(
+// // callback(
+// // {
+// // returns: "int16_t",
+// // },
+// // () => -42
+// // )
+// // ).toBe(-42);
+
+// // cb_identity_neg_42_int32_t(
+// // callback(
+// // {
+// // returns: "int32_t",
+// // },
+// // () => -42
+// // )
+// // ).toBe(-42);
+
+// close();
+// }
+
+// it("run ffi fast", () => {
+// ffiRunner(getTypes(true));
+// });
+
+// it("run ffi", () => {
+// ffiRunner(getTypes(false));
+// });
diff --git a/test/bun.js/fs-stream.js b/test/bun.js/fs-stream.js
new file mode 100644
index 000000000..4b71c95b7
--- /dev/null
+++ b/test/bun.js/fs-stream.js
@@ -0,0 +1,23 @@
+import { createReadStream, createWriteStream, readFileSync } from "fs";
+
+await new Promise((resolve, reject) => {
+ createReadStream("fs-stream.js")
+ .pipe(createWriteStream("/tmp/fs-stream.copy.js"))
+ .once("error", (err) => reject(err))
+ .once("finish", () => {
+ try {
+ const copied = readFileSync("/tmp/fs-stream.copy.js", "utf8");
+ const real = readFileSync("/tmp/fs-stream.js", "utf8");
+ if (copied !== real) {
+ reject(
+ new Error("fs-stream.js is not the same as fs-stream.copy.js")
+ );
+ return;
+ }
+
+ resolve(true);
+ } catch (err) {
+ reject(err);
+ }
+ });
+});
diff --git a/test/bun.js/fs.test.js b/test/bun.js/fs.test.js
new file mode 100644
index 000000000..79ac60eaa
--- /dev/null
+++ b/test/bun.js/fs.test.js
@@ -0,0 +1,244 @@
+import { gc } from "bun";
+import { describe, expect, it } from "bun:test";
+import {
+ closeSync,
+ existsSync,
+ mkdirSync,
+ openSync,
+ readdirSync,
+ readFile,
+ readFileSync,
+ readSync,
+ writeFileSync,
+ writeSync,
+} from "node:fs";
+
+const Buffer = globalThis.Buffer || Uint8Array;
+
+if (!import.meta.dir) {
+ import.meta.dir = ".";
+}
+
+describe("mkdirSync", () => {
+ it("should create a directory", () => {
+ const tempdir = `/tmp/fs.test.js/${Date.now()}/1234/hi`;
+ expect(existsSync(tempdir)).toBe(false);
+ expect(tempdir.includes(mkdirSync(tempdir, { recursive: true }))).toBe(
+ true
+ );
+ expect(existsSync(tempdir)).toBe(true);
+ });
+});
+
+it("readdirSync on import.meta.dir", () => {
+ const dirs = readdirSync(import.meta.dir);
+ expect(dirs.length > 0).toBe(true);
+ var match = false;
+ gc(true);
+ for (let i = 0; i < dirs.length; i++) {
+ if (dirs[i] === import.meta.file) {
+ match = true;
+ }
+ }
+ gc(true);
+ expect(match).toBe(true);
+});
+
+it("readdirSync on import.meta.dir with trailing slash", () => {
+ const dirs = readdirSync(import.meta.dir + "/");
+ expect(dirs.length > 0).toBe(true);
+ // this file should exist in it
+ var match = false;
+ for (let i = 0; i < dirs.length; i++) {
+ if (dirs[i] === import.meta.file) {
+ match = true;
+ }
+ }
+ expect(match).toBe(true);
+});
+
+it("readdirSync works on empty directories", () => {
+ const path = `/tmp/fs-test-empty-dir-${(
+ Math.random() * 100000 +
+ 100
+ ).toString(32)}`;
+ mkdirSync(path, { recursive: true });
+ expect(readdirSync(path).length).toBe(0);
+});
+
+it("readdirSync works on directories with under 32 files", () => {
+ const path = `/tmp/fs-test-one-dir-${(Math.random() * 100000 + 100).toString(
+ 32
+ )}`;
+ mkdirSync(path, { recursive: true });
+ writeFileSync(`${path}/a`, "a");
+ const results = readdirSync(path);
+ expect(results.length).toBe(1);
+ expect(results[0]).toBe("a");
+});
+
+it("readdirSync throws when given a file path", () => {
+ try {
+ readdirSync(import.meta.path);
+ throw new Error("should not get here");
+ } catch (exception) {
+ expect(exception.name).toBe("ENOTDIR");
+ }
+});
+
+it("readdirSync throws when given a path that doesn't exist", () => {
+ try {
+ readdirSync(import.meta.path + "/does-not-exist/really");
+ throw new Error("should not get here");
+ } catch (exception) {
+ expect(exception.name).toBe("ENOTDIR");
+ }
+});
+
+it("readdirSync throws when given a file path with trailing slash", () => {
+ try {
+ readdirSync(import.meta.path + "/");
+ throw new Error("should not get here");
+ } catch (exception) {
+ expect(exception.name).toBe("ENOTDIR");
+ }
+});
+
+describe("readSync", () => {
+ const firstFourBytes = new Uint32Array(
+ new TextEncoder().encode("File").buffer
+ )[0];
+ it("works with a position set to 0", () => {
+ const fd = openSync(import.meta.dir + "/readFileSync.txt", "r");
+ const four = new Uint8Array(4);
+
+ {
+ const count = readSync(fd, four, 0, 4, 0);
+ const u32 = new Uint32Array(four.buffer)[0];
+ expect(u32).toBe(firstFourBytes);
+ expect(count).toBe(4);
+ }
+ closeSync(fd);
+ });
+ it("works without position set", () => {
+ const fd = openSync(import.meta.dir + "/readFileSync.txt", "r");
+ const four = new Uint8Array(4);
+ {
+ const count = readSync(fd, four);
+ const u32 = new Uint32Array(four.buffer)[0];
+ expect(u32).toBe(firstFourBytes);
+ expect(count).toBe(4);
+ }
+ closeSync(fd);
+ });
+});
+
+describe("writeSync", () => {
+ it("works with a position set to 0", () => {
+ const fd = openSync(import.meta.dir + "/writeFileSync.txt", "w+");
+ const four = new Uint8Array(4);
+
+ {
+ const count = writeSync(fd, new TextEncoder().encode("File"), 0, 4, 0);
+ expect(count).toBe(4);
+ }
+ closeSync(fd);
+ });
+ it("works without position set", () => {
+ const fd = openSync(import.meta.dir + "/writeFileSync.txt", "w+");
+ const four = new Uint8Array(4);
+ {
+ const count = writeSync(fd, new TextEncoder().encode("File"));
+ expect(count).toBe(4);
+ }
+ closeSync(fd);
+ });
+});
+
+describe("readFileSync", () => {
+ it("works", () => {
+ const text = readFileSync(import.meta.dir + "/readFileSync.txt", "utf8");
+ expect(text).toBe("File read successfully");
+ });
+
+ it("works with a file url", () => {
+ const text = readFileSync(
+ new URL("file://" + import.meta.dir + "/readFileSync.txt"),
+ "utf8"
+ );
+ expect(text).toBe("File read successfully");
+ });
+
+ it("returning Buffer works", () => {
+ const text = readFileSync(import.meta.dir + "/readFileSync.txt");
+ const encoded = [
+ 70, 105, 108, 101, 32, 114, 101, 97, 100, 32, 115, 117, 99, 99, 101, 115,
+ 115, 102, 117, 108, 108, 121,
+ ];
+ for (let i = 0; i < encoded.length; i++) {
+ expect(text[i]).toBe(encoded[i]);
+ }
+ });
+});
+
+describe("readFile", () => {
+ it("works", async () => {
+ await new Promise((resolve, reject) => {
+ readFile(import.meta.dir + "/readFileSync.txt", "utf8", (err, text) => {
+ expect(text).toBe("File read successfully");
+ resolve(true);
+ });
+ });
+ });
+
+ it("returning Buffer works", async () => {
+ await new Promise((resolve, reject) => {
+ readFile(import.meta.dir + "/readFileSync.txt", (err, text) => {
+ const encoded = [
+ 70, 105, 108, 101, 32, 114, 101, 97, 100, 32, 115, 117, 99, 99, 101,
+ 115, 115, 102, 117, 108, 108, 121,
+ ];
+ for (let i = 0; i < encoded.length; i++) {
+ expect(text[i]).toBe(encoded[i]);
+ }
+ resolve(true);
+ });
+ });
+ });
+});
+
+describe("writeFileSync", () => {
+ it("works", () => {
+ const path = `/tmp/${Date.now()}.writeFileSync.txt`;
+ writeFileSync(path, "File written successfully", "utf8");
+
+ expect(readFileSync(path, "utf8")).toBe("File written successfully");
+ });
+
+ it("returning Buffer works", () => {
+ const buffer = new Buffer([
+ 70, 105, 108, 101, 32, 119, 114, 105, 116, 116, 101, 110, 32, 115, 117,
+ 99, 99, 101, 115, 115, 102, 117, 108, 108, 121,
+ ]);
+ const path = `/tmp/${Date.now()}.blob.writeFileSync.txt`;
+ writeFileSync(path, buffer);
+ const out = readFileSync(path);
+
+ for (let i = 0; i < buffer.length; i++) {
+ expect(buffer[i]).toBe(out[i]);
+ }
+ });
+ it("returning ArrayBuffer works", () => {
+ const buffer = new Buffer([
+ 70, 105, 108, 101, 32, 119, 114, 105, 116, 116, 101, 110, 32, 115, 117,
+ 99, 99, 101, 115, 115, 102, 117, 108, 108, 121,
+ ]);
+ const path = `/tmp/${Date.now()}.blob2.writeFileSync.txt`;
+ writeFileSync(path, buffer);
+ const out = readFileSync(path);
+
+ for (let i = 0; i < buffer.length; i++) {
+ expect(buffer[i]).toBe(out[i]);
+ }
+ });
+});
diff --git a/test/bun.js/gc.js b/test/bun.js/gc.js
new file mode 100644
index 000000000..9212e8b76
--- /dev/null
+++ b/test/bun.js/gc.js
@@ -0,0 +1,15 @@
+export function gc() {
+ // console.trace("GC");
+ Bun.gc(true);
+}
+
+// we must ensure that finalizers are run
+// so that the reference-counting logic is exercised
+export function gcTick(trace = false) {
+ trace && console.trace("");
+ // console.trace("hello");
+ gc();
+ return new Promise((resolve) => {
+ setTimeout(resolve, 0);
+ });
+}
diff --git a/test/bun.js/globals.test.js b/test/bun.js/globals.test.js
new file mode 100644
index 000000000..b498e0e8e
--- /dev/null
+++ b/test/bun.js/globals.test.js
@@ -0,0 +1,39 @@
+import { it, describe, expect } from "bun:test";
+
+it("extendable", () => {
+ const classes = [
+ Blob,
+ TextDecoder,
+ TextEncoder,
+ Request,
+ Response,
+ Headers,
+ HTMLRewriter,
+ Bun.Transpiler,
+ ];
+ // None of these should error
+ for (let Class of classes) {
+ var Foo = class extends Class {};
+ var bar = new Foo();
+ expect(bar instanceof Class).toBe(true);
+ expect(!!Class.prototype).toBe(true);
+ expect(typeof Class.prototype).toBe("object");
+ }
+ expect(true).toBe(true);
+});
+
+it("name", () => {
+ const classes = [
+ ["Blob", Blob],
+ ["TextDecoder", TextDecoder],
+ ["TextEncoder", TextEncoder],
+ ["Request", Request],
+ ["Response", Response],
+ ["Headers", Headers],
+ ["HTMLRewriter", HTMLRewriter],
+ ["Transpiler", Bun.Transpiler],
+ ];
+ for (let [name, Class] of classes) {
+ expect(Class.name).toBe(name);
+ }
+});
diff --git a/test/bun.js/hash.test.js b/test/bun.js/hash.test.js
new file mode 100644
index 000000000..71ad5a229
--- /dev/null
+++ b/test/bun.js/hash.test.js
@@ -0,0 +1,36 @@
+import fs from "fs";
+import { it, expect } from "bun:test";
+import path from "path";
+
+it(`Bun.hash()`, () => {
+ Bun.hash("hello world");
+ Bun.hash(new TextEncoder().encode("hello world"));
+});
+it(`Bun.hash.wyhash()`, () => {
+ Bun.hash.wyhash("hello world");
+ Bun.hash.wyhash(new TextEncoder().encode("hello world"));
+});
+it(`Bun.hash.adler32()`, () => {
+ Bun.hash.adler32("hello world");
+ Bun.hash.adler32(new TextEncoder().encode("hello world"));
+});
+it(`Bun.hash.crc32()`, () => {
+ Bun.hash.crc32("hello world");
+ Bun.hash.crc32(new TextEncoder().encode("hello world"));
+});
+it(`Bun.hash.cityHash32()`, () => {
+ Bun.hash.cityHash32("hello world");
+ Bun.hash.cityHash32(new TextEncoder().encode("hello world"));
+});
+it(`Bun.hash.cityHash64()`, () => {
+ Bun.hash.cityHash64("hello world");
+ Bun.hash.cityHash64(new TextEncoder().encode("hello world"));
+});
+it(`Bun.hash.murmur32v3()`, () => {
+ Bun.hash.murmur32v3("hello world");
+ Bun.hash.murmur32v3(new TextEncoder().encode("hello world"));
+});
+it(`Bun.hash.murmur64v2()`, () => {
+ Bun.hash.murmur64v2("hello world");
+ Bun.hash.murmur64v2(new TextEncoder().encode("hello world"));
+});
diff --git a/test/bun.js/html-rewriter.test.js b/test/bun.js/html-rewriter.test.js
new file mode 100644
index 000000000..29b765c2f
--- /dev/null
+++ b/test/bun.js/html-rewriter.test.js
@@ -0,0 +1,293 @@
+import { describe, it, expect } from "bun:test";
+import { gcTick } from "./gc";
+
+var setTimeoutAsync = (fn, delay) => {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ try {
+ resolve(fn());
+ } catch (e) {
+ reject(e);
+ }
+ }, delay);
+ });
+};
+
+describe("HTMLRewriter", () => {
+ it("HTMLRewriter: async replacement", async () => {
+ await gcTick();
+ const res = new HTMLRewriter()
+ .on("div", {
+ async element(element) {
+ await setTimeoutAsync(() => {
+ element.setInnerContent("<span>replace</span>", { html: true });
+ }, 5);
+ },
+ })
+ .transform(new Response("<div>example.com</div>"));
+ await gcTick();
+ expect(await res.text()).toBe("<div><span>replace</span></div>");
+ await gcTick();
+ });
+
+ it("supports element handlers", async () => {
+ var rewriter = new HTMLRewriter();
+ rewriter.on("div", {
+ element(element) {
+ element.setInnerContent("<blink>it worked!</blink>", { html: true });
+ },
+ });
+ var input = new Response("<div>hello</div>");
+ var output = rewriter.transform(input);
+ expect(await output.text()).toBe("<div><blink>it worked!</blink></div>");
+ });
+
+ it("(from file) supports element handlers", async () => {
+ var rewriter = new HTMLRewriter();
+ rewriter.on("div", {
+ element(element) {
+ element.setInnerContent("<blink>it worked!</blink>", { html: true });
+ },
+ });
+ await Bun.write("/tmp/html-rewriter.txt.js", "<div>hello</div>");
+ var input = new Response(Bun.file("/tmp/html-rewriter.txt.js"));
+ var output = rewriter.transform(input);
+ expect(await output.text()).toBe("<div><blink>it worked!</blink></div>");
+ });
+
+ it("supports attribute iterator", async () => {
+ var rewriter = new HTMLRewriter();
+ var expected = [
+ ["first", ""],
+ ["second", "alrihgt"],
+ ["third", "123"],
+ ["fourth", "5"],
+ ["fifth", "helloooo"],
+ ];
+ rewriter.on("div", {
+ element(element2) {
+ for (let attr of element2.attributes) {
+ const stack = expected.shift();
+ expect(stack[0]).toBe(attr[0]);
+ expect(stack[1]).toBe(attr[1]);
+ }
+ },
+ });
+ var input = new Response(
+ '<div first second="alrihgt" third="123" fourth=5 fifth=helloooo>hello</div>'
+ );
+ var output = rewriter.transform(input);
+ expect(await output.text()).toBe(
+ '<div first second="alrihgt" third="123" fourth=5 fifth=helloooo>hello</div>'
+ );
+ expect(expected.length).toBe(0);
+ });
+
+ it("handles element specific mutations", async () => {
+ // prepend/append
+ let res = new HTMLRewriter()
+ .on("p", {
+ element(element) {
+ element.prepend("<span>prepend</span>");
+ element.prepend("<span>prepend html</span>", { html: true });
+ element.append("<span>append</span>");
+ element.append("<span>append html</span>", { html: true });
+ },
+ })
+ .transform(new Response("<p>test</p>"));
+ expect(await res.text()).toBe(
+ [
+ "<p>",
+ "<span>prepend html</span>",
+ "&lt;span&gt;prepend&lt;/span&gt;",
+ "test",
+ "&lt;span&gt;append&lt;/span&gt;",
+ "<span>append html</span>",
+ "</p>",
+ ].join("")
+ );
+
+ // setInnerContent
+ res = new HTMLRewriter()
+ .on("p", {
+ element(element) {
+ element.setInnerContent("<span>replace</span>");
+ },
+ })
+ .transform(new Response("<p>test</p>"));
+ expect(await res.text()).toBe("<p>&lt;span&gt;replace&lt;/span&gt;</p>");
+ res = new HTMLRewriter()
+ .on("p", {
+ element(element) {
+ element.setInnerContent("<span>replace</span>", { html: true });
+ },
+ })
+ .transform(new Response("<p>test</p>"));
+ expect(await res.text()).toBe("<p><span>replace</span></p>");
+
+ // removeAndKeepContent
+ res = new HTMLRewriter()
+ .on("p", {
+ element(element) {
+ element.removeAndKeepContent();
+ },
+ })
+ .transform(new Response("<p>test</p>"));
+ expect(await res.text()).toBe("test");
+ });
+
+ it("handles element class properties", async () => {
+ class Handler {
+ constructor(content) {
+ this.content = content;
+ }
+
+ // noinspection JSUnusedGlobalSymbols
+ element(element) {
+ element.setInnerContent(this.content);
+ }
+ }
+ const res = new HTMLRewriter()
+ .on("p", new Handler("new"))
+ .transform(new Response("<p>test</p>"));
+ expect(await res.text()).toBe("<p>new</p>");
+ });
+
+ const commentsMutationsInput = "<p><!--test--></p>";
+ const commentsMutationsExpected = {
+ beforeAfter: [
+ "<p>",
+ "&lt;span&gt;before&lt;/span&gt;",
+ "<span>before html</span>",
+ "<!--test-->",
+ "<span>after html</span>",
+ "&lt;span&gt;after&lt;/span&gt;",
+ "</p>",
+ ].join(""),
+ replace: "<p>&lt;span&gt;replace&lt;/span&gt;</p>",
+ replaceHtml: "<p><span>replace</span></p>",
+ remove: "<p></p>",
+ };
+
+ const commentPropertiesMacro = async (func) => {
+ const res = func(new HTMLRewriter(), (comment) => {
+ expect(comment.removed).toBe(false);
+ expect(comment.text).toBe("test");
+ comment.text = "new";
+ expect(comment.text).toBe("new");
+ }).transform(new Response("<p><!--test--></p>"));
+ expect(await res.text()).toBe("<p><!--new--></p>");
+ };
+
+ it("HTMLRewriter: handles comment properties", () =>
+ commentPropertiesMacro((rw, comments) => {
+ rw.on("p", { comments });
+ return rw;
+ }));
+
+ it("selector tests", async () => {
+ const checkSelector = async (selector, input, expected) => {
+ const res = new HTMLRewriter()
+ .on(selector, {
+ element(element) {
+ element.setInnerContent("new");
+ },
+ })
+ .transform(new Response(input));
+ expect(await res.text()).toBe(expected);
+ };
+
+ await checkSelector("*", "<h1>1</h1><p>2</p>", "<h1>new</h1><p>new</p>");
+ await checkSelector("p", "<h1>1</h1><p>2</p>", "<h1>1</h1><p>new</p>");
+ await checkSelector(
+ "p:nth-child(2)",
+ "<div><p>1</p><p>2</p><p>3</p></div>",
+ "<div><p>1</p><p>new</p><p>3</p></div>"
+ );
+ await checkSelector(
+ "p:first-child",
+ "<div><p>1</p><p>2</p><p>3</p></div>",
+ "<div><p>new</p><p>2</p><p>3</p></div>"
+ );
+ await checkSelector(
+ "p:nth-of-type(2)",
+ "<div><p>1</p><h1>2</h1><p>3</p><h1>4</h1><p>5</p></div>",
+ "<div><p>1</p><h1>2</h1><p>new</p><h1>4</h1><p>5</p></div>"
+ );
+ await checkSelector(
+ "p:first-of-type",
+ "<div><h1>1</h1><p>2</p><p>3</p></div>",
+ "<div><h1>1</h1><p>new</p><p>3</p></div>"
+ );
+ await checkSelector(
+ "p:not(:first-child)",
+ "<div><p>1</p><p>2</p><p>3</p></div>",
+ "<div><p>1</p><p>new</p><p>new</p></div>"
+ );
+ await checkSelector(
+ "p.red",
+ '<p class="red">1</p><p>2</p>',
+ '<p class="red">new</p><p>2</p>'
+ );
+ await checkSelector(
+ "h1#header",
+ '<h1 id="header">1</h1><h1>2</h1>',
+ '<h1 id="header">new</h1><h1>2</h1>'
+ );
+ await checkSelector(
+ "p[data-test]",
+ "<p data-test>1</p><p>2</p>",
+ "<p data-test>new</p><p>2</p>"
+ );
+ await checkSelector(
+ 'p[data-test="one"]',
+ '<p data-test="one">1</p><p data-test="two">2</p>',
+ '<p data-test="one">new</p><p data-test="two">2</p>'
+ );
+ await checkSelector(
+ 'p[data-test="one" i]',
+ '<p data-test="one">1</p><p data-test="OnE">2</p><p data-test="two">3</p>',
+ '<p data-test="one">new</p><p data-test="OnE">new</p><p data-test="two">3</p>'
+ );
+ await checkSelector(
+ 'p[data-test="one" s]',
+ '<p data-test="one">1</p><p data-test="OnE">2</p><p data-test="two">3</p>',
+ '<p data-test="one">new</p><p data-test="OnE">2</p><p data-test="two">3</p>'
+ );
+ await checkSelector(
+ 'p[data-test~="two"]',
+ '<p data-test="one two three">1</p><p data-test="one two">2</p><p data-test="one">3</p>',
+ '<p data-test="one two three">new</p><p data-test="one two">new</p><p data-test="one">3</p>'
+ );
+ await checkSelector(
+ 'p[data-test^="a"]',
+ '<p data-test="a1">1</p><p data-test="a2">2</p><p data-test="b1">3</p>',
+ '<p data-test="a1">new</p><p data-test="a2">new</p><p data-test="b1">3</p>'
+ );
+ await checkSelector(
+ 'p[data-test$="1"]',
+ '<p data-test="a1">1</p><p data-test="a2">2</p><p data-test="b1">3</p>',
+ '<p data-test="a1">new</p><p data-test="a2">2</p><p data-test="b1">new</p>'
+ );
+ await checkSelector(
+ 'p[data-test*="b"]',
+ '<p data-test="abc">1</p><p data-test="ab">2</p><p data-test="a">3</p>',
+ '<p data-test="abc">new</p><p data-test="ab">new</p><p data-test="a">3</p>'
+ );
+ await checkSelector(
+ 'p[data-test|="a"]',
+ '<p data-test="a">1</p><p data-test="a-1">2</p><p data-test="a2">3</p>',
+ '<p data-test="a">new</p><p data-test="a-1">new</p><p data-test="a2">3</p>'
+ );
+ await checkSelector(
+ "div span",
+ "<div><h1><span>1</span></h1><span>2</span><b>3</b></div>",
+ "<div><h1><span>new</span></h1><span>new</span><b>3</b></div>"
+ );
+ await checkSelector(
+ "div > span",
+ "<div><h1><span>1</span></h1><span>2</span><b>3</b></div>",
+ "<div><h1><span>1</span></h1><span>new</span><b>3</b></div>"
+ );
+ });
+});
diff --git a/test/bun.js/import-meta.test.js b/test/bun.js/import-meta.test.js
new file mode 100644
index 000000000..0e2faa903
--- /dev/null
+++ b/test/bun.js/import-meta.test.js
@@ -0,0 +1,33 @@
+import { it, expect } from "bun:test";
+import * as Module from "node:module";
+import sync from "./require-json.json";
+
+const { path, dir } = import.meta;
+
+it("import.meta.resolveSync", () => {
+ expect(
+ import.meta.resolveSync("./" + import.meta.file, import.meta.path)
+ ).toBe(path);
+ const require = Module.createRequire(import.meta.path);
+ expect(require.resolve(import.meta.path)).toBe(path);
+ expect(require.resolve("./" + import.meta.file)).toBe(path);
+
+ // check it works with URL objects
+ expect(
+ Module.createRequire(new URL(import.meta.url)).resolve(import.meta.path)
+ ).toBe(import.meta.path);
+});
+
+it("import.meta.require", () => {
+ expect(import.meta.require("./require-json.json").hello).toBe(sync.hello);
+ const require = Module.createRequire(import.meta.path);
+ expect(require("./require-json.json").hello).toBe(sync.hello);
+});
+
+it("import.meta.dir", () => {
+ expect(dir.endsWith("/bun/test/bun.js")).toBe(true);
+});
+
+it("import.meta.path", () => {
+ expect(path.endsWith("/bun/test/bun.js/import-meta.test.js")).toBe(true);
+});
diff --git a/test/bun.js/inline.macro.js b/test/bun.js/inline.macro.js
new file mode 100644
index 000000000..ff0292d0a
--- /dev/null
+++ b/test/bun.js/inline.macro.js
@@ -0,0 +1,3 @@
+export function whatDidIPass(expr, ctx) {
+ return ctx;
+}
diff --git a/test/bun.js/inspect.test.js b/test/bun.js/inspect.test.js
new file mode 100644
index 000000000..bf5021c33
--- /dev/null
+++ b/test/bun.js/inspect.test.js
@@ -0,0 +1,96 @@
+import { it, expect } from "bun:test";
+
+it("jsx with two elements", () => {
+ const input = Bun.inspect(
+ <div hello="quoted">
+ <input type="text" value={"123"} />
+ string inside child
+ </div>
+ );
+
+ const output = `<div hello="quoted">
+ <input type="text" value="123" />
+ string inside child
+</div>`;
+
+ expect(input).toBe(output);
+});
+
+const Foo = () => <div hello="quoted">foo</div>;
+
+it("jsx with anon component", () => {
+ const input = Bun.inspect(<Foo />);
+
+ const output = `<NoName />`;
+
+ expect(input).toBe(output);
+});
+
+it("jsx with fragment", () => {
+ const input = Bun.inspect(<>foo bar</>);
+
+ const output = `<>foo bar</>`;
+
+ expect(input).toBe(output);
+});
+
+it("inspect", () => {
+ expect(Bun.inspect(new TypeError("what")).includes("TypeError: what")).toBe(
+ true
+ );
+ expect("hi").toBe("hi");
+ expect(Bun.inspect(1)).toBe("1");
+ expect(Bun.inspect(1, "hi")).toBe("1 hi");
+ expect(Bun.inspect([])).toBe("[]");
+ expect(Bun.inspect({})).toBe("{ }");
+ expect(Bun.inspect({ hello: 1 })).toBe("{ hello: 1 }");
+ expect(Bun.inspect({ hello: 1, there: 2 })).toBe("{ hello: 1, there: 2 }");
+ expect(Bun.inspect({ hello: "1", there: 2 })).toBe(
+ '{ hello: "1", there: 2 }'
+ );
+ expect(Bun.inspect({ 'hello-"there': "1", there: 2 })).toBe(
+ '{ "hello-\\"there": "1", there: 2 }'
+ );
+ var str = "123";
+ while (str.length < 4096) {
+ str += "123";
+ }
+ expect(Bun.inspect(str)).toBe(str);
+ // expect(Bun.inspect(new Headers())).toBe("Headers (0 KB) {}");
+ expect(Bun.inspect(new Response()).length > 0).toBe(true);
+ // expect(
+ // JSON.stringify(
+ // new Headers({
+ // hi: "ok",
+ // })
+ // )
+ // ).toBe('{"hi":"ok"}');
+ expect(Bun.inspect(new Set())).toBe("Set {}");
+ expect(Bun.inspect(new Map())).toBe("Map {}");
+ expect(Bun.inspect(new Map([["foo", "bar"]]))).toBe(
+ 'Map(1) {\n "foo": "bar",\n}'
+ );
+ expect(Bun.inspect(new Set(["bar"]))).toBe('Set(1) {\n "bar",\n}');
+ expect(Bun.inspect(<div>foo</div>)).toBe("<div>foo</div>");
+ expect(Bun.inspect(<div hello>foo</div>)).toBe("<div hello=true>foo</div>");
+ expect(Bun.inspect(<div hello={1}>foo</div>)).toBe("<div hello=1>foo</div>");
+ expect(Bun.inspect(<div hello={123}>hi</div>)).toBe(
+ "<div hello=123>hi</div>"
+ );
+ expect(Bun.inspect(<div hello="quoted">quoted</div>)).toBe(
+ '<div hello="quoted">quoted</div>'
+ );
+ expect(
+ Bun.inspect(
+ <div hello="quoted">
+ <input type="text" value={"123"} />
+ </div>
+ )
+ ).toBe(
+ `
+<div hello="quoted">
+ <input type="text" value="123" />
+</div>`.trim()
+ );
+ expect(Bun.inspect(BigInt(32))).toBe("32n");
+});
diff --git a/test/bun.js/macro-check.js b/test/bun.js/macro-check.js
new file mode 100644
index 000000000..0f494a4e7
--- /dev/null
+++ b/test/bun.js/macro-check.js
@@ -0,0 +1,7 @@
+export function keepSecondArgument(bacon1234) {
+ return bacon1234.arguments[1];
+}
+
+export function bacon(heloooo) {
+ return heloooo.arguments[1];
+}
diff --git a/test/bun.js/microtask.test.js b/test/bun.js/microtask.test.js
new file mode 100644
index 000000000..18956b1e5
--- /dev/null
+++ b/test/bun.js/microtask.test.js
@@ -0,0 +1,80 @@
+import { it } from "bun:test";
+
+it("queueMicrotask", async () => {
+ // You can verify this test is correct by copy pasting this into a browser's console and checking it doesn't throw an error.
+ var run = 0;
+
+ await new Promise((resolve, reject) => {
+ queueMicrotask(() => {
+ if (run++ != 0) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ queueMicrotask(() => {
+ if (run++ != 3) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ });
+ });
+ queueMicrotask(() => {
+ if (run++ != 1) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ queueMicrotask(() => {
+ if (run++ != 4) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+
+ queueMicrotask(() => {
+ if (run++ != 6) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ });
+ });
+ });
+ queueMicrotask(() => {
+ if (run++ != 2) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ queueMicrotask(() => {
+ if (run++ != 5) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+
+ queueMicrotask(() => {
+ if (run++ != 7) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ resolve(true);
+ });
+ });
+ });
+ });
+
+ {
+ var passed = false;
+ try {
+ queueMicrotask(1234);
+ } catch (exception) {
+ passed = exception instanceof TypeError;
+ }
+
+ if (!passed)
+ throw new Error(
+ "queueMicrotask should throw a TypeError if the argument is not a function"
+ );
+ }
+
+ {
+ var passed = false;
+ try {
+ queueMicrotask();
+ } catch (exception) {
+ passed = exception instanceof TypeError;
+ }
+
+ if (!passed)
+ throw new Error(
+ "queueMicrotask should throw a TypeError if the argument is empty"
+ );
+ }
+});
diff --git a/test/bun.js/mmap.test.js b/test/bun.js/mmap.test.js
new file mode 100644
index 000000000..2b15a4000
--- /dev/null
+++ b/test/bun.js/mmap.test.js
@@ -0,0 +1,69 @@
+import { describe, it, expect } from "bun:test";
+import { gcTick } from "./gc";
+
+describe("Bun.mmap", async () => {
+ await gcTick();
+ const path = `/tmp/bun-mmap-test_${Math.random()}.txt`;
+ await gcTick();
+ await Bun.write(path, "hello");
+ await gcTick();
+
+ it("mmap finalizer", async () => {
+ let map = Bun.mmap(path);
+ await gcTick();
+ const map2 = Bun.mmap(path);
+
+ map = null;
+ await gcTick();
+ });
+
+ it("mmap passed to other syscalls", async () => {
+ const map = Bun.mmap(path);
+ await gcTick();
+ await Bun.write(path + "1", map);
+ await gcTick();
+ const text = await (await Bun.file(path + "1")).text();
+ await gcTick();
+
+ expect(text).toBe(new TextDecoder().decode(map));
+ });
+
+ it("mmap sync", async () => {
+ const map = Bun.mmap(path);
+ await gcTick();
+ const map2 = Bun.mmap(path);
+ await gcTick();
+
+ const old = map[0];
+ await gcTick();
+ map[0] = 0;
+ await gcTick();
+ expect(map2[0]).toBe(0);
+
+ map2[0] = old;
+ await gcTick();
+ expect(map[0]).toBe(old);
+ await gcTick();
+ await Bun.write(path, "olleh");
+ await gcTick();
+ expect(new TextDecoder().decode(map)).toBe("olleh");
+ await gcTick();
+ });
+
+ it("mmap private", async () => {
+ await gcTick();
+ const map = Bun.mmap(path, { shared: true });
+ await gcTick();
+ const map2 = Bun.mmap(path, { shared: false });
+ await gcTick();
+ const old = map[0];
+
+ await gcTick();
+ map2[0] = 0;
+ await gcTick();
+ expect(map2[0]).toBe(0);
+ await gcTick();
+ expect(map[0]).toBe(old);
+ await gcTick();
+ });
+});
diff --git a/test/bun.js/node-builtins.test.js b/test/bun.js/node-builtins.test.js
new file mode 100644
index 000000000..df31c64fc
--- /dev/null
+++ b/test/bun.js/node-builtins.test.js
@@ -0,0 +1,16 @@
+import { describe, it, expect } from "bun:test";
+
+import { EventEmitter } from "events";
+
+describe("EventEmitter", () => {
+ it("should emit events", () => {
+ const emitter = new EventEmitter();
+ var called = false;
+ const listener = () => {
+ called = true;
+ };
+ emitter.on("test", listener);
+ emitter.emit("test");
+ expect(called).toBe(true);
+ });
+});
diff --git a/test/bun.js/path.test.js b/test/bun.js/path.test.js
new file mode 100644
index 000000000..997368150
--- /dev/null
+++ b/test/bun.js/path.test.js
@@ -0,0 +1,457 @@
+const { file } = import.meta;
+
+import { describe, it, expect } from "bun:test";
+import * as path from "node:path";
+import assert from "assert";
+
+const strictEqual = (...args) => {
+ assert.strictEqual(...args);
+ expect(true).toBe(true);
+};
+
+it("path.basename", () => {
+ strictEqual(path.basename(file), "path.test.js");
+ strictEqual(path.basename(file, ".js"), "path.test");
+ strictEqual(path.basename(".js", ".js"), "");
+ strictEqual(path.basename(""), "");
+ strictEqual(path.basename("/dir/basename.ext"), "basename.ext");
+ strictEqual(path.basename("/basename.ext"), "basename.ext");
+ strictEqual(path.basename("basename.ext"), "basename.ext");
+ strictEqual(path.basename("basename.ext/"), "basename.ext");
+ strictEqual(path.basename("basename.ext//"), "basename.ext");
+ strictEqual(path.basename("aaa/bbb", "/bbb"), "bbb");
+ strictEqual(path.basename("aaa/bbb", "a/bbb"), "bbb");
+ strictEqual(path.basename("aaa/bbb", "bbb"), "bbb");
+ strictEqual(path.basename("aaa/bbb//", "bbb"), "bbb");
+ strictEqual(path.basename("aaa/bbb", "bb"), "b");
+ strictEqual(path.basename("aaa/bbb", "b"), "bb");
+ strictEqual(path.basename("/aaa/bbb", "/bbb"), "bbb");
+ strictEqual(path.basename("/aaa/bbb", "a/bbb"), "bbb");
+ strictEqual(path.basename("/aaa/bbb", "bbb"), "bbb");
+ strictEqual(path.basename("/aaa/bbb//", "bbb"), "bbb");
+ strictEqual(path.basename("/aaa/bbb", "bb"), "b");
+ strictEqual(path.basename("/aaa/bbb", "b"), "bb");
+ strictEqual(path.basename("/aaa/bbb"), "bbb");
+ strictEqual(path.basename("/aaa/"), "aaa");
+ strictEqual(path.basename("/aaa/b"), "b");
+ strictEqual(path.basename("/a/b"), "b");
+ strictEqual(path.basename("//a"), "a");
+ strictEqual(path.basename("a", "a"), "");
+
+ // // On Windows a backslash acts as a path separator.
+ strictEqual(path.win32.basename("\\dir\\basename.ext"), "basename.ext");
+ strictEqual(path.win32.basename("\\basename.ext"), "basename.ext");
+ strictEqual(path.win32.basename("basename.ext"), "basename.ext");
+ strictEqual(path.win32.basename("basename.ext\\"), "basename.ext");
+ strictEqual(path.win32.basename("basename.ext\\\\"), "basename.ext");
+ strictEqual(path.win32.basename("foo"), "foo");
+ strictEqual(path.win32.basename("aaa\\bbb", "\\bbb"), "bbb");
+ strictEqual(path.win32.basename("aaa\\bbb", "a\\bbb"), "bbb");
+ strictEqual(path.win32.basename("aaa\\bbb", "bbb"), "bbb");
+ strictEqual(path.win32.basename("aaa\\bbb\\\\\\\\", "bbb"), "bbb");
+ strictEqual(path.win32.basename("aaa\\bbb", "bb"), "b");
+ strictEqual(path.win32.basename("aaa\\bbb", "b"), "bb");
+ strictEqual(path.win32.basename("C:"), "");
+ strictEqual(path.win32.basename("C:."), ".");
+ strictEqual(path.win32.basename("C:\\"), "");
+ strictEqual(path.win32.basename("C:\\dir\\base.ext"), "base.ext");
+ strictEqual(path.win32.basename("C:\\basename.ext"), "basename.ext");
+ strictEqual(path.win32.basename("C:basename.ext"), "basename.ext");
+ strictEqual(path.win32.basename("C:basename.ext\\"), "basename.ext");
+ strictEqual(path.win32.basename("C:basename.ext\\\\"), "basename.ext");
+ strictEqual(path.win32.basename("C:foo"), "foo");
+ strictEqual(path.win32.basename("file:stream"), "file:stream");
+ strictEqual(path.win32.basename("a", "a"), "");
+
+ // On unix a backslash is just treated as any other character.
+ strictEqual(
+ path.posix.basename("\\dir\\basename.ext"),
+ "\\dir\\basename.ext"
+ );
+ strictEqual(path.posix.basename("\\basename.ext"), "\\basename.ext");
+ strictEqual(path.posix.basename("basename.ext"), "basename.ext");
+ strictEqual(path.posix.basename("basename.ext\\"), "basename.ext\\");
+ strictEqual(path.posix.basename("basename.ext\\\\"), "basename.ext\\\\");
+ strictEqual(path.posix.basename("foo"), "foo");
+
+ // POSIX filenames may include control characters
+ // c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html
+ const controlCharFilename = `Icon${String.fromCharCode(13)}`;
+ strictEqual(
+ path.posix.basename(`/a/b/${controlCharFilename}`),
+ controlCharFilename
+ );
+});
+
+it("path.join", () => {
+ const failures = [];
+ const backslashRE = /\\/g;
+
+ const joinTests = [
+ [
+ [path.posix.join],
+ // Arguments result
+ [
+ [[".", "x/b", "..", "/b/c.js"], "x/b/c.js"],
+ // [[], '.'],
+ [["/.", "x/b", "..", "/b/c.js"], "/x/b/c.js"],
+ [["/foo", "../../../bar"], "/bar"],
+ [["foo", "../../../bar"], "../../bar"],
+ [["foo/", "../../../bar"], "../../bar"],
+ [["foo/x", "../../../bar"], "../bar"],
+ [["foo/x", "./bar"], "foo/x/bar"],
+ [["foo/x/", "./bar"], "foo/x/bar"],
+ [["foo/x/", ".", "bar"], "foo/x/bar"],
+ [["./"], "./"],
+ [[".", "./"], "./"],
+ [[".", ".", "."], "."],
+ [[".", "./", "."], "."],
+ [[".", "/./", "."], "."],
+ [[".", "/////./", "."], "."],
+ [["."], "."],
+ [["", "."], "."],
+ [["", "foo"], "foo"],
+ [["foo", "/bar"], "foo/bar"],
+ [["", "/foo"], "/foo"],
+ [["", "", "/foo"], "/foo"],
+ [["", "", "foo"], "foo"],
+ [["foo", ""], "foo"],
+ [["foo/", ""], "foo/"],
+ [["foo", "", "/bar"], "foo/bar"],
+ [["./", "..", "/foo"], "../foo"],
+ [["./", "..", "..", "/foo"], "../../foo"],
+ [[".", "..", "..", "/foo"], "../../foo"],
+ [["", "..", "..", "/foo"], "../../foo"],
+ [["/"], "/"],
+ [["/", "."], "/"],
+ [["/", ".."], "/"],
+ [["/", "..", ".."], "/"],
+ [[""], "."],
+ [["", ""], "."],
+ [[" /foo"], " /foo"],
+ [[" ", "foo"], " /foo"],
+ [[" ", "."], " "],
+ [[" ", "/"], " /"],
+ [[" ", ""], " "],
+ [["/", "foo"], "/foo"],
+ [["/", "/foo"], "/foo"],
+ [["/", "//foo"], "/foo"],
+ [["/", "", "/foo"], "/foo"],
+ [["", "/", "foo"], "/foo"],
+ [["", "/", "/foo"], "/foo"],
+ ],
+ ],
+ ];
+
+ // // Windows-specific join tests
+ // joinTests.push([
+ // path.win32.join,
+ // joinTests[0][1].slice(0).concat([
+ // // Arguments result
+ // // UNC path expected
+ // [["//foo/bar"], "\\\\foo\\bar\\"],
+ // [["\\/foo/bar"], "\\\\foo\\bar\\"],
+ // [["\\\\foo/bar"], "\\\\foo\\bar\\"],
+ // // UNC path expected - server and share separate
+ // [["//foo", "bar"], "\\\\foo\\bar\\"],
+ // [["//foo/", "bar"], "\\\\foo\\bar\\"],
+ // [["//foo", "/bar"], "\\\\foo\\bar\\"],
+ // // UNC path expected - questionable
+ // [["//foo", "", "bar"], "\\\\foo\\bar\\"],
+ // [["//foo/", "", "bar"], "\\\\foo\\bar\\"],
+ // [["//foo/", "", "/bar"], "\\\\foo\\bar\\"],
+ // // UNC path expected - even more questionable
+ // [["", "//foo", "bar"], "\\\\foo\\bar\\"],
+ // [["", "//foo/", "bar"], "\\\\foo\\bar\\"],
+ // [["", "//foo/", "/bar"], "\\\\foo\\bar\\"],
+ // // No UNC path expected (no double slash in first component)
+ // [["\\", "foo/bar"], "\\foo\\bar"],
+ // [["\\", "/foo/bar"], "\\foo\\bar"],
+ // [["", "/", "/foo/bar"], "\\foo\\bar"],
+ // // No UNC path expected (no non-slashes in first component -
+ // // questionable)
+ // [["//", "foo/bar"], "\\foo\\bar"],
+ // [["//", "/foo/bar"], "\\foo\\bar"],
+ // [["\\\\", "/", "/foo/bar"], "\\foo\\bar"],
+ // [["//"], "\\"],
+ // // No UNC path expected (share name missing - questionable).
+ // [["//foo"], "\\foo"],
+ // [["//foo/"], "\\foo\\"],
+ // [["//foo", "/"], "\\foo\\"],
+ // [["//foo", "", "/"], "\\foo\\"],
+ // // No UNC path expected (too many leading slashes - questionable)
+ // [["///foo/bar"], "\\foo\\bar"],
+ // [["////foo", "bar"], "\\foo\\bar"],
+ // [["\\\\\\/foo/bar"], "\\foo\\bar"],
+ // // Drive-relative vs drive-absolute paths. This merely describes the
+ // // status quo, rather than being obviously right
+ // [["c:"], "c:."],
+ // [["c:."], "c:."],
+ // [["c:", ""], "c:."],
+ // [["", "c:"], "c:."],
+ // [["c:.", "/"], "c:.\\"],
+ // [["c:.", "file"], "c:file"],
+ // [["c:", "/"], "c:\\"],
+ // [["c:", "file"], "c:\\file"],
+ // ]),
+ // ]);
+ joinTests.forEach((test) => {
+ if (!Array.isArray(test[0])) test[0] = [test[0]];
+ test[0].forEach((join) => {
+ test[1].forEach((test) => {
+ const actual = join.apply(null, test[0]);
+ const expected = test[1];
+ // For non-Windows specific tests with the Windows join(), we need to try
+ // replacing the slashes since the non-Windows specific tests' `expected`
+ // use forward slashes
+ let actualAlt;
+ let os;
+ if (join === path.win32.join) {
+ actualAlt = actual.replace(backslashRE, "/");
+ os = "win32";
+ } else {
+ os = "posix";
+ }
+ if (actual !== expected && actualAlt !== expected) {
+ const delimiter = test[0].map(JSON.stringify).join(",");
+ const message = `path.${os}.join(${delimiter})\n expect=${JSON.stringify(
+ expected
+ )}\n actual=${JSON.stringify(actual)}`;
+ failures.push(`\n${message}`);
+ }
+ });
+ });
+ });
+ strictEqual(failures.length, 0, failures.join(""));
+});
+
+it("path.relative", () => {
+ const failures = [];
+
+ const relativeTests = [
+ // [
+ // path.win32.relative,
+ // // Arguments result
+ // [
+ // ["c:/blah\\blah", "d:/games", "d:\\games"],
+ // ["c:/aaaa/bbbb", "c:/aaaa", ".."],
+ // ["c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"],
+ // ["c:/aaaa/bbbb", "c:/aaaa/bbbb", ""],
+ // ["c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"],
+ // ["c:/aaaa/", "c:/aaaa/cccc", "cccc"],
+ // ["c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"],
+ // ["c:/aaaa/bbbb", "d:\\", "d:\\"],
+ // ["c:/AaAa/bbbb", "c:/aaaa/bbbb", ""],
+ // ["c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"],
+ // ["C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."],
+ // [
+ // "C:\\foo\\test",
+ // "C:\\foo\\test\\bar\\package.json",
+ // "bar\\package.json",
+ // ],
+ // ["C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"],
+ // ["C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"],
+ // ["\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"],
+ // ["\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."],
+ // ["\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"],
+ // ["\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"],
+ // ["C:\\baz-quux", "C:\\baz", "..\\baz"],
+ // ["C:\\baz", "C:\\baz-quux", "..\\baz-quux"],
+ // ["\\\\foo\\baz-quux", "\\\\foo\\baz", "..\\baz"],
+ // ["\\\\foo\\baz", "\\\\foo\\baz-quux", "..\\baz-quux"],
+ // ["C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"],
+ // ["\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"],
+ // ],
+ // ],
+ [
+ path.posix.relative,
+ // Arguments result
+ [
+ ["/var/lib", "/var", ".."],
+ ["/var/lib", "/bin", "../../bin"],
+ ["/var/lib", "/var/lib", ""],
+ ["/var/lib", "/var/apache", "../apache"],
+ ["/var/", "/var/lib", "lib"],
+ ["/", "/var/lib", "var/lib"],
+ ["/foo/test", "/foo/test/bar/package.json", "bar/package.json"],
+ ["/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."],
+ ["/foo/bar/baz-quux", "/foo/bar/baz", "../baz"],
+ ["/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"],
+ ["/baz-quux", "/baz", "../baz"],
+ ["/baz", "/baz-quux", "../baz-quux"],
+ ["/page1/page2/foo", "/", "../../.."],
+ ],
+ ],
+ ];
+
+ relativeTests.forEach((test) => {
+ const relative = test[0];
+ test[1].forEach((test) => {
+ const actual = relative(test[0], test[1]);
+ const expected = test[2];
+ if (actual !== expected) {
+ const os = relative === path.win32.relative ? "win32" : "posix";
+ const message = `path.${os}.relative(${test
+ .slice(0, 2)
+ .map(JSON.stringify)
+ .join(",")})\n expect=${JSON.stringify(
+ expected
+ )}\n actual=${JSON.stringify(actual)}`;
+ failures.push(`\n${message}`);
+ }
+ });
+ });
+
+ strictEqual(failures.length, 0, failures.join(""));
+ expect(true).toBe(true);
+});
+
+it("path.normalize", () => {
+ // strictEqual(
+ // path.win32.normalize("./fixtures///b/../b/c.js"),
+ // "fixtures\\b\\c.js"
+ // );
+ // strictEqual(path.win32.normalize("/foo/../../../bar"), "\\bar");
+ // strictEqual(path.win32.normalize("a//b//../b"), "a\\b");
+ // strictEqual(path.win32.normalize("a//b//./c"), "a\\b\\c");
+ // strictEqual(path.win32.normalize("a//b//."), "a\\b");
+ // strictEqual(
+ // path.win32.normalize("//server/share/dir/file.ext"),
+ // "\\\\server\\share\\dir\\file.ext"
+ // );
+ // strictEqual(path.win32.normalize("/a/b/c/../../../x/y/z"), "\\x\\y\\z");
+ // strictEqual(path.win32.normalize("C:"), "C:.");
+ // strictEqual(path.win32.normalize("C:..\\abc"), "C:..\\abc");
+ // strictEqual(path.win32.normalize("C:..\\..\\abc\\..\\def"), "C:..\\..\\def");
+ // strictEqual(path.win32.normalize("C:\\."), "C:\\");
+ // strictEqual(path.win32.normalize("file:stream"), "file:stream");
+ // strictEqual(path.win32.normalize("bar\\foo..\\..\\"), "bar\\");
+ // strictEqual(path.win32.normalize("bar\\foo..\\.."), "bar");
+ // strictEqual(path.win32.normalize("bar\\foo..\\..\\baz"), "bar\\baz");
+ // strictEqual(path.win32.normalize("bar\\foo..\\"), "bar\\foo..\\");
+ // strictEqual(path.win32.normalize("bar\\foo.."), "bar\\foo..");
+ // strictEqual(path.win32.normalize("..\\foo..\\..\\..\\bar"), "..\\..\\bar");
+ // strictEqual(
+ // path.win32.normalize("..\\...\\..\\.\\...\\..\\..\\bar"),
+ // "..\\..\\bar"
+ // );
+ // strictEqual(
+ // path.win32.normalize("../../../foo/../../../bar"),
+ // "..\\..\\..\\..\\..\\bar"
+ // );
+ // strictEqual(
+ // path.win32.normalize("../../../foo/../../../bar/../../"),
+ // "..\\..\\..\\..\\..\\..\\"
+ // );
+ // strictEqual(
+ // path.win32.normalize("../foobar/barfoo/foo/../../../bar/../../"),
+ // "..\\..\\"
+ // );
+ // strictEqual(
+ // path.win32.normalize("../.../../foobar/../../../bar/../../baz"),
+ // "..\\..\\..\\..\\baz"
+ // );
+ // strictEqual(path.win32.normalize("foo/bar\\baz"), "foo\\bar\\baz");
+
+ strictEqual(
+ path.posix.normalize("./fixtures///b/../b/c.js"),
+ "fixtures/b/c.js"
+ );
+ strictEqual(path.posix.normalize("/foo/../../../bar"), "/bar");
+ strictEqual(path.posix.normalize("a//b//../b"), "a/b");
+ strictEqual(path.posix.normalize("a//b//./c"), "a/b/c");
+ strictEqual(path.posix.normalize("a//b//."), "a/b");
+ strictEqual(path.posix.normalize("/a/b/c/../../../x/y/z"), "/x/y/z");
+ strictEqual(path.posix.normalize("///..//./foo/.//bar"), "/foo/bar");
+ strictEqual(path.posix.normalize("bar/foo../../"), "bar/");
+ strictEqual(path.posix.normalize("bar/foo../.."), "bar");
+ strictEqual(path.posix.normalize("bar/foo../../baz"), "bar/baz");
+ strictEqual(path.posix.normalize("bar/foo../"), "bar/foo../");
+ strictEqual(path.posix.normalize("bar/foo.."), "bar/foo..");
+ console.log("A");
+ strictEqual(path.posix.normalize("../foo../../../bar"), "../../bar");
+ console.log("B");
+ strictEqual(path.posix.normalize("../.../.././.../../../bar"), "../../bar");
+ strictEqual(
+ path.posix.normalize("../../../foo/../../../bar"),
+ "../../../../../bar"
+ );
+ strictEqual(
+ path.posix.normalize("../../../foo/../../../bar/../../"),
+ "../../../../../../"
+ );
+ strictEqual(
+ path.posix.normalize("../foobar/barfoo/foo/../../../bar/../../"),
+ "../../"
+ );
+ strictEqual(
+ path.posix.normalize("../.../../foobar/../../../bar/../../baz"),
+ "../../../../baz"
+ );
+ strictEqual(path.posix.normalize("foo/bar\\baz"), "foo/bar\\baz");
+});
+
+it("path.resolve", () => {
+ const failures = [];
+ const slashRE = /\//g;
+ const backslashRE = /\\/g;
+
+ const resolveTests = [
+ // [
+ // path.win32.resolve,
+ // // Arguments result
+ // [
+ // [["c:/blah\\blah", "d:/games", "c:../a"], "c:\\blah\\a"],
+ // [["c:/ignore", "d:\\a/b\\c/d", "\\e.exe"], "d:\\e.exe"],
+ // [["c:/ignore", "c:/some/file"], "c:\\some\\file"],
+ // [["d:/ignore", "d:some/dir//"], "d:\\ignore\\some\\dir"],
+ // [["."], process.cwd()],
+ // [["//server/share", "..", "relative\\"], "\\\\server\\share\\relative"],
+ // [["c:/", "//"], "c:\\"],
+ // [["c:/", "//dir"], "c:\\dir"],
+ // [["c:/", "//server/share"], "\\\\server\\share\\"],
+ // [["c:/", "//server//share"], "\\\\server\\share\\"],
+ // [["c:/", "///some//dir"], "c:\\some\\dir"],
+ // [
+ // ["C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js"],
+ // "C:\\foo\\tmp.3\\cycles\\root.js",
+ // ],
+ // ],
+ // ],
+ [
+ path.posix.resolve,
+ // Arguments result
+ [
+ [["/var/lib", "../", "file/"], "/var/file"],
+ [["/var/lib", "/../", "file/"], "/file"],
+ [["a/b/c/", "../../.."], process.cwd()],
+ [["."], process.cwd()],
+ [["/some/dir", ".", "/absolute/"], "/absolute"],
+ [
+ ["/foo/tmp.3/", "../tmp.3/cycles/root.js"],
+ "/foo/tmp.3/cycles/root.js",
+ ],
+ ],
+ ],
+ ];
+ const isWindows = false;
+ resolveTests.forEach(([resolve, tests]) => {
+ tests.forEach(([test, expected]) => {
+ const actual = resolve.apply(null, test);
+ let actualAlt;
+ const os = resolve === path.win32.resolve ? "win32" : "posix";
+ if (resolve === path.win32.resolve && !isWindows)
+ actualAlt = actual.replace(backslashRE, "/");
+ else if (resolve !== path.win32.resolve && isWindows)
+ actualAlt = actual.replace(slashRE, "\\");
+
+ const message = `path.${os}.resolve(${test
+ .map(JSON.stringify)
+ .join(",")})\n expect=${JSON.stringify(
+ expected
+ )}\n actual=${JSON.stringify(actual)}`;
+ if (actual !== expected && actualAlt !== expected) failures.push(message);
+ });
+ });
+ strictEqual(failures.length, 0, failures.join("\n"));
+});
diff --git a/test/bun.js/performance.test.js b/test/bun.js/performance.test.js
new file mode 100644
index 000000000..5e8520638
--- /dev/null
+++ b/test/bun.js/performance.test.js
@@ -0,0 +1,18 @@
+import { expect, it } from "bun:test";
+
+it("performance.now() should be monotonic", () => {
+ const first = performance.now();
+ const second = performance.now();
+ const third = performance.now();
+ const fourth = performance.now();
+ const fifth = performance.now();
+ const sixth = performance.now();
+ expect(first < second).toBe(true);
+ expect(second < third).toBe(true);
+ expect(third < fourth).toBe(true);
+ expect(fourth < fifth).toBe(true);
+ expect(fifth < sixth).toBe(true);
+ expect(Bun.nanoseconds() > 0).toBe(true);
+ expect(Bun.nanoseconds() > sixth).toBe(true);
+ expect(typeof Bun.nanoseconds() === "number").toBe(true);
+});
diff --git a/test/bun.js/process-nexttick.js b/test/bun.js/process-nexttick.js
new file mode 100644
index 000000000..337977c0a
--- /dev/null
+++ b/test/bun.js/process-nexttick.js
@@ -0,0 +1,91 @@
+// You can verify this test is correct by copy pasting this into a browser's console and checking it doesn't throw an error.
+var run = 0;
+
+var queueMicrotask = process.nextTick;
+
+await new Promise((resolve, reject) => {
+ queueMicrotask(() => {
+ if (run++ != 0) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ queueMicrotask(() => {
+ if (run++ != 3) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ });
+ });
+ queueMicrotask(() => {
+ if (run++ != 1) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ queueMicrotask(() => {
+ if (run++ != 4) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+
+ queueMicrotask(() => {
+ if (run++ != 6) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ });
+ });
+ });
+ queueMicrotask(() => {
+ if (run++ != 2) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ queueMicrotask(() => {
+ if (run++ != 5) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+
+ queueMicrotask(() => {
+ if (run++ != 7) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ resolve(true);
+ });
+ });
+ });
+});
+
+{
+ var passed = false;
+ try {
+ queueMicrotask(1234);
+ } catch (exception) {
+ passed = exception instanceof TypeError;
+ }
+
+ if (!passed)
+ throw new Error(
+ "queueMicrotask should throw a TypeError if the argument is not a function"
+ );
+}
+
+{
+ var passed = false;
+ try {
+ queueMicrotask();
+ } catch (exception) {
+ passed = exception instanceof TypeError;
+ }
+
+ if (!passed)
+ throw new Error(
+ "queueMicrotask should throw a TypeError if the argument is empty"
+ );
+}
+
+await new Promise((resolve, reject) => {
+ process.nextTick(
+ (first, second) => {
+ console.log(first, second);
+ if (first !== 12345 || second !== "hello")
+ reject(new Error("process.nextTick called with wrong arguments"));
+ resolve(true);
+ },
+ 12345,
+ "hello"
+ );
+});
diff --git a/test/bun.js/process-nexttick.test.js b/test/bun.js/process-nexttick.test.js
new file mode 100644
index 000000000..ac53399c0
--- /dev/null
+++ b/test/bun.js/process-nexttick.test.js
@@ -0,0 +1,93 @@
+import { it } from "bun:test";
+
+it("process.nextTick", async () => {
+ // You can verify this test is correct by copy pasting this into a browser's console and checking it doesn't throw an error.
+ var run = 0;
+ var queueMicrotask = process.nextTick;
+
+ await new Promise((resolve, reject) => {
+ queueMicrotask(() => {
+ if (run++ != 0) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ queueMicrotask(() => {
+ if (run++ != 3) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ });
+ });
+ queueMicrotask(() => {
+ if (run++ != 1) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ queueMicrotask(() => {
+ if (run++ != 4) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+
+ queueMicrotask(() => {
+ if (run++ != 6) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ });
+ });
+ });
+ queueMicrotask(() => {
+ if (run++ != 2) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ queueMicrotask(() => {
+ if (run++ != 5) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+
+ queueMicrotask(() => {
+ if (run++ != 7) {
+ reject(new Error("Microtask execution order is wrong: " + run));
+ }
+ resolve(true);
+ });
+ });
+ });
+ });
+
+ {
+ var passed = false;
+ try {
+ queueMicrotask(1234);
+ } catch (exception) {
+ passed = exception instanceof TypeError;
+ }
+
+ if (!passed)
+ throw new Error(
+ "queueMicrotask should throw a TypeError if the argument is not a function"
+ );
+ }
+
+ {
+ var passed = false;
+ try {
+ queueMicrotask();
+ } catch (exception) {
+ passed = exception instanceof TypeError;
+ }
+
+ if (!passed)
+ throw new Error(
+ "queueMicrotask should throw a TypeError if the argument is empty"
+ );
+ }
+
+ await new Promise((resolve, reject) => {
+ process.nextTick(
+ (first, second) => {
+ if (first !== 12345 || second !== "hello")
+ reject(new Error("process.nextTick called with wrong arguments"));
+ resolve(true);
+ },
+ 12345,
+ "hello"
+ );
+ });
+});
diff --git a/test/bun.js/process.test.js b/test/bun.js/process.test.js
new file mode 100644
index 000000000..f82834a04
--- /dev/null
+++ b/test/bun.js/process.test.js
@@ -0,0 +1,54 @@
+import { describe, it } from "bun:test";
+
+it("process", () => {
+ // this property isn't implemented yet but it should at least return a string
+ const isNode = !process.isBun;
+
+ if (!isNode && process.title !== "bun")
+ throw new Error("process.title is not 'bun'");
+
+ if (typeof process.env.USER !== "string")
+ throw new Error("process.env is not an object");
+
+ if (process.env.USER.length === 0)
+ throw new Error("process.env is missing a USER property");
+
+ if (process.platform !== "darwin" && process.platform !== "linux")
+ throw new Error("process.platform is invalid");
+
+ if (isNode) throw new Error("process.isBun is invalid");
+
+ // partially to test it doesn't crash due to various strange types
+ process.env.BACON = "yummy";
+ if (process.env.BACON !== "yummy") {
+ throw new Error("process.env is not writable");
+ }
+
+ delete process.env.BACON;
+ if (typeof process.env.BACON !== "undefined") {
+ throw new Error("process.env is not deletable");
+ }
+
+ process.env.BACON = "yummy";
+ if (process.env.BACON !== "yummy") {
+ throw new Error("process.env is not re-writable");
+ }
+
+ if (JSON.parse(JSON.stringify(process.env)).BACON !== "yummy") {
+ throw new Error("process.env is not serializable");
+ }
+
+ if (typeof JSON.parse(JSON.stringify(process.env)).toJSON !== "undefined") {
+ throw new Error(
+ "process.env should call toJSON to hide its internal state"
+ );
+ }
+
+ var { env, ...proces } = process;
+ console.log(JSON.stringify(proces, null, 2));
+ console.log(proces);
+
+ console.log("CWD", process.cwd());
+ console.log("SET CWD", process.chdir("../"));
+ console.log("CWD", process.cwd());
+});
diff --git a/test/bun.js/readFileSync.txt b/test/bun.js/readFileSync.txt
new file mode 100644
index 000000000..ddc94b988
--- /dev/null
+++ b/test/bun.js/readFileSync.txt
@@ -0,0 +1 @@
+File read successfully \ No newline at end of file
diff --git a/test/bun.js/readdir.js b/test/bun.js/readdir.js
new file mode 100644
index 000000000..18c111d0a
--- /dev/null
+++ b/test/bun.js/readdir.js
@@ -0,0 +1,9 @@
+const { readdirSync } = require("fs");
+
+const count = parseInt(process.env.ITERATIONS || "1", 10) || 1;
+
+for (let i = 0; i < count; i++) {
+ readdirSync(".");
+}
+
+console.log(readdirSync("."));
diff --git a/test/bun.js/reportError.test.js b/test/bun.js/reportError.test.js
new file mode 100644
index 000000000..e51f93309
--- /dev/null
+++ b/test/bun.js/reportError.test.js
@@ -0,0 +1,25 @@
+import { it } from "bun:test";
+
+it("reportError", () => {
+ console.log("---BEGIN REPORT ERROR TEST--");
+ // make sure we don't crash when given non-sensical types
+ reportError(new Error("reportError Test!"));
+ reportError(true);
+ reportError(false);
+ reportError(null);
+ reportError(123);
+ reportError(Infinity);
+ reportError(NaN);
+ reportError(-NaN);
+ reportError("");
+ reportError(new Uint8Array(1));
+ reportError(new Uint8Array(0));
+ reportError(new ArrayBuffer(0));
+ reportError(new ArrayBuffer(1));
+ reportError("string");
+ reportError([]);
+ reportError([123, null]);
+ reportError({});
+ reportError([{}]);
+ console.log("---END REPORT ERROR TEST--");
+});
diff --git a/test/bun.js/require-json.json b/test/bun.js/require-json.json
new file mode 100644
index 000000000..6414edc0e
--- /dev/null
+++ b/test/bun.js/require-json.json
@@ -0,0 +1,3 @@
+{
+ "hello": -123
+}
diff --git a/test/bun.js/resolve-typescript-file.tsx b/test/bun.js/resolve-typescript-file.tsx
new file mode 100644
index 000000000..ff8b4c563
--- /dev/null
+++ b/test/bun.js/resolve-typescript-file.tsx
@@ -0,0 +1 @@
+export default {};
diff --git a/test/bun.js/resolve.test.js b/test/bun.js/resolve.test.js
new file mode 100644
index 000000000..56162de4f
--- /dev/null
+++ b/test/bun.js/resolve.test.js
@@ -0,0 +1,100 @@
+import { it, expect } from "bun:test";
+import { mkdirSync, writeFileSync } from "fs";
+import { join } from "path";
+
+it("import.meta.resolve", async () => {
+ expect(await import.meta.resolve("./resolve.test.js")).toBe(import.meta.path);
+
+ expect(await import.meta.resolve("./resolve.test.js", import.meta.path)).toBe(
+ import.meta.path
+ );
+
+ expect(
+ // optional second param can be any path, including a dir
+ await import.meta.resolve(
+ "./bun.js/resolve.test.js",
+ join(import.meta.path, "../")
+ )
+ ).toBe(import.meta.path);
+
+ // can be a package path
+ expect(
+ (await import.meta.resolve("react", import.meta.path)).length > 0
+ ).toBe(true);
+
+ // file extensions are optional
+ expect(await import.meta.resolve("./resolve.test")).toBe(import.meta.path);
+
+ // works with tsconfig.json "paths"
+ expect(await import.meta.resolve("foo/bar")).toBe(
+ join(import.meta.path, "../baz.js")
+ );
+
+ // works with package.json "exports"
+ writePackageJSONExportsFixture();
+ expect(await import.meta.resolve("package-json-exports/baz")).toBe(
+ join(import.meta.path, "../node_modules/package-json-exports/foo/bar.js")
+ );
+
+ // works with TypeScript compiler edgecases like:
+ // - If the file ends with .js and it doesn't exist, try again with .ts and .tsx
+ expect(await import.meta.resolve("./resolve-typescript-file.js")).toBe(
+ join(import.meta.path, "../resolve-typescript-file.tsx")
+ );
+ expect(await import.meta.resolve("./resolve-typescript-file.tsx")).toBe(
+ join(import.meta.path, "../resolve-typescript-file.tsx")
+ );
+
+ // throws a ResolveError on failure
+ try {
+ await import.meta.resolve("THIS FILE DOESNT EXIST");
+ throw new Error("Test failed");
+ } catch (exception) {
+ expect(exception instanceof ResolveError).toBe(true);
+ expect(exception.referrer).toBe(import.meta.path);
+ expect(exception.name).toBe("ResolveError");
+ }
+});
+
+// the slightly lower level API, which doesn't prefill the second param
+// and expects a directory instead of a filepath
+it("Bun.resolve", async () => {
+ expect(await Bun.resolve("./resolve.test.js", import.meta.dir)).toBe(
+ import.meta.path
+ );
+});
+
+// synchronous
+it("Bun.resolveSync", () => {
+ expect(Bun.resolveSync("./resolve.test.js", import.meta.dir)).toBe(
+ import.meta.path
+ );
+});
+
+function writePackageJSONExportsFixture() {
+ try {
+ mkdirSync(
+ join(import.meta.dir, "./node_modules/package-json-exports/foo"),
+ {
+ recursive: true,
+ }
+ );
+ } catch (exception) {}
+ writeFileSync(
+ join(import.meta.dir, "./node_modules/package-json-exports/foo/bar.js"),
+ "export const bar = 1;"
+ );
+ writeFileSync(
+ join(import.meta.dir, "./node_modules/package-json-exports/package.json"),
+ JSON.stringify(
+ {
+ name: "package-json-exports",
+ exports: {
+ "./baz": "./foo/bar.js",
+ },
+ },
+ null,
+ 2
+ )
+ );
+}
diff --git a/test/bun.js/response.file.test.js b/test/bun.js/response.file.test.js
new file mode 100644
index 000000000..2d0b6506e
--- /dev/null
+++ b/test/bun.js/response.file.test.js
@@ -0,0 +1,218 @@
+import fs from "fs";
+import { it, expect } from "bun:test";
+import path from "path";
+import { gcTick } from "./gc";
+
+it("Bun.file not found returns ENOENT", async () => {
+ try {
+ await gcTick();
+ await Bun.file("/does/not/exist.txt").text();
+ await gcTick();
+ } catch (exception) {
+ expect(exception.code).toBe("ENOENT");
+ }
+ await gcTick();
+});
+
+it("Bun.write('out.txt', 'string')", async () => {
+ for (let erase of [true, false]) {
+ if (erase) {
+ try {
+ fs.unlinkSync(path.join("/tmp", "out.txt"));
+ } catch (e) {}
+ }
+ await gcTick();
+ expect(await Bun.write("/tmp/out.txt", "string")).toBe("string".length);
+ await gcTick();
+ const out = Bun.file("/tmp/out.txt");
+ await gcTick();
+ expect(await out.text()).toBe("string");
+ await gcTick();
+ expect(await out.text()).toBe(fs.readFileSync("/tmp/out.txt", "utf8"));
+ await gcTick();
+ }
+});
+
+it("Bun.write blob", async () => {
+ await Bun.write(
+ Bun.file("/tmp/response-file.test.txt"),
+ Bun.file(path.join(import.meta.dir, "fetch.js.txt"))
+ );
+ await gcTick();
+ await Bun.write(Bun.file("/tmp/response-file.test.txt"), "blah blah blha");
+ await gcTick();
+ await Bun.write(
+ Bun.file("/tmp/response-file.test.txt"),
+ new Uint32Array(1024)
+ );
+ await gcTick();
+ await Bun.write("/tmp/response-file.test.txt", new Uint32Array(1024));
+ await gcTick();
+ expect(
+ await Bun.write(
+ new TextEncoder().encode("/tmp/response-file.test.txt"),
+ new Uint32Array(1024)
+ )
+ ).toBe(new Uint32Array(1024).byteLength);
+ await gcTick();
+});
+
+it("Bun.file -> Bun.file", async () => {
+ try {
+ fs.unlinkSync(path.join("/tmp", "fetch.js.in"));
+ } catch (e) {}
+ await gcTick();
+ try {
+ fs.unlinkSync(path.join("/tmp", "fetch.js.out"));
+ } catch (e) {}
+ await gcTick();
+ const file = path.join(import.meta.dir, "fetch.js.txt");
+ await gcTick();
+ const text = fs.readFileSync(file, "utf8");
+ fs.writeFileSync("/tmp/fetch.js.in", text);
+ await gcTick();
+ {
+ const result = await Bun.write(
+ Bun.file("/tmp/fetch.js.out"),
+ Bun.file("/tmp/fetch.js.in")
+ );
+ await gcTick();
+ expect(await Bun.file("/tmp/fetch.js.out").text()).toBe(text);
+ await gcTick();
+ }
+
+ {
+ await Bun.write(
+ Bun.file("/tmp/fetch.js.in").slice(0, (text.length / 2) | 0),
+ Bun.file("/tmp/fetch.js.out")
+ );
+ expect(await Bun.file("/tmp/fetch.js.in").text()).toBe(
+ text.substring(0, (text.length / 2) | 0)
+ );
+ }
+
+ {
+ await gcTick();
+ await Bun.write("/tmp/fetch.js.in", Bun.file("/tmp/fetch.js.out"));
+ await gcTick();
+ expect(await Bun.file("/tmp/fetch.js.in").text()).toBe(text);
+ }
+});
+
+it("Bun.file", async () => {
+ const file = path.join(import.meta.dir, "fetch.js.txt");
+ await gcTick();
+ expect(await Bun.file(file).text()).toBe(fs.readFileSync(file, "utf8"));
+ await gcTick();
+});
+
+it("Bun.file as a Blob", async () => {
+ const filePath = path.join(import.meta.path, "../fetch.js.txt");
+ const fixture = fs.readFileSync(filePath, "utf8");
+ // this is a Blob object with the same interface as the one returned by fetch
+ // internally, instead of a byte array, it stores the file path!
+ // this enables several performance optimizations
+ var blob = Bun.file(filePath);
+ await gcTick();
+
+ // no size because we haven't read it from disk yet
+ expect(blob.size).toBe(0);
+ await gcTick();
+ // now it reads "./fetch.js.txt" from the filesystem
+ // it's lazy, only loads once we ask for it
+ // if it fails, the promise will reject at this point
+ expect(await blob.text()).toBe(fixture);
+ await gcTick();
+ // now that it's loaded, the size updates
+ expect(blob.size).toBe(fixture.length);
+ await gcTick();
+ // and it only loads once for _all_ blobs pointing to that file path
+ // until all references are released
+ expect((await blob.arrayBuffer()).byteLength).toBe(fixture.length);
+ await gcTick();
+
+ const array = new Uint8Array(await blob.arrayBuffer());
+ await gcTick();
+ const text = fixture;
+ for (let i = 0; i < text.length; i++) {
+ expect(array[i]).toBe(text.charCodeAt(i));
+ }
+ await gcTick();
+ expect(blob.size).toBe(fixture.length);
+ blob = null;
+ await gcTick();
+ await new Promise((resolve) => setTimeout(resolve, 1));
+ // now we're back
+ var blob = Bun.file(filePath);
+ expect(blob.size).toBe(0);
+});
+
+it("Response -> Bun.file", async () => {
+ const file = path.join(import.meta.dir, "fetch.js.txt");
+ await gcTick();
+ const text = fs.readFileSync(file, "utf8");
+ await gcTick();
+ const response = new Response(Bun.file(file));
+ await gcTick();
+ expect(await response.text()).toBe(text);
+ await gcTick();
+});
+
+it("Bun.file -> Response", async () => {
+ // ensure the file doesn't already exist
+ try {
+ fs.unlinkSync("/tmp/fetch.js.out");
+ } catch {}
+ await gcTick();
+ const file = path.join(import.meta.dir, "fetch.js.txt");
+ await gcTick();
+ const text = fs.readFileSync(file, "utf8");
+ await gcTick();
+ const resp = await fetch("https://example.com");
+ await gcTick();
+
+ expect(await Bun.write("/tmp/fetch.js.out", resp)).toBe(text.length);
+ await gcTick();
+ expect(await Bun.file("/tmp/fetch.js.out").text()).toBe(text);
+ await gcTick();
+});
+
+it("Response -> Bun.file -> Response -> text", async () => {
+ await gcTick();
+ const file = path.join(import.meta.dir, "fetch.js.txt");
+ await gcTick();
+ const text = fs.readFileSync(file, "utf8");
+ await gcTick();
+ const response = new Response(Bun.file(file));
+ await gcTick();
+ const response2 = response.clone();
+ await gcTick();
+ expect(await response2.text()).toBe(text);
+ await gcTick();
+});
+
+// If you write nothing to a file, it shouldn't modify it
+// If you want to truncate a file, it should be more explicit
+it("Bun.write('output.html', '')", async () => {
+ await Bun.write("/tmp/output.html", "lalalala");
+ expect(await Bun.write("/tmp/output.html", "")).toBe(0);
+ expect(await Bun.file("/tmp/output.html").text()).toBe("lalalala");
+});
+
+// Since Bun.file is resolved lazily, this needs to specifically be checked
+it("Bun.write('output.html', HTMLRewriter.transform(Bun.file)))", async () => {
+ var rewriter = new HTMLRewriter();
+ rewriter.on("div", {
+ element(element) {
+ element.setInnerContent("<blink>it worked!</blink>", { html: true });
+ },
+ });
+ await Bun.write("/tmp/html-rewriter.txt.js", "<div>hello</div>");
+ var input = new Response(Bun.file("/tmp/html-rewriter.txt.js"));
+ var output = rewriter.transform(input);
+ const outpath = `/tmp/html-rewriter.${Date.now()}.html`;
+ await Bun.write(outpath, output);
+ expect(await Bun.file(outpath).text()).toBe(
+ "<div><blink>it worked!</blink></div>"
+ );
+});
diff --git a/test/bun.js/serve.test.ts b/test/bun.js/serve.test.ts
new file mode 100644
index 000000000..8b785dd25
--- /dev/null
+++ b/test/bun.js/serve.test.ts
@@ -0,0 +1,82 @@
+import { file, serve } from "bun";
+import { expect, it } from "bun:test";
+import { readFileSync } from "fs";
+import { resolve } from "path";
+
+var port = 40000;
+
+it("should work for a hello world", async () => {
+ const server = serve({
+ port: port++,
+ fetch(req) {
+ return new Response(`Hello, world!`);
+ },
+ });
+ const response = await fetch(`http://localhost:${server.port}`);
+ expect(await response.text()).toBe("Hello, world!");
+ server.stop();
+});
+
+it("should work for a file", async () => {
+ const fixture = resolve(import.meta.dir, "./fetch.js.txt");
+ const textToExpect = readFileSync(fixture, "utf-8");
+
+ const server = serve({
+ port: port++,
+ fetch(req) {
+ return new Response(file(fixture));
+ },
+ });
+ const response = await fetch(`http://localhost:${server.port}`);
+ expect(await response.text()).toBe(textToExpect);
+ server.stop();
+});
+
+it("fetch should work with headers", async () => {
+ const fixture = resolve(import.meta.dir, "./fetch.js.txt");
+
+ const server = serve({
+ port: port++,
+ fetch(req) {
+ if (req.headers.get("X-Foo") !== "bar") {
+ return new Response("X-Foo header not set", { status: 500 });
+ }
+ return new Response(file(fixture), {
+ headers: { "X-Both-Ways": "1" },
+ });
+ },
+ });
+ const response = await fetch(`http://localhost:${server.port}`, {
+ headers: {
+ "X-Foo": "bar",
+ },
+ });
+
+ expect(response.status).toBe(200);
+ expect(response.headers.get("X-Both-Ways")).toBe("1");
+ server.stop();
+});
+
+var count = 200;
+it(`should work for a file ${count} times`, async () => {
+ const fixture = resolve(import.meta.dir, "./fetch.js.txt");
+ const textToExpect = readFileSync(fixture, "utf-8");
+ var ran = 0;
+ const server = serve({
+ port: port++,
+ async fetch(req) {
+ return new Response(file(fixture));
+ },
+ });
+
+ // this gets stuck if run about 200 times awaiting all the promises
+ // when the promises are run altogether, instead of one at a time
+ // it's hard to say if this only happens here due to some weird stuff with the test runner
+ // or if it's "real" issue
+ for (let i = 0; i < count; i++) {
+ const response = await fetch(`http://localhost:${server.port}`);
+ expect(await response.text()).toBe(textToExpect);
+ }
+
+ server.stop();
+});
diff --git a/test/bun.js/setInterval.test.js b/test/bun.js/setInterval.test.js
new file mode 100644
index 000000000..f633998cd
--- /dev/null
+++ b/test/bun.js/setInterval.test.js
@@ -0,0 +1,35 @@
+import { it, expect } from "bun:test";
+
+it("setInterval", async () => {
+ var counter = 0;
+ var start;
+ const result = await new Promise((resolve, reject) => {
+ start = performance.now();
+
+ var id = setInterval(() => {
+ counter++;
+ if (counter === 10) {
+ resolve(counter);
+ clearInterval(id);
+ }
+ }, 1);
+ });
+
+ expect(result).toBe(10);
+ expect(performance.now() - start >= 10).toBe(true);
+});
+
+it("clearInterval", async () => {
+ var called = false;
+ const id = setInterval(() => {
+ called = true;
+ expect(false).toBe(true);
+ }, 1);
+ clearInterval(id);
+ await new Promise((resolve, reject) => {
+ setInterval(() => {
+ resolve();
+ }, 10);
+ });
+ expect(called).toBe(false);
+});
diff --git a/test/bun.js/setTimeout.test.js b/test/bun.js/setTimeout.test.js
new file mode 100644
index 000000000..55f71712c
--- /dev/null
+++ b/test/bun.js/setTimeout.test.js
@@ -0,0 +1,39 @@
+import { it, expect } from "bun:test";
+
+it("setTimeout", async () => {
+ var lastID = -1;
+ const result = await new Promise((resolve, reject) => {
+ var numbers = [];
+
+ for (let i = 1; i < 100; i++) {
+ const id = setTimeout(() => {
+ numbers.push(i);
+ if (i === 99) {
+ resolve(numbers);
+ }
+ }, i);
+ expect(id > lastID).toBe(true);
+ lastID = id;
+ }
+ });
+
+ for (let j = 0; j < result.length; j++) {
+ expect(result[j]).toBe(j + 1);
+ }
+ expect(result.length).toBe(99);
+});
+
+it("clearTimeout", async () => {
+ var called = false;
+ const id = setTimeout(() => {
+ called = true;
+ expect(false).toBe(true);
+ }, 1);
+ clearTimeout(id);
+ await new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve();
+ }, 10);
+ });
+ expect(called).toBe(false);
+});
diff --git a/test/bun.js/shadow.test.js b/test/bun.js/shadow.test.js
new file mode 100644
index 000000000..3fffcac90
--- /dev/null
+++ b/test/bun.js/shadow.test.js
@@ -0,0 +1,10 @@
+import { describe, it, expect } from "bun:test";
+
+it("shadow realm works", () => {
+ const red = new ShadowRealm();
+ globalThis.someValue = 1;
+ // Affects only the ShadowRealm's global
+ const result = red.evaluate("globalThis.someValue = 2;");
+ expect(globalThis.someValue).toBe(1);
+ expect(result).toBe(2);
+});
diff --git a/test/bun.js/sleep.js b/test/bun.js/sleep.js
new file mode 100644
index 000000000..080597424
--- /dev/null
+++ b/test/bun.js/sleep.js
@@ -0,0 +1,10 @@
+const interval = 0.01;
+const now = performance.now();
+console.time("Slept");
+Bun.sleepSync(interval);
+const elapsed = performance.now() - now;
+if (elapsed < interval) {
+ throw new Error("Didn't sleep");
+}
+
+console.timeEnd("Slept");
diff --git a/test/bun.js/solid-dom-fixtures/SVG/code.js b/test/bun.js/solid-dom-fixtures/SVG/code.js
new file mode 100644
index 000000000..0ffded054
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/SVG/code.js
@@ -0,0 +1,74 @@
+const template = (
+ <svg width="400" height="180">
+ <rect
+ stroke-width="2"
+ x="50"
+ y="20"
+ rx="20"
+ ry="20"
+ width="150"
+ height="150"
+ style="fill:red;stroke:black;stroke-width:5;opacity:0.5"
+ />
+ <linearGradient gradientTransform="rotate(25)">
+ <stop offset="0%"></stop>
+ </linearGradient>
+ </svg>
+);
+
+const template2 = (
+ <svg width="400" height="180">
+ <rect
+ className={state.name}
+ stroke-width={state.width}
+ x={state.x}
+ y={state.y}
+ rx="20"
+ ry="20"
+ width="150"
+ height="150"
+ style={{
+ fill: "red",
+ stroke: "black",
+ "stroke-width": props.stroke,
+ opacity: 0.5,
+ }}
+ />
+ </svg>
+);
+
+const template3 = (
+ <svg width="400" height="180">
+ <rect {...props} />
+ </svg>
+);
+
+const template4 = <rect x="50" y="20" width="150" height="150" />;
+
+const template5 = (
+ <>
+ <rect x="50" y="20" width="150" height="150" />
+ </>
+);
+
+const template6 = (
+ <Component>
+ <rect x="50" y="20" width="150" height="150" />
+ </Component>
+);
+
+const template7 = (
+ <svg viewBox={"0 0 160 40"} xmlns="http://www.w3.org/2000/svg">
+ <a xlink:href={url}>
+ <text x="10" y="25">
+ MDN Web Docs
+ </text>
+ </a>
+ </svg>
+);
+
+const template8 = (
+ <svg viewBox={"0 0 160 40"} xmlns="http://www.w3.org/2000/svg">
+ <text x="10" y="25" textContent={text} />
+ </svg>
+);
diff --git a/test/bun.js/solid-dom-fixtures/SVG/output.bun.js b/test/bun.js/solid-dom-fixtures/SVG/output.bun.js
new file mode 100644
index 000000000..44d092f15
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/SVG/output.bun.js
@@ -0,0 +1,33 @@
+var _tmpl$1 = _template$('<svg width="400" height="180"><rect stroke-width="2" x="50" y="20" rx="20" ry="20" width="150" height="150"/><linearGradient gradientTransform="rotate(25)"><stop offset="0%"/></linearGradient></svg>', 4), _tmpl$2 = _template$('<svg width="400" height="180"><rect rx="20" ry="20" width="150" height="150"/></svg>', 2), _tmpl$3 = _template$('<svg width="400" height="180"><rect/></svg>', 2), _tmpl$4 = _template$('<rect x="50" y="20" width="150" height="150"/>', 0), _tmpl$5 = _template$('<svg viewBox="0 0 160 40" xmlns="http://www.w3.org/2000/svg"><a><text x="10" y="25">MDN Web Docs</text></a></svg>', 6), _tmpl$6 = _template$('<svg viewBox="0 0 160 40" xmlns="http://www.w3.org/2000/svg"><text x="10" y="25"/></svg>', 2);
+const template = _tmpl$1.cloneNode(true);
+const template2 = () => {
+ var _el = _tmpl$1.cloneNode(true);
+ effect(() => {
+ return setAttribute(_el, "className", state.name);
+ });
+ effect(() => {
+ return setAttribute(_el, "stroke-width", state.width);
+ });
+ effect(() => {
+ return setAttribute(_el, "x", state.x);
+ });
+ effect(() => {
+ return setAttribute(_el, "y", state.y);
+ });
+ ;
+ return _el;
+};
+const template3 = _tmpl$3.cloneNode(true);
+const template4 = _tmpl$4.cloneNode(true);
+const template5 = ;
+const template6 = createComponent(Component, {});
+const template7 = () => {
+ var _el = _tmpl$4.cloneNode(true);
+ setAttribute(_el, "xlink:href", url);
+ return _el;
+};
+const template8 = () => {
+ var _el = _tmpl$5.cloneNode(true);
+ setAttribute(_el, "textContent", text);
+ return _el;
+};
diff --git a/test/bun.js/solid-dom-fixtures/SVG/output.js b/test/bun.js/solid-dom-fixtures/SVG/output.js
new file mode 100644
index 000000000..edac460af
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/SVG/output.js
@@ -0,0 +1,108 @@
+import { template as _$template } from "r-dom";
+import { setAttributeNS as _$setAttributeNS } from "r-dom";
+import { createComponent as _$createComponent } from "r-dom";
+import { spread as _$spread } from "r-dom";
+import { setAttribute as _$setAttribute } from "r-dom";
+import { effect as _$effect } from "r-dom";
+
+const _tmpl$ = /*#__PURE__*/ _$template(
+ `<svg width="400" height="180"><rect stroke-width="2" x="50" y="20" rx="20" ry="20" width="150" height="150" style="fill:red;stroke:black;stroke-width:5;opacity:0.5"></rect><linearGradient gradientTransform="rotate(25)"><stop offset="0%"></stop></linearGradient></svg>`,
+ 8
+ ),
+ _tmpl$2 = /*#__PURE__*/ _$template(
+ `<svg width="400" height="180"><rect rx="20" ry="20" width="150" height="150"></rect></svg>`,
+ 4
+ ),
+ _tmpl$3 = /*#__PURE__*/ _$template(
+ `<svg width="400" height="180"><rect></rect></svg>`,
+ 4
+ ),
+ _tmpl$4 = /*#__PURE__*/ _$template(
+ `<svg><rect x="50" y="20" width="150" height="150"></rect></svg>`,
+ 4,
+ true
+ ),
+ _tmpl$5 = /*#__PURE__*/ _$template(
+ `<svg viewBox="0 0 160 40" xmlns="http://www.w3.org/2000/svg"><a><text x="10" y="25">MDN Web Docs</text></a></svg>`,
+ 6
+ ),
+ _tmpl$6 = /*#__PURE__*/ _$template(
+ `<svg viewBox="0 0 160 40" xmlns="http://www.w3.org/2000/svg"><text x="10" y="25"></text></svg>`,
+ 4
+ );
+
+const template = _tmpl$.cloneNode(true);
+
+const template2 = (() => {
+ const _el$2 = _tmpl$2.cloneNode(true),
+ _el$3 = _el$2.firstChild;
+
+ _el$3.style.setProperty("fill", "red");
+
+ _el$3.style.setProperty("stroke", "black");
+
+ _el$3.style.setProperty("opacity", "0.5");
+
+ _$effect(
+ (_p$) => {
+ const _v$ = state.name,
+ _v$2 = state.width,
+ _v$3 = state.x,
+ _v$4 = state.y,
+ _v$5 = props.stroke;
+ _v$ !== _p$._v$ && _$setAttribute(_el$3, "class", (_p$._v$ = _v$));
+ _v$2 !== _p$._v$2 &&
+ _$setAttribute(_el$3, "stroke-width", (_p$._v$2 = _v$2));
+ _v$3 !== _p$._v$3 && _$setAttribute(_el$3, "x", (_p$._v$3 = _v$3));
+ _v$4 !== _p$._v$4 && _$setAttribute(_el$3, "y", (_p$._v$4 = _v$4));
+ _v$5 !== _p$._v$5 &&
+ _el$3.style.setProperty("stroke-width", (_p$._v$5 = _v$5));
+ return _p$;
+ },
+ {
+ _v$: undefined,
+ _v$2: undefined,
+ _v$3: undefined,
+ _v$4: undefined,
+ _v$5: undefined,
+ }
+ );
+
+ return _el$2;
+})();
+
+const template3 = (() => {
+ const _el$4 = _tmpl$3.cloneNode(true),
+ _el$5 = _el$4.firstChild;
+
+ _$spread(_el$5, props, true, false);
+
+ return _el$4;
+})();
+
+const template4 = _tmpl$4.cloneNode(true);
+
+const template5 = _tmpl$4.cloneNode(true);
+
+const template6 = _$createComponent(Component, {
+ get children() {
+ return _tmpl$4.cloneNode(true);
+ },
+});
+
+const template7 = (() => {
+ const _el$9 = _tmpl$5.cloneNode(true),
+ _el$10 = _el$9.firstChild;
+
+ _$setAttributeNS(_el$10, "http://www.w3.org/1999/xlink", "xlink:href", url);
+
+ return _el$9;
+})();
+
+const template8 = (() => {
+ const _el$11 = _tmpl$6.cloneNode(true),
+ _el$12 = _el$11.firstChild;
+
+ _el$12.textContent = text;
+ return _el$11;
+})();
diff --git a/test/bun.js/solid-dom-fixtures/attributeExpressions/code.js b/test/bun.js/solid-dom-fixtures/attributeExpressions/code.js
new file mode 100644
index 000000000..b64949434
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/attributeExpressions/code.js
@@ -0,0 +1,115 @@
+const selected = true;
+let id = "my-h1";
+let link;
+const template = (
+ <div
+ id="main"
+ {...results}
+ classList={{ selected: unknown }}
+ style={{ color }}
+ >
+ <h1
+ class="base"
+ id={id}
+ {...results()}
+ disabled
+ readonly=""
+ title={welcoming()}
+ style={{ "background-color": color(), "margin-right": "40px" }}
+ classList={{ dynamic: dynamic(), selected }}
+ >
+ <a href={"/"} ref={link} classList={{ "ccc ddd": true }} readonly={value}>
+ Welcome
+ </a>
+ </h1>
+ </div>
+);
+
+const template2 = (
+ <div {...getProps("test")}>
+ <div textContent={rowId} />
+ <div textContent={row.label} />
+ <div innerHTML={"<div/>"} />
+ </div>
+);
+
+const template3 = (
+ <div
+ id={/*@once*/ state.id}
+ style={/*@once*/ { "background-color": state.color }}
+ name={state.name}
+ textContent={/*@once*/ state.content}
+ />
+);
+
+const template4 = (
+ <div class="hi" className={state.class} classList={{ "ccc:ddd": true }} />
+);
+
+const template5 = <div class="a" className="b"></div>;
+
+const template6 = <div style={someStyle()} textContent="Hi" />;
+
+const template7 = (
+ <div
+ style={{
+ "background-color": color(),
+ "margin-right": "40px",
+ ...props.style,
+ }}
+ style:padding-top={props.top}
+ class:my-class={props.active}
+ />
+);
+
+let refTarget;
+const template8 = <div ref={refTarget} />;
+
+const template9 = <div ref={(e) => console.log(e)} />;
+
+const template10 = <div ref={refFactory()} />;
+
+const template11 = <div use:something use:another={thing} use:zero={0} />;
+
+const template12 = <div prop:htmlFor={thing} />;
+
+const template13 = <input type="checkbox" checked={true} />;
+
+const template14 = <input type="checkbox" checked={state.visible} />;
+
+const template15 = <div class="`a">`$`</div>;
+
+const template16 = (
+ <button
+ class="static"
+ classList={{
+ hi: "k",
+ }}
+ type="button"
+ >
+ Write
+ </button>
+);
+
+const template17 = (
+ <button
+ classList={{
+ a: true,
+ b: true,
+ c: true,
+ }}
+ onClick={increment}
+ >
+ Hi
+ </button>
+);
+
+const template18 = (
+ <div
+ {...{
+ get [key()]() {
+ return props.value;
+ },
+ }}
+ />
+);
diff --git a/test/bun.js/solid-dom-fixtures/attributeExpressions/output.bun.js b/test/bun.js/solid-dom-fixtures/attributeExpressions/output.bun.js
new file mode 100644
index 000000000..4bb3e1b39
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/attributeExpressions/output.bun.js
@@ -0,0 +1,155 @@
+var _tmpl = _template$(
+ '<div id="main"><h1 class="base" disabled readonly><a href="/">Welcome</a></h1></div>',
+ 6
+ ),
+ _tmpl$2 = _template$(
+ '<div><div/><div/><div innerHTML="&lt;div/&gt;"/></div>',
+ 2
+ ),
+ _tmpl$2 = _template$("<div/>", 0),
+ _tmpl$3 = _template$('<div class="hi"/>', 0),
+ _tmpl$5 = _template$('<div class="a" class="b"/>', 0),
+ _tmpl$5 = _template$('<div textContent="Hi"/>', 0),
+ _tmpl$6 = _template$("<div use:something use:zero=0/>", 0),
+ _tmpl$8 = _template$('<input type="checkbox" checked/>', 0),
+ _tmpl$8 = _template$('<input type="checkbox"/>', 0),
+ _tmpl$10 = _template$('<div class="`a">`$`</div>', 2),
+ _tmpl$10 = _template$(
+ '<button class="static" type="button">Write</button>',
+ 2
+ ),
+ _tmpl$11 = _template$("<button>Hi</button>", 2);
+const selected = true;
+let id = "my-h1";
+let link;
+const template = () => {
+ var _el = _tmpl.cloneNode(true),
+ _el$1 = _el.firstChild,
+ _el$2 = _el$1.nextSibling;
+ effect(() => {
+ return setAttribute(_el, "classList", { selected: unknown });
+ });
+ setAttribute(_el$1, "id", id);
+ effect(() => {
+ return setAttribute(_el$1, "title", welcoming());
+ });
+ effect(() => {
+ return setAttribute(_el$1, "classList", { dynamic: dynamic(), selected });
+ });
+ setAttribute(_el$2, "ref", link);
+ effect(() => {
+ return setAttribute(_el$2, "classList", { "ccc ddd": true });
+ });
+ setAttribute(_el$2, "readonly", value);
+ return _el;
+};
+const template2 = () => {
+ var _el = _tmpl$1.cloneNode(true),
+ _el$1 = _el.firstChild;
+ setAttribute(_el, "textContent", rowId);
+ effect(() => {
+ return setAttribute(_el$1, "textContent", row.label);
+ });
+ return _el;
+};
+const template3 = () => {
+ var _el = _tmpl$2.cloneNode(true);
+ effect(() => {
+ return setAttribute(_el, "id", state.id);
+ });
+ effect(() => {
+ return setAttribute(_el, "name", state.name);
+ });
+ effect(() => {
+ return setAttribute(_el, "textContent", state.content);
+ });
+ return _el;
+};
+const template4 = () => {
+ var _el = _tmpl$3.cloneNode(true);
+ effect(() => {
+ return setAttribute(_el, "className", state.class);
+ });
+ effect(() => {
+ return setAttribute(_el, "classList", { "ccc:ddd": true });
+ });
+ return _el;
+};
+const template5 = _tmpl$5.cloneNode(true);
+const template6 = () => {
+ var _el = _tmpl$5.cloneNode(true);
+ return _el;
+};
+const template7 = () => {
+ var _el = _tmpl$2.cloneNode(true);
+ effect(() => {
+ return setAttribute(_el, "style:padding-top", props.top);
+ });
+ effect(() => {
+ return setAttribute(_el, "class:my-class", props.active);
+ });
+ return _el;
+};
+let refTarget;
+const template8 = () => {
+ var _el = _tmpl$2.cloneNode(true);
+ setAttribute(_el, "ref", refTarget);
+ return _el;
+};
+const template9 = () => {
+ var _el = _tmpl$2.cloneNode(true);
+ effect(() => {
+ return setAttribute(_el, "ref", (e) => console.log(e));
+ });
+ return _el;
+};
+const template10 = () => {
+ var _el = _tmpl$2.cloneNode(true);
+ effect(() => {
+ return setAttribute(_el, "ref", refFactory());
+ });
+ return _el;
+};
+const template11 = () => {
+ var _el = _tmpl$6.cloneNode(true);
+ setAttribute(_el, "use:another", thing);
+ return _el;
+};
+const template12 = () => {
+ var _el = _tmpl$2.cloneNode(true);
+ setAttribute(_el, "prop:htmlFor", thing);
+ return _el;
+};
+const template13 = _tmpl$8.cloneNode(true);
+const template14 = () => {
+ var _el = _tmpl$8.cloneNode(true);
+ effect(() => {
+ return setAttribute(_el, "checked", state.visible);
+ });
+ return _el;
+};
+const template15 = _tmpl$10.cloneNode(true);
+const template16 = () => {
+ var _el = _tmpl$10.cloneNode(true);
+ effect(() => {
+ return setAttribute(_el, "classList", {
+ hi: "k",
+ });
+ });
+ return _el;
+};
+const template17 = () => {
+ var _el = _tmpl$11.cloneNode(true);
+ effect(() => {
+ return setAttribute(_el, "classList", {
+ a: true,
+ b: true,
+ c: true,
+ });
+ });
+ effect(() => {
+ return (_el.$$click = increment);
+ });
+ return _el;
+};
+const template18 = _tmpl$2.cloneNode(true);
diff --git a/test/bun.js/solid-dom-fixtures/attributeExpressions/output.js b/test/bun.js/solid-dom-fixtures/attributeExpressions/output.js
new file mode 100644
index 000000000..14f700218
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/attributeExpressions/output.js
@@ -0,0 +1,241 @@
+const _tmpl$ = /*#__PURE__*/ _$template(
+ `<div id="main"><h1 class="base selected" id="my-h1" disabled readonly=""><a href="/">Welcome</a></h1></div>`,
+ 6
+ ),
+ _tmpl$2 = /*#__PURE__*/ _$template(
+ `<div><div></div><div> </div><div></div></div>`,
+ 8
+ ),
+ _tmpl$3 = /*#__PURE__*/ _$template(`<div></div>`, 2),
+ _tmpl$4 = /*#__PURE__*/ _$template(`<div class="a b"></div>`, 2),
+ _tmpl$5 = /*#__PURE__*/ _$template(`<input type="checkbox">`, 1),
+ _tmpl$6 = /*#__PURE__*/ _$template(`<div class="\`a">\`$\`</div>`, 2),
+ _tmpl$7 = /*#__PURE__*/ _$template(
+ `<button class="static hi" type="button">Write</button>`,
+ 2
+ ),
+ _tmpl$8 = /*#__PURE__*/ _$template(`<button class="a b c">Hi</button>`, 2);
+
+const selected = true;
+let id = "my-h1";
+let link;
+
+const template = (() => {
+ const _el$ = _tmpl$.cloneNode(true),
+ _el$2 = _el$.firstChild,
+ _el$3 = _el$2.firstChild;
+
+ _$spread(_el$, results, false, true);
+
+ _el$.classList.toggle("selected", unknown);
+
+ _el$.style.setProperty("color", color);
+
+ _$spread(_el$2, results, false, true);
+
+ _el$2.style.setProperty("margin-right", "40px");
+
+ const _ref$ = link;
+ typeof _ref$ === "function" ? _ref$(_el$3) : (link = _el$3);
+
+ _$classList(_el$3, {
+ "ccc ddd": true,
+ });
+
+ _el$3.readOnly = value;
+
+ _$effect(
+ (_p$) => {
+ const _v$ = welcoming(),
+ _v$2 = color(),
+ _v$3 = !!dynamic();
+
+ _v$ !== _p$._v$ && _$setAttribute(_el$2, "title", (_p$._v$ = _v$));
+ _v$2 !== _p$._v$2 &&
+ _el$2.style.setProperty("background-color", (_p$._v$2 = _v$2));
+ _v$3 !== _p$._v$3 && _el$2.classList.toggle("dynamic", (_p$._v$3 = _v$3));
+ return _p$;
+ },
+ {
+ _v$: undefined,
+ _v$2: undefined,
+ _v$3: undefined,
+ }
+ );
+
+ return _el$;
+})();
+
+const template2 = (() => {
+ const _el$4 = _tmpl$2.cloneNode(true),
+ _el$5 = _el$4.firstChild,
+ _el$6 = _el$5.nextSibling,
+ _el$7 = _el$6.firstChild,
+ _el$8 = _el$6.nextSibling;
+
+ _$spread(_el$4, () => getProps("test"), false, true);
+
+ _el$5.textContent = rowId;
+ _el$8.innerHTML = "<div/>";
+
+ _$effect(() => (_el$7.data = row.label));
+
+ return _el$4;
+})();
+
+const template3 = (() => {
+ const _el$9 = _tmpl$3.cloneNode(true);
+
+ _$setAttribute(_el$9, "id", state.id);
+
+ _el$9.style.setProperty("background-color", state.color);
+
+ _el$9.textContent = state.content;
+
+ _$effect(() => _$setAttribute(_el$9, "name", state.name));
+
+ return _el$9;
+})();
+
+const template4 = (() => {
+ const _el$10 = _tmpl$3.cloneNode(true);
+
+ _$classList(_el$10, {
+ "ccc:ddd": true,
+ });
+
+ _$effect(() => _$className(_el$10, `hi ${state.class || ""}`));
+
+ return _el$10;
+})();
+
+const template5 = _tmpl$4.cloneNode(true);
+
+const template6 = (() => {
+ const _el$12 = _tmpl$3.cloneNode(true);
+
+ _el$12.textContent = "Hi";
+
+ _$effect((_$p) => _$style(_el$12, someStyle(), _$p));
+
+ return _el$12;
+})();
+
+const template7 = (() => {
+ const _el$13 = _tmpl$3.cloneNode(true);
+
+ _$effect(
+ (_p$) => {
+ const _v$4 = {
+ "background-color": color(),
+ "margin-right": "40px",
+ ...props.style,
+ },
+ _v$5 = props.top,
+ _v$6 = !!props.active;
+
+ _p$._v$4 = _$style(_el$13, _v$4, _p$._v$4);
+ _v$5 !== _p$._v$5 &&
+ _el$13.style.setProperty("padding-top", (_p$._v$5 = _v$5));
+ _v$6 !== _p$._v$6 &&
+ _el$13.classList.toggle("my-class", (_p$._v$6 = _v$6));
+ return _p$;
+ },
+ {
+ _v$4: undefined,
+ _v$5: undefined,
+ _v$6: undefined,
+ }
+ );
+
+ return _el$13;
+})();
+
+let refTarget;
+
+const template8 = (() => {
+ const _el$14 = _tmpl$3.cloneNode(true);
+
+ const _ref$2 = refTarget;
+ typeof _ref$2 === "function" ? _ref$2(_el$14) : (refTarget = _el$14);
+ return _el$14;
+})();
+
+const template9 = (() => {
+ const _el$15 = _tmpl$3.cloneNode(true);
+
+ ((e) => console.log(e))(_el$15);
+
+ return _el$15;
+})();
+
+const template10 = (() => {
+ const _el$16 = _tmpl$3.cloneNode(true);
+
+ const _ref$3 = refFactory();
+
+ typeof _ref$3 === "function" && _ref$3(_el$16);
+ return _el$16;
+})();
+
+const template11 = (() => {
+ const _el$17 = _tmpl$3.cloneNode(true);
+
+ zero(_el$17, () => 0);
+ another(_el$17, () => thing);
+ something(_el$17, () => true);
+ return _el$17;
+})();
+
+const template12 = (() => {
+ const _el$18 = _tmpl$3.cloneNode(true);
+
+ _el$18.htmlFor = thing;
+ return _el$18;
+})();
+
+const template13 = (() => {
+ const _el$19 = _tmpl$5.cloneNode(true);
+
+ _el$19.checked = true;
+ return _el$19;
+})();
+
+const template14 = (() => {
+ const _el$20 = _tmpl$5.cloneNode(true);
+
+ _$effect(() => (_el$20.checked = state.visible));
+
+ return _el$20;
+})();
+
+const template15 = _tmpl$6.cloneNode(true);
+
+const template16 = _tmpl$7.cloneNode(true);
+
+const template17 = (() => {
+ const _el$23 = _tmpl$8.cloneNode(true);
+
+ _$addEventListener(_el$23, "click", increment, true);
+
+ return _el$23;
+})();
+
+const template18 = (() => {
+ const _el$24 = _tmpl$3.cloneNode(true);
+
+ _$spread(
+ _el$24,
+ () => ({
+ get [key()]() {
+ return props.value;
+ },
+ }),
+ false,
+ false
+ );
+
+ return _el$24;
+})();
+
+_$delegateEvents(["click"]);
diff --git a/test/bun.js/solid-dom-fixtures/components/code.js b/test/bun.js/solid-dom-fixtures/components/code.js
new file mode 100644
index 000000000..f3bd159d6
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/components/code.js
@@ -0,0 +1,161 @@
+import { Show } from "somewhere";
+
+const Child = (props) => {
+ const [s, set] = createSignal();
+ return (
+ <>
+ <div ref={props.ref}>Hello {props.name}</div>
+ <div ref={set}>{props.children}</div>
+ </>
+ );
+};
+
+const template = (props) => {
+ let childRef;
+ const { content } = props;
+ return (
+ <div>
+ <Child name="John" {...props} ref={childRef} booleanProperty>
+ <div>From Parent</div>
+ </Child>
+ <Child name="Jason" {...dynamicSpread()} ref={props.ref}>
+ {/* Comment Node */}
+ <div>{content}</div>
+ </Child>
+ <Context.Consumer ref={props.consumerRef()}>
+ {(context) => context}
+ </Context.Consumer>
+ </div>
+ );
+};
+
+const template2 = (
+ <Child
+ name="Jake"
+ dynamic={state.data}
+ stale={/*@once*/ state.data}
+ handleClick={clickHandler}
+ hyphen-ated={state.data}
+ ref={(el) => (e = el)}
+ />
+);
+
+const template3 = (
+ <Child>
+ <div />
+ <div />
+ <div />
+ After
+ </Child>
+);
+
+const [s, set] = createSignal();
+const template4 = <Child ref={set}>{<div />}</Child>;
+
+const template5 = <Child dynamic={state.dynamic}>{state.dynamic}</Child>;
+
+// builtIns
+const template6 = (
+ <For each={state.list} fallback={<Loading />}>
+ {(item) => <Show when={state.condition}>{item}</Show>}
+ </For>
+);
+
+const template7 = (
+ <Child>
+ <div />
+ {state.dynamic}
+ </Child>
+);
+
+const template8 = (
+ <Child>
+ {(item) => item}
+ {(item) => item}
+ </Child>
+);
+
+const template9 = <_garbage>Hi</_garbage>;
+
+const template10 = (
+ <div>
+ <Link>new</Link>
+ {" | "}
+ <Link>comments</Link>
+ {" | "}
+ <Link>show</Link>
+ {" | "}
+ <Link>ask</Link>
+ {" | "}
+ <Link>jobs</Link>
+ {" | "}
+ <Link>submit</Link>
+ </div>
+);
+
+const template11 = (
+ <div>
+ <Link>new</Link>
+ {" | "}
+ <Link>comments</Link>
+ <Link>show</Link>
+ {" | "}
+ <Link>ask</Link>
+ <Link>jobs</Link>
+ {" | "}
+ <Link>submit</Link>
+ </div>
+);
+
+const template12 = (
+ <div>
+ {" | "}
+ <Link>comments</Link>
+ {" | "}
+ {" | "}
+ {" | "}
+ <Link>show</Link>
+ {" | "}
+ </div>
+);
+
+class Template13 {
+ render() {
+ <Component prop={this.something} onClick={() => this.shouldStay}>
+ <Nested prop={this.data}>{this.content}</Nested>
+ </Component>;
+ }
+}
+
+const Template14 = <Component>{data()}</Component>;
+
+const Template15 = <Component {...props} />;
+
+const Template16 = <Component something={something} {...props} />;
+
+const Template17 = (
+ <Pre>
+ <span>1</span> <span>2</span> <span>3</span>
+ </Pre>
+);
+const Template18 = (
+ <Pre>
+ <span>1</span>
+ <span>2</span>
+ <span>3</span>
+ </Pre>
+);
+
+const Template19 = <Component {...s.dynamic()} />;
+
+const Template20 = <Component class={prop.red ? "red" : "green"} />;
+
+const template21 = (
+ <Component
+ {...{
+ get [key()]() {
+ return props.value;
+ },
+ }}
+ />
+);
diff --git a/test/bun.js/solid-dom-fixtures/components/output.bun.js b/test/bun.js/solid-dom-fixtures/components/output.bun.js
new file mode 100644
index 000000000..5ab4d5614
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/components/output.bun.js
@@ -0,0 +1,205 @@
+var _tmpl = _template$("<div><div>From Parent</div><div</div></div>", 9), _tmpl$1 = _template$("<div> | | | | | </div>", 8), _tmpl$2 = _template$("<div> | | | </div>", 8);
+import {Show} from "somewhere";
+const Child = (props) => {
+ const [s, set] = createSignal();
+ return ;
+};
+const template = (props) => {
+ let childRef;
+ const { content } = props;
+ return () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, createComponent(Child, {
+ name: "John",
+ ref: childRef,
+ booleanProperty: true
+ }), null);
+ insert(_tmpl, content, null);
+ insert(_tmpl, createComponent(Child, {
+ name: "Jason",
+ ref: props.ref
+ }), null);
+ insert(_tmpl, createComponent(Context.Consumer, {
+ ref: props.consumerRef(),
+ get children: [
+ (context) => context
+ ]
+ }), null);
+ return _tmpl;
+ };
+};
+const template2 = createComponent(Child, {
+ name: "Jake",
+ dynamic: state.data,
+ stale: state.data,
+ handleClick: clickHandler,
+ "hyphen-ated": state.data,
+ get ref: () => {
+ return (el) => e = el;
+ }
+});
+const template3 = createComponent(Child, {
+ get children: [
+ "After"
+ ]
+});
+const [s, set] = createSignal();
+const template4 = createComponent(Child, {
+ ref: set
+});
+const template5 = createComponent(Child, {
+ dynamic: state.dynamic,
+ get children: [
+ state.dynamic
+ ]
+});
+const template6 = createComponent(For, {
+ each: state.list,
+ fallback: ,
+ get children: [
+ (item) => createComponent(Show, {
+ when: state.condition,
+ get children: [
+ item
+ ]
+ })
+ ]
+});
+const template7 = createComponent(Child, {
+ get children: [
+ state.dynamic
+ ]
+});
+const template8 = createComponent(Child, {
+ get children: [
+ (item) => item,
+ (item) => item
+ ]
+});
+const template9 = createComponent(_garbage, {
+ get children: [
+ "Hi"
+ ]
+});
+const template10 = () => {
+ var _tmpl$1 = _tmpl$1.cloneNode(true);
+ insert(_tmpl$1, createComponent(Link, {
+ get children: [
+ "new"
+ ]
+ }), null);
+ insert(_tmpl$1, createComponent(Link, {
+ get children: [
+ "comments"
+ ]
+ }), null);
+ insert(_tmpl$1, createComponent(Link, {
+ get children: [
+ "show"
+ ]
+ }), null);
+ insert(_tmpl$1, createComponent(Link, {
+ get children: [
+ "ask"
+ ]
+ }), null);
+ insert(_tmpl$1, createComponent(Link, {
+ get children: [
+ "jobs"
+ ]
+ }), null);
+ insert(_tmpl$1, createComponent(Link, {
+ get children: [
+ "submit"
+ ]
+ }), null);
+ return _tmpl$1;
+};
+const template11 = () => {
+ var _tmpl$2 = _tmpl$2.cloneNode(true);
+ insert(_tmpl$2, createComponent(Link, {
+ get children: [
+ "new"
+ ]
+ }), null);
+ insert(_tmpl$2, createComponent(Link, {
+ get children: [
+ "comments"
+ ]
+ }), null);
+ insert(_tmpl$2, createComponent(Link, {
+ get children: [
+ "show"
+ ]
+ }), null);
+ insert(_tmpl$2, createComponent(Link, {
+ get children: [
+ "ask"
+ ]
+ }), null);
+ insert(_tmpl$2, createComponent(Link, {
+ get children: [
+ "jobs"
+ ]
+ }), null);
+ insert(_tmpl$2, createComponent(Link, {
+ get children: [
+ "submit"
+ ]
+ }), null);
+ return _tmpl$2;
+};
+const template12 = () => {
+ var _tmpl$1 = _tmpl$1.cloneNode(true);
+ insert(_tmpl$1, createComponent(Link, {
+ get children: [
+ "comments"
+ ]
+ }), null);
+ insert(_tmpl$1, createComponent(Link, {
+ get children: [
+ "show"
+ ]
+ }), null);
+ return _tmpl$1;
+};
+
+class Template13 {
+ render() {
+ createComponent(Component, {
+ prop: this.something,
+ get onClick: () => {
+ return () => this.shouldStay;
+ },
+ get children: [
+ createComponent(Nested, {
+ prop: this.data,
+ get children: [
+ this.content
+ ]
+ })
+ ]
+ });
+ }
+}
+const Template14 = createComponent(Component, {
+ get children: [
+ data()
+ ]
+});
+const Template15 = createComponent(Component, {});
+const Template16 = createComponent(Component, {
+ something
+});
+const Template17 = createComponent(Pre, {
+ get children: [
+ " ",
+ " "
+ ]
+});
+const Template18 = createComponent(Pre, {});
+const Template19 = createComponent(Component, {});
+const Template20 = createComponent(Component, {
+ class: prop.red ? "red" : "green"
+});
+const template21 = createComponent(Component, {});
diff --git a/test/bun.js/solid-dom-fixtures/components/output.js b/test/bun.js/solid-dom-fixtures/components/output.js
new file mode 100644
index 000000000..0c49d60a3
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/components/output.js
@@ -0,0 +1,443 @@
+import { template as _$template } from "r-dom";
+import { memo as _$memo } from "r-dom";
+import { For as _$For } from "r-dom";
+import { createComponent as _$createComponent } from "r-dom";
+import { mergeProps as _$mergeProps } from "r-dom";
+import { insert as _$insert } from "r-dom";
+
+const _tmpl$ = /*#__PURE__*/ _$template(`<div>Hello </div>`, 2),
+ _tmpl$2 = /*#__PURE__*/ _$template(`<div></div>`, 2),
+ _tmpl$3 = /*#__PURE__*/ _$template(`<div>From Parent</div>`, 2),
+ _tmpl$4 = /*#__PURE__*/ _$template(
+ `<div> | <!> | <!> | <!> | <!> | </div>`,
+ 6
+ ),
+ _tmpl$5 = /*#__PURE__*/ _$template(`<div> | <!> | <!> | </div>`, 4),
+ _tmpl$6 = /*#__PURE__*/ _$template(`<div> | <!> | | | <!> | </div>`, 4),
+ _tmpl$7 = /*#__PURE__*/ _$template(`<span>1</span>`, 2),
+ _tmpl$8 = /*#__PURE__*/ _$template(`<span>2</span>`, 2),
+ _tmpl$9 = /*#__PURE__*/ _$template(`<span>3</span>`, 2);
+
+import { Show } from "somewhere";
+
+const Child = (props) => {
+ const [s, set] = createSignal();
+ return [
+ (() => {
+ const _el$ = _tmpl$.cloneNode(true),
+ _el$2 = _el$.firstChild;
+
+ const _ref$ = props.ref;
+ typeof _ref$ === "function" ? _ref$(_el$) : (props.ref = _el$);
+
+ _$insert(_el$, () => props.name, null);
+
+ return _el$;
+ })(),
+ (() => {
+ const _el$3 = _tmpl$2.cloneNode(true);
+
+ set(_el$3);
+
+ _$insert(_el$3, () => props.children);
+
+ return _el$3;
+ })(),
+ ];
+};
+
+const template = (props) => {
+ let childRef;
+ const { content } = props;
+ return (() => {
+ const _el$4 = _tmpl$2.cloneNode(true);
+
+ _$insert(
+ _el$4,
+ _$createComponent(
+ Child,
+ _$mergeProps(
+ {
+ name: "John",
+ },
+ props,
+ {
+ ref(r$) {
+ const _ref$2 = childRef;
+ typeof _ref$2 === "function" ? _ref$2(r$) : (childRef = r$);
+ },
+
+ booleanProperty: true,
+
+ get children() {
+ return _tmpl$3.cloneNode(true);
+ },
+ }
+ )
+ ),
+ null
+ );
+
+ _$insert(
+ _el$4,
+ _$createComponent(
+ Child,
+ _$mergeProps(
+ {
+ name: "Jason",
+ },
+ dynamicSpread,
+ {
+ ref(r$) {
+ const _ref$3 = props.ref;
+ typeof _ref$3 === "function" ? _ref$3(r$) : (props.ref = r$);
+ },
+
+ get children() {
+ const _el$6 = _tmpl$2.cloneNode(true);
+
+ _$insert(_el$6, content);
+
+ return _el$6;
+ },
+ }
+ )
+ ),
+ null
+ );
+
+ _$insert(
+ _el$4,
+ _$createComponent(Context.Consumer, {
+ ref(r$) {
+ const _ref$4 = props.consumerRef();
+
+ typeof _ref$4 === "function" && _ref$4(r$);
+ },
+
+ children: (context) => context,
+ }),
+ null
+ );
+
+ return _el$4;
+ })();
+};
+
+const template2 = _$createComponent(Child, {
+ name: "Jake",
+
+ get dynamic() {
+ return state.data;
+ },
+
+ stale: state.data,
+ handleClick: clickHandler,
+
+ get ["hyphen-ated"]() {
+ return state.data;
+ },
+
+ ref: (el) => (e = el),
+});
+
+const template3 = _$createComponent(Child, {
+ get children() {
+ return [
+ _tmpl$2.cloneNode(true),
+ _tmpl$2.cloneNode(true),
+ _tmpl$2.cloneNode(true),
+ "After",
+ ];
+ },
+});
+
+const [s, set] = createSignal();
+
+const template4 = _$createComponent(Child, {
+ ref: set,
+
+ get children() {
+ return _tmpl$2.cloneNode(true);
+ },
+});
+
+const template5 = _$createComponent(Child, {
+ get dynamic() {
+ return state.dynamic;
+ },
+
+ get children() {
+ return state.dynamic;
+ },
+}); // builtIns
+
+const template6 = _$createComponent(_$For, {
+ get each() {
+ return state.list;
+ },
+
+ get fallback() {
+ return _$createComponent(Loading, {});
+ },
+
+ children: (item) =>
+ _$createComponent(Show, {
+ get when() {
+ return state.condition;
+ },
+
+ children: item,
+ }),
+});
+
+const template7 = _$createComponent(Child, {
+ get children() {
+ return [_tmpl$2.cloneNode(true), _$memo(() => state.dynamic)];
+ },
+});
+
+const template8 = _$createComponent(Child, {
+ get children() {
+ return [(item) => item, (item) => item];
+ },
+});
+
+const template9 = _$createComponent(_garbage, {
+ children: "Hi",
+});
+
+const template10 = (() => {
+ const _el$12 = _tmpl$4.cloneNode(true),
+ _el$13 = _el$12.firstChild,
+ _el$18 = _el$13.nextSibling,
+ _el$14 = _el$18.nextSibling,
+ _el$19 = _el$14.nextSibling,
+ _el$15 = _el$19.nextSibling,
+ _el$20 = _el$15.nextSibling,
+ _el$16 = _el$20.nextSibling,
+ _el$21 = _el$16.nextSibling,
+ _el$17 = _el$21.nextSibling;
+
+ _$insert(
+ _el$12,
+ _$createComponent(Link, {
+ children: "new",
+ }),
+ _el$13
+ );
+
+ _$insert(
+ _el$12,
+ _$createComponent(Link, {
+ children: "comments",
+ }),
+ _el$18
+ );
+
+ _$insert(
+ _el$12,
+ _$createComponent(Link, {
+ children: "show",
+ }),
+ _el$19
+ );
+
+ _$insert(
+ _el$12,
+ _$createComponent(Link, {
+ children: "ask",
+ }),
+ _el$20
+ );
+
+ _$insert(
+ _el$12,
+ _$createComponent(Link, {
+ children: "jobs",
+ }),
+ _el$21
+ );
+
+ _$insert(
+ _el$12,
+ _$createComponent(Link, {
+ children: "submit",
+ }),
+ null
+ );
+
+ return _el$12;
+})();
+
+const template11 = (() => {
+ const _el$22 = _tmpl$5.cloneNode(true),
+ _el$23 = _el$22.firstChild,
+ _el$26 = _el$23.nextSibling,
+ _el$24 = _el$26.nextSibling,
+ _el$27 = _el$24.nextSibling,
+ _el$25 = _el$27.nextSibling;
+
+ _$insert(
+ _el$22,
+ _$createComponent(Link, {
+ children: "new",
+ }),
+ _el$23
+ );
+
+ _$insert(
+ _el$22,
+ _$createComponent(Link, {
+ children: "comments",
+ }),
+ _el$26
+ );
+
+ _$insert(
+ _el$22,
+ _$createComponent(Link, {
+ children: "show",
+ }),
+ _el$26
+ );
+
+ _$insert(
+ _el$22,
+ _$createComponent(Link, {
+ children: "ask",
+ }),
+ _el$27
+ );
+
+ _$insert(
+ _el$22,
+ _$createComponent(Link, {
+ children: "jobs",
+ }),
+ _el$27
+ );
+
+ _$insert(
+ _el$22,
+ _$createComponent(Link, {
+ children: "submit",
+ }),
+ null
+ );
+
+ return _el$22;
+})();
+
+const template12 = (() => {
+ const _el$28 = _tmpl$6.cloneNode(true),
+ _el$29 = _el$28.firstChild,
+ _el$34 = _el$29.nextSibling,
+ _el$30 = _el$34.nextSibling,
+ _el$35 = _el$30.nextSibling,
+ _el$33 = _el$35.nextSibling;
+
+ _$insert(
+ _el$28,
+ _$createComponent(Link, {
+ children: "comments",
+ }),
+ _el$34
+ );
+
+ _$insert(
+ _el$28,
+ _$createComponent(Link, {
+ children: "show",
+ }),
+ _el$35
+ );
+
+ return _el$28;
+})();
+
+class Template13 {
+ render() {
+ const _self$ = this;
+
+ _$createComponent(Component, {
+ get prop() {
+ return _self$.something;
+ },
+
+ onClick: () => _self$.shouldStay,
+
+ get children() {
+ return _$createComponent(Nested, {
+ get prop() {
+ return _self$.data;
+ },
+
+ get children() {
+ return _self$.content;
+ },
+ });
+ },
+ });
+ }
+}
+
+const Template14 = _$createComponent(Component, {
+ get children() {
+ return data();
+ },
+});
+
+const Template15 = _$createComponent(Component, props);
+
+const Template16 = _$createComponent(
+ Component,
+ _$mergeProps(
+ {
+ something: something,
+ },
+ props
+ )
+);
+
+const Template17 = _$createComponent(Pre, {
+ get children() {
+ return [
+ _tmpl$7.cloneNode(true),
+ " ",
+ _tmpl$8.cloneNode(true),
+ " ",
+ _tmpl$9.cloneNode(true),
+ ];
+ },
+});
+
+const Template18 = _$createComponent(Pre, {
+ get children() {
+ return [
+ _tmpl$7.cloneNode(true),
+ _tmpl$8.cloneNode(true),
+ _tmpl$9.cloneNode(true),
+ ];
+ },
+});
+
+const Template19 = _$createComponent(
+ Component,
+ _$mergeProps(() => s.dynamic())
+);
+
+const Template20 = _$createComponent(Component, {
+ get ["class"]() {
+ return prop.red ? "red" : "green";
+ },
+});
+
+const template21 = _$createComponent(
+ Component,
+ _$mergeProps(() => ({
+ get [key()]() {
+ return props.value;
+ },
+ }))
+);
diff --git a/test/bun.js/solid-dom-fixtures/conditionalExpressions/code.js b/test/bun.js/solid-dom-fixtures/conditionalExpressions/code.js
new file mode 100644
index 000000000..80f1a6a4f
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/conditionalExpressions/code.js
@@ -0,0 +1,71 @@
+const template1 = <div>{simple}</div>;
+
+const template2 = <div>{state.dynamic}</div>;
+
+const template3 = <div>{simple ? good : bad}</div>;
+
+const template4 = <div>{simple ? good() : bad}</div>;
+
+const template5 = <div>{state.dynamic ? good() : bad}</div>;
+
+const template6 = <div>{state.dynamic && good()}</div>;
+
+const template7 = (
+ <div>{state.count > 5 ? (state.dynamic ? best : good()) : bad}</div>
+);
+
+const template8 = <div>{state.dynamic && state.something && good()}</div>;
+
+const template9 = <div>{(state.dynamic && good()) || bad}</div>;
+
+const template10 = (
+ <div>{state.a ? "a" : state.b ? "b" : state.c ? "c" : "fallback"}</div>
+);
+
+const template11 = (
+ <div>{state.a ? a() : state.b ? b() : state.c ? "c" : "fallback"}</div>
+);
+
+const template12 = <Comp render={state.dynamic ? good() : bad} />;
+
+// no dynamic predicate
+const template13 = <Comp render={state.dynamic ? good : bad} />;
+
+const template14 = <Comp render={state.dynamic && good()} />;
+
+// no dynamic predicate
+const template15 = <Comp render={state.dynamic && good} />;
+
+const template16 = <Comp render={state.dynamic || good()} />;
+
+const template17 = <Comp render={state.dynamic ? <Comp /> : <Comp />} />;
+
+const template18 = <Comp>{state.dynamic ? <Comp /> : <Comp />}</Comp>;
+
+const template19 = <div innerHTML={state.dynamic ? <Comp /> : <Comp />} />;
+
+const template20 = <div>{state.dynamic ? <Comp /> : <Comp />}</div>;
+
+const template21 = <Comp render={state?.dynamic ? "a" : "b"} />;
+
+const template22 = <Comp>{state?.dynamic ? "a" : "b"}</Comp>;
+
+const template23 = <div innerHTML={state?.dynamic ? "a" : "b"} />;
+
+const template24 = <div>{state?.dynamic ? "a" : "b"}</div>;
+
+const template25 = <Comp render={state.dynamic ?? <Comp />} />;
+
+const template26 = <Comp>{state.dynamic ?? <Comp />}</Comp>;
+
+const template27 = <div innerHTML={state.dynamic ?? <Comp />} />;
+
+const template28 = <div>{state.dynamic ?? <Comp />}</div>;
+
+const template29 = <div>{(thing() && thing1()) ?? thing2() ?? thing3()}</div>;
+
+const template30 = <div>{thing() || thing1() || thing2()}</div>;
+
+const template31 = (
+ <Comp value={count() ? (count() ? count() : count()) : count()} />
+);
diff --git a/test/bun.js/solid-dom-fixtures/conditionalExpressions/output.bun.js b/test/bun.js/solid-dom-fixtures/conditionalExpressions/output.bun.js
new file mode 100644
index 000000000..36c3f649b
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/conditionalExpressions/output.bun.js
@@ -0,0 +1,144 @@
+var _tmpl = template("<div</div>", 2), _tmpl$1 = template("<div/>", 0);
+const template1 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, simple, null);
+ return _tmpl;
+};
+const template2 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, state.dynamic, null);
+ return _tmpl;
+};
+const template3 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, simple ? good : bad, null);
+ return _tmpl;
+};
+const template4 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, simple ? good() : bad, null);
+ return _tmpl;
+};
+const template5 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, state.dynamic ? good() : bad, null);
+ return _tmpl;
+};
+const template6 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, state.dynamic && good(), null);
+ return _tmpl;
+};
+const template7 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, state.count > 5 ? state.dynamic ? best : good() : bad, null);
+ return _tmpl;
+};
+const template8 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, state.dynamic && state.something && good(), null);
+ return _tmpl;
+};
+const template9 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, state.dynamic && good() || bad, null);
+ return _tmpl;
+};
+const template10 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, state.a ? "a" : state.b ? "b" : state.c ? "c" : "fallback", null);
+ return _tmpl;
+};
+const template11 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, state.a ? a() : state.b ? b() : state.c ? "c" : "fallback", null);
+ return _tmpl;
+};
+const template12 = createComponent(Comp, {
+ render: state.dynamic ? good() : bad
+});
+const template13 = createComponent(Comp, {
+ render: state.dynamic ? good : bad
+});
+const template14 = createComponent(Comp, {
+ render: state.dynamic && good()
+});
+const template15 = createComponent(Comp, {
+ render: state.dynamic && good
+});
+const template16 = createComponent(Comp, {
+ render: state.dynamic || good()
+});
+const template17 = createComponent(Comp, {
+ render: state.dynamic ? createComponent(Comp, {}) : createComponent(Comp, {})
+});
+const template18 = createComponent(Comp, {
+ get children: [
+ state.dynamic ? createComponent(Comp, {}) : createComponent(Comp, {})
+ ]
+});
+const template19 = () => {
+ var _el = _tmpl$1.cloneNode(true);
+ effect(() => {
+ return setAttribute(_el, "innerHTML", state.dynamic ? createComponent(Comp, {}) : createComponent(Comp, {}));
+ });
+ return _el;
+};
+const template20 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, state.dynamic ? createComponent(Comp, {}) : createComponent(Comp, {}), null);
+ return _tmpl;
+};
+const template21 = createComponent(Comp, {
+ render: state?.dynamic ? "a" : "b"
+});
+const template22 = createComponent(Comp, {
+ get children: [
+ state?.dynamic ? "a" : "b"
+ ]
+});
+const template23 = () => {
+ var _el = _tmpl$1.cloneNode(true);
+ effect(() => {
+ return setAttribute(_el, "innerHTML", state?.dynamic ? "a" : "b");
+ });
+ return _el;
+};
+const template24 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, state?.dynamic ? "a" : "b", null);
+ return _tmpl;
+};
+const template25 = createComponent(Comp, {
+ render: state.dynamic ?? createComponent(Comp, {})
+});
+const template26 = createComponent(Comp, {
+ get children: [
+ state.dynamic ?? createComponent(Comp, {})
+ ]
+});
+const template27 = () => {
+ var _el = _tmpl$1.cloneNode(true);
+ effect(() => {
+ return setAttribute(_el, "innerHTML", state.dynamic ?? createComponent(Comp, {}));
+ });
+ return _el;
+};
+const template28 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, state.dynamic ?? createComponent(Comp, {}), null);
+ return _tmpl;
+};
+const template29 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, (thing() && thing1()) ?? thing2() ?? thing3(), null);
+ return _tmpl;
+};
+const template30 = () => {
+ var _tmpl = _tmpl.cloneNode(true);
+ insert(_tmpl, thing() || thing1() || thing2(), null);
+ return _tmpl;
+};
+const template31 = createComponent(Comp, {
+ value: count() ? count() ? count() : count() : count()
+});
diff --git a/test/bun.js/solid-dom-fixtures/conditionalExpressions/output.js b/test/bun.js/solid-dom-fixtures/conditionalExpressions/output.js
new file mode 100644
index 000000000..1511f4222
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/conditionalExpressions/output.js
@@ -0,0 +1,319 @@
+import { template as _$template } from "r-dom";
+import { effect as _$effect } from "r-dom";
+import { createComponent as _$createComponent } from "r-dom";
+import { memo as _$memo } from "r-dom";
+import { insert as _$insert } from "r-dom";
+
+const _tmpl$ = /*#__PURE__*/ _$template(`<div></div>`, 2);
+
+const template1 = (() => {
+ const _el$ = _tmpl$.cloneNode(true);
+
+ _$insert(_el$, simple);
+
+ return _el$;
+})();
+
+const template2 = (() => {
+ const _el$2 = _tmpl$.cloneNode(true);
+
+ _$insert(_el$2, () => state.dynamic);
+
+ return _el$2;
+})();
+
+const template3 = (() => {
+ const _el$3 = _tmpl$.cloneNode(true);
+
+ _$insert(_el$3, simple ? good : bad);
+
+ return _el$3;
+})();
+
+const template4 = (() => {
+ const _el$4 = _tmpl$.cloneNode(true);
+
+ _$insert(_el$4, () => (simple ? good() : bad));
+
+ return _el$4;
+})();
+
+const template5 = (() => {
+ const _el$5 = _tmpl$.cloneNode(true);
+
+ _$insert(
+ _el$5,
+ (() => {
+ const _c$ = _$memo(() => !!state.dynamic, true);
+
+ return () => (_c$() ? good() : bad);
+ })()
+ );
+
+ return _el$5;
+})();
+
+const template6 = (() => {
+ const _el$6 = _tmpl$.cloneNode(true);
+
+ _$insert(
+ _el$6,
+ (() => {
+ const _c$2 = _$memo(() => !!state.dynamic, true);
+
+ return () => _c$2() && good();
+ })()
+ );
+
+ return _el$6;
+})();
+
+const template7 = (() => {
+ const _el$7 = _tmpl$.cloneNode(true);
+
+ _$insert(
+ _el$7,
+ (() => {
+ const _c$3 = _$memo(() => state.count > 5, true);
+
+ return () =>
+ _c$3()
+ ? (() => {
+ const _c$4 = _$memo(() => !!state.dynamic, true);
+
+ return () => (_c$4() ? best : good());
+ })()
+ : bad;
+ })()
+ );
+
+ return _el$7;
+})();
+
+const template8 = (() => {
+ const _el$8 = _tmpl$.cloneNode(true);
+
+ _$insert(
+ _el$8,
+ (() => {
+ const _c$5 = _$memo(() => !!(state.dynamic && state.something), true);
+
+ return () => _c$5() && good();
+ })()
+ );
+
+ return _el$8;
+})();
+
+const template9 = (() => {
+ const _el$9 = _tmpl$.cloneNode(true);
+
+ _$insert(
+ _el$9,
+ (() => {
+ const _c$6 = _$memo(() => !!state.dynamic, true);
+
+ return () => (_c$6() && good()) || bad;
+ })()
+ );
+
+ return _el$9;
+})();
+
+const template10 = (() => {
+ const _el$10 = _tmpl$.cloneNode(true);
+
+ _$insert(_el$10, () =>
+ state.a ? "a" : state.b ? "b" : state.c ? "c" : "fallback"
+ );
+
+ return _el$10;
+})();
+
+const template11 = (() => {
+ const _el$11 = _tmpl$.cloneNode(true);
+
+ _$insert(
+ _el$11,
+ (() => {
+ const _c$7 = _$memo(() => !!state.a, true);
+
+ return () =>
+ _c$7()
+ ? a()
+ : (() => {
+ const _c$8 = _$memo(() => !!state.b, true);
+
+ return () => (_c$8() ? b() : state.c ? "c" : "fallback");
+ })();
+ })()
+ );
+
+ return _el$11;
+})();
+
+const template12 = _$createComponent(Comp, {
+ get render() {
+ return _$memo(() => !!state.dynamic, true)() ? good() : bad;
+ },
+}); // no dynamic predicate
+
+const template13 = _$createComponent(Comp, {
+ get render() {
+ return state.dynamic ? good : bad;
+ },
+});
+
+const template14 = _$createComponent(Comp, {
+ get render() {
+ return _$memo(() => !!state.dynamic, true)() && good();
+ },
+}); // no dynamic predicate
+
+const template15 = _$createComponent(Comp, {
+ get render() {
+ return state.dynamic && good;
+ },
+});
+
+const template16 = _$createComponent(Comp, {
+ get render() {
+ return state.dynamic || good();
+ },
+});
+
+const template17 = _$createComponent(Comp, {
+ get render() {
+ return _$memo(() => !!state.dynamic, true)()
+ ? _$createComponent(Comp, {})
+ : _$createComponent(Comp, {});
+ },
+});
+
+const template18 = _$createComponent(Comp, {
+ get children() {
+ return _$memo(() => !!state.dynamic, true)()
+ ? _$createComponent(Comp, {})
+ : _$createComponent(Comp, {});
+ },
+});
+
+const template19 = (() => {
+ const _el$12 = _tmpl$.cloneNode(true);
+
+ _$effect(
+ () =>
+ (_el$12.innerHTML = state.dynamic
+ ? _$createComponent(Comp, {})
+ : _$createComponent(Comp, {}))
+ );
+
+ return _el$12;
+})();
+
+const template20 = (() => {
+ const _el$13 = _tmpl$.cloneNode(true);
+
+ _$insert(
+ _el$13,
+ (() => {
+ const _c$9 = _$memo(() => !!state.dynamic, true);
+
+ return () =>
+ _c$9() ? _$createComponent(Comp, {}) : _$createComponent(Comp, {});
+ })()
+ );
+
+ return _el$13;
+})();
+
+const template21 = _$createComponent(Comp, {
+ get render() {
+ return state?.dynamic ? "a" : "b";
+ },
+});
+
+const template22 = _$createComponent(Comp, {
+ get children() {
+ return state?.dynamic ? "a" : "b";
+ },
+});
+
+const template23 = (() => {
+ const _el$14 = _tmpl$.cloneNode(true);
+
+ _$effect(() => (_el$14.innerHTML = state?.dynamic ? "a" : "b"));
+
+ return _el$14;
+})();
+
+const template24 = (() => {
+ const _el$15 = _tmpl$.cloneNode(true);
+
+ _$insert(_el$15, () => (state?.dynamic ? "a" : "b"));
+
+ return _el$15;
+})();
+
+const template25 = _$createComponent(Comp, {
+ get render() {
+ return state.dynamic ?? _$createComponent(Comp, {});
+ },
+});
+
+const template26 = _$createComponent(Comp, {
+ get children() {
+ return state.dynamic ?? _$createComponent(Comp, {});
+ },
+});
+
+const template27 = (() => {
+ const _el$16 = _tmpl$.cloneNode(true);
+
+ _$effect(
+ () => (_el$16.innerHTML = state.dynamic ?? _$createComponent(Comp, {}))
+ );
+
+ return _el$16;
+})();
+
+const template28 = (() => {
+ const _el$17 = _tmpl$.cloneNode(true);
+
+ _$insert(_el$17, () => state.dynamic ?? _$createComponent(Comp, {}));
+
+ return _el$17;
+})();
+
+const template29 = (() => {
+ const _el$18 = _tmpl$.cloneNode(true);
+
+ _$insert(
+ _el$18,
+ (() => {
+ const _c$10 = _$memo(() => !!thing(), true);
+
+ return () => (_c$10() && thing1()) ?? thing2() ?? thing3();
+ })()
+ );
+
+ return _el$18;
+})();
+
+const template30 = (() => {
+ const _el$19 = _tmpl$.cloneNode(true);
+
+ _$insert(_el$19, () => thing() || thing1() || thing2());
+
+ return _el$19;
+})();
+
+const template31 = _$createComponent(Comp, {
+ get value() {
+ return _$memo(() => !!count(), true)()
+ ? _$memo(() => !!count(), true)()
+ ? count()
+ : count()
+ : count();
+ },
+});
diff --git a/test/bun.js/solid-dom-fixtures/customElements/code.js b/test/bun.js/solid-dom-fixtures/customElements/code.js
new file mode 100644
index 000000000..f2e2bd02d
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/customElements/code.js
@@ -0,0 +1,29 @@
+const template = (
+ <my-element
+ some-attr={name}
+ notProp={data}
+ attr:my-attr={data}
+ prop:someProp={data}
+ />
+);
+
+const template2 = (
+ <my-element
+ some-attr={state.name}
+ notProp={state.data}
+ attr:my-attr={state.data}
+ prop:someProp={state.data}
+ />
+);
+
+const template3 = (
+ <my-element>
+ <header slot="head">Title</header>
+ </my-element>
+);
+
+const template4 = (
+ <>
+ <slot name="head"></slot>
+ </>
+);
diff --git a/test/bun.js/solid-dom-fixtures/customElements/output.bun.js b/test/bun.js/solid-dom-fixtures/customElements/output.bun.js
new file mode 100644
index 000000000..79c46280c
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/customElements/output.bun.js
@@ -0,0 +1,27 @@
+var _tmpl = _template$("<my-element/>", 0), _tmpl$2 = _template$('<my-element><header slot="head">Title</header></my-element>', 4);
+const template = () => {
+ var _el = _tmpl.cloneNode(true);
+ setAttribute(_el, "some-attr", name);
+ setAttribute(_el, "notProp", data);
+ setAttribute(_el, "attr:my-attr", data);
+ setAttribute(_el, "prop:someProp", data);
+ return _el;
+};
+const template2 = () => {
+ var _el = _tmpl.cloneNode(true);
+ effect(() => {
+ return setAttribute(_el, "some-attr", state.name);
+ });
+ effect(() => {
+ return setAttribute(_el, "notProp", state.data);
+ });
+ effect(() => {
+ return setAttribute(_el, "attr:my-attr", state.data);
+ });
+ effect(() => {
+ return setAttribute(_el, "prop:someProp", state.data);
+ });
+ return _el;
+};
+const template3 = _tmpl$2.cloneNode(true);
+const template4 = ;
diff --git a/test/bun.js/solid-dom-fixtures/customElements/output.js b/test/bun.js/solid-dom-fixtures/customElements/output.js
new file mode 100644
index 000000000..79274ce2c
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/customElements/output.js
@@ -0,0 +1,66 @@
+import { template as _$template } from "r-dom";
+import { effect as _$effect } from "r-dom";
+import { getOwner as _$getOwner } from "r-dom";
+import { setAttribute as _$setAttribute } from "r-dom";
+
+const _tmpl$ = /*#__PURE__*/ _$template(`<my-element></my-element>`, 2),
+ _tmpl$2 = /*#__PURE__*/ _$template(
+ `<my-element><header slot="head">Title</header></my-element>`,
+ 4
+ ),
+ _tmpl$3 = /*#__PURE__*/ _$template(`<slot name="head"></slot>`, 2);
+
+const template = (() => {
+ const _el$ = document.importNode(_tmpl$, true);
+
+ _el$.someAttr = name;
+ _el$.notprop = data;
+
+ _$setAttribute(_el$, "my-attr", data);
+
+ _el$.someProp = data;
+ _el$._$owner = _$getOwner();
+ return _el$;
+})();
+
+const template2 = (() => {
+ const _el$2 = document.importNode(_tmpl$, true);
+
+ _el$2._$owner = _$getOwner();
+
+ _$effect(
+ (_p$) => {
+ const _v$ = state.name,
+ _v$2 = state.data,
+ _v$3 = state.data,
+ _v$4 = state.data;
+ _v$ !== _p$._v$ && (_el$2.someAttr = _p$._v$ = _v$);
+ _v$2 !== _p$._v$2 && (_el$2.notprop = _p$._v$2 = _v$2);
+ _v$3 !== _p$._v$3 && _$setAttribute(_el$2, "my-attr", (_p$._v$3 = _v$3));
+ _v$4 !== _p$._v$4 && (_el$2.someProp = _p$._v$4 = _v$4);
+ return _p$;
+ },
+ {
+ _v$: undefined,
+ _v$2: undefined,
+ _v$3: undefined,
+ _v$4: undefined,
+ }
+ );
+
+ return _el$2;
+})();
+
+const template3 = (() => {
+ const _el$3 = document.importNode(_tmpl$2, true);
+
+ _el$3._$owner = _$getOwner();
+ return _el$3;
+})();
+
+const template4 = (() => {
+ const _el$4 = _tmpl$3.cloneNode(true);
+
+ _el$4._$owner = _$getOwner();
+ return _el$4;
+})();
diff --git a/test/bun.js/solid-dom-fixtures/eventExpressions/code.js b/test/bun.js/solid-dom-fixtures/eventExpressions/code.js
new file mode 100644
index 000000000..78bc5e199
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/eventExpressions/code.js
@@ -0,0 +1,32 @@
+function hoisted1() {
+ console.log("hoisted");
+}
+const hoisted2 = () => console.log("hoisted delegated");
+
+const template = (
+ <div id="main">
+ <button onchange={() => console.log("bound")}>Change Bound</button>
+ <button onChange={[(id) => console.log("bound", id), id]}>
+ Change Bound
+ </button>
+ <button onchange={handler}>Change Bound</button>
+ <button onchange={[handler]}>Change Bound</button>
+ <button onchange={hoisted1}>Change Bound</button>
+ <button onclick={() => console.log("delegated")}>Click Delegated</button>
+ <button onClick={[(id) => console.log("delegated", id), rowId]}>
+ Click Delegated
+ </button>
+ <button onClick={handler}>Click Delegated</button>
+ <button onClick={[handler]}>Click Delegated</button>
+ <button onClick={hoisted2}>Click Delegated</button>
+ <button
+ on:click={() => console.log("listener")}
+ on:CAPS-ev={() => console.log("custom")}
+ >
+ Click Listener
+ </button>
+ <button oncapture:camelClick={() => console.log("listener")}>
+ Click Capture
+ </button>
+ </div>
+);
diff --git a/test/bun.js/solid-dom-fixtures/eventExpressions/output.bun.js b/test/bun.js/solid-dom-fixtures/eventExpressions/output.bun.js
new file mode 100644
index 000000000..5d90654f9
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/eventExpressions/output.bun.js
@@ -0,0 +1,57 @@
+var _tmpl$1 = _template$(
+ '<div id="main"><button>Change Bound</button><button>Change Bound</button><button>Change Bound</button><button>Change Bound</button><button>Change Bound</button><button>Click Delegated</button><button>Click Delegated</button><button>Click Delegated</button><button>Click Delegated</button><button>Click Delegated</button><button>Click Listener</button><button>Click Capture</button></div>',
+ 26
+);
+function hoisted1() {
+ console.log("hoisted");
+}
+const hoisted2 = () => console.log("hoisted delegated");
+const template = () => {
+ var _el = _tmpl.cloneNode(true),
+ _el$1 = _el.firstChild,
+ _el$2 = _el$1.nextSibling,
+ _el$3 = _el$2.nextSibling,
+ _el$4 = _el$3.nextSibling,
+ _el$5 = _el$4.nextSibling,
+ _el$6 = _el$5.nextSibling,
+ _el$7 = _el$6.nextSibling,
+ _el$8 = _el$7.nextSibling,
+ _el$9 = _el$8.nextSibling,
+ _el$10 = _el$9.nextSibling,
+ _el$11 = _el$10.nextSibling;
+ effect(() => {
+ return setAttribute(_el, "onchange", () => console.log("bound"));
+ });
+ effect(() => {
+ return setAttribute(_el$1, "onChange", [
+ (id) => console.log("bound", id),
+ id,
+ ]);
+ });
+ setAttribute(_el$2, "onchange", handler);
+ effect(() => {
+ return setAttribute(_el$3, "onchange", [handler]);
+ });
+ setAttribute(_el$4, "onchange", hoisted1);
+ _el$5.$$click = () => console.log("delegated");
+ effect(() => {
+ return (_el$6.$$click = [(id) => console.log("delegated", id), rowId]);
+ });
+ effect(() => {
+ return (_el$7.$$click = handler);
+ });
+ effect(() => {
+ return (_el$8.$$click = [handler]);
+ });
+ effect(() => {
+ return (_el$9.$$click = hoisted2);
+ });
+ _el$10.addEventListener("click", () => console.log("listener"));
+ _el$10.addEventListener("CAPS-ev", () => console.log("custom"));
+ _el$11.addEventListener(
+ "apture:camelClick",
+ () => console.log("listener"),
+ true
+ );
+ return _el;
+};
diff --git a/test/bun.js/solid-dom-fixtures/eventExpressions/output.js b/test/bun.js/solid-dom-fixtures/eventExpressions/output.js
new file mode 100644
index 000000000..c24a1f89f
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/eventExpressions/output.js
@@ -0,0 +1,63 @@
+import { template as _$template } from "r-dom";
+import { delegateEvents as _$delegateEvents } from "r-dom";
+import { addEventListener as _$addEventListener } from "r-dom";
+
+const _tmpl$ = /*#__PURE__*/ _$template(
+ `<div id="main"><button>Change Bound</button><button>Change Bound</button><button>Change Bound</button><button>Change Bound</button><button>Change Bound</button><button>Click Delegated</button><button>Click Delegated</button><button>Click Delegated</button><button>Click Delegated</button><button>Click Delegated</button><button>Click Listener</button><button>Click Capture</button></div>`,
+ 26
+);
+
+function hoisted1() {
+ console.log("hoisted");
+}
+
+const hoisted2 = () => console.log("hoisted delegated");
+
+const template = (() => {
+ const _el$ = _tmpl$.cloneNode(true),
+ _el$2 = _el$.firstChild,
+ _el$3 = _el$2.nextSibling,
+ _el$4 = _el$3.nextSibling,
+ _el$5 = _el$4.nextSibling,
+ _el$6 = _el$5.nextSibling,
+ _el$7 = _el$6.nextSibling,
+ _el$8 = _el$7.nextSibling,
+ _el$9 = _el$8.nextSibling,
+ _el$10 = _el$9.nextSibling,
+ _el$11 = _el$10.nextSibling,
+ _el$12 = _el$11.nextSibling,
+ _el$13 = _el$12.nextSibling;
+
+ _el$2.addEventListener("change", () => console.log("bound"));
+
+ _el$3.addEventListener("change", (e) =>
+ ((id) => console.log("bound", id))(id, e)
+ );
+
+ _$addEventListener(_el$4, "change", handler);
+
+ _el$5.addEventListener("change", handler);
+
+ _el$6.addEventListener("change", hoisted1);
+
+ _el$7.$$click = () => console.log("delegated");
+
+ _el$8.$$click = (id) => console.log("delegated", id);
+
+ _el$8.$$clickData = rowId;
+
+ _$addEventListener(_el$9, "click", handler, true);
+
+ _el$10.$$click = handler;
+ _el$11.$$click = hoisted2;
+
+ _el$12.addEventListener("click", () => console.log("listener"));
+
+ _el$12.addEventListener("CAPS-ev", () => console.log("custom"));
+
+ _el$13.addEventListener("camelClick", () => console.log("listener"), true);
+
+ return _el$;
+})();
+
+_$delegateEvents(["click"]);
diff --git a/test/bun.js/solid-dom-fixtures/fragments/code.js b/test/bun.js/solid-dom-fixtures/fragments/code.js
new file mode 100644
index 000000000..0b6021e44
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/fragments/code.js
@@ -0,0 +1,83 @@
+const multiStatic = (
+ <>
+ <div>First</div>
+ <div>Last</div>
+ </>
+);
+
+const multiExpression = (
+ <>
+ <div>First</div>
+ {inserted}
+ <div>Last</div>
+ After
+ </>
+);
+
+const multiDynamic = (
+ <>
+ <div id={state.first}>First</div>
+ {state.inserted}
+ <div id={state.last}>Last</div>
+ After
+ </>
+);
+
+const singleExpression = <>{inserted}</>;
+
+const singleDynamic = <>{inserted()}</>;
+
+const firstStatic = (
+ <>
+ {inserted}
+ <div />
+ </>
+);
+
+const firstDynamic = (
+ <>
+ {inserted()}
+ <div />
+ </>
+);
+
+const firstComponent = (
+ <>
+ <Component />
+ <div />
+ </>
+);
+
+const lastStatic = (
+ <>
+ <div />
+ {inserted}
+ </>
+);
+
+const lastDynamic = (
+ <>
+ <div />
+ {inserted()}
+ </>
+);
+
+const lastComponent = (
+ <>
+ <div />
+ <Component />
+ </>
+);
+
+const spaces = (
+ <>
+ <span>1</span> <span>2</span> <span>3</span>
+ </>
+);
+const multiLineTrailing = (
+ <>
+ <span>1</span>
+ <span>2</span>
+ <span>3</span>
+ </>
+);
diff --git a/test/bun.js/solid-dom-fixtures/fragments/output.bun.js b/test/bun.js/solid-dom-fixtures/fragments/output.bun.js
new file mode 100644
index 000000000..54d980cee
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/fragments/output.bun.js
@@ -0,0 +1,13 @@
+const multiStatic = ;
+const multiExpression = ;
+const multiDynamic = ;
+const singleExpression = ;
+const singleDynamic = ;
+const firstStatic = ;
+const firstDynamic = ;
+const firstComponent = ;
+const lastStatic = ;
+const lastDynamic = ;
+const lastComponent = ;
+const spaces = ;
+const multiLineTrailing = ;
diff --git a/test/bun.js/solid-dom-fixtures/fragments/output.js b/test/bun.js/solid-dom-fixtures/fragments/output.js
new file mode 100644
index 000000000..5fe0c767c
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/fragments/output.js
@@ -0,0 +1,66 @@
+import { template as _$template } from "r-dom";
+import { createComponent as _$createComponent } from "r-dom";
+import { memo as _$memo } from "r-dom";
+import { setAttribute as _$setAttribute } from "r-dom";
+import { effect as _$effect } from "r-dom";
+
+const _tmpl$ = /*#__PURE__*/ _$template(`<div>First</div>`, 2),
+ _tmpl$2 = /*#__PURE__*/ _$template(`<div>Last</div>`, 2),
+ _tmpl$3 = /*#__PURE__*/ _$template(`<div></div>`, 2),
+ _tmpl$4 = /*#__PURE__*/ _$template(`<span>1</span>`, 2),
+ _tmpl$5 = /*#__PURE__*/ _$template(`<span>2</span>`, 2),
+ _tmpl$6 = /*#__PURE__*/ _$template(`<span>3</span>`, 2);
+
+const multiStatic = [_tmpl$.cloneNode(true), _tmpl$2.cloneNode(true)];
+const multiExpression = [
+ _tmpl$.cloneNode(true),
+ inserted,
+ _tmpl$2.cloneNode(true),
+ "After",
+];
+const multiDynamic = [
+ (() => {
+ const _el$5 = _tmpl$.cloneNode(true);
+
+ _$effect(() => _$setAttribute(_el$5, "id", state.first));
+
+ return _el$5;
+ })(),
+ _$memo(() => state.inserted),
+ (() => {
+ const _el$6 = _tmpl$2.cloneNode(true);
+
+ _$effect(() => _$setAttribute(_el$6, "id", state.last));
+
+ return _el$6;
+ })(),
+ "After",
+];
+const singleExpression = inserted;
+
+const singleDynamic = _$memo(inserted);
+
+const firstStatic = [inserted, _tmpl$3.cloneNode(true)];
+const firstDynamic = [_$memo(inserted), _tmpl$3.cloneNode(true)];
+const firstComponent = [
+ _$createComponent(Component, {}),
+ _tmpl$3.cloneNode(true),
+];
+const lastStatic = [_tmpl$3.cloneNode(true), inserted];
+const lastDynamic = [_tmpl$3.cloneNode(true), _$memo(inserted)];
+const lastComponent = [
+ _tmpl$3.cloneNode(true),
+ _$createComponent(Component, {}),
+];
+const spaces = [
+ _tmpl$4.cloneNode(true),
+ " ",
+ _tmpl$5.cloneNode(true),
+ " ",
+ _tmpl$6.cloneNode(true),
+];
+const multiLineTrailing = [
+ _tmpl$4.cloneNode(true),
+ _tmpl$5.cloneNode(true),
+ _tmpl$6.cloneNode(true),
+];
diff --git a/test/bun.js/solid-dom-fixtures/insertChildren/code.js b/test/bun.js/solid-dom-fixtures/insertChildren/code.js
new file mode 100644
index 000000000..41d3d017e
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/insertChildren/code.js
@@ -0,0 +1,36 @@
+const children = <div />;
+const dynamic = {
+ children,
+};
+const template = <Module children={children} />;
+const template2 = <module children={children} />;
+const template3 = <module children={children}>Hello</module>;
+const template4 = (
+ <module children={children}>
+ <Hello />
+ </module>
+);
+const template5 = <module children={dynamic.children} />;
+const template6 = <Module children={dynamic.children} />;
+const template7 = <module {...dynamic} />;
+const template8 = <module {...dynamic}>Hello</module>;
+const template9 = <module {...dynamic}>{dynamic.children}</module>;
+const template10 = <Module {...dynamic}>Hello</Module>;
+const template11 = <module children={/*@once*/ state.children} />;
+const template12 = <Module children={/*@once*/ state.children} />;
+const template13 = <module>{...children}</module>;
+const template14 = <Module>{...children}</Module>;
+const template15 = <module>{...dynamic.children}</module>;
+const template16 = <Module>{...dynamic.children}</Module>;
+const template18 = <module>Hi {...children}</module>;
+const template19 = <Module>Hi {...children}</Module>;
+const template20 = <module>{children()}</module>;
+const template21 = <Module>{children()}</Module>;
+const template22 = <module>{state.children()}</module>;
+const template23 = <Module>{state.children()}</Module>;
+
+const tiles = [];
+tiles.push(<div>Test 1</div>);
+const template24 = <div>{tiles}</div>;
+
+const comma = <div>{(expression(), "static")}</div>;
diff --git a/test/bun.js/solid-dom-fixtures/insertChildren/output.js b/test/bun.js/solid-dom-fixtures/insertChildren/output.js
new file mode 100644
index 000000000..9ad937742
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/insertChildren/output.js
@@ -0,0 +1,185 @@
+import { template as _$template } from "r-dom";
+import { mergeProps as _$mergeProps } from "r-dom";
+import { spread as _$spread } from "r-dom";
+import { insert as _$insert } from "r-dom";
+import { createComponent as _$createComponent } from "r-dom";
+
+const _tmpl$ = /*#__PURE__*/ _$template(`<div></div>`, 2),
+ _tmpl$2 = /*#__PURE__*/ _$template(`<module></module>`, 2),
+ _tmpl$3 = /*#__PURE__*/ _$template(`<module>Hello</module>`, 2),
+ _tmpl$4 = /*#__PURE__*/ _$template(`<module>Hi </module>`, 2),
+ _tmpl$5 = /*#__PURE__*/ _$template(`<div>Test 1</div>`, 2);
+
+const children = _tmpl$.cloneNode(true);
+
+const dynamic = {
+ children,
+};
+
+const template = _$createComponent(Module, {
+ children: children,
+});
+
+const template2 = (() => {
+ const _el$2 = _tmpl$2.cloneNode(true);
+
+ _$insert(_el$2, children);
+
+ return _el$2;
+})();
+
+const template3 = _tmpl$3.cloneNode(true);
+
+const template4 = (() => {
+ const _el$4 = _tmpl$2.cloneNode(true);
+
+ _$insert(_el$4, _$createComponent(Hello, {}));
+
+ return _el$4;
+})();
+
+const template5 = (() => {
+ const _el$5 = _tmpl$2.cloneNode(true);
+
+ _$insert(_el$5, () => dynamic.children);
+
+ return _el$5;
+})();
+
+const template6 = _$createComponent(Module, {
+ get children() {
+ return dynamic.children;
+ },
+});
+
+const template7 = (() => {
+ const _el$6 = _tmpl$2.cloneNode(true);
+
+ _$spread(_el$6, dynamic, false, false);
+
+ return _el$6;
+})();
+
+const template8 = (() => {
+ const _el$7 = _tmpl$3.cloneNode(true);
+
+ _$spread(_el$7, dynamic, false, true);
+
+ return _el$7;
+})();
+
+const template9 = (() => {
+ const _el$8 = _tmpl$2.cloneNode(true);
+
+ _$spread(_el$8, dynamic, false, true);
+
+ _$insert(_el$8, () => dynamic.children);
+
+ return _el$8;
+})();
+
+const template10 = _$createComponent(
+ Module,
+ _$mergeProps(dynamic, {
+ children: "Hello",
+ })
+);
+
+const template11 = (() => {
+ const _el$9 = _tmpl$2.cloneNode(true);
+
+ _$insert(_el$9, state.children);
+
+ return _el$9;
+})();
+
+const template12 = _$createComponent(Module, {
+ children: state.children,
+});
+
+const template13 = (() => {
+ const _el$10 = _tmpl$2.cloneNode(true);
+
+ _$insert(_el$10, children);
+
+ return _el$10;
+})();
+
+const template14 = _$createComponent(Module, {
+ children: children,
+});
+
+const template15 = (() => {
+ const _el$11 = _tmpl$2.cloneNode(true);
+
+ _$insert(_el$11, () => dynamic.children);
+
+ return _el$11;
+})();
+
+const template16 = _$createComponent(Module, {
+ get children() {
+ return dynamic.children;
+ },
+});
+
+const template18 = (() => {
+ const _el$12 = _tmpl$4.cloneNode(true);
+
+ _$insert(_el$12, children, null);
+
+ return _el$12;
+})();
+
+const template19 = _$createComponent(Module, {
+ get children() {
+ return ["Hi ", children];
+ },
+});
+
+const template20 = (() => {
+ const _el$13 = _tmpl$2.cloneNode(true);
+
+ _$insert(_el$13, children);
+
+ return _el$13;
+})();
+
+const template21 = _$createComponent(Module, {
+ get children() {
+ return children();
+ },
+});
+
+const template22 = (() => {
+ const _el$14 = _tmpl$2.cloneNode(true);
+
+ _$insert(_el$14, () => state.children());
+
+ return _el$14;
+})();
+
+const template23 = _$createComponent(Module, {
+ get children() {
+ return state.children();
+ },
+});
+
+const tiles = [];
+tiles.push(_tmpl$5.cloneNode(true));
+
+const template24 = (() => {
+ const _el$16 = _tmpl$.cloneNode(true);
+
+ _$insert(_el$16, tiles);
+
+ return _el$16;
+})();
+
+const comma = (() => {
+ const _el$17 = _tmpl$.cloneNode(true);
+
+ _$insert(_el$17, () => (expression(), "static"));
+
+ return _el$17;
+})();
diff --git a/test/bun.js/solid-dom-fixtures/namespaceElements/code.js b/test/bun.js/solid-dom-fixtures/namespaceElements/code.js
new file mode 100644
index 000000000..7ad410329
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/namespaceElements/code.js
@@ -0,0 +1,6 @@
+const template = <module.A />;
+const template2 = <module.a.B />;
+const template3 = <module.A.B />;
+const template4 = <module.a-b />;
+const template5 = <module.a-b.c-d />;
+const template6 = <namespace:tag />;
diff --git a/test/bun.js/solid-dom-fixtures/namespaceElements/output.js b/test/bun.js/solid-dom-fixtures/namespaceElements/output.js
new file mode 100644
index 000000000..162ffb140
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/namespaceElements/output.js
@@ -0,0 +1,16 @@
+import { template as _$template } from "r-dom";
+import { createComponent as _$createComponent } from "r-dom";
+
+const _tmpl$ = /*#__PURE__*/ _$template(`<namespace:tag></namespace:tag>`, 2);
+
+const template = _$createComponent(module.A, {});
+
+const template2 = _$createComponent(module.a.B, {});
+
+const template3 = _$createComponent(module.A.B, {});
+
+const template4 = _$createComponent(module["a-b"], {});
+
+const template5 = _$createComponent(module["a-b"]["c-d"], {});
+
+const template6 = _tmpl$.cloneNode(true);
diff --git a/test/bun.js/solid-dom-fixtures/simpleElements/code.js b/test/bun.js/solid-dom-fixtures/simpleElements/code.js
new file mode 100644
index 000000000..c3537ee7d
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/simpleElements/code.js
@@ -0,0 +1,9 @@
+const template = (
+ <div id="main">
+ <style>{"div { color: red; }"}</style>
+ <h1>Welcome</h1>
+ <label for={"entry"}>Edit:</label>
+ <input id="entry" type="text" />
+ {/* Comment Node */}
+ </div>
+);
diff --git a/test/bun.js/solid-dom-fixtures/simpleElements/output.bun.js b/test/bun.js/solid-dom-fixtures/simpleElements/output.bun.js
new file mode 100644
index 000000000..72d61c1e3
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/simpleElements/output.bun.js
@@ -0,0 +1,5 @@
+var _tmpl$1 = _template$(
+ '<div id="main"><style>div { color: red; }</style><h1>Welcome</h1><label for="entry">Edit:</label><input id="entry" type="text"/></div>',
+ 8
+);
+const template = _tmpl$1.cloneNode(true);
diff --git a/test/bun.js/solid-dom-fixtures/simpleElements/output.js b/test/bun.js/solid-dom-fixtures/simpleElements/output.js
new file mode 100644
index 000000000..5d16f6767
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/simpleElements/output.js
@@ -0,0 +1,8 @@
+import { template as _$template } from "r-dom";
+
+const _tmpl$ = /*#__PURE__*/ _$template(
+ `<div id="main"><style>div { color: red; }</style><h1>Welcome</h1><label for="entry">Edit:</label><input id="entry" type="text"></div>`,
+ 9
+);
+
+const template = _tmpl$.cloneNode(true);
diff --git a/test/bun.js/solid-dom-fixtures/textInterpolation/code.js b/test/bun.js/solid-dom-fixtures/textInterpolation/code.js
new file mode 100644
index 000000000..21698ea89
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/textInterpolation/code.js
@@ -0,0 +1,72 @@
+const trailing = <span>Hello </span>;
+const leading = <span> John</span>;
+
+/* prettier-ignore */
+const extraSpaces = <span>Hello John</span>;
+
+const trailingExpr = <span>Hello {name}</span>;
+const leadingExpr = <span>{greeting} John</span>;
+
+/* prettier-ignore */
+const multiExpr = <span>{greeting} {name}</span>;
+
+/* prettier-ignore */
+const multiExprSpaced = <span> {greeting} {name} </span>;
+
+/* prettier-ignore */
+const multiExprTogether = <span> {greeting}{name} </span>;
+
+/* prettier-ignore */
+const multiLine = <span>
+
+ Hello
+
+</span>
+
+/* prettier-ignore */
+const multiLineTrailingSpace = <span>
+ Hello
+ John
+</span>
+
+/* prettier-ignore */
+const multiLineNoTrailingSpace = <span>
+ Hello
+ John
+</span>
+
+/* prettier-ignore */
+const escape = <span>
+ &nbsp;&lt;Hi&gt;&nbsp;
+</span>
+
+/* prettier-ignore */
+const escape2 = <Comp>
+ &nbsp;&lt;Hi&gt;&nbsp;
+</Comp>
+
+/* prettier-ignore */
+const escape3 = <>
+ &nbsp;&lt;Hi&gt;&nbsp;
+</>
+
+const injection = <span>Hi{"<script>alert();</script>"}</span>;
+
+let value = "World";
+const evaluated = <span>Hello {value + "!"}</span>;
+
+let number = 4 + 5;
+const evaluatedNonString = <span>4 + 5 = {number}</span>;
+
+const newLineLiteral = (
+ <div>
+ {s}
+ {"\n"}d
+ </div>
+);
+
+const trailingSpace = <div>{expr}</div>;
+
+const trailingSpaceComp = <Comp>{expr}</Comp>;
+
+const trailingSpaceFrag = <>{expr}</>;
diff --git a/test/bun.js/solid-dom-fixtures/textInterpolation/output.bun.js b/test/bun.js/solid-dom-fixtures/textInterpolation/output.bun.js
new file mode 100644
index 000000000..eb4c5347a
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/textInterpolation/output.bun.js
@@ -0,0 +1,71 @@
+var _tmpl$1 = template("<span>Hello </span>", 2), _tmpl$2 = template("<span> John</span>", 2), _tmpl$3 = template("<span>Hello John</span>", 2), _tmpl$3 = template("<span> </span>", 4), _tmpl$4 = template("<span> </span>", 4), _tmpl$5 = template("<span> </span>", 4), _tmpl$7 = template("<span>Hello</span>", 2), _tmpl$8 = template("<span>Hello John</span>", 2), _tmpl$9 = template("<span> &lt;Hi&gt; </span>", 2), _tmpl$10 = template("<span>Hi&lt;script&gt;alert();&lt;/script&gt;</span>", 2), _tmpl$10 = template("<span>4 + 5 = </span>", 3), _tmpl$11 = template("<div>\nd</div>", 3), _tmpl$12 = template("<div</div>", 2);
+const trailing = _tmpl$1.cloneNode(true);
+const leading = _tmpl$2.cloneNode(true);
+const extraSpaces = _tmpl$3.cloneNode(true);
+const trailingExpr = () => {
+ var _tmpl$1 = _tmpl$1.cloneNode(true);
+ insert(_tmpl$1, name, null);
+ return _tmpl$1;
+};
+const leadingExpr = () => {
+ var _tmpl$2 = _tmpl$2.cloneNode(true);
+ insert(_tmpl$2, greeting, null);
+ return _tmpl$2;
+};
+const multiExpr = () => {
+ var _tmpl$3 = _tmpl$3.cloneNode(true);
+ insert(_tmpl$3, greeting, null);
+ insert(_tmpl$3, name, null);
+ return _tmpl$3;
+};
+const multiExprSpaced = () => {
+ var _tmpl$4 = _tmpl$4.cloneNode(true);
+ insert(_tmpl$4, greeting, null);
+ insert(_tmpl$4, name, null);
+ return _tmpl$4;
+};
+const multiExprTogether = () => {
+ var _tmpl$5 = _tmpl$5.cloneNode(true);
+ insert(_tmpl$5, greeting, null);
+ insert(_tmpl$5, name, null);
+ return _tmpl$5;
+};
+const multiLine = _tmpl$7.cloneNode(true);
+const multiLineTrailingSpace = _tmpl$8.cloneNode(true);
+const multiLineNoTrailingSpace = _tmpl$8.cloneNode(true);
+const escape = _tmpl$9.cloneNode(true);
+const escape2 = createComponent(Comp, {
+ get children: [
+ "\xA0<Hi>\xA0"
+ ]
+});
+const escape3 = ;
+const injection = _tmpl$10.cloneNode(true);
+let value = "World";
+const evaluated = () => {
+ var _tmpl$1 = _tmpl$1.cloneNode(true);
+ insert(_tmpl$1, value + "!", null);
+ return _tmpl$1;
+};
+let number = 4 + 5;
+const evaluatedNonString = () => {
+ var _tmpl$10 = _tmpl$10.cloneNode(true);
+ insert(_tmpl$10, number, null);
+ return _tmpl$10;
+};
+const newLineLiteral = () => {
+ var _tmpl$11 = _tmpl$11.cloneNode(true);
+ insert(_tmpl$11, s, null);
+ return _tmpl$11;
+};
+const trailingSpace = () => {
+ var _tmpl$12 = _tmpl$12.cloneNode(true);
+ insert(_tmpl$12, expr, null);
+ return _tmpl$12;
+};
+const trailingSpaceComp = createComponent(Comp, {
+ get children: [
+ expr
+ ]
+});
+const trailingSpaceFrag = ;
diff --git a/test/bun.js/solid-dom-fixtures/textInterpolation/output.js b/test/bun.js/solid-dom-fixtures/textInterpolation/output.js
new file mode 100644
index 000000000..b86a631fb
--- /dev/null
+++ b/test/bun.js/solid-dom-fixtures/textInterpolation/output.js
@@ -0,0 +1,144 @@
+import { template as _$template } from "r-dom";
+import { createComponent as _$createComponent } from "r-dom";
+import { insert as _$insert } from "r-dom";
+
+const _tmpl$ = /*#__PURE__*/ _$template(`<span>Hello </span>`, 2),
+ _tmpl$2 = /*#__PURE__*/ _$template(`<span> John</span>`, 2),
+ _tmpl$3 = /*#__PURE__*/ _$template(`<span>Hello John</span>`, 2),
+ _tmpl$4 = /*#__PURE__*/ _$template(`<span> </span>`, 2),
+ _tmpl$5 = /*#__PURE__*/ _$template(`<span> <!> <!> </span>`, 4),
+ _tmpl$6 = /*#__PURE__*/ _$template(`<span> <!> </span>`, 3),
+ _tmpl$7 = /*#__PURE__*/ _$template(`<span>Hello</span>`, 2),
+ _tmpl$8 = /*#__PURE__*/ _$template(`<span>&nbsp;&lt;Hi&gt;&nbsp;</span>`, 2),
+ _tmpl$9 = /*#__PURE__*/ _$template(
+ `<span>Hi&lt;script>alert();&lt;/script></span>`,
+ 2
+ ),
+ _tmpl$10 = /*#__PURE__*/ _$template(`<span>Hello World!</span>`, 2),
+ _tmpl$11 = /*#__PURE__*/ _$template(`<span>4 + 5 = 9</span>`, 2),
+ _tmpl$12 = /*#__PURE__*/ _$template(
+ `<div>
+d</div>`,
+ 2
+ ),
+ _tmpl$13 = /*#__PURE__*/ _$template(`<div></div>`, 2);
+
+const trailing = _tmpl$.cloneNode(true);
+
+const leading = _tmpl$2.cloneNode(true);
+/* prettier-ignore */
+
+const extraSpaces = _tmpl$3.cloneNode(true);
+
+const trailingExpr = (() => {
+ const _el$4 = _tmpl$.cloneNode(true),
+ _el$5 = _el$4.firstChild;
+
+ _$insert(_el$4, name, null);
+
+ return _el$4;
+})();
+
+const leadingExpr = (() => {
+ const _el$6 = _tmpl$2.cloneNode(true),
+ _el$7 = _el$6.firstChild;
+
+ _$insert(_el$6, greeting, _el$7);
+
+ return _el$6;
+})();
+/* prettier-ignore */
+
+const multiExpr = (() => {
+ const _el$8 = _tmpl$4.cloneNode(true),
+ _el$9 = _el$8.firstChild;
+
+ _$insert(_el$8, greeting, _el$9);
+
+ _$insert(_el$8, name, null);
+
+ return _el$8;
+})();
+/* prettier-ignore */
+
+const multiExprSpaced = (() => {
+ const _el$10 = _tmpl$5.cloneNode(true),
+ _el$11 = _el$10.firstChild,
+ _el$14 = _el$11.nextSibling,
+ _el$12 = _el$14.nextSibling,
+ _el$15 = _el$12.nextSibling,
+ _el$13 = _el$15.nextSibling;
+
+ _$insert(_el$10, greeting, _el$14);
+
+ _$insert(_el$10, name, _el$15);
+
+ return _el$10;
+})();
+/* prettier-ignore */
+
+const multiExprTogether = (() => {
+ const _el$16 = _tmpl$6.cloneNode(true),
+ _el$17 = _el$16.firstChild,
+ _el$19 = _el$17.nextSibling,
+ _el$18 = _el$19.nextSibling;
+
+ _$insert(_el$16, greeting, _el$19);
+
+ _$insert(_el$16, name, _el$19);
+
+ return _el$16;
+})();
+/* prettier-ignore */
+
+const multiLine = _tmpl$7.cloneNode(true);
+/* prettier-ignore */
+
+const multiLineTrailingSpace = _tmpl$3.cloneNode(true);
+/* prettier-ignore */
+
+const multiLineNoTrailingSpace = _tmpl$3.cloneNode(true);
+/* prettier-ignore */
+
+const escape = _tmpl$8.cloneNode(true);
+/* prettier-ignore */
+
+const escape2 = _$createComponent(Comp, {
+ children: "\xA0<Hi>\xA0"
+});
+/* prettier-ignore */
+
+const escape3 = "\xA0<Hi>\xA0";
+
+const injection = _tmpl$9.cloneNode(true);
+
+let value = "World";
+
+const evaluated = _tmpl$10.cloneNode(true);
+
+let number = 4 + 5;
+
+const evaluatedNonString = _tmpl$11.cloneNode(true);
+
+const newLineLiteral = (() => {
+ const _el$27 = _tmpl$12.cloneNode(true),
+ _el$28 = _el$27.firstChild;
+
+ _$insert(_el$27, s, _el$28);
+
+ return _el$27;
+})();
+
+const trailingSpace = (() => {
+ const _el$29 = _tmpl$13.cloneNode(true);
+
+ _$insert(_el$29, expr);
+
+ return _el$29;
+})();
+
+const trailingSpaceComp = _$createComponent(Comp, {
+ children: expr,
+});
+
+const trailingSpaceFrag = expr;
diff --git a/test/bun.js/some-fs.js b/test/bun.js/some-fs.js
new file mode 100644
index 000000000..e6b31f162
--- /dev/null
+++ b/test/bun.js/some-fs.js
@@ -0,0 +1,51 @@
+const { mkdirSync, existsSync } = require("fs");
+
+var performance = globalThis.performance;
+if (!performance) {
+ try {
+ performance = require("perf_hooks").performance;
+ } catch (e) {}
+}
+
+const count = parseInt(process.env.ITERATIONS || "1", 10) || 1;
+var tempdir = `/tmp/some-fs-test/dir/${Date.now()}/hi`;
+
+for (let i = 0; i < count; i++) {
+ tempdir += `/${i.toString(36)}`;
+}
+
+if (existsSync(tempdir)) {
+ throw new Error(
+ `existsSync reports ${tempdir} exists, but it probably does not`
+ );
+}
+
+var origTempDir = tempdir;
+var iterations = new Array(count * count).fill("");
+var total = 0;
+for (let i = 0; i < count; i++) {
+ for (let j = 0; j < count; j++) {
+ iterations[total++] = `${origTempDir}/${j.toString(36)}-${i.toString(36)}`;
+ }
+}
+tempdir = origTempDir;
+mkdirSync(origTempDir, { recursive: true });
+const recurse = { recursive: false };
+const start = performance.now();
+for (let i = 0; i < total; i++) {
+ mkdirSync(iterations[i], recurse);
+}
+
+console.log("MKDIR " + total + " depth took:", performance.now() - start, "ms");
+
+if (!existsSync(tempdir)) {
+ throw new Error(
+ "Expected directory to exist after mkdirSync, but it doesn't"
+ );
+}
+
+if (mkdirSync(tempdir, { recursive: true })) {
+ throw new Error(
+ "mkdirSync shouldn't return directory name on existing directories"
+ );
+}
diff --git a/test/bun.js/sql-raw.test.js b/test/bun.js/sql-raw.test.js
new file mode 100644
index 000000000..ea7f72bd6
--- /dev/null
+++ b/test/bun.js/sql-raw.test.js
@@ -0,0 +1,71 @@
+import { expect, it } from "bun:test";
+
+var SQL = globalThis[Symbol.for("Bun.lazy")]("sqlite");
+
+it("works", () => {
+ const handle = SQL.open("/tmp/northwind.sqlite");
+
+ const stmt = SQL.prepare(
+ handle,
+ 'SELECT * FROM "Order" WHERE OrderDate > date($date)'
+ );
+ expect(stmt.toString()).toBe(
+ `SELECT * FROM "Order" WHERE OrderDate > date(NULL)`
+ );
+
+ expect(
+ Array.isArray(
+ stmt.all({
+ // do the conversion this way so that this test runs in multiple timezones
+ $date: new Date(
+ new Date(1996, 8, 1, 0, 0, 0, 0).toUTCString()
+ ).toISOString(),
+ })
+ )
+ ).toBe(true);
+ expect(stmt.toString()).toBe(
+ `SELECT * FROM "Order" WHERE OrderDate > date('1996-09-01T07:00:00.000Z')`
+ );
+
+ var ran = stmt.run({
+ $date: new Date(
+ new Date(1997, 8, 1, 0, 0, 0, 0).toUTCString()
+ ).toISOString(),
+ });
+ expect(Array.isArray(ran)).toBe(false);
+ expect(ran === undefined).toBe(true);
+ expect(stmt.toString()).toBe(
+ `SELECT * FROM "Order" WHERE OrderDate > date('1997-09-01T07:00:00.000Z')`
+ );
+
+ expect(
+ Array.isArray(
+ stmt.get({
+ $date: new Date(
+ new Date(1998, 8, 1, 0, 0, 0, 0).toUTCString()
+ ).toISOString(),
+ })
+ )
+ ).toBe(false);
+ expect(stmt.toString()).toBe(
+ `SELECT * FROM "Order" WHERE OrderDate > date('1998-09-01T07:00:00.000Z')`
+ );
+ expect(stmt.paramsCount).toBe(1);
+ expect(stmt.columnsCount).toBe(14);
+ expect(stmt.columns.length).toBe(14);
+ stmt.finalize();
+ SQL.close(handle);
+});
+
+it("SQL.run works", () => {
+ const handle = SQL.open("/tmp/northwind.sqlite");
+ expect(typeof handle).toBe("number");
+
+ expect(
+ SQL.run(handle, 'SELECT * FROM "Order" WHERE OrderDate > date($date)', {
+ $date: new Date(1996, 8, 1).toISOString(),
+ })
+ ).toBe(undefined);
+
+ SQL.close(handle);
+});
diff --git a/test/bun.js/sqlite.test.js b/test/bun.js/sqlite.test.js
new file mode 100644
index 000000000..2250f97f0
--- /dev/null
+++ b/test/bun.js/sqlite.test.js
@@ -0,0 +1,430 @@
+import { expect, it } from "bun:test";
+import { Database, constants } from "bun:sqlite";
+
+var encode = (text) => Buffer.from(text);
+
+it("Database.open", () => {
+ // in a folder which doesn't exist
+ try {
+ Database.open(
+ "/this/database/does/not/exist.sqlite",
+ constants.SQLITE_OPEN_READWRITE
+ );
+ throw new Error("Expected an error to be thrown");
+ } catch (error) {
+ expect(error.message).toBe("unable to open database file");
+ }
+
+ // in a file which doesn't exist
+ try {
+ Database.open(
+ `/tmp/database-${Math.random()}.sqlite`,
+ constants.SQLITE_OPEN_READWRITE
+ );
+ throw new Error("Expected an error to be thrown");
+ } catch (error) {
+ expect(error.message).toBe("unable to open database file");
+ }
+
+ // in a file which doesn't exist
+ try {
+ Database.open(`/tmp/database-${Math.random()}.sqlite`, { readonly: true });
+ throw new Error("Expected an error to be thrown");
+ } catch (error) {
+ expect(error.message).toBe("unable to open database file");
+ }
+
+ // in a file which doesn't exist
+ try {
+ Database.open(`/tmp/database-${Math.random()}.sqlite`, { readwrite: true });
+ throw new Error("Expected an error to be thrown");
+ } catch (error) {
+ expect(error.message).toBe("unable to open database file");
+ }
+
+ // create works
+ {
+ var db = Database.open(`/tmp/database-${Math.random()}.sqlite`, {
+ create: true,
+ });
+ db.close();
+ }
+
+ // this should not throw
+ // it creates an in-memory db
+ new Database().close();
+});
+
+it("creates", () => {
+ const db = Database.open(":memory:");
+ db.exec(
+ "CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT, value INTEGER, created TEXT, deci FLOAT, blobby BLOB)"
+ );
+ const stmt = db.prepare(
+ "INSERT INTO test (name, value, deci, created, blobby) VALUES (?, ?, ?, ?, ?)"
+ );
+
+ stmt.run([
+ "foo",
+ 1,
+ Math.fround(1.111),
+ new Date(1995, 12, 19).toISOString(),
+ encode("Hello World"),
+ ]);
+ stmt.run([
+ "bar",
+ 2,
+ Math.fround(2.222),
+ new Date(1995, 12, 19).toISOString(),
+ encode("Hello World"),
+ ]);
+ stmt.run([
+ "baz",
+ 3,
+ Math.fround(3.333),
+ new Date(1995, 12, 19).toISOString(),
+ encode("Hello World"),
+ ]);
+
+ stmt.finalize();
+
+ const stmt2 = db.prepare("SELECT * FROM test");
+ expect(JSON.stringify(stmt2.get())).toBe(
+ JSON.stringify({
+ id: 1,
+ name: "foo",
+ value: 1,
+ created: new Date(1995, 12, 19).toISOString(),
+ deci: Math.fround(1.111),
+ blobby: encode("Hello World"),
+ })
+ );
+
+ expect(JSON.stringify(stmt2.all())).toBe(
+ JSON.stringify([
+ {
+ id: 1,
+ name: "foo",
+ value: 1,
+ created: new Date(1995, 12, 19).toISOString(),
+ deci: Math.fround(1.111),
+ blobby: encode("Hello World"),
+ },
+ {
+ id: 2,
+ name: "bar",
+ value: 2,
+ created: new Date(1995, 12, 19).toISOString(),
+ deci: Math.fround(2.222),
+ blobby: encode("Hello World"),
+ },
+ {
+ id: 3,
+ name: "baz",
+ value: 3,
+ created: new Date(1995, 12, 19).toISOString(),
+ deci: Math.fround(3.333),
+ blobby: encode("Hello World"),
+ },
+ ])
+ );
+ expect(stmt2.run()).toBe(undefined);
+
+ // not necessary to run but it's a good practice
+ stmt2.finalize();
+});
+
+it("typechecks", () => {
+ const db = Database.open(":memory:");
+ db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)");
+ db.exec('INSERT INTO test (name) VALUES ("Hello")');
+ db.exec('INSERT INTO test (name) VALUES ("World")');
+
+ const q = db.prepare("SELECT * FROM test WHERE (name = ?)");
+
+ var expectfail = (val) => {
+ try {
+ q.run([val]);
+ throw new Error("Expected error");
+ } catch (e) {
+ expect(e.message !== "Expected error").toBe(true);
+ expect(e.name).toBe("TypeError");
+ }
+
+ try {
+ q.all([val]);
+ throw new Error("Expected error");
+ } catch (e) {
+ expect(e.message !== "Expected error").toBe(true);
+ expect(e.name).toBe("TypeError");
+ }
+
+ try {
+ q.get([val]);
+ throw new Error("Expected error");
+ } catch (e) {
+ expect(e.message !== "Expected error").toBe(true);
+ expect(e.name).toBe("TypeError");
+ }
+ };
+
+ expectfail(Symbol("oh hai"));
+ expectfail(new Date());
+ expectfail(class Foo {});
+ expectfail(() => class Foo {});
+ expectfail(new RangeError("what"));
+ expectfail(new Map());
+ expectfail(new Map([["foo", "bar"]]));
+ expectfail(new Set());
+ expectfail(new Set([1, 2, 3]));
+});
+
+it("db.query supports TypedArray", () => {
+ const db = Database.open(":memory:");
+
+ db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, blobby BLOB)");
+
+ const stmt = db.prepare("INSERT INTO test (blobby) VALUES (?)");
+ stmt.run([encode("Hello World")]);
+ stmt.finalize();
+
+ const stmt2 = db.prepare("SELECT * FROM test");
+ expect(JSON.stringify(stmt2.get())).toBe(
+ JSON.stringify({
+ id: 1,
+ blobby: encode("Hello World"),
+ })
+ );
+
+ const stmt3 = db.prepare("SELECT * FROM test WHERE (blobby = ?)");
+
+ expect(JSON.stringify(stmt3.get([encode("Hello World")]))).toBe(
+ JSON.stringify({
+ id: 1,
+ blobby: encode("Hello World"),
+ })
+ );
+
+ expect(
+ JSON.stringify(
+ db
+ .query("SELECT * FROM test WHERE (blobby = ?)")
+ .get([encode("Hello World")])
+ )
+ ).toBe(
+ JSON.stringify({
+ id: 1,
+ blobby: encode("Hello World"),
+ })
+ );
+
+ expect(stmt3.get([encode("Hello World NOT")])).toBe(null);
+});
+
+it("supports serialize/deserialize", () => {
+ const db = Database.open(":memory:");
+ db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)");
+ db.exec('INSERT INTO test (name) VALUES ("Hello")');
+ db.exec('INSERT INTO test (name) VALUES ("World")');
+
+ const input = db.serialize();
+ const db2 = new Database(input);
+
+ const stmt = db2.prepare("SELECT * FROM test");
+ expect(JSON.stringify(stmt.get())).toBe(
+ JSON.stringify({
+ id: 1,
+ name: "Hello",
+ })
+ );
+
+ expect(JSON.stringify(stmt.all())).toBe(
+ JSON.stringify([
+ {
+ id: 1,
+ name: "Hello",
+ },
+ {
+ id: 2,
+ name: "World",
+ },
+ ])
+ );
+ db2.exec("insert into test (name) values ('foo')");
+ expect(JSON.stringify(stmt.all())).toBe(
+ JSON.stringify([
+ {
+ id: 1,
+ name: "Hello",
+ },
+ {
+ id: 2,
+ name: "World",
+ },
+ {
+ id: 3,
+ name: "foo",
+ },
+ ])
+ );
+
+ const db3 = new Database(input, { readonly: true });
+ try {
+ db3.exec("insert into test (name) values ('foo')");
+ throw new Error("Expected error");
+ } catch (e) {
+ expect(e.message).toBe("attempt to write a readonly database");
+ }
+});
+
+it("db.query()", () => {
+ const db = Database.open(":memory:");
+ db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)");
+
+ expect(db[Symbol.for("Bun.Database.cache.count")]).toBe(0);
+
+ var q = db.query("SELECT * FROM test WHERE name = ?");
+ expect(q.get("Hello") === null).toBe(true);
+
+ db.exec('INSERT INTO test (name) VALUES ("Hello")');
+ db.exec('INSERT INTO test (name) VALUES ("World")');
+
+ var rows = db.query("SELECT * FROM test WHERE name = ?").all(["Hello"]);
+
+ expect(JSON.stringify(rows)).toBe(JSON.stringify([{ id: 1, name: "Hello" }]));
+
+ rows = db.query("SELECT * FROM test WHERE name = ?").all(["World"]);
+
+ // if this fails, it means the query caching failed to update
+ expect(JSON.stringify(rows)).toBe(JSON.stringify([{ id: 2, name: "World" }]));
+
+ rows = db.query("SELECT * FROM test WHERE name = ?").all(["Hello"]);
+ expect(JSON.stringify(rows)).toBe(JSON.stringify([{ id: 1, name: "Hello" }]));
+
+ // check that the query is cached
+ expect(db[Symbol.for("Bun.Database.cache.count")]).toBe(1);
+
+ db.clearQueryCache();
+
+ // check clearing the cache decremented the counter
+ expect(db[Symbol.for("Bun.Database.cache.count")]).toBe(0);
+
+ q.finalize();
+ try {
+ // check clearing the cache decremented the counter
+
+ q.all(["Hello"]);
+ throw new Error("Should have thrown");
+ } catch (e) {
+ expect(e.message !== "Should have thrown").toBe(true);
+ }
+
+ // check that invalid queries are not cached
+ // and invalid queries throw
+ try {
+ db.query("SELECT * FROM BACON", ["Hello"]).all();
+ throw new Error("Should have thrown");
+ } catch (e) {
+ expect(e.message !== "Should have thrown").toBe(true);
+ expect(db[Symbol.for("Bun.Database.cache.count")]).toBe(0);
+ }
+
+ // check that it supports multiple arguments
+ expect(
+ JSON.stringify(
+ db
+ .query("SELECT * FROM test where (name = ? OR name = ?)")
+ .all(["Hello", "Fooooo"])
+ )
+ ).toBe(JSON.stringify([{ id: 1, name: "Hello" }]));
+ expect(
+ JSON.stringify(
+ db
+ .query("SELECT * FROM test where (name = ? OR name = ?)")
+ .all("Hello", "Fooooo")
+ )
+ ).toBe(JSON.stringify([{ id: 1, name: "Hello" }]));
+
+ // throws if insufficeint arguments
+ try {
+ db.query("SELECT * FROM test where (name = ? OR name = ?)").all("Hello");
+ } catch (e) {
+ expect(e.message).toBe("Expected 2 values, got 1");
+ }
+
+ // named parameters
+ expect(
+ JSON.stringify(
+ db
+ .query("SELECT * FROM test where (name = $hello OR name = $goodbye)")
+ .all({
+ $hello: "Hello",
+ $goodbye: "Fooooo",
+ })
+ )
+ ).toBe(JSON.stringify([{ id: 1, name: "Hello" }]));
+
+ db.close();
+
+ // Check that a closed database doesn't crash
+ // and does throw an error when trying to run a query
+ try {
+ db.query("SELECT * FROM test WHERE name = ?").all(["Hello"]);
+ throw new Error("Should have thrown");
+ } catch (e) {
+ expect(e.message !== "Should have thrown").toBe(true);
+ }
+
+ // check that we can call close multiple times
+ // it should not throw so that your code doesn't break
+ db.close();
+ db.close();
+ db.close();
+});
+
+it("db.transaction()", () => {
+ const db = Database.open(":memory:");
+
+ db.exec(
+ "CREATE TABLE cats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER)"
+ );
+
+ const insert = db.prepare(
+ "INSERT INTO cats (name, age) VALUES (@name, @age)"
+ );
+
+ expect(db.inTransaction).toBe(false);
+ const insertMany = db.transaction((cats) => {
+ expect(db.inTransaction).toBe(true);
+ try {
+ for (const cat of cats) insert.run(cat);
+ } catch (exception) {
+ throw exception;
+ }
+ });
+
+ try {
+ insertMany([
+ { "@name": "Joey", "@age": 2 },
+ { "@name": "Sally", "@age": 4 },
+ { "@name": "Junior", "@age": 1 },
+ { "@name": "Sally", "@age": 4 },
+ ]);
+ throw new Error("Should have thrown");
+ } catch (exception) {
+ expect(exception.message).toBe("constraint failed");
+ }
+
+ expect(db.inTransaction).toBe(false);
+ expect(db.query("SELECT * FROM cats").all().length).toBe(0);
+
+ expect(db.inTransaction).toBe(false);
+ insertMany([
+ { "@name": "Joey", "@age": 2 },
+ { "@name": "Sally", "@age": 4 },
+ { "@name": "Junior", "@age": 1 },
+ ]);
+ expect(db.inTransaction).toBe(false);
+ expect(db.query("SELECT * FROM cats").all().length).toBe(3);
+ expect(db.inTransaction).toBe(false);
+});
diff --git a/test/bun.js/streams.test.js b/test/bun.js/streams.test.js
new file mode 100644
index 000000000..ccbea1d09
--- /dev/null
+++ b/test/bun.js/streams.test.js
@@ -0,0 +1,317 @@
+import { file, readableStreamToArrayBuffer, readableStreamToArray } from "bun";
+import { expect, it, beforeEach, afterEach } from "bun:test";
+import { writeFileSync } from "node:fs";
+import { gc } from "./gc";
+new Uint8Array();
+
+beforeEach(() => gc());
+afterEach(() => gc());
+
+it("exists globally", () => {
+ expect(typeof ReadableStream).toBe("function");
+ expect(typeof ReadableStreamBYOBReader).toBe("function");
+ expect(typeof ReadableStreamBYOBRequest).toBe("function");
+ expect(typeof ReadableStreamDefaultController).toBe("function");
+ expect(typeof ReadableStreamDefaultReader).toBe("function");
+ expect(typeof TransformStream).toBe("function");
+ expect(typeof TransformStreamDefaultController).toBe("function");
+ expect(typeof WritableStream).toBe("function");
+ expect(typeof WritableStreamDefaultController).toBe("function");
+ expect(typeof WritableStreamDefaultWriter).toBe("function");
+ expect(typeof ByteLengthQueuingStrategy).toBe("function");
+ expect(typeof CountQueuingStrategy).toBe("function");
+});
+
+it("ReadableStream (direct)", async () => {
+ var stream = new ReadableStream({
+ pull(controller) {
+ controller.write("hello");
+ controller.write("world");
+ controller.close();
+ },
+ cancel() {},
+ type: "direct",
+ });
+ var reader = stream.getReader();
+ const chunk = await reader.read();
+ expect(chunk.value.join("")).toBe(Buffer.from("helloworld").join(""));
+ expect((await reader.read()).done).toBe(true);
+ expect((await reader.read()).done).toBe(true);
+});
+
+it("ReadableStream (bytes)", async () => {
+ var stream = new ReadableStream({
+ start(controller) {
+ controller.enqueue(Buffer.from("abdefgh"));
+ },
+ pull(controller) {},
+ cancel() {},
+ type: "bytes",
+ });
+ const chunks = [];
+ const chunk = await stream.getReader().read();
+ chunks.push(chunk.value);
+ expect(chunks[0].join("")).toBe(Buffer.from("abdefgh").join(""));
+});
+
+it("ReadableStream (default)", async () => {
+ var stream = new ReadableStream({
+ start(controller) {
+ controller.enqueue(Buffer.from("abdefgh"));
+ controller.close();
+ },
+ pull(controller) {},
+ cancel() {},
+ });
+ const chunks = [];
+ const chunk = await stream.getReader().read();
+ chunks.push(chunk.value);
+ expect(chunks[0].join("")).toBe(Buffer.from("abdefgh").join(""));
+});
+
+it("readableStreamToArray", async () => {
+ var queue = [Buffer.from("abdefgh")];
+ var stream = new ReadableStream({
+ pull(controller) {
+ var chunk = queue.shift();
+ if (chunk) {
+ controller.enqueue(chunk);
+ } else {
+ controller.close();
+ }
+ },
+ cancel() {},
+ type: "bytes",
+ });
+
+ const chunks = await readableStreamToArray(stream);
+
+ expect(chunks[0].join("")).toBe(Buffer.from("abdefgh").join(""));
+});
+
+it("readableStreamToArrayBuffer (bytes)", async () => {
+ var queue = [Buffer.from("abdefgh")];
+ var stream = new ReadableStream({
+ pull(controller) {
+ var chunk = queue.shift();
+ if (chunk) {
+ controller.enqueue(chunk);
+ } else {
+ controller.close();
+ }
+ },
+ cancel() {},
+ type: "bytes",
+ });
+ const buffer = await readableStreamToArrayBuffer(stream);
+ expect(new TextDecoder().decode(new Uint8Array(buffer))).toBe("abdefgh");
+});
+
+it("readableStreamToArrayBuffer (default)", async () => {
+ var queue = [Buffer.from("abdefgh")];
+ var stream = new ReadableStream({
+ pull(controller) {
+ var chunk = queue.shift();
+ if (chunk) {
+ controller.enqueue(chunk);
+ } else {
+ controller.close();
+ }
+ },
+ cancel() {},
+ });
+
+ const buffer = await readableStreamToArrayBuffer(stream);
+ expect(new TextDecoder().decode(new Uint8Array(buffer))).toBe("abdefgh");
+});
+
+it("ReadableStream for Blob", async () => {
+ var blob = new Blob(["abdefgh", "ijklmnop"]);
+ expect(await blob.text()).toBe("abdefghijklmnop");
+ var stream;
+ try {
+ stream = blob.stream();
+ stream = blob.stream();
+ } catch (e) {
+ console.error(e);
+ console.error(e.stack);
+ }
+ const chunks = [];
+ var reader;
+ reader = stream.getReader();
+
+ while (true) {
+ var chunk;
+ try {
+ chunk = await reader.read();
+ } catch (e) {
+ console.error(e);
+ console.error(e.stack);
+ }
+
+ if (chunk.done) break;
+ chunks.push(new TextDecoder().decode(chunk.value));
+ }
+ expect(chunks.join("")).toBe(
+ new TextDecoder().decode(Buffer.from("abdefghijklmnop"))
+ );
+});
+
+it("ReadableStream for File", async () => {
+ var blob = file(import.meta.dir + "/fetch.js.txt");
+ var stream = blob.stream(24);
+ const chunks = [];
+ var reader = stream.getReader();
+ stream = undefined;
+ while (true) {
+ const chunk = await reader.read();
+ gc(true);
+ if (chunk.done) break;
+ chunks.push(chunk.value);
+ expect(chunk.value.byteLength <= 24).toBe(true);
+ gc(true);
+ }
+ reader = undefined;
+ const output = new Uint8Array(await blob.arrayBuffer()).join("");
+ const input = chunks.map((a) => a.join("")).join("");
+ expect(output).toBe(input);
+ gc(true);
+});
+
+it("ReadableStream for File errors", async () => {
+ try {
+ var blob = file(import.meta.dir + "/fetch.js.txt.notfound");
+ blob.stream().getReader();
+ throw new Error("should not reach here");
+ } catch (e) {
+ expect(e.code).toBe("ENOENT");
+ expect(e.syscall).toBe("open");
+ }
+});
+
+it("ReadableStream for empty blob closes immediately", async () => {
+ var blob = new Blob([]);
+ var stream = blob.stream();
+ const chunks = [];
+ var reader = stream.getReader();
+ while (true) {
+ const chunk = await reader.read();
+ if (chunk.done) break;
+ chunks.push(chunk.value);
+ }
+
+ expect(chunks.length).toBe(0);
+});
+
+it("ReadableStream for empty file closes immediately", async () => {
+ writeFileSync("/tmp/bun-empty-file-123456", "");
+ var blob = file("/tmp/bun-empty-file-123456");
+ var stream;
+ try {
+ stream = blob.stream();
+ } catch (e) {
+ console.error(e.stack);
+ }
+ const chunks = [];
+ var reader = stream.getReader();
+ while (true) {
+ const chunk = await reader.read();
+ if (chunk.done) break;
+ chunks.push(chunk.value);
+ }
+
+ expect(chunks.length).toBe(0);
+});
+
+it("new Response(stream).arrayBuffer() (bytes)", async () => {
+ var queue = [Buffer.from("abdefgh")];
+ var stream = new ReadableStream({
+ pull(controller) {
+ var chunk = queue.shift();
+ if (chunk) {
+ controller.enqueue(chunk);
+ } else {
+ controller.close();
+ }
+ },
+ cancel() {},
+ type: "bytes",
+ });
+ const buffer = await new Response(stream).arrayBuffer();
+ expect(new TextDecoder().decode(new Uint8Array(buffer))).toBe("abdefgh");
+});
+
+it("new Response(stream).arrayBuffer() (default)", async () => {
+ var queue = [Buffer.from("abdefgh")];
+ var stream = new ReadableStream({
+ pull(controller) {
+ var chunk = queue.shift();
+ if (chunk) {
+ controller.enqueue(chunk);
+ } else {
+ controller.close();
+ }
+ },
+ cancel() {},
+ });
+ const buffer = await new Response(stream).arrayBuffer();
+ expect(new TextDecoder().decode(new Uint8Array(buffer))).toBe("abdefgh");
+});
+
+it("new Response(stream).text() (default)", async () => {
+ var queue = [Buffer.from("abdefgh")];
+ var stream = new ReadableStream({
+ pull(controller) {
+ var chunk = queue.shift();
+ if (chunk) {
+ controller.enqueue(chunk);
+ } else {
+ controller.close();
+ }
+ },
+ cancel() {},
+ });
+ const text = await new Response(stream).text();
+ expect(text).toBe("abdefgh");
+});
+
+it("new Response(stream).json() (default)", async () => {
+ var queue = [Buffer.from(JSON.stringify({ hello: true }))];
+ var stream = new ReadableStream({
+ pull(controller) {
+ var chunk = queue.shift();
+ if (chunk) {
+ controller.enqueue(chunk);
+ } else {
+ controller.close();
+ }
+ },
+ cancel() {},
+ });
+ const json = await new Response(stream).json();
+ expect(json.hello).toBe(true);
+});
+
+it("new Response(stream).blob() (default)", async () => {
+ var queue = [Buffer.from(JSON.stringify({ hello: true }))];
+ var stream = new ReadableStream({
+ pull(controller) {
+ var chunk = queue.shift();
+ if (chunk) {
+ controller.enqueue(chunk);
+ } else {
+ controller.close();
+ }
+ },
+ cancel() {},
+ });
+ const blob = await new Response(stream).blob();
+ expect(await blob.text()).toBe('{"hello":true}');
+});
+
+it("Blob.stream() -> new Response(stream).text()", async () => {
+ var blob = new Blob(["abdefgh"]);
+ var stream = blob.stream();
+ const text = await new Response(stream).text();
+ expect(text).toBe("abdefgh");
+});
diff --git a/test/bun.js/text-encoder.test.js b/test/bun.js/text-encoder.test.js
new file mode 100644
index 000000000..5687e0222
--- /dev/null
+++ b/test/bun.js/text-encoder.test.js
@@ -0,0 +1,212 @@
+import { expect, it, describe } from "bun:test";
+import { gc as gcTrace } from "./gc";
+
+const getByteLength = (str) => {
+ // returns the byte length of an utf8 string
+ var s = str.length;
+ for (var i = str.length - 1; i >= 0; i--) {
+ var code = str.charCodeAt(i);
+ if (code > 0x7f && code <= 0x7ff) s++;
+ else if (code > 0x7ff && code <= 0xffff) s += 2;
+ if (code >= 0xdc00 && code <= 0xdfff) i--; //trail surrogate
+ }
+ return s;
+};
+
+describe("TextDecoder", () => {
+ it("should decode ascii text", () => {
+ const decoder = new TextDecoder("latin1");
+ gcTrace(true);
+ expect(decoder.encoding).toBe("windows-1252");
+ gcTrace(true);
+ expect(decoder.decode(new Uint8Array([0x41, 0x42, 0x43]))).toBe("ABC");
+ gcTrace(true);
+ const result = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33];
+ gcTrace(true);
+ expect(decoder.decode(Uint8Array.from(result))).toBe(
+ String.fromCharCode(...result)
+ );
+ gcTrace(true);
+ });
+
+ it("should decode unicode text", () => {
+ const decoder = new TextDecoder();
+ gcTrace(true);
+ var text = `❤️ Red Heart`;
+
+ const bytes = [
+ 226, 157, 164, 239, 184, 143, 32, 82, 101, 100, 32, 72, 101, 97, 114, 116,
+ ];
+ const decoded = decoder.decode(Uint8Array.from(bytes));
+ expect(decoder.encoding).toBe("utf-8");
+
+ gcTrace(true);
+
+ for (let i = 0; i < text.length; i++) {
+ expect(decoded.charCodeAt(i)).toBe(text.charCodeAt(i));
+ }
+ expect(decoded).toHaveLength(text.length);
+ gcTrace(true);
+ });
+
+ it("should decode unicode text with multiple consecutive emoji", () => {
+ const decoder = new TextDecoder();
+ const encoder = new TextEncoder();
+ gcTrace(true);
+ var text = `❤️❤️❤️❤️❤️❤️ Red Heart`;
+
+ text += ` ✨ Sparkles 🔥 Fire 😀 😃 😄 😁 😆 😅 😂 🤣 🥲 ☺️ 😊 😇 🙂 🙃 😉 😌 😍 🥰 😘 😗 😙 😚 😋 😛 😝 😜 🤪 🤨 🧐 🤓 😎 🥸 🤩 🥳 😏 😒 😞 😔 😟 😕 🙁 ☹️ 😣 😖 😫 😩 🥺 😢 😭 😤 😠 😡 🤬 🤯 😳 🥵 🥶 😱 😨 😰`;
+ gcTrace(true);
+ expect(decoder.decode(encoder.encode(text))).toBe(text);
+ gcTrace(true);
+ const bytes = new Uint8Array(getByteLength(text) * 8);
+ gcTrace(true);
+ const amount = encoder.encodeInto(text, bytes);
+ gcTrace(true);
+ expect(decoder.decode(bytes.subarray(0, amount.written))).toBe(text);
+ gcTrace(true);
+ });
+});
+
+describe("TextEncoder", () => {
+ it("should encode latin1 text", () => {
+ gcTrace(true);
+ const text = "Hello World!";
+ const encoder = new TextEncoder();
+ gcTrace(true);
+ const encoded = encoder.encode(text);
+ gcTrace(true);
+ expect(encoded instanceof Uint8Array).toBe(true);
+ expect(encoded.length).toBe(text.length);
+ gcTrace(true);
+ const result = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33];
+ for (let i = 0; i < result.length; i++) {
+ expect(encoded[i]).toBe(result[i]);
+ }
+ });
+
+ it("should encode long latin1 text", async () => {
+ const text = "Hello World!".repeat(1000);
+ const encoder = new TextEncoder();
+ gcTrace(true);
+ const encoded = encoder.encode(text);
+ gcTrace(true);
+ expect(encoded instanceof Uint8Array).toBe(true);
+ expect(encoded.length).toBe(text.length);
+ gcTrace(true);
+ const decoded = new TextDecoder().decode(encoded);
+ expect(decoded).toBe(text);
+ gcTrace();
+ await new Promise((resolve) => setTimeout(resolve, 1));
+ gcTrace();
+ expect(decoded).toBe(text);
+ });
+
+ it("should encode latin1 rope text", () => {
+ var text = "Hello";
+ text += " ";
+ text += "World!";
+
+ gcTrace(true);
+ const encoder = new TextEncoder();
+ const encoded = encoder.encode(text);
+ gcTrace(true);
+ const into = new Uint8Array(100);
+ const out = encoder.encodeInto(text, into);
+ gcTrace(true);
+ expect(out.read).toBe(text.length);
+ expect(out.written).toBe(encoded.length);
+
+ expect(encoded instanceof Uint8Array).toBe(true);
+ const result = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33];
+ for (let i = 0; i < result.length; i++) {
+ expect(encoded[i]).toBe(result[i]);
+ expect(encoded[i]).toBe(into[i]);
+ }
+ expect(encoded.length).toBe(getByteLength(text));
+ });
+
+ it("should encode utf-16 text", () => {
+ var text = `❤️ Red Heart
+ ✨ Sparkles
+ 🔥 Fire
+ `;
+ var encoder = new TextEncoder();
+ var decoder = new TextDecoder();
+ gcTrace(true);
+ expect(decoder.decode(encoder.encode(text))).toBe(text);
+ gcTrace(true);
+ });
+
+ // this test is from a web platform test in WebKit
+ describe("should use a unicode replacement character for invalid surrogate pairs", () => {
+ var bad = [
+ {
+ encoding: "utf-16le",
+ input: [0x00, 0xd8],
+ expected: "\uFFFD",
+ name: "lone surrogate lead",
+ },
+ {
+ encoding: "utf-16le",
+ input: [0x00, 0xdc],
+ expected: "\uFFFD",
+ name: "lone surrogate trail",
+ },
+ {
+ encoding: "utf-16le",
+ input: [0x00, 0xd8, 0x00, 0x00],
+ expected: "\uFFFD\u0000",
+ name: "unmatched surrogate lead",
+ },
+ {
+ encoding: "utf-16le",
+ input: [0x00, 0xdc, 0x00, 0x00],
+ expected: "\uFFFD\u0000",
+ name: "unmatched surrogate trail",
+ },
+ {
+ encoding: "utf-16le",
+ input: [0x00, 0xdc, 0x00, 0xd8],
+ expected: "\uFFFD\uFFFD",
+ name: "swapped surrogate pair",
+ },
+ ];
+
+ bad.forEach(function (t) {
+ it(t.encoding + " - " + t.name, () => {
+ gcTrace(true);
+ expect(
+ new TextDecoder(t.encoding).decode(new Uint8Array(t.input))
+ ).toBe(t.expected);
+ expect(
+ new TextDecoder(t.encoding).decode(
+ new Uint16Array(new Uint8Array(t.input).buffer)
+ )
+ ).toBe(t.expected);
+ gcTrace(true);
+ });
+ // test(function () {
+ // assert_throws_js(TypeError, function () {
+ // new TextDecoder(t.encoding, { fatal: true }).decode(
+ // new Uint8Array(t.input)
+ // );
+ // });
+ // }, t.encoding + " - " + t.name + " (fatal flag set)");
+ });
+ });
+
+ it("should encode utf-16 rope text", () => {
+ gcTrace(true);
+ var textReal = `❤️ Red Heart ✨ Sparkles 🔥 Fire`;
+
+ var a = textReal.split("");
+ var text = "";
+ for (let j of a) {
+ text += j;
+ }
+
+ var encoder = new TextEncoder();
+ expect(new TextDecoder().decode(encoder.encode(text))).toBe(textReal);
+ });
+});
diff --git a/test/bun.js/toml-fixture.toml b/test/bun.js/toml-fixture.toml
new file mode 100644
index 000000000..090563ef7
--- /dev/null
+++ b/test/bun.js/toml-fixture.toml
@@ -0,0 +1,39 @@
+
+framework = "next"
+origin = "http://localhost:5000"
+inline.array = [1234, 4, 5, 6]
+
+
+[macros]
+react-relay = { "graphql" = "node_modules/bun-macro-relay/bun-macro-relay.tsx" }
+
+[install.scopes]
+"@mybigcompany2" = { "token" = "123456", "url" = "https://registry.mybigcompany.com" }
+"@mybigcompany3" = { "token" = "123456", "url" = "https://registry.mybigcompany.com", "three" = 4 }
+
+
+[install.scopes."@mybigcompany"]
+token = "123456"
+url = "https://registry.mybigcompany.com"
+
+[bundle.packages]
+"@emotion/react" = true
+
+
+[dev]
+foo = 123
+"foo.bar" = "baz"
+"abba.baba" = "baba"
+dabba = -123
+doo = 123.456
+one.two.three = 4
+
+[[array]]
+entry_one = "one"
+entry_two = "two"
+
+[[array]]
+entry_one = "three"
+
+[[array.nested]]
+entry_one = "four"
diff --git a/test/bun.js/toml.test.js b/test/bun.js/toml.test.js
new file mode 100644
index 000000000..44141b2d4
--- /dev/null
+++ b/test/bun.js/toml.test.js
@@ -0,0 +1,30 @@
+import { describe, it, expect } from "bun:test";
+import { gc } from "./gc";
+
+it("syntax", async () => {
+ gc();
+
+ const toml = (await import("./toml-fixture.toml")).default;
+ gc();
+
+ expect(toml.framework).toBe("next");
+ expect(toml.bundle.packages["@emotion/react"]).toBe(true);
+ expect(toml.array[0].entry_one).toBe("one");
+ expect(toml.array[0].entry_two).toBe("two");
+ expect(toml.array[1].entry_one).toBe("three");
+ expect(toml.array[1].entry_two).toBe(undefined);
+ expect(toml.array[1].nested[0].entry_one).toBe("four");
+ expect(toml.dev.one.two.three).toBe(4);
+ expect(toml.dev.foo).toBe(123);
+ expect(toml.inline.array[0]).toBe(1234);
+ expect(toml.inline.array[1]).toBe(4);
+ expect(toml.dev["foo.bar"]).toBe("baz");
+ expect(toml.install.scopes["@mybigcompany"].url).toBe(
+ "https://registry.mybigcompany.com"
+ );
+ expect(toml.install.scopes["@mybigcompany2"].url).toBe(
+ "https://registry.mybigcompany.com"
+ );
+ expect(toml.install.scopes["@mybigcompany3"].three).toBe(4);
+ gc();
+});
diff --git a/test/bun.js/transpiler.test.js b/test/bun.js/transpiler.test.js
new file mode 100644
index 000000000..f8da4c18c
--- /dev/null
+++ b/test/bun.js/transpiler.test.js
@@ -0,0 +1,1675 @@
+import { expect, it, describe } from "bun:test";
+
+describe("Bun.Transpiler", () => {
+ describe("exports.replace", () => {
+ const transpiler = new Bun.Transpiler({
+ exports: {
+ replace: {
+ // export var foo = function() { }
+ // =>
+ // export var foo = "bar";
+ foo: "bar",
+
+ // export const getStaticProps = /* code */
+ // =>
+ // export var __N_SSG = true;
+ getStaticProps: ["__N_SSG", true],
+ getStaticPaths: ["__N_SSG", true],
+ // export function getStaticProps(ctx) { /* code */ }
+ // =>
+ // export var __N_SSP = true;
+ getServerSideProps: ["__N_SSP", true],
+ },
+
+ // Explicitly remove the top-level export, even if it is in use by
+ // another part of the file
+ eliminate: ["loader", "localVarToRemove"],
+ },
+ /* only per-file for now, so this isn't good yet */
+ treeShaking: true,
+
+ // remove non-bare unused exports, even if they may have side effects
+ // Consistent with tsc & esbuild, this is enabled by default for TypeScript files
+ // this flag lets you enable it for JavaScript files
+ // this already existed, just wasn't exposed in the API
+ trimUnusedImports: true,
+ });
+
+ it("a deletes dead exports and any imports only referenced in dead regions", () => {
+ const out = transpiler.transformSync(`
+ import {getUserById} from './my-database';
+
+ export async function getStaticProps(ctx){
+ return { props: { user: await getUserById(ctx.params.id) } };
+ }
+
+ export default function MyComponent({user}) {
+ getStaticProps();
+ return <div id='user'>{user.name}</div>;
+ }
+ `);
+ });
+
+ it("deletes dead exports and any imports only referenced in dead regions", () => {
+ const output = transpiler.transformSync(`
+ import deadFS from 'fs';
+ import liveFS from 'fs';
+
+ export var deleteMe = 100;
+
+ export function loader() {
+ deadFS.readFileSync("/etc/passwd");
+ liveFS.readFileSync("/etc/passwd");
+ }
+
+ export function action() {
+ require("foo");
+ liveFS.readFileSync("/etc/passwd")
+ deleteMe = 101;
+ }
+
+ export function baz() {
+ require("bar");
+ }
+ `);
+ expect(output.includes("loader")).toBe(false);
+ expect(output.includes("react")).toBe(false);
+ expect(output.includes("action")).toBe(true);
+ expect(output.includes("deadFS")).toBe(false);
+ expect(output.includes("liveFS")).toBe(true);
+ });
+
+ it("supports replacing exports", () => {
+ const output = transpiler.transformSync(`
+ import deadFS from 'fs';
+ import anotherDeadFS from 'fs';
+ import liveFS from 'fs';
+
+ export var localVarToRemove = deadFS.readFileSync("/etc/passwd");
+ export var localVarToReplace = 1;
+
+ var getStaticProps = function () {
+ deadFS.readFileSync("/etc/passwd")
+ };
+
+ export {getStaticProps}
+
+ export function baz() {
+ liveFS.readFileSync("/etc/passwd");
+ require("bar");
+ }
+ `);
+ expect(output.includes("loader")).toBe(false);
+ expect(output.includes("react")).toBe(false);
+ expect(output.includes("deadFS")).toBe(false);
+ expect(output.includes("default")).toBe(false);
+ expect(output.includes("anotherDeadFS")).toBe(false);
+ expect(output.includes("liveFS")).toBe(true);
+ expect(output.includes("__N_SSG")).toBe(true);
+ expect(output.includes("localVarToReplace")).toBe(true);
+ expect(output.includes("localVarToRemove")).toBe(false);
+ });
+ });
+
+ const transpiler = new Bun.Transpiler({
+ loader: "tsx",
+ define: {
+ "process.env.NODE_ENV": JSON.stringify("development"),
+ user_undefined: "undefined",
+ },
+ macro: {
+ react: {
+ bacon: `${import.meta.dir}/macro-check.js`,
+ },
+ },
+ platform: "browser",
+ });
+ const bunTranspiler = new Bun.Transpiler({
+ loader: "tsx",
+ define: {
+ "process.env.NODE_ENV": JSON.stringify("development"),
+ user_undefined: "undefined",
+ },
+ platform: "bun",
+ macro: {
+ inline: {
+ whatDidIPass: `${import.meta.dir}/inline.macro.js`,
+ },
+ react: {
+ bacon: `${import.meta.dir}/macro-check.js`,
+ },
+ },
+ });
+
+ const code = `import { useParams } from "remix";
+ import type { LoaderFunction, ActionFunction } from "remix";
+ import { type xx } from 'mod';
+ import { type xx as yy } from 'mod';
+ import { type 'xx' as yy } from 'mod';
+ import { type if as yy } from 'mod';
+ import React, { type ReactNode, Component as Romponent, Component } from 'react';
+
+
+ export const loader: LoaderFunction = async ({
+ params
+ }) => {
+ console.log(params.postId);
+ };
+
+ export const action: ActionFunction = async ({
+ params
+ }) => {
+ console.log(params.postId);
+ };
+
+ export default function PostRoute() {
+ const params = useParams();
+ console.log(params.postId);
+ }
+
+
+
+
+
+ `;
+
+ it("JSX", () => {
+ var bun = new Bun.Transpiler({
+ loader: "jsx",
+ define: {
+ "process.env.NODE_ENV": JSON.stringify("development"),
+ },
+ });
+ expect(bun.transformSync("export var foo = <div foo />")).toBe(
+ `export var foo = jsx("div", {
+ foo: true
+}, undefined, false, undefined, this);
+`
+ );
+ expect(bun.transformSync("export var foo = <div foo={foo} />")).toBe(
+ `export var foo = jsx("div", {
+ foo
+}, undefined, false, undefined, this);
+`
+ );
+ expect(bun.transformSync("export var foo = <div {...foo} />")).toBe(
+ `export var foo = jsx("div", {
+ ...foo
+}, undefined, false, undefined, this);
+`
+ );
+
+ expect(bun.transformSync("export var hi = <div {foo} />")).toBe(
+ `export var hi = jsx("div", {
+ foo
+}, undefined, false, undefined, this);
+`
+ );
+ expect(bun.transformSync("export var hi = <div {foo.bar.baz} />")).toBe(
+ `export var hi = jsx("div", {
+ baz: foo.bar.baz
+}, undefined, false, undefined, this);
+`
+ );
+ expect(bun.transformSync("export var hi = <div {foo?.bar?.baz} />")).toBe(
+ `export var hi = jsx("div", {
+ baz: foo?.bar?.baz
+}, undefined, false, undefined, this);
+`
+ );
+ expect(
+ bun.transformSync("export var hi = <div {foo['baz'].bar?.baz} />")
+ ).toBe(
+ `export var hi = jsx("div", {
+ baz: foo["baz"].bar?.baz
+}, undefined, false, undefined, this);
+`
+ );
+
+ // cursed
+ expect(
+ bun.transformSync(
+ "export var hi = <div {foo[{name: () => true}.name].hi} />"
+ )
+ ).toBe(
+ `export var hi = jsx("div", {
+ hi: foo[{ name: () => true }.name].hi
+}, undefined, false, undefined, this);
+`
+ );
+ expect(
+ bun.transformSync("export var hi = <Foo {process.env.NODE_ENV} />")
+ ).toBe(
+ `export var hi = jsx(Foo, {
+ NODE_ENV: "development"
+}, undefined, false, undefined, this);
+`
+ );
+
+ expect(
+ bun.transformSync("export var hi = <div {foo['baz'].bar?.baz} />")
+ ).toBe(
+ `export var hi = jsx("div", {
+ baz: foo["baz"].bar?.baz
+}, undefined, false, undefined, this);
+`
+ );
+ try {
+ bun.transformSync("export var hi = <div {foo}={foo}= />");
+ throw new Error("Expected error");
+ } catch (e) {
+ expect(e.errors[0].message.includes('Expected ">"')).toBe(true);
+ }
+
+ expect(
+ bun.transformSync("export var hi = <div {Foo}><Foo></Foo></div>")
+ ).toBe(
+ `export var hi = jsx("div", {
+ Foo,
+ children: jsx(Foo, {}, undefined, false, undefined, this)
+}, undefined, false, undefined, this);
+`
+ );
+ expect(
+ bun.transformSync("export var hi = <div {Foo}><Foo></Foo></div>")
+ ).toBe(
+ `export var hi = jsx("div", {
+ Foo,
+ children: jsx(Foo, {}, undefined, false, undefined, this)
+}, undefined, false, undefined, this);
+`
+ );
+
+ expect(bun.transformSync("export var hi = <div>{123}}</div>").trim()).toBe(
+ `export var hi = jsx("div", {
+ children: [
+ 123,
+ "}"
+ ]
+}, undefined, true, undefined, this);
+ `.trim()
+ );
+ });
+
+ describe("inline JSX", () => {
+ const inliner = new Bun.Transpiler({
+ loader: "tsx",
+ define: {
+ "process.env.NODE_ENV": JSON.stringify("production"),
+ user_undefined: "undefined",
+ },
+ platform: "bun",
+ jsxOptimizationInline: true,
+ treeShaking: false,
+ });
+
+ it("inlines static JSX into object literals", () => {
+ expect(
+ inliner
+ .transformSync(
+ `
+export var hi = <div>{123}</div>
+export var hiWithKey = <div key="hey">{123}</div>
+export var hiWithRef = <div ref={foo}>{123}</div>
+
+export var ComponentThatChecksDefaultProps = <Hello></Hello>
+export var ComponentThatChecksDefaultPropsAndHasChildren = <Hello>my child</Hello>
+export var ComponentThatHasSpreadCausesDeopt = <Hello {...spread} />
+
+`.trim()
+ )
+ .trim()
+ ).toBe(
+ `var $$typeof = Symbol.for("react.element");
+export var hi = {
+ $$typeof,
+ type: "div",
+ key: null,
+ ref: null,
+ props: {
+ children: 123
+ },
+ _owner: null
+};
+export var hiWithKey = {
+ $$typeof,
+ type: "div",
+ key: "hey",
+ ref: null,
+ props: {
+ children: 123
+ },
+ _owner: null
+};
+export var hiWithRef = jsx("div", {
+ ref: foo,
+ children: 123
+});
+export var ComponentThatChecksDefaultProps = {
+ $$typeof,
+ type: Hello,
+ key: null,
+ ref: null,
+ props: Hello.defaultProps || {},
+ _owner: null
+};
+export var ComponentThatChecksDefaultPropsAndHasChildren = {
+ $$typeof,
+ type: Hello,
+ key: null,
+ ref: null,
+ props: __merge({
+ children: "my child"
+ }, Hello.defaultProps),
+ _owner: null
+};
+export var ComponentThatHasSpreadCausesDeopt = jsx(Hello, {
+ ...spread
+});
+`.trim()
+ );
+ });
+ });
+
+ it("require with a dynamic non-string expression", () => {
+ var nodeTranspiler = new Bun.Transpiler({ platform: "node" });
+ expect(nodeTranspiler.transformSync("require('hi' + bar)")).toBe(
+ 'require("hi" + bar);\n'
+ );
+ });
+
+ it("CommonJS", () => {
+ var nodeTranspiler = new Bun.Transpiler({ platform: "node" });
+ expect(nodeTranspiler.transformSync("module.require('hi' + 123)")).toBe(
+ 'require("hi" + 123);\n'
+ );
+
+ expect(
+ nodeTranspiler.transformSync("module.require(1 ? 'foo' : 'bar')")
+ ).toBe('require("foo");\n');
+ expect(nodeTranspiler.transformSync("require(1 ? 'foo' : 'bar')")).toBe(
+ 'require("foo");\n'
+ );
+
+ expect(
+ nodeTranspiler.transformSync("module.require(unknown ? 'foo' : 'bar')")
+ ).toBe('unknown ? require("foo") : require("bar");\n');
+ });
+
+ describe("regressions", () => {
+ it("unexpected super", () => {
+ const input = `
+ 'use strict';
+
+ const ErrorReportingMixinBase = require('./mixin-base');
+ const PositionTrackingPreprocessorMixin = require('../position-tracking/preprocessor-mixin');
+ const Mixin = require('../../utils/mixin');
+
+ class ErrorReportingPreprocessorMixin extends ErrorReportingMixinBase {
+ constructor(preprocessor, opts) {
+ super(preprocessor, opts);
+
+ this.posTracker = Mixin.install(preprocessor, PositionTrackingPreprocessorMixin);
+ this.lastErrOffset = -1;
+ }
+
+ _reportError(code) {
+ //NOTE: avoid reporting error twice on advance/retreat
+ if (this.lastErrOffset !== this.posTracker.offset) {
+ this.lastErrOffset = this.posTracker.offset;
+ super._reportError(code);
+ }
+ }
+ }
+
+ module.exports = ErrorReportingPreprocessorMixin;
+
+
+`;
+ expect(transpiler.transformSync(input, "js").length > 0).toBe(true);
+ });
+ });
+
+ describe("scanImports", () => {
+ it("reports import paths, excluding types", () => {
+ const imports = transpiler.scanImports(code, "tsx");
+ expect(imports.filter(({ path }) => path === "remix")).toHaveLength(1);
+ expect(imports.filter(({ path }) => path === "mod")).toHaveLength(0);
+ expect(imports.filter(({ path }) => path === "react")).toHaveLength(1);
+ expect(imports).toHaveLength(2);
+ });
+ });
+
+ const parsed = (
+ code,
+ trim = true,
+ autoExport = false,
+ transpiler_ = transpiler
+ ) => {
+ if (autoExport) {
+ code = "export default (" + code + ")";
+ }
+
+ var out = transpiler_.transformSync(code, "js");
+ if (autoExport && out.startsWith("export default ")) {
+ out = out.substring("export default ".length);
+ }
+
+ if (trim) {
+ out = out.trim();
+
+ if (out.endsWith(";")) {
+ out = out.substring(0, out.length - 1);
+ }
+
+ return out.trim();
+ }
+
+ return out;
+ };
+
+ const expectPrinted = (code, out) => {
+ expect(parsed(code, true, true)).toBe(out);
+ };
+
+ const expectPrinted_ = (code, out) => {
+ expect(parsed(code, !out.endsWith(";\n"), false)).toBe(out);
+ };
+
+ const expectBunPrinted_ = (code, out) => {
+ expect(parsed(code, !out.endsWith(";\n"), false, bunTranspiler)).toBe(out);
+ };
+
+ const expectParseError = (code, message) => {
+ try {
+ parsed(code, false, false);
+ } catch (er) {
+ var err = er;
+ if (er instanceof AggregateError) {
+ err = err.errors[0];
+ }
+
+ expect(er.message).toBe(message);
+
+ return;
+ }
+
+ throw new Error("Expected parse error for code\n\t" + code);
+ };
+ const ts = {
+ parsed: (code, trim = true, autoExport = false) => {
+ if (autoExport) {
+ code = "export default (" + code + ")";
+ }
+
+ var out = transpiler.transformSync(code, "ts");
+ if (autoExport && out.startsWith("export default ")) {
+ out = out.substring("export default ".length);
+ }
+
+ if (trim) {
+ out = out.trim();
+
+ if (out.endsWith(";")) {
+ out = out.substring(0, out.length - 1);
+ }
+
+ return out.trim();
+ }
+
+ return out;
+ },
+
+ expectPrinted: (code, out) => {
+ expect(ts.parsed(code, true, true)).toBe(out);
+ },
+
+ expectPrinted_: (code, out) => {
+ expect(ts.parsed(code, !out.endsWith(";\n"), false)).toBe(out);
+ },
+
+ expectParseError: (code, message) => {
+ try {
+ ts.parsed(code, false, false);
+ } catch (er) {
+ var err = er;
+ if (er instanceof AggregateError) {
+ err = err.errors[0];
+ }
+
+ expect(er.message).toBe(message);
+
+ return;
+ }
+
+ throw new Error("Expected parse error for code\n\t" + code);
+ },
+ };
+
+ describe("parser", () => {
+ it("arrays", () => {
+ expectPrinted("[]", "[]");
+ expectPrinted("[,]", "[,]");
+ expectPrinted("[1]", "[1]");
+ expectPrinted("[1,]", "[1]");
+ expectPrinted("[,1]", "[, 1]");
+ expectPrinted("[1,2]", "[1, 2]");
+ expectPrinted("[,1,2]", "[, 1, 2]");
+ expectPrinted("[1,,2]", "[1, , 2]");
+ expectPrinted("[1,2,]", "[1, 2]");
+ expectPrinted("[1,2,,]", "[1, 2, ,]");
+ });
+
+ it("exponentiation", () => {
+ expectPrinted("(delete x) ** 0", "(delete x) ** 0");
+ expectPrinted("(delete x.prop) ** 0", "(delete x.prop) ** 0");
+ expectPrinted("(delete x[0]) ** 0", "(delete x[0]) ** 0");
+
+ expectPrinted("(delete x?.prop) ** 0", "(delete x?.prop) ** 0");
+
+ expectPrinted("(void x) ** 0", "(void x) ** 0");
+ expectPrinted("(typeof x) ** 0", "(typeof x) ** 0");
+ expectPrinted("(+x) ** 0", "(+x) ** 0");
+ expectPrinted("(-x) ** 0", "(-x) ** 0");
+ expectPrinted("(~x) ** 0", "(~x) ** 0");
+ expectPrinted("(!x) ** 0", "(!x) ** 0");
+ expectPrinted("(await x) ** 0", "(await x) ** 0");
+ expectPrinted("(await -x) ** 0", "(await -x) ** 0");
+
+ expectPrinted("--x ** 2", "--x ** 2");
+ expectPrinted("++x ** 2", "++x ** 2");
+ expectPrinted("x-- ** 2", "x-- ** 2");
+ expectPrinted("x++ ** 2", "x++ ** 2");
+
+ expectPrinted("(-x) ** 2", "(-x) ** 2");
+ expectPrinted("(+x) ** 2", "(+x) ** 2");
+ expectPrinted("(~x) ** 2", "(~x) ** 2");
+ expectPrinted("(!x) ** 2", "(!x) ** 2");
+ expectPrinted("(-1) ** 2", "(-1) ** 2");
+ expectPrinted("(+1) ** 2", "1 ** 2");
+ expectPrinted("(~1) ** 2", "(~1) ** 2");
+ expectPrinted("(!1) ** 2", "false ** 2");
+ expectPrinted("(void x) ** 2", "(void x) ** 2");
+ expectPrinted("(delete x) ** 2", "(delete x) ** 2");
+ expectPrinted("(typeof x) ** 2", "(typeof x) ** 2");
+ expectPrinted("undefined ** 2", "undefined ** 2");
+
+ expectParseError("-x ** 2", "Unexpected **");
+ expectParseError("+x ** 2", "Unexpected **");
+ expectParseError("~x ** 2", "Unexpected **");
+ expectParseError("!x ** 2", "Unexpected **");
+ expectParseError("void x ** 2", "Unexpected **");
+ expectParseError("delete x ** 2", "Unexpected **");
+ expectParseError("typeof x ** 2", "Unexpected **");
+
+ expectParseError("-x.y() ** 2", "Unexpected **");
+ expectParseError("+x.y() ** 2", "Unexpected **");
+ expectParseError("~x.y() ** 2", "Unexpected **");
+ expectParseError("!x.y() ** 2", "Unexpected **");
+ expectParseError("void x.y() ** 2", "Unexpected **");
+ expectParseError("delete x.y() ** 2", "Unexpected **");
+ expectParseError("typeof x.y() ** 2", "Unexpected **");
+
+ expectParseError("delete x ** 0", "Unexpected **");
+ expectParseError("delete x.prop ** 0", "Unexpected **");
+ expectParseError("delete x[0] ** 0", "Unexpected **");
+ expectParseError("delete x?.prop ** 0", "Unexpected **");
+ expectParseError("void x ** 0", "Unexpected **");
+ expectParseError("typeof x ** 0", "Unexpected **");
+ expectParseError("+x ** 0", "Unexpected **");
+ expectParseError("-x ** 0", "Unexpected **");
+ expectParseError("~x ** 0", "Unexpected **");
+ expectParseError("!x ** 0", "Unexpected **");
+ expectParseError("await x ** 0", "Unexpected **");
+ expectParseError("await -x ** 0", "Unexpected **");
+ });
+
+ it("await", () => {
+ expectPrinted("await x", "await x");
+ expectPrinted("await +x", "await +x");
+ expectPrinted("await -x", "await -x");
+ expectPrinted("await ~x", "await ~x");
+ expectPrinted("await !x", "await !x");
+ expectPrinted("await --x", "await --x");
+ expectPrinted("await ++x", "await ++x");
+ expectPrinted("await x--", "await x--");
+ expectPrinted("await x++", "await x++");
+ expectPrinted("await void x", "await void x");
+ expectPrinted("await typeof x", "await typeof x");
+ expectPrinted("await (x * y)", "await (x * y)");
+ expectPrinted("await (x ** y)", "await (x ** y)");
+
+ expectPrinted_(
+ "async function f() { await delete x }",
+ "async function f() {\n await delete x;\n}"
+ );
+
+ // expectParseError(
+ // "await delete x",
+ // "Delete of a bare identifier cannot be used in an ECMAScript module"
+ // );
+ });
+
+ it("import assert", () => {
+ expectPrinted_(
+ `import json from "./foo.json" assert { type: "json" };`,
+ `import json from "./foo.json"`
+ );
+ expectPrinted_(
+ `import json from "./foo.json";`,
+ `import json from "./foo.json"`
+ );
+ expectPrinted_(
+ `import("./foo.json", { type: "json" });`,
+ `import("./foo.json")`
+ );
+ });
+
+ it("import with unicode escape", () => {
+ expectPrinted_(
+ `import { name } from 'mod\\u1011';`,
+ `import {name} from "mod\\u1011"`
+ );
+ });
+
+ it("fold string addition", () => {
+ expectPrinted_(
+ `export const foo = "a" + "b";`,
+ `export const foo = "ab"`
+ );
+ expectPrinted_(
+ `export const foo = "F" + "0" + "F" + "0123456789" + "ABCDEF" + "0123456789ABCDEFF0123456789ABCDEF00" + "b";`,
+ `export const foo = "F0F0123456789ABCDEF0123456789ABCDEFF0123456789ABCDEF00b"`
+ );
+ expectPrinted_(
+ `export const foo = "a" + 1 + "b";`,
+ `export const foo = "a" + 1 + "b"`
+ );
+ expectPrinted_(
+ `export const foo = "a" + "b" + 1 + "b";`,
+ `export const foo = "ab" + 1 + "b"`
+ );
+ expectPrinted_(
+ `export const foo = "a" + "b" + 1 + "b" + "c";`,
+ `export const foo = "ab" + 1 + "bc"`
+ );
+ });
+
+ it("numeric constants", () => {
+ expectBunPrinted_("export const foo = 1 + 2", "export const foo = 3");
+ expectBunPrinted_("export const foo = 1 - 2", "export const foo = -1");
+ expectBunPrinted_("export const foo = 1 * 2", "export const foo = 2");
+ });
+
+ it("pass objects to macros", () => {
+ var object = {
+ helloooooooo: {
+ message: [12345],
+ },
+ };
+
+ const output = bunTranspiler.transformSync(
+ `
+ import {whatDidIPass} from 'inline';
+
+ export function foo() {
+ return whatDidIPass();
+ }
+ `,
+ object
+ );
+ expect(output).toBe(`export function foo() {
+ return {
+ helloooooooo: {
+ message: [
+ 12345
+ ]
+ }
+ };
+}
+`);
+ });
+
+ it("rewrite string to length", () => {
+ expectPrinted_(
+ `export const foo = "a".length + "b".length;`,
+ `export const foo = 1 + 1`
+ );
+ expectBunPrinted_(
+ `export const foo = "a".length + "b".length;`,
+ `export const foo = 2`
+ );
+ });
+
+ describe("Bun.js", () => {
+ it("require -> import.meta.require", () => {
+ expectBunPrinted_(
+ `export const foo = require('bar.node')`,
+ `export const foo = import.meta.require("bar.node")`
+ );
+ });
+
+ it("require.resolve -> import.meta.resolveSync", () => {
+ expectBunPrinted_(
+ `export const foo = require.resolve('bar.node')`,
+ `export const foo = import.meta.resolveSync("bar.node")`
+ );
+ });
+
+ it('require.resolve(path, {paths: ["blah"]}) -> import.meta.resolveSync', () => {
+ expectBunPrinted_(
+ `export const foo = require.resolve('bar.node', {paths: ["blah"]})`,
+ `export const foo = import.meta.resolveSync("bar.node", { paths: ["blah"] })`
+ );
+ });
+ });
+
+ describe("Browsers", () => {
+ it('require.resolve("my-module") -> "/resolved/my-module"', () => {
+ // the module resolver & linker doesn't run with Bun.Transpiler
+ // so in this test, it becomes the same path string
+ expectPrinted_(
+ `export const foo = require.resolve('my-module')`,
+ `export const foo = "my-module"`
+ );
+ });
+ });
+
+ it("define", () => {
+ expectPrinted_(
+ `export default typeof user_undefined === 'undefined';`,
+ `export default true`
+ );
+ expectPrinted_(
+ `export default typeof user_undefined !== 'undefined';`,
+ `export default false`
+ );
+
+ expectPrinted_(
+ `export default typeof user_undefined !== 'undefined';`,
+ `export default false`
+ );
+ expectPrinted_(`export default !user_undefined;`, `export default true`);
+ });
+
+ it("decls", () => {
+ // expectParseError("var x = 0", "");
+ // expectParseError("let x = 0", "");
+ // expectParseError("const x = 0", "");
+ // expectParseError("for (var x = 0;;) ;", "");
+ // expectParseError("for (let x = 0;;) ;", "");
+ // expectParseError("for (const x = 0;;) ;", "");
+
+ // expectParseError("for (var x in y) ;", "");
+ // expectParseError("for (let x in y) ;", "");
+ // expectParseError("for (const x in y) ;", "");
+ // expectParseError("for (var x of y) ;", "");
+ // expectParseError("for (let x of y) ;", "");
+ // expectParseError("for (const x of y) ;", "");
+
+ // expectParseError("var x", "");
+ // expectParseError("let x", "");
+ expectParseError("const x", 'The constant "x" must be initialized');
+ expectParseError("const {}", "This constant must be initialized");
+ expectParseError("const []", "This constant must be initialized");
+ // expectParseError("for (var x;;) ;", "");
+ // expectParseError("for (let x;;) ;", "");
+ expectParseError(
+ "for (const x;;) ;",
+ 'The constant "x" must be initialized'
+ );
+ expectParseError(
+ "for (const {};;) ;",
+ "This constant must be initialized"
+ );
+ expectParseError(
+ "for (const [];;) ;",
+ "This constant must be initialized"
+ );
+
+ // Make sure bindings are visited during parsing
+ expectPrinted_("var {[x]: y} = {}", "var { [x]: y } = {}");
+ expectPrinted_("var {...x} = {}", "var { ...x } = {}");
+
+ // Test destructuring patterns
+ expectPrinted_("var [...x] = []", "var [...x] = []");
+ expectPrinted_("var {...x} = {}", "var { ...x } = {}");
+
+ expectPrinted_(
+ "export var foo = ([...x] = []) => {}",
+ "export var foo = ([...x] = []) => {\n}"
+ );
+
+ expectPrinted_(
+ "export var foo = ({...x} = {}) => {}",
+ "export var foo = ({ ...x } = {}) => {\n}"
+ );
+
+ expectParseError("var [...x,] = []", 'Unexpected "," after rest pattern');
+ expectParseError("var {...x,} = {}", 'Unexpected "," after rest pattern');
+ expectParseError(
+ "export default function() { return ([...x,] = []) => {} }",
+ "Unexpected trailing comma after rest element"
+ );
+ expectParseError(
+ "({...x,} = {}) => {}",
+ "Unexpected trailing comma after rest element"
+ );
+
+ expectPrinted_("[b, ...c] = d", "[b, ...c] = d");
+ expectPrinted_("([b, ...c] = d)", "[b, ...c] = d");
+ expectPrinted_("({b, ...c} = d)", "({ b, ...c } = d)");
+ expectPrinted_("({a = b} = c)", "({ a = b } = c)");
+ expectPrinted_("({a: b = c} = d)", "({ a: b = c } = d)");
+ expectPrinted_("({a: b.c} = d)", "({ a: b.c } = d)");
+ expectPrinted_("[a = {}] = b", "[a = {}] = b");
+ expectPrinted_("[[...a, b].x] = c", "[[...a, b].x] = c");
+ expectPrinted_("[{...a, b}.x] = c", "[{ ...a, b }.x] = c");
+ expectPrinted_("({x: [...a, b].x} = c)", "({ x: [...a, b].x } = c)");
+ expectPrinted_("({x: {...a, b}.x} = c)", "({ x: { ...a, b }.x } = c)");
+ expectPrinted_("[x = [...a, b]] = c", "[x = [...a, b]] = c");
+ expectPrinted_("[x = {...a, b}] = c", "[x = { ...a, b }] = c");
+ expectPrinted_("({x = [...a, b]} = c)", "({ x = [...a, b] } = c)");
+ expectPrinted_("({x = {...a, b}} = c)", "({ x = { ...a, b } } = c)");
+
+ expectPrinted_("(x = y)", "x = y");
+ expectPrinted_("([] = [])", "[] = []");
+ expectPrinted_("({} = {})", "({} = {})");
+ expectPrinted_("([[]] = [[]])", "[[]] = [[]]");
+ expectPrinted_("({x: {}} = {x: {}})", "({ x: {} } = { x: {} })");
+ expectPrinted_("(x) = y", "x = y");
+ expectParseError("([]) = []", "Invalid assignment target");
+ expectParseError("({}) = {}", "Invalid assignment target");
+ expectParseError("[([])] = [[]]", "Invalid assignment target");
+ expectParseError("({x: ({})} = {x: {}})", "Invalid assignment target");
+ expectParseError(
+ "(([]) = []) => {}",
+ "Unexpected parentheses in binding pattern"
+ );
+ expectParseError(
+ "(({}) = {}) => {}",
+ "Unexpected parentheses in binding pattern"
+ );
+ expectParseError("function f(([]) = []) {}", "Parse error");
+ expectParseError(
+ "function f(({}) = {}) {}",
+ "Parse error"
+ // 'Expected identifier but found "("\n'
+ );
+
+ expectPrinted_("for (x in y) ;", "for (x in y) {\n}");
+ expectPrinted_("for ([] in y) ;", "for ([] in y) {\n}");
+ expectPrinted_("for ({} in y) ;", "for ({} in y) {\n}");
+ expectPrinted_("for ((x) in y) ;", "for (x in y) {\n}");
+ expectParseError("for (([]) in y) ;", "Invalid assignment target");
+ expectParseError("for (({}) in y) ;", "Invalid assignment target");
+
+ expectPrinted_("for (x of y) ;", "for (x of y) {\n}");
+ expectPrinted_("for ([] of y) ;", "for ([] of y) {\n}");
+ expectPrinted_("for ({} of y) ;", "for ({} of y) {\n}");
+ expectPrinted_("for ((x) of y) ;", "for (x of y) {\n}");
+ expectParseError("for (([]) of y) ;", "Invalid assignment target");
+ expectParseError("for (({}) of y) ;", "Invalid assignment target");
+
+ expectParseError("[[...a, b]] = c", 'Unexpected "," after rest pattern');
+ expectParseError("[{...a, b}] = c", 'Unexpected "," after rest pattern');
+ expectParseError(
+ "({x: [...a, b]} = c)",
+ 'Unexpected "," after rest pattern'
+ );
+ expectParseError(
+ "({x: {...a, b}} = c)",
+ 'Unexpected "," after rest pattern'
+ );
+ expectParseError("[b, ...c,] = d", 'Unexpected "," after rest pattern');
+ expectParseError("([b, ...c,] = d)", 'Unexpected "," after rest pattern');
+ expectParseError("({b, ...c,} = d)", 'Unexpected "," after rest pattern');
+ expectParseError("({a = b})", 'Unexpected "="');
+ expectParseError("({x = {a = b}} = c)", 'Unexpected "="');
+ expectParseError("[a = {b = c}] = d", 'Unexpected "="');
+
+ expectPrinted_(
+ "for ([{a = {}}] in b) {}",
+ "for ([{ a = {} }] in b) {\n}"
+ );
+ expectPrinted_(
+ "for ([{a = {}}] of b) {}",
+ "for ([{ a = {} }] of b) {\n}"
+ );
+ expectPrinted_("for ({a = {}} in b) {}", "for ({ a = {} } in b) {\n}");
+ expectPrinted_("for ({a = {}} of b) {}", "for ({ a = {} } of b) {\n}");
+
+ expectParseError("({a = {}} in b)", 'Unexpected "="');
+ expectParseError("[{a = {}}]\nof()", 'Unexpected "="');
+ expectParseError(
+ "for ([...a, b] in c) {}",
+ 'Unexpected "," after rest pattern'
+ );
+ expectParseError(
+ "for ([...a, b] of c) {}",
+ 'Unexpected "," after rest pattern'
+ );
+ });
+
+ it("regexp", () => {
+ expectPrinted("/x/g", "/x/g");
+ expectPrinted("/x/i", "/x/i");
+ expectPrinted("/x/m", "/x/m");
+ expectPrinted("/x/s", "/x/s");
+ expectPrinted("/x/u", "/x/u");
+ expectPrinted("/x/y", "/x/y");
+ expectPrinted("/gimme/g", "/gimme/g");
+ expectPrinted("/gimgim/g", "/gimgim/g");
+
+ expectParseError(
+ "/x/msuygig",
+ 'Duplicate flag "g" in regular expression'
+ );
+ });
+
+ it("identifier escapes", () => {
+ expectPrinted_("var _\u0076\u0061\u0072", "var _var");
+ expectParseError(
+ "var \u0076\u0061\u0072",
+ 'Expected identifier but found "\u0076\u0061\u0072"'
+ );
+ expectParseError(
+ "\\u0076\\u0061\\u0072 foo",
+ "Unexpected \\u0076\\u0061\\u0072"
+ );
+
+ expectPrinted_("foo._\u0076\u0061\u0072", "foo._var");
+ expectPrinted_("foo.\u0076\u0061\u0072", "foo.var");
+
+ // expectParseError("\u200Ca", 'Unexpected "\\u200c"');
+ // expectParseError("\u200Da", 'Unexpected "\\u200d"');
+ });
+ });
+
+ it("private identifiers", () => {
+ expectParseError("#foo", "Unexpected #foo");
+ expectParseError("#foo in this", "Unexpected #foo");
+ expectParseError("this.#foo", 'Expected identifier but found "#foo"');
+ expectParseError("this?.#foo", 'Expected identifier but found "#foo"');
+ expectParseError("({ #foo: 1 })", 'Expected identifier but found "#foo"');
+ expectParseError(
+ "class Foo { x = { #foo: 1 } }",
+ 'Expected identifier but found "#foo"'
+ );
+ expectParseError("class Foo { x = #foo }", 'Expected "in" but found "}"');
+ expectParseError(
+ "class Foo { #foo; foo() { delete this.#foo } }",
+ 'Deleting the private name "#foo" is forbidden'
+ );
+ expectParseError(
+ "class Foo { #foo; foo() { delete this?.#foo } }",
+ 'Deleting the private name "#foo" is forbidden'
+ );
+ expectParseError(
+ "class Foo extends Bar { #foo; foo() { super.#foo } }",
+ 'Expected identifier but found "#foo"'
+ );
+ expectParseError(
+ "class Foo { #foo = () => { for (#foo in this) ; } }",
+ "Unexpected #foo"
+ );
+ expectParseError(
+ "class Foo { #foo = () => { for (x = #foo in this) ; } }",
+ "Unexpected #foo"
+ );
+ expectPrinted_("class Foo { #foo }", "class Foo {\n #foo;\n}");
+ expectPrinted_("class Foo { #foo = 1 }", "class Foo {\n #foo = 1;\n}");
+ expectPrinted_(
+ "class Foo { #foo = #foo in this }",
+ "class Foo {\n #foo = #foo in this;\n}"
+ );
+ expectPrinted_(
+ "class Foo { #foo = #foo in (#bar in this); #bar }",
+ "class Foo {\n #foo = #foo in (#bar in this);\n #bar;\n}"
+ );
+ expectPrinted_(
+ "class Foo { #foo() {} }",
+ "class Foo {\n #foo() {\n }\n}"
+ );
+ expectPrinted_(
+ "class Foo { get #foo() {} }",
+ "class Foo {\n get #foo() {\n }\n}"
+ );
+ expectPrinted_(
+ "class Foo { set #foo(x) {} }",
+ "class Foo {\n set #foo(x) {\n }\n}"
+ );
+ expectPrinted_(
+ "class Foo { static #foo }",
+ "class Foo {\n static #foo;\n}"
+ );
+ expectPrinted_(
+ "class Foo { static #foo = 1 }",
+ "class Foo {\n static #foo = 1;\n}"
+ );
+ expectPrinted_(
+ "class Foo { static #foo() {} }",
+ "class Foo {\n static #foo() {\n }\n}"
+ );
+ expectPrinted_(
+ "class Foo { static get #foo() {} }",
+ "class Foo {\n static get #foo() {\n }\n}"
+ );
+ expectPrinted_(
+ "class Foo { static set #foo(x) {} }",
+ "class Foo {\n static set #foo(x) {\n }\n}"
+ );
+
+ expectParseError(
+ "class Foo { #foo = #foo in #bar in this; #bar }",
+ "Unexpected #bar"
+ );
+
+ expectParseError(
+ "class Foo { #constructor }",
+ 'Invalid field name "#constructor"'
+ );
+ expectParseError(
+ "class Foo { #constructor() {} }",
+ 'Invalid method name "#constructor"'
+ );
+ expectParseError(
+ "class Foo { static #constructor }",
+ 'Invalid field name "#constructor"'
+ );
+ expectParseError(
+ "class Foo { static #constructor() {} }",
+ 'Invalid method name "#constructor"'
+ );
+ expectParseError(
+ "class Foo { #\\u0063onstructor }",
+ 'Invalid field name "#constructor"'
+ );
+ expectParseError(
+ "class Foo { #\\u0063onstructor() {} }",
+ 'Invalid method name "#constructor"'
+ );
+ expectParseError(
+ "class Foo { static #\\u0063onstructor }",
+ 'Invalid field name "#constructor"'
+ );
+ expectParseError(
+ "class Foo { static #\\u0063onstructor() {} }",
+ 'Invalid method name "#constructor"'
+ );
+ const errorText = '"#foo" has already been declared';
+ expectParseError("class Foo { #foo; #foo }", errorText);
+ expectParseError("class Foo { #foo; static #foo }", errorText);
+ expectParseError("class Foo { static #foo; #foo }", errorText);
+ expectParseError("class Foo { #foo; #foo() {} }", errorText);
+ expectParseError("class Foo { #foo; get #foo() {} }", errorText);
+ expectParseError("class Foo { #foo; set #foo(x) {} }", errorText);
+ expectParseError("class Foo { #foo() {} #foo }", errorText);
+ expectParseError("class Foo { get #foo() {} #foo }", errorText);
+ expectParseError("class Foo { set #foo(x) {} #foo }", errorText);
+ expectParseError("class Foo { get #foo() {} get #foo() {} }", errorText);
+ expectParseError("class Foo { set #foo(x) {} set #foo(x) {} }", errorText);
+ expectParseError(
+ "class Foo { get #foo() {} set #foo(x) {} #foo }",
+ errorText
+ );
+ expectParseError(
+ "class Foo { set #foo(x) {} get #foo() {} #foo }",
+ errorText
+ );
+
+ expectPrinted_(
+ "class Foo { get #foo() {} set #foo(x) { this.#foo } }",
+ "class Foo {\n get #foo() {\n }\n set #foo(x) {\n this.#foo;\n }\n}"
+ );
+ expectPrinted_(
+ "class Foo { set #foo(x) { this.#foo } get #foo() {} }",
+ "class Foo {\n set #foo(x) {\n this.#foo;\n }\n get #foo() {\n }\n}"
+ );
+ expectPrinted_(
+ "class Foo { #foo } class Bar { #foo }",
+ "class Foo {\n #foo;\n}\n\nclass Bar {\n #foo;\n}"
+ );
+ expectPrinted_(
+ "class Foo { foo = this.#foo; #foo }",
+ "class Foo {\n foo = this.#foo;\n #foo;\n}"
+ );
+ expectPrinted_(
+ "class Foo { foo = this?.#foo; #foo }",
+ "class Foo {\n foo = this?.#foo;\n #foo;\n}"
+ );
+ expectParseError(
+ "class Foo { #foo } class Bar { foo = this.#foo }",
+ 'Private name "#foo" must be declared in an enclosing class'
+ );
+ expectParseError(
+ "class Foo { #foo } class Bar { foo = this?.#foo }",
+ 'Private name "#foo" must be declared in an enclosing class'
+ );
+ expectParseError(
+ "class Foo { #foo } class Bar { foo = #foo in this }",
+ 'Private name "#foo" must be declared in an enclosing class'
+ );
+
+ expectPrinted_(
+ `class Foo {
+ #if
+ #im() { return this.#im(this.#if) }
+ static #sf
+ static #sm() { return this.#sm(this.#sf) }
+ foo() {
+ return class {
+ #inner() {
+ return [this.#im, this?.#inner, this?.x.#if]
+ }
+ }
+ }
+}
+`,
+ `class Foo {
+ #if;
+ #im() {
+ return this.#im(this.#if);
+ }
+ static #sf;
+ static #sm() {
+ return this.#sm(this.#sf);
+ }
+ foo() {
+ return class {
+ #inner() {
+ return [this.#im, this?.#inner, this?.x.#if];
+ }
+ };
+ }
+}`
+ );
+ });
+
+ it("type only exports", () => {
+ let { expectPrinted_, expectParseError } = ts;
+ expectPrinted_("export type {foo, bar as baz} from 'bar'", "");
+ expectPrinted_("export type {foo, bar as baz}", "");
+ expectPrinted_("export type {foo} from 'bar'; x", "x");
+ expectPrinted_("export type {foo} from 'bar'\nx", "x");
+ expectPrinted_("export type {default} from 'bar'", "");
+ expectPrinted_(
+ "export { type } from 'mod'; type",
+ 'export { type } from "mod";\ntype'
+ );
+ expectPrinted_(
+ "export { type, as } from 'mod'",
+ 'export { type, as } from "mod"'
+ );
+ expectPrinted_(
+ "export { x, type foo } from 'mod'; x",
+ 'export { x } from "mod";\nx'
+ );
+ expectPrinted_(
+ "export { x, type as } from 'mod'; x",
+ 'export { x } from "mod";\nx'
+ );
+ expectPrinted_(
+ "export { x, type foo as bar } from 'mod'; x",
+ 'export { x } from "mod";\nx'
+ );
+ expectPrinted_(
+ "export { x, type foo as as } from 'mod'; x",
+ 'export { x } from "mod";\nx'
+ );
+ expectPrinted_(
+ "export { type as as } from 'mod'; as",
+ 'export { type as as } from "mod";\nas'
+ );
+ expectPrinted_(
+ "export { type as foo } from 'mod'; foo",
+ 'export { type as foo } from "mod";\nfoo'
+ );
+ expectPrinted_(
+ "export { type as type } from 'mod'; type",
+ 'export { type } from "mod";\ntype'
+ );
+ expectPrinted_(
+ "export { x, type as as foo } from 'mod'; x",
+ 'export { x } from "mod";\nx'
+ );
+ expectPrinted_(
+ "export { x, type as as as } from 'mod'; x",
+ 'export { x } from "mod";\nx'
+ );
+ expectPrinted_(
+ "export { x, type type as as } from 'mod'; x",
+ 'export { x } from "mod";\nx'
+ );
+ expectPrinted_(
+ "export { x, \\u0074ype y }; let x, y",
+ "export { x };\nlet x, y"
+ );
+ expectPrinted_(
+ "export { x, \\u0074ype y } from 'mod'",
+ 'export { x } from "mod"'
+ );
+ expectPrinted_(
+ "export { x, type if } from 'mod'",
+ 'export { x } from "mod"'
+ );
+ expectPrinted_("export { x, type y as if }; let x", "export { x };\nlet x");
+ expectPrinted_("export { type x };", "");
+ });
+
+ it("delete + optional chain", () => {
+ expectPrinted_("delete foo.bar.baz", "delete foo.bar.baz");
+ expectPrinted_("delete foo?.bar.baz", "delete foo?.bar.baz");
+ expectPrinted_("delete foo?.bar?.baz", "delete foo?.bar?.baz");
+ });
+
+ it("useDefineForConst TypeScript class initialization", () => {
+ var { expectPrinted_ } = ts;
+ expectPrinted_(
+ `
+class Foo {
+ constructor(public x: string = "hey") {}
+ bar: number;
+}
+`.trim(),
+ `
+class Foo {
+ x;
+ constructor(x = "hey") {
+ this.x = x;
+ }
+ bar;
+}
+`.trim()
+ );
+ });
+
+ it("class static blocks", () => {
+ expectPrinted_(
+ "class Foo { static {} }",
+ "class Foo {\n static {\n }\n}"
+ );
+ expectPrinted_(
+ "class Foo { static {} x = 1 }",
+ "class Foo {\n static {\n }\n x = 1;\n}"
+ );
+ expectPrinted_(
+ "class Foo { static { this.foo() } }",
+ "class Foo {\n static {\n this.foo();\n }\n}"
+ );
+
+ expectParseError(
+ "class Foo { static { yield } }",
+ '"yield" is a reserved word and cannot be used in strict mode'
+ );
+ expectParseError(
+ "class Foo { static { await } }",
+ 'The keyword "await" cannot be used here'
+ );
+ expectParseError(
+ "class Foo { static { return } }",
+ "A return statement cannot be used here"
+ );
+ expectParseError(
+ "class Foo { static { break } }",
+ 'Cannot use "break" here'
+ );
+ expectParseError(
+ "class Foo { static { continue } }",
+ 'Cannot use "continue" here'
+ );
+ expectParseError(
+ "x: { class Foo { static { break x } } }",
+ 'There is no containing label named "x"'
+ );
+ expectParseError(
+ "x: { class Foo { static { continue x } } }",
+ 'There is no containing label named "x"'
+ );
+
+ expectParseError(
+ "class Foo { get #x() { this.#x = 1 } }",
+ 'Writing to getter-only property "#x" will throw'
+ );
+ expectParseError(
+ "class Foo { get #x() { this.#x += 1 } }",
+ 'Writing to getter-only property "#x" will throw'
+ );
+ expectParseError(
+ "class Foo { set #x(x) { this.#x } }",
+ 'Reading from setter-only property "#x" will throw'
+ );
+ expectParseError(
+ "class Foo { set #x(x) { this.#x += 1 } }",
+ 'Reading from setter-only property "#x" will throw'
+ );
+
+ // Writing to method warnings
+ expectParseError(
+ "class Foo { #x() { this.#x = 1 } }",
+ 'Writing to read-only method "#x" will throw'
+ );
+ expectParseError(
+ "class Foo { #x() { this.#x += 1 } }",
+ 'Writing to read-only method "#x" will throw'
+ );
+ });
+
+ describe("simplification", () => {
+ it("unary operator", () => {
+ expectPrinted("a = !(b, c)", "a = (b , !c)");
+ });
+
+ it("constant folding", () => {
+ expectPrinted("1 && 2", "2");
+ expectPrinted("1 || 2", "1");
+ expectPrinted("0 && 1", "0");
+ expectPrinted("0 || 1", "1");
+
+ expectPrinted("null ?? 1", "1");
+ expectPrinted("undefined ?? 1", "1");
+ expectPrinted("0 ?? 1", "0");
+ expectPrinted("false ?? 1", "false");
+ expectPrinted('"" ?? 1', '""');
+
+ expectPrinted("typeof undefined", '"undefined"');
+ expectPrinted("typeof null", '"object"');
+ expectPrinted("typeof false", '"boolean"');
+ expectPrinted("typeof true", '"boolean"');
+ expectPrinted("typeof 123", '"number"');
+ expectPrinted("typeof 123n", '"bigint"');
+ expectPrinted("typeof 'abc'", '"string"');
+ expectPrinted("typeof function() {}", '"function"');
+ expectPrinted("typeof (() => {})", '"function"');
+ expectPrinted("typeof {}", "typeof {}");
+ expectPrinted("typeof []", "typeof []");
+
+ expectPrinted("undefined === undefined", "true");
+ expectPrinted("undefined !== undefined", "false");
+ expectPrinted("undefined == undefined", "true");
+ expectPrinted("undefined != undefined", "false");
+
+ expectPrinted("null === null", "true");
+ expectPrinted("null !== null", "false");
+ expectPrinted("null == null", "true");
+ expectPrinted("null != null", "false");
+
+ expectPrinted("undefined === null", "undefined === null");
+ expectPrinted("undefined !== null", "undefined !== null");
+ expectPrinted("undefined == null", "undefined == null");
+ expectPrinted("undefined != null", "undefined != null");
+
+ expectPrinted("true === true", "true");
+ expectPrinted("true === false", "false");
+ expectPrinted("true !== true", "false");
+ expectPrinted("true !== false", "true");
+ expectPrinted("true == true", "true");
+ expectPrinted("true == false", "false");
+ expectPrinted("true != true", "false");
+ expectPrinted("true != false", "true");
+
+ expectPrinted("1 === 1", "true");
+ expectPrinted("1 === 2", "false");
+ expectPrinted("1 === '1'", '1 === "1"');
+ expectPrinted("1 == 1", "true");
+ expectPrinted("1 == 2", "false");
+ expectPrinted("1 == '1'", '1 == "1"');
+
+ expectPrinted("1 !== 1", "false");
+ expectPrinted("1 !== 2", "true");
+ expectPrinted("1 !== '1'", '1 !== "1"');
+ expectPrinted("1 != 1", "false");
+ expectPrinted("1 != 2", "true");
+ expectPrinted("1 != '1'", '1 != "1"');
+
+ expectPrinted("'a' === '\\x61'", "true");
+ expectPrinted("'a' === '\\x62'", "false");
+ expectPrinted("'a' === 'abc'", "false");
+ expectPrinted("'a' !== '\\x61'", "false");
+ expectPrinted("'a' !== '\\x62'", "true");
+ expectPrinted("'a' !== 'abc'", "true");
+ expectPrinted("'a' == '\\x61'", "true");
+ expectPrinted("'a' == '\\x62'", "false");
+ expectPrinted("'a' == 'abc'", "false");
+ expectPrinted("'a' != '\\x61'", "false");
+ expectPrinted("'a' != '\\x62'", "true");
+ expectPrinted("'a' != 'abc'", "true");
+
+ expectPrinted("'a' + 'b'", '"ab"');
+ expectPrinted("'a' + 'bc'", '"abc"');
+ expectPrinted("'ab' + 'c'", '"abc"');
+ expectPrinted("x + 'a' + 'b'", 'x + "ab"');
+ expectPrinted("x + 'a' + 'bc'", 'x + "abc"');
+ expectPrinted("x + 'ab' + 'c'", 'x + "abc"');
+ expectPrinted("'a' + 1", '"a" + 1');
+ expectPrinted("x * 'a' + 'b'", 'x * "a" + "b"');
+
+ expectPrinted("'string' + `template`", `"stringtemplate"`);
+
+ expectPrinted("`template` + 'string'", "`templatestring`");
+
+ // TODO: string template simplification
+ // expectPrinted("'string' + `a${foo}b`", "`stringa${foo}b`");
+ // expectPrinted("'string' + tag`template`", '"string" + tag`template`;');
+ // expectPrinted("`a${foo}b` + 'string'", "`a${foo}bstring`");
+ // expectPrinted("tag`template` + 'string'", 'tag`template` + "string"');
+ // expectPrinted("`template` + `a${foo}b`", "`templatea${foo}b`");
+ // expectPrinted("`a${foo}b` + `template`", "`a${foo}btemplate`");
+ // expectPrinted("`a${foo}b` + `x${bar}y`", "`a${foo}bx${bar}y`");
+ // expectPrinted(
+ // "`a${i}${j}bb` + `xxx${bar}yyyy`",
+ // "`a${i}${j}bbxxx${bar}yyyy`"
+ // );
+ // expectPrinted(
+ // "`a${foo}bb` + `xxx${i}${j}yyyy`",
+ // "`a${foo}bbxxx${i}${j}yyyy`"
+ // );
+ // expectPrinted(
+ // "`template` + tag`template2`",
+ // "`template` + tag`template2`"
+ // );
+ // expectPrinted(
+ // "tag`template` + `template2`",
+ // "tag`template` + `template2`"
+ // );
+
+ expectPrinted("123", "123");
+ expectPrinted("123 .toString()", "123 .toString()");
+ expectPrinted("-123", "-123");
+ expectPrinted("(-123).toString()", "(-123).toString()");
+ expectPrinted("-0", "-0");
+ expectPrinted("(-0).toString()", "(-0).toString()");
+ expectPrinted("-0 === 0", "true");
+
+ expectPrinted("NaN", "NaN");
+ expectPrinted("NaN.toString()", "NaN.toString()");
+ expectPrinted("NaN === NaN", "false");
+
+ expectPrinted("Infinity", "Infinity");
+ expectPrinted("Infinity.toString()", "Infinity.toString()");
+ expectPrinted("(-Infinity).toString()", "(-Infinity).toString()");
+ expectPrinted("Infinity === Infinity", "true");
+ expectPrinted("Infinity === -Infinity", "false");
+
+ expectPrinted("123n === 1_2_3n", "true");
+ });
+ describe("type coercions", () => {
+ const dead = `
+ if ("") {
+ TEST_FAIL
+ }
+
+ if (false) {
+ TEST_FAIL
+ }
+
+ if (0) {
+ TEST_FAIL
+ }
+
+ if (void 0) {
+ TEST_FAIL
+ }
+
+ if (null) {
+ TEST_FAIL
+ }
+
+ var should_be_true = typeof "" === "string" || false
+ var should_be_false = typeof "" !== "string" && TEST_FAIL;
+ var should_be_false_2 = typeof true === "string" && TEST_FAIL;
+ var should_be_false_3 = typeof false === "string" && TEST_FAIL;
+ var should_be_false_4 = typeof 123n === "string" && TEST_FAIL;
+ var should_be_false_5 = typeof function(){} === "string" && TEST_FAIL;
+ var should_be_kept = typeof globalThis.BACON === "string" && TEST_OK;
+ var should_be_kept_1 = typeof TEST_OK === "string";
+
+ var should_be_kept_2 = TEST_OK ?? true;
+ var should_be_kept_4 = { "TEST_OK": true } ?? TEST_FAIL;
+ var should_be_false_6 = false ?? TEST_FAIL;
+ var should_be_true_7 = true ?? TEST_FAIL;
+ `;
+ const out = transpiler.transformSync(dead);
+
+ for (let line of out.split("\n")) {
+ it(line, () => {
+ if (line.includes("should_be_kept")) {
+ expect(line.includes("TEST_OK")).toBe(true);
+ }
+
+ if (line.includes("should_be_false")) {
+ if (!line.includes("= false"))
+ throw new Error(`Expected false in "${line}"`);
+ expect(line.includes("= false")).toBe(true);
+ }
+
+ if (line.includes("TEST_FAIL")) {
+ throw new Error(`"${line}"\n\tshould not contain TEST_FAIL`);
+ }
+ });
+ }
+ });
+ });
+
+ describe("scan", () => {
+ it("reports all export names", () => {
+ const { imports, exports } = transpiler.scan(code);
+
+ expect(exports[0]).toBe("action");
+ expect(exports[2]).toBe("loader");
+ expect(exports[1]).toBe("default");
+ expect(exports).toHaveLength(3);
+
+ expect(imports.filter(({ path }) => path === "remix")).toHaveLength(1);
+ expect(imports.filter(({ path }) => path === "mod")).toHaveLength(0);
+ expect(imports.filter(({ path }) => path === "react")).toHaveLength(1);
+ expect(imports).toHaveLength(2);
+ });
+ });
+
+ describe("transform", () => {
+ it("supports macros", async () => {
+ const out = await transpiler.transform(`
+ import {keepSecondArgument} from 'macro:${
+ import.meta.dir
+ }/macro-check.js';
+
+ export default keepSecondArgument("Test failed", "Test passed");
+ export function otherNamesStillWork() {}
+ `);
+ expect(out.includes("Test failed")).toBe(false);
+ expect(out.includes("Test passed")).toBe(true);
+
+ // ensure both the import and the macro function call are removed
+ expect(out.includes("keepSecondArgument")).toBe(false);
+ expect(out.includes("otherNamesStillWork")).toBe(true);
+ });
+
+ it("sync supports macros", () => {
+ const out = transpiler.transformSync(`
+ import {keepSecondArgument} from 'macro:${
+ import.meta.dir
+ }/macro-check.js';
+
+ export default keepSecondArgument("Test failed", "Test passed");
+ export function otherNamesStillWork() {
+
+ }
+ `);
+ expect(out.includes("Test failed")).toBe(false);
+ expect(out.includes("Test passed")).toBe(true);
+
+ expect(out.includes("keepSecondArgument")).toBe(false);
+ expect(out.includes("otherNamesStillWork")).toBe(true);
+ });
+
+ const importLines = [
+ "import {createElement, bacon} from 'react';",
+ "import {bacon, createElement} from 'react';",
+ ];
+ describe("sync supports macros remap", () => {
+ for (let importLine of importLines) {
+ it(importLine, () => {
+ var thisCode = `
+ ${importLine}
+
+ export default bacon("Test failed", "Test passed");
+ export function otherNamesStillWork() {
+ return createElement("div");
+ }
+
+ `;
+ var out = transpiler.transformSync(thisCode);
+ try {
+ expect(out.includes("Test failed")).toBe(false);
+ expect(out.includes("Test passed")).toBe(true);
+
+ expect(out.includes("bacon")).toBe(false);
+ expect(out.includes("createElement")).toBe(true);
+ } catch (e) {
+ console.log("Failing code:\n\n" + out + "\n");
+ throw e;
+ }
+ });
+ }
+ });
+
+ it("macro remap removes import statement if its the only used one", () => {
+ const out = transpiler.transformSync(`
+ import {bacon} from 'react';
+
+ export default bacon("Test failed", "Test passed");
+ `);
+
+ expect(out.includes("Test failed")).toBe(false);
+ expect(out.includes("Test passed")).toBe(true);
+
+ expect(out.includes("bacon")).toBe(false);
+ expect(out.includes("import")).toBe(false);
+ });
+
+ it("removes types", () => {
+ expect(code.includes("mod")).toBe(true);
+ expect(code.includes("xx")).toBe(true);
+ expect(code.includes("ActionFunction")).toBe(true);
+ expect(code.includes("LoaderFunction")).toBe(true);
+ expect(code.includes("ReactNode")).toBe(true);
+ expect(code.includes("React")).toBe(true);
+ expect(code.includes("Component")).toBe(true);
+ const out = transpiler.transformSync(code);
+
+ expect(out.includes("ActionFunction")).toBe(false);
+ expect(out.includes("LoaderFunction")).toBe(false);
+ expect(out.includes("mod")).toBe(false);
+ expect(out.includes("xx")).toBe(false);
+ expect(out.includes("ReactNode")).toBe(false);
+ const { exports } = transpiler.scan(out);
+ exports.sort();
+
+ expect(exports[0]).toBe("action");
+ expect(exports[2]).toBe("loader");
+ expect(exports[1]).toBe("default");
+ expect(exports).toHaveLength(3);
+ });
+ });
+});
diff --git a/test/bun.js/tsconfig.json b/test/bun.js/tsconfig.json
new file mode 100644
index 000000000..9a6c36e06
--- /dev/null
+++ b/test/bun.js/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "compilerOptions": {
+ "lib": ["ESNext"],
+ "module": "esnext",
+ "target": "esnext",
+ "noEmit": true,
+ "allowJs": true,
+ "typeRoots": ["../../types"],
+ "types": ["bun"],
+ "allowSyntheticDefaultImports": true,
+ "baseUrl": ".",
+ "paths": {
+ "foo/bar": ["baz.js"]
+ }
+ }
+}
diff --git a/test/bun.js/unsafe.test.js b/test/bun.js/unsafe.test.js
new file mode 100644
index 000000000..741dc0241
--- /dev/null
+++ b/test/bun.js/unsafe.test.js
@@ -0,0 +1,51 @@
+import { test, expect, it, describe } from "bun:test";
+import { gc } from "./gc";
+
+it("arrayBufferToString u8", async () => {
+ var encoder = new TextEncoder();
+ const bytes = encoder.encode("hello world");
+ gc(true);
+ expect(Bun.unsafe.arrayBufferToString(bytes)).toBe("hello world");
+ gc(true);
+ await new Promise((resolve) => setTimeout(resolve, 0));
+ gc(true);
+});
+
+it("arrayBufferToString ArrayBuffer", async () => {
+ var encoder = new TextEncoder();
+ var bytes = encoder.encode("hello world");
+ gc(true);
+ const out = Bun.unsafe.arrayBufferToString(bytes.buffer);
+ expect(out).toBe("hello world");
+ gc(true);
+ await new Promise((resolve) => setTimeout(resolve, 0));
+ globalThis.bytes = bytes;
+ gc(true);
+ expect(out).toBe("hello world");
+});
+
+it("arrayBufferToString u16", () => {
+ var encoder = new TextEncoder();
+ const bytes = encoder.encode("hello world");
+ var uint16 = new Uint16Array(bytes.byteLength);
+ uint16.set(bytes);
+ const charCodes = Bun.unsafe
+ .arrayBufferToString(uint16)
+ .split("")
+ .map((a) => a.charCodeAt(0));
+ gc(true);
+ for (let i = 0; i < charCodes.length; i++) {
+ expect("hello world"[i]).toBe(String.fromCharCode(charCodes[i]));
+ }
+ gc(true);
+ expect(charCodes.length).toBe("hello world".length);
+ gc(true);
+});
+
+it("Bun.allocUnsafe", () => {
+ var buffer = Bun.allocUnsafe(1024);
+ expect(buffer instanceof Uint8Array).toBe(true);
+ expect(buffer.length).toBe(1024);
+ buffer[0] = 0;
+ expect(buffer[0]).toBe(0);
+});
diff --git a/test/bun.js/url.test.ts b/test/bun.js/url.test.ts
new file mode 100644
index 000000000..37ea2008b
--- /dev/null
+++ b/test/bun.js/url.test.ts
@@ -0,0 +1,102 @@
+import { describe, it, expect } from "bun:test";
+
+describe("url", () => {
+ it("prints", () => {
+ expect(Bun.inspect(new URL("https://example.com"))).toBe(
+ "https://example.com/"
+ );
+
+ expect(
+ Bun.inspect(
+ new URL(
+ "https://github.com/Jarred-Sumner/bun/issues/135?hello%20i%20have%20spaces%20thank%20you%20good%20night"
+ )
+ )
+ ).toBe(
+ "https://github.com/Jarred-Sumner/bun/issues/135?hello%20i%20have%20spaces%20thank%20you%20good%20night"
+ );
+ });
+ it("works", () => {
+ const inputs: [
+ [
+ string,
+ {
+ hash: string;
+ host: string;
+ hostname: string;
+ href: string;
+ origin: string;
+ password: string;
+ pathname: string;
+ port: string;
+ protocol: string;
+ search: string;
+ username: string;
+ }
+ ]
+ ] = [
+ [
+ "https://username:password@api.foo.bar.com:9999/baz/okay/i/123?ran=out&of=things#to-use-as-a-placeholder",
+ {
+ hash: "#to-use-as-a-placeholder",
+ host: "api.foo.bar.com:9999",
+ hostname: "api.foo.bar.com",
+ href: "https://username:password@api.foo.bar.com:9999/baz/okay/i/123?ran=out&of=things#to-use-as-a-placeholder",
+ origin: "https://api.foo.bar.com:9999",
+ password: "password",
+ pathname: "/baz/okay/i/123",
+ port: "9999",
+ protocol: "https:",
+ search: "?ran=out&of=things",
+ username: "username",
+ },
+ ],
+ [
+ "https://url.spec.whatwg.org/#url-serializing",
+ {
+ hash: "#url-serializing",
+ host: "url.spec.whatwg.org",
+ hostname: "url.spec.whatwg.org",
+ href: "https://url.spec.whatwg.org/#url-serializing",
+ origin: "https://url.spec.whatwg.org",
+ password: "",
+ pathname: "/",
+ port: "",
+ protocol: "https:",
+ search: "",
+ username: "",
+ },
+ ],
+ [
+ "https://url.spec.whatwg.org#url-serializing",
+ {
+ hash: "#url-serializing",
+ host: "url.spec.whatwg.org",
+ hostname: "url.spec.whatwg.org",
+ href: "https://url.spec.whatwg.org/#url-serializing",
+ origin: "https://url.spec.whatwg.org",
+ password: "",
+ pathname: "/",
+ port: "",
+ protocol: "https:",
+ search: "",
+ username: "",
+ },
+ ],
+ ];
+
+ for (let [url, values] of inputs) {
+ const result = new URL(url);
+ expect(result.hash).toBe(values.hash);
+ expect(result.host).toBe(values.host);
+ expect(result.hostname).toBe(values.hostname);
+ expect(result.href).toBe(values.href);
+ expect(result.password).toBe(values.password);
+ expect(result.pathname).toBe(values.pathname);
+ expect(result.port).toBe(values.port);
+ expect(result.protocol).toBe(values.protocol);
+ expect(result.search).toBe(values.search);
+ expect(result.username).toBe(values.username);
+ }
+ });
+});
diff --git a/test/bun.js/wasm-return-1-test.zig b/test/bun.js/wasm-return-1-test.zig
new file mode 100644
index 000000000..d46bdae92
--- /dev/null
+++ b/test/bun.js/wasm-return-1-test.zig
@@ -0,0 +1,5 @@
+export fn hello() i32 {
+ return 1;
+}
+
+pub fn main() void {}
diff --git a/test/bun.js/wasm.js b/test/bun.js/wasm.js
new file mode 100644
index 000000000..a4daaaffe
--- /dev/null
+++ b/test/bun.js/wasm.js
@@ -0,0 +1 @@
+import * as wasm from "./wasm-return-1-test.wasm";
diff --git a/test/bun.js/wasm.test.js b/test/bun.js/wasm.test.js
new file mode 100644
index 000000000..ab88d5beb
--- /dev/null
+++ b/test/bun.js/wasm.test.js
@@ -0,0 +1,20 @@
+import { it } from "bun:test";
+// import * as wasm from "./wasm-return-1-test.wasm";
+
+// import { readFileSync } from "fs";
+
+// it("wasm readFileSync", async () => {
+// console.log("here");
+// console.log(wasm.hello());
+// });
+
+// it("wasm import", async () => {
+// console.log("heyt");
+// try {
+// console.log("hi");
+// expect(wasm.hello()).toBe(1);
+// } catch (err) {
+// console.error(err);
+// throw err;
+// }
+// });
diff --git a/test/bun.js/web-globals.test.js b/test/bun.js/web-globals.test.js
new file mode 100644
index 000000000..ac7c22e84
--- /dev/null
+++ b/test/bun.js/web-globals.test.js
@@ -0,0 +1,46 @@
+import { expect, test } from "bun:test";
+
+test("exists", () => {
+ expect(typeof URL !== "undefined").toBe(true);
+ expect(typeof URLSearchParams !== "undefined").toBe(true);
+ expect(typeof DOMException !== "undefined").toBe(true);
+ expect(typeof Event !== "undefined").toBe(true);
+ expect(typeof EventTarget !== "undefined").toBe(true);
+ expect(typeof AbortController !== "undefined").toBe(true);
+ expect(typeof AbortSignal !== "undefined").toBe(true);
+ expect(typeof CustomEvent !== "undefined").toBe(true);
+ expect(typeof Headers !== "undefined").toBe(true);
+ expect(typeof ErrorEvent !== "undefined").toBe(true);
+ expect(typeof CloseEvent !== "undefined").toBe(true);
+ expect(typeof MessageEvent !== "undefined").toBe(true);
+ expect(typeof TextEncoder !== "undefined").toBe(true);
+ expect(typeof WebSocket !== "undefined").toBe(true);
+});
+
+test("CloseEvent", () => {
+ var event = new CloseEvent("close", { reason: "world" });
+ expect(event.type).toBe("close");
+ const target = new EventTarget();
+ var called = false;
+ target.addEventListener("close", ({ type, reason }) => {
+ expect(type).toBe("close");
+ expect(reason).toBe("world");
+ called = true;
+ });
+ target.dispatchEvent(event);
+ expect(called).toBe(true);
+});
+
+test("MessageEvent", () => {
+ var event = new MessageEvent("message", { data: "world" });
+ expect(event.type).toBe("message");
+ const target = new EventTarget();
+ var called = false;
+ target.addEventListener("message", ({ type, data }) => {
+ expect(type).toBe("message");
+ expect(data).toBe("world");
+ called = true;
+ });
+ target.dispatchEvent(event);
+ expect(called).toBe(true);
+});
diff --git a/test/bun.js/websocket.test.js b/test/bun.js/websocket.test.js
new file mode 100644
index 000000000..ab825fa63
--- /dev/null
+++ b/test/bun.js/websocket.test.js
@@ -0,0 +1,79 @@
+import { describe, it, expect } from "bun:test";
+import { unsafe } from "bun";
+import { gc } from "./gc";
+
+const TEST_WEBSOCKET_HOST =
+ process.env.TEST_WEBSOCKET_HOST || "wss://ws.postman-echo.com/raw";
+
+describe("WebSocket", () => {
+ it("should connect", async () => {
+ const ws = new WebSocket(TEST_WEBSOCKET_HOST);
+ await new Promise((resolve, reject) => {
+ ws.onopen = resolve;
+ ws.onerror = reject;
+ });
+ var closed = new Promise((resolve, reject) => {
+ ws.onclose = resolve;
+ });
+ ws.close();
+ await closed;
+ });
+
+ it("should send and receive messages", async () => {
+ const ws = new WebSocket(TEST_WEBSOCKET_HOST);
+ await new Promise((resolve, reject) => {
+ ws.onopen = resolve;
+ ws.onerror = reject;
+ ws.onclose = () => {
+ reject("WebSocket closed");
+ };
+ });
+ const count = 10;
+
+ // 10 messages in burst
+ var promise = new Promise((resolve, reject) => {
+ var remain = count;
+ ws.onmessage = (event) => {
+ gc(true);
+ expect(event.data).toBe("Hello World!");
+ remain--;
+
+ if (remain <= 0) {
+ ws.onmessage = () => {};
+ resolve();
+ }
+ };
+ ws.onerror = reject;
+ });
+
+ for (let i = 0; i < count; i++) {
+ ws.send("Hello World!");
+ gc(true);
+ }
+
+ await promise;
+ var echo = 0;
+
+ // 10 messages one at a time
+ function waitForEcho() {
+ return new Promise((resolve, reject) => {
+ gc(true);
+ const msg = `Hello World! ${echo++}`;
+ ws.onmessage = (event) => {
+ expect(event.data).toBe(msg);
+ resolve();
+ };
+ ws.onerror = reject;
+ ws.onclose = reject;
+ ws.send(msg);
+ gc(true);
+ });
+ }
+ gc(true);
+ for (let i = 0; i < count; i++) await waitForEcho();
+ ws.onclose = () => {};
+ ws.onerror = () => {};
+ ws.close();
+ gc(true);
+ });
+});
diff --git a/test/bun.js/writeFileSync.txt b/test/bun.js/writeFileSync.txt
new file mode 100644
index 000000000..a0fe4515f
--- /dev/null
+++ b/test/bun.js/writeFileSync.txt
@@ -0,0 +1 @@
+File \ No newline at end of file
diff --git a/test/bun.js/zlib.test.js b/test/bun.js/zlib.test.js
new file mode 100644
index 000000000..aecb095cc
--- /dev/null
+++ b/test/bun.js/zlib.test.js
@@ -0,0 +1,18 @@
+import { describe, it, expect } from "bun:test";
+import { gzipSync, deflateSync, inflateSync, gunzipSync } from "bun";
+
+describe("zlib", () => {
+ it("should be able to deflate and inflate", () => {
+ const data = new TextEncoder().encode("Hello World!".repeat(1));
+ const compressed = deflateSync(data);
+ const decompressed = inflateSync(compressed);
+ expect(decompressed.join("")).toBe(data.join(""));
+ });
+
+ it("should be able to gzip and gunzip", () => {
+ const data = new TextEncoder().encode("Hello World!".repeat(1));
+ const compressed = gzipSync(data);
+ const decompressed = gunzipSync(compressed);
+ expect(decompressed.join("")).toBe(data.join(""));
+ });
+});