diff --git a/src/bun.js/ConsoleObject.zig b/src/bun.js/ConsoleObject.zig index 20e1de9898..b9a24edf18 100644 --- a/src/bun.js/ConsoleObject.zig +++ b/src/bun.js/ConsoleObject.zig @@ -1425,6 +1425,7 @@ pub const Formatter = struct { o, // o O, // O c, // c + j, // j }; fn writeWithFormatting( @@ -1466,6 +1467,7 @@ pub const Formatter = struct { 'O' => .O, 'd', 'i' => .i, 'c' => .c, + 'j' => .j, '%' => { // print up to and including the first % const end = slice[0..i]; @@ -1625,6 +1627,16 @@ pub const Formatter = struct { .c => { // TODO: Implement %c }, + + .j => { + // JSON.stringify the value + var str = bun.String.empty; + defer str.deref(); + + try next_value.jsonStringify(global, 0, &str); + this.addForNewLine(str.length()); + writer.print("{f}", .{str}); + }, } if (this.remaining_values.len == 0) break; }, diff --git a/test/js/web/console/console-log.expected.txt b/test/js/web/console/console-log.expected.txt index 882b76de63..9a6cdf569c 100644 --- a/test/js/web/console/console-log.expected.txt +++ b/test/js/web/console/console-log.expected.txt @@ -236,7 +236,7 @@ Hello World 123 Hello %vWorld 123 Hello NaN %i Hello NaN % 1 -Hello NaN %j 1 +Hello NaN 1 Hello \5 6, Hello %i 5 6 %d 1 diff --git a/test/regression/issue/24234.test.ts b/test/regression/issue/24234.test.ts new file mode 100644 index 0000000000..5bf23a5d21 --- /dev/null +++ b/test/regression/issue/24234.test.ts @@ -0,0 +1,72 @@ +import { expect, test } from "bun:test"; +import { bunEnv, bunExe } from "harness"; + +test("console.log with %j should format as JSON", async () => { + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", "console.log('%j', {foo: 'bar'})"], + env: bunEnv, + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect(stderr).toBe(""); + expect(stdout).toBe('{"foo":"bar"}\n'); + expect(exitCode).toBe(0); +}); + +test("console.log with %j should handle arrays", async () => { + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", "console.log('%j', [1, 2, 3])"], + env: bunEnv, + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect(stderr).toBe(""); + expect(stdout).toBe("[1,2,3]\n"); + expect(exitCode).toBe(0); +}); + +test("console.log with %j should handle nested objects", async () => { + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", "console.log('%j', {a: {b: {c: 123}}})"], + env: bunEnv, + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect(stderr).toBe(""); + expect(stdout).toBe('{"a":{"b":{"c":123}}}\n'); + expect(exitCode).toBe(0); +}); + +test("console.log with %j should handle primitives", async () => { + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", "console.log('%j %j %j %j', 'string', 123, true, null)"], + env: bunEnv, + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect(stderr).toBe(""); + expect(stdout).toBe('"string" 123 true null\n'); + expect(exitCode).toBe(0); +}); + +test("console.log with %j and additional text", async () => { + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", "console.log('Result: %j', {status: 'ok'})"], + env: bunEnv, + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect(stderr).toBe(""); + expect(stdout).toBe('Result: {"status":"ok"}\n'); + expect(exitCode).toBe(0); +});