import { SyncSubprocess } from "bun"; import { describe, expect, test } from "bun:test"; import { rmSync, writeFileSync } from "fs"; import { bunEnv, bunExe, isWindows, tmpdirSync } from "harness"; import { tmpdir } from "os"; import { join, sep } from "path"; for (const flag of ["-e", "--print"]) { describe(`bun ${flag}`, () => { test("it works", async () => { const input = flag === "--print" ? '"hello world"' : 'console.log("hello world")'; let { stdout } = Bun.spawnSync({ cmd: [bunExe(), flag, input], env: bunEnv, }); expect(stdout.toString("utf8")).toEqual("hello world\n"); }); test("import, tsx, require in esm, import.meta", async () => { const ref = await import("react"); const input = flag === "--print" ? 'import {version} from "react"; console.log(JSON.stringify({version,file:import.meta.path,require:require("react").version})); world' : 'import {version} from "react"; console.log(JSON.stringify({version,file:import.meta.path,require:require("react").version})); console.log(world);'; let { stdout } = Bun.spawnSync({ cmd: [bunExe(), flag, input], env: bunEnv, }); const json = { version: ref.version, file: join(process.cwd(), "[eval]"), require: ref.version, }; expect(stdout.toString("utf8")).toEqual(JSON.stringify(json) + "\nworld\n"); }); test("error has source map info 1", async () => { let { stderr } = Bun.spawnSync({ cmd: [bunExe(), flag, '(throw new Error("hi" as 2))'], env: bunEnv, }); expect(stderr.toString("utf8")).toInclude('"hi" as 2'); expect(stderr.toString("utf8")).toInclude("Unexpected throw"); }); test("process.argv", async () => { function testProcessArgv(args: string[], expected: string[]) { const input = flag === "--print" ? "process.argv" : "console.log(process.argv)"; let { stdout, stderr, exitCode } = Bun.spawnSync({ cmd: [bunExe(), flag, input, ...args], env: bunEnv, }); expect(stderr.toString("utf8")).toBe(""); expect(JSON.parse(stdout.toString("utf8"))).toEqual(expected); expect(exitCode).toBe(0); } // replace the trailin const exe = isWindows ? bunExe().replaceAll("/", "\\") : bunExe(); testProcessArgv([], [exe]); testProcessArgv(["abc", "def"], [exe, "abc", "def"]); testProcessArgv(["--", "abc", "def"], [exe, "abc", "def"]); // testProcessArgv(["--", "abc", "--", "def"], [exe, "abc", "--", "def"]); }); test("process._eval", async () => { const code = flag === "--print" ? "process._eval" : "console.log(process._eval)"; const { stdout } = Bun.spawnSync({ cmd: [bunExe(), flag, code], env: bunEnv, }); expect(stdout.toString("utf8")).toEqual(code + "\n"); }); test("does not crash in non-latin1 directory", async () => { const dir = join(tmpdirSync(), "eval-test-开始学习"); await Bun.write(join(dir, "index.js"), "console.log('hello world')"); const { stdout, stderr, exitCode } = Bun.spawnSync({ cmd: [bunExe(), flag, "import './index.js'"], env: bunEnv, cwd: dir, stdout: "pipe", stderr: "pipe", stdin: "ignore", }); expect(stderr.toString("utf8")).toBe(""); expect(stdout.toString("utf8")).toEqual("hello world\n" + (flag === "--print" ? "undefined\n" : "")); expect(exitCode).toBe(0); }); }); } describe("--print for cjs/esm", () => { test("eval result between esm imports", async () => { let cwd = tmpdirSync(); writeFileSync(join(cwd, "foo.js"), "'foo'"); writeFileSync(join(cwd, "bar.js"), "'bar'"); let { stdout, stderr, exitCode } = Bun.spawnSync({ cmd: [bunExe(), "--print", 'import "./foo.js"; 123; import "./bar.js"'], cwd: cwd, env: bunEnv, }); expect(stderr.toString("utf8")).toBe(""); expect(stdout.toString("utf8")).toEqual("123\n"); expect(exitCode).toBe(0); rmSync(cwd, { recursive: true, force: true }); }); test("forced cjs", async () => { let { stdout, stderr, exitCode } = Bun.spawnSync({ cmd: [bunExe(), "--print", "module.exports; 123"], env: bunEnv, }); expect(stderr.toString("utf8")).toBe(""); expect(stdout.toString("utf8")).toEqual("123\n"); expect(exitCode).toBe(0); }); test("module, exports, require, __filename, __dirname", async () => { let { stdout, stderr, exitCode } = Bun.spawnSync({ cmd: [ bunExe(), "--print", ` console.log(typeof module, typeof exports, typeof require, typeof __filename, typeof __dirname); 123 `, ], env: bunEnv, }); expect(stderr.toString("utf8")).toBe(""); expect(stdout.toString("utf8")).toEqual("object object function string string\n123\n"); expect(exitCode).toBe(0); }); test("module._compile is require('module').prototype._compile", async () => { const { stdout, exitCode } = Bun.spawnSync({ cmd: [bunExe(), "-p", "module._compile === require('module').prototype._compile"], env: bunEnv, }); expect(stdout.toString()).toBe("true\n"); expect(exitCode).toBe(0); }); }); function group(run: (code: string) => SyncSubprocess<"pipe", "inherit">) { test("it works", async () => { const { stdout } = run('console.log("hello world")'); expect(stdout.toString("utf8")).toEqual("hello world\n"); }); test("it gets a correct specifer", async () => { const { stdout } = run("console.log(import.meta.path)"); expect(stdout.toString("utf8")).toEndWith(sep + "[stdin]\n"); }); test("it can require", async () => { const { stdout } = run(` const process = require("node:process"); console.log(process.platform); `); expect(stdout.toString("utf8")).toEqual(process.platform + "\n"); }); test("it can import", async () => { const { stdout } = run(` import * as process from "node:process"; console.log(process.platform); `); expect(stdout.toString("utf8")).toEqual(process.platform + "\n"); }); test("process.argv", async () => { const { stdout } = run("console.log(process.argv)"); const exe = isWindows ? bunExe().replaceAll("/", "\\") : bunExe(); expect(JSON.parse(stdout.toString("utf8"))).toEqual([exe, "-"]); }); test("process._eval", async () => { const code = "console.log(process._eval)"; const { stdout } = run(code); // the file piping one on windows can include extra carriage returns if (isWindows) { expect(stdout.toString("utf8")).toInclude(code); } else { expect(stdout.toString("utf8")).toEqual(code + "\n"); } }); } describe("bun run - < file-path.js", () => { function run(code: string) { // bash only supports / as path separator const file = join(tmpdir(), "bun-run-eval-test.js").replaceAll("\\", "/"); require("fs").writeFileSync(file, code); try { let result; if (process.platform === "win32") { result = Bun.spawnSync(["powershell", "-c", `Get-Content ${file} | ${bunExe()} run -`], { env: bunEnv, stderr: "inherit", }); } else { result = Bun.spawnSync(["bash", "-c", `${bunExe()} run - < ${file}`], { env: bunEnv, stderr: "inherit", }); } if (!result.success) { queueMicrotask(() => { throw new Error("bun run - < file-path.js failed"); }); } return result; } finally { try { require("fs").unlinkSync(file); } catch (e) {} } } group(run); }); describe("echo | bun run -", () => { function run(code: string) { const result = Bun.spawnSync([bunExe(), "run", "-"], { env: bunEnv, stdin: Buffer.from(code), stderr: "inherit", }); if (!result.success) { queueMicrotask(() => { throw new Error("bun run - failed"); }); } return result; } group(run); }); test("process._eval (undefined for normal run)", async () => { const cwd = tmpdirSync(); const file = join(cwd, "test.js"); writeFileSync(file, "console.log(typeof process._eval)"); const { stdout } = Bun.spawnSync({ cmd: [bunExe(), "run", file], cwd: cwd, env: bunEnv, }); expect(stdout.toString("utf8")).toEqual("undefined\n"); rmSync(cwd, { recursive: true, force: true }); });