mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
feat: implement --json flag for JSON output format
- Add --json boolean flag that modifies output format to JSON
- Works with --print: outputs expression result as JSON
- Works with --eval: acts like --print but outputs JSON
- Supports capturing module exports for main modules (CommonJS/ESM)
- Uses jsonStringify for proper JSON serialization
- Handles all JavaScript types including promises, dates, objects, arrays
- Shows error messages for non-serializable types (BigInt, circular refs)
Test results:
bun --print '42' --json # outputs: 42
bun --print '({x: 1})' --json # outputs: {"x":1}
bun --eval '({x: 1})' --json # outputs: {"x":1}
bun --eval 'null' --json # outputs: null
bun --eval 'undefined' --json # outputs: (nothing)
Includes comprehensive tests covering all data types and edge cases.
This commit is contained in:
@@ -263,6 +263,9 @@ pub const Run = struct {
|
||||
vm.hot_reload = this.ctx.debug.hot_reload;
|
||||
vm.onUnhandledRejection = &onUnhandledRejectionBeforeClose;
|
||||
|
||||
// Enable capturing entry point result if --json is set (and not using --eval with script)
|
||||
vm.capture_entry_point_result = this.ctx.runtime_options.json and this.ctx.runtime_options.eval.script.len == 0;
|
||||
|
||||
this.addConditionalGlobals();
|
||||
do_redis_preconnect: {
|
||||
// This must happen within the API lock, which is why it's not in the "doPreconnect" function
|
||||
|
||||
@@ -18,6 +18,8 @@ comptime {
|
||||
@export(&setEntryPointEvalResultESM, .{ .name = "Bun__VM__setEntryPointEvalResultESM" });
|
||||
@export(&setEntryPointEvalResultCJS, .{ .name = "Bun__VM__setEntryPointEvalResultCJS" });
|
||||
@export(&specifierIsEvalEntryPoint, .{ .name = "Bun__VM__specifierIsEvalEntryPoint" });
|
||||
@export(&shouldCaptureEntryPointResult, .{ .name = "Bun__VM__shouldCaptureEntryPointResult" });
|
||||
@export(&isMainModule, .{ .name = "Bun__VM__isMainModule" });
|
||||
@export(&string_allocation_limit, .{ .name = "Bun__stringSyntheticAllocationLimit" });
|
||||
@export(&allowAddons, .{ .name = "Bun__VM__allowAddons" });
|
||||
@export(&allowRejectionHandledWarning, .{ .name = "Bun__VM__allowRejectionHandledWarning" });
|
||||
@@ -136,6 +138,8 @@ entry_point_result: struct {
|
||||
value: jsc.Strong.Optional = .empty,
|
||||
cjs_set_value: bool = false,
|
||||
} = .{},
|
||||
capture_entry_point_result: bool = false,
|
||||
main_module_key: jsc.Strong.Optional = .empty,
|
||||
|
||||
auto_install_dependencies: bool = false,
|
||||
|
||||
@@ -799,6 +803,26 @@ pub fn specifierIsEvalEntryPoint(this: *VirtualMachine, specifier: JSValue) call
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn shouldCaptureEntryPointResult(this: *VirtualMachine) callconv(.C) bool {
|
||||
return this.capture_entry_point_result;
|
||||
}
|
||||
|
||||
pub fn isMainModule(this: *VirtualMachine, specifier: JSValue) callconv(.C) bool {
|
||||
// If we're capturing for --json and no main module has been set yet
|
||||
if (this.capture_entry_point_result and !this.main_module_key.has()) {
|
||||
this.main_module_key.set(this.global, specifier);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if this matches the stored main module
|
||||
if (this.main_module_key.has()) {
|
||||
const main_key = this.main_module_key.get() orelse return false;
|
||||
return specifier.isSameValue(main_key, this.global) catch return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn setEntryPointEvalResultESM(this: *VirtualMachine, result: JSValue) callconv(.C) void {
|
||||
// allow esm evaluate to set value multiple times
|
||||
if (!this.entry_point_result.cjs_set_value) {
|
||||
|
||||
@@ -108,6 +108,7 @@ static bool canPerformFastEnumeration(Structure* s)
|
||||
|
||||
extern "C" bool Bun__VM__specifierIsEvalEntryPoint(void*, EncodedJSValue);
|
||||
extern "C" void Bun__VM__setEntryPointEvalResultCJS(void*, EncodedJSValue);
|
||||
extern "C" bool Bun__VM__isMainModule(void*, EncodedJSValue);
|
||||
|
||||
static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObject, JSCommonJSModule* moduleObject, JSString* dirname, JSValue filename)
|
||||
{
|
||||
@@ -145,7 +146,10 @@ static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObj
|
||||
moduleObject->hasEvaluated = true;
|
||||
};
|
||||
|
||||
if (Bun__VM__specifierIsEvalEntryPoint(globalObject->bunVM(), JSValue::encode(filename))) [[unlikely]] {
|
||||
bool isEvalOrMainModule = Bun__VM__specifierIsEvalEntryPoint(globalObject->bunVM(), JSValue::encode(filename)) ||
|
||||
Bun__VM__isMainModule(globalObject->bunVM(), JSValue::encode(filename));
|
||||
|
||||
if (isEvalOrMainModule) [[unlikely]] {
|
||||
initializeModuleObject();
|
||||
scope.assertNoExceptionExceptTermination();
|
||||
|
||||
@@ -163,7 +167,14 @@ static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObj
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
ASSERT(result);
|
||||
|
||||
Bun__VM__setEntryPointEvalResultCJS(globalObject->bunVM(), JSValue::encode(result));
|
||||
if (Bun__VM__specifierIsEvalEntryPoint(globalObject->bunVM(), JSValue::encode(filename))) {
|
||||
Bun__VM__setEntryPointEvalResultCJS(globalObject->bunVM(), JSValue::encode(result));
|
||||
} else if (Bun__VM__isMainModule(globalObject->bunVM(), JSValue::encode(filename))) {
|
||||
// For --json main modules, capture the module.exports
|
||||
auto exports_final = moduleObject->exportsObject();
|
||||
RETURN_IF_EXCEPTION(scope, false);
|
||||
Bun__VM__setEntryPointEvalResultCJS(globalObject->bunVM(), JSValue::encode(exports_final));
|
||||
}
|
||||
|
||||
RELEASE_AND_RETURN(scope, true);
|
||||
}
|
||||
|
||||
@@ -4440,6 +4440,7 @@ JSC::JSValue GlobalObject::moduleLoaderEvaluate(JSGlobalObject* lexicalGlobalObj
|
||||
|
||||
extern "C" bool Bun__VM__specifierIsEvalEntryPoint(void*, EncodedJSValue);
|
||||
extern "C" void Bun__VM__setEntryPointEvalResultESM(void*, EncodedJSValue);
|
||||
extern "C" bool Bun__VM__isMainModule(void*, EncodedJSValue);
|
||||
|
||||
JSC::JSValue EvalGlobalObject::moduleLoaderEvaluate(JSGlobalObject* lexicalGlobalObject,
|
||||
JSModuleLoader* moduleLoader, JSValue key,
|
||||
@@ -4449,7 +4450,8 @@ JSC::JSValue EvalGlobalObject::moduleLoaderEvaluate(JSGlobalObject* lexicalGloba
|
||||
Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
if (scriptFetcher && scriptFetcher.isObject()) [[unlikely]] {
|
||||
if (Bun__VM__specifierIsEvalEntryPoint(globalObject->bunVM(), JSValue::encode(key))) {
|
||||
if (Bun__VM__specifierIsEvalEntryPoint(globalObject->bunVM(), JSValue::encode(key)) ||
|
||||
Bun__VM__isMainModule(globalObject->bunVM(), JSValue::encode(key))) {
|
||||
Bun__VM__setEntryPointEvalResultESM(globalObject->bunVM(), JSValue::encode(scriptFetcher));
|
||||
}
|
||||
return scriptFetcher;
|
||||
@@ -4458,7 +4460,8 @@ JSC::JSValue EvalGlobalObject::moduleLoaderEvaluate(JSGlobalObject* lexicalGloba
|
||||
JSC::JSValue result = moduleLoader->evaluateNonVirtual(lexicalGlobalObject, key, moduleRecordValue,
|
||||
scriptFetcher, sentValue, resumeMode);
|
||||
|
||||
if (Bun__VM__specifierIsEvalEntryPoint(globalObject->bunVM(), JSValue::encode(key))) {
|
||||
if (Bun__VM__specifierIsEvalEntryPoint(globalObject->bunVM(), JSValue::encode(key)) ||
|
||||
Bun__VM__isMainModule(globalObject->bunVM(), JSValue::encode(key))) {
|
||||
Bun__VM__setEntryPointEvalResultESM(globalObject->bunVM(), JSValue::encode(result));
|
||||
}
|
||||
|
||||
|
||||
35
test/cli/json-flag-simple.test.ts
Normal file
35
test/cli/json-flag-simple.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe } from "harness";
|
||||
|
||||
describe("--json flag simple tests", () => {
|
||||
test("--print with --json outputs JSON", async () => {
|
||||
const proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "42", "--json"],
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const output = await new Response(proc.stdout).text();
|
||||
await proc.exited;
|
||||
|
||||
expect(output.trim()).toBe("42");
|
||||
proc.unref();
|
||||
});
|
||||
|
||||
test("--eval with --json outputs JSON", async () => {
|
||||
const proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--eval", "({x: 1, y: 2})", "--json"],
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const output = await new Response(proc.stdout).text();
|
||||
await proc.exited;
|
||||
|
||||
const parsed = JSON.parse(output.trim());
|
||||
expect(parsed).toEqual({ x: 1, y: 2 });
|
||||
proc.unref();
|
||||
});
|
||||
});
|
||||
341
test/cli/json-flag.test.ts
Normal file
341
test/cli/json-flag.test.ts
Normal file
@@ -0,0 +1,341 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
|
||||
|
||||
describe("--json flag", () => {
|
||||
describe("with --print", () => {
|
||||
test("primitive values", async () => {
|
||||
// Number
|
||||
{
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "42", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stderr).toBe("");
|
||||
expect(stdout.trim()).toBe("42");
|
||||
}
|
||||
|
||||
// String
|
||||
{
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", '"hello world"', "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
expect(stdout.trim()).toBe('"hello world"');
|
||||
}
|
||||
|
||||
// Boolean
|
||||
{
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "true", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
expect(stdout.trim()).toBe("true");
|
||||
}
|
||||
|
||||
// null
|
||||
{
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "null", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
expect(stdout.trim()).toBe("null");
|
||||
}
|
||||
|
||||
// undefined (should output nothing)
|
||||
{
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "undefined", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
expect(stdout.trim()).toBe("");
|
||||
}
|
||||
});
|
||||
|
||||
test("complex objects", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "({x: 1, y: 'test', z: [1,2,3], nested: {a: true}})", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(stderr).toBe("");
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed).toEqual({
|
||||
x: 1,
|
||||
y: "test",
|
||||
z: [1, 2, 3],
|
||||
nested: { a: true },
|
||||
});
|
||||
});
|
||||
|
||||
test("arrays", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "[1, 'two', {three: 3}, [4,5]]", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed).toEqual([1, "two", { three: 3 }, [4, 5]]);
|
||||
});
|
||||
|
||||
test("dates", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", 'new Date("2024-01-15T12:30:00.000Z")', "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
expect(stdout.trim()).toBe('"2024-01-15T12:30:00.000Z"');
|
||||
});
|
||||
|
||||
test("promises", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "Promise.resolve({resolved: true})", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed).toEqual({ resolved: true });
|
||||
});
|
||||
|
||||
test("circular references show error message", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "(() => { const obj = {}; obj.circular = obj; return obj; })()", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
// Error is printed to stdout, not stderr
|
||||
expect(stdout).toContain("JSON.stringify cannot serialize cyclic structures");
|
||||
});
|
||||
|
||||
test("functions are undefined in JSON", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "({fn: () => {}, value: 42})", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed).toEqual({ value: 42 }); // fn should be omitted
|
||||
});
|
||||
|
||||
test("--print without --json uses console formatter", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "({x: 1, y: 2})"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
// Console formatter includes spaces and formatting
|
||||
expect(stdout.trim()).toContain("x:");
|
||||
expect(stdout.trim()).toContain("y:");
|
||||
expect(stdout.trim()).not.toBe('{"x":1,"y":2}');
|
||||
});
|
||||
});
|
||||
|
||||
describe("with --eval", () => {
|
||||
test("--eval without --json doesn't print", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--eval", "42"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
expect(stdout.trim()).toBe("");
|
||||
});
|
||||
|
||||
test("--eval with --json prints result as JSON", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--eval", "({result: 'success'})", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed).toEqual({ result: "success" });
|
||||
});
|
||||
|
||||
test("expressions with side effects", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--eval", "console.log('side effect'); ({value: 123})", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
const lines = stdout.trim().split("\n");
|
||||
expect(lines[0]).toBe("side effect"); // console.log output
|
||||
expect(JSON.parse(lines[1])).toEqual({ value: 123 }); // JSON output
|
||||
});
|
||||
});
|
||||
|
||||
describe("--json with regular script files", () => {
|
||||
test("regular script files do not output JSON (only --print and --eval do)", async () => {
|
||||
const dir = tempDirWithFiles("json-flag-script", {
|
||||
"script.js": `
|
||||
console.log("This will show");
|
||||
const data = { value: 123 };
|
||||
data; // This won't be captured - regular scripts don't have return values
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--json", "script.js"],
|
||||
env: bunEnv,
|
||||
cwd: dir,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
// Only console.log output appears, no JSON
|
||||
expect(stdout.trim()).toBe("This will show");
|
||||
});
|
||||
|
||||
test("--json flag is primarily for --print and --eval", async () => {
|
||||
// Test to document that --json is meant for use with --print and --eval
|
||||
// Regular script files don't have a meaningful return value to capture
|
||||
const dir = tempDirWithFiles("json-flag-doc", {
|
||||
"data.js": `module.exports = { value: 42 };`,
|
||||
});
|
||||
|
||||
// This shows the intended usage - evaluating the module
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--eval", "require('./data.js')", "--json"],
|
||||
env: bunEnv,
|
||||
cwd: dir,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed).toEqual({ value: 42 });
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge cases", () => {
|
||||
test("BigInt serialization", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "({bigint: 123n})", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
// BigInt error is printed to stdout
|
||||
expect(stdout).toContain("JSON.stringify cannot serialize BigInt");
|
||||
});
|
||||
|
||||
test("Symbol serialization", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "({sym: Symbol('test'), value: 1})", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
// Symbols are omitted in JSON
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed).toEqual({ value: 1 });
|
||||
});
|
||||
|
||||
test("NaN and Infinity", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "({nan: NaN, inf: Infinity, negInf: -Infinity})", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed).toEqual({
|
||||
nan: null,
|
||||
inf: null,
|
||||
negInf: null,
|
||||
});
|
||||
});
|
||||
|
||||
test("deeply nested objects", async () => {
|
||||
const deepObj = "({a: {b: {c: {d: {e: {f: {g: {h: 'deep'}}}}}}}})";
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", deepObj, "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed.a.b.c.d.e.f.g.h).toBe("deep");
|
||||
});
|
||||
|
||||
test("large arrays", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--print", "Array.from({length: 1000}, (_, i) => i)", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed.length).toBe(1000);
|
||||
expect(parsed[0]).toBe(0);
|
||||
expect(parsed[999]).toBe(999);
|
||||
});
|
||||
});
|
||||
|
||||
describe("flag combinations", () => {
|
||||
test("--json can appear before --print", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--json", "--print", "({order: 'reversed'})"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed).toEqual({ order: "reversed" });
|
||||
});
|
||||
|
||||
test("--json can appear before --eval", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--json", "--eval", "({order: 'eval-reversed'})"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed).toEqual({ order: "eval-reversed" });
|
||||
});
|
||||
|
||||
test("multiple --json flags are idempotent", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--json", "--print", "({multiple: true})", "--json"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [stdout] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed).toEqual({ multiple: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user