Files
bun.sh/test/regression/issue/24234.test.ts
robobun 0f7494569e fix(console): implement %j format specifier for JSON output (#25195)
## Summary
- Implements the `%j` format specifier for `console.log` and related
console methods
- `%j` outputs the JSON stringified representation of the value
- Previously, `%j` was not recognized and was left as literal text in
the output

## Test plan
- [x] Run `bun bd test test/regression/issue/24234.test.ts` - all 5
tests pass
- [x] Verify tests fail with system Bun (`USE_SYSTEM_BUN=1`) to confirm
fix validity
- [x] Manual verification: `console.log('%j', {foo: 'bar'})` outputs
`{"foo":"bar"}`

## Example

Before (bug):
```
$ bun -e "console.log('%j %s', {foo: 'bar'}, 'hello')"
%j [object Object] hello
```

After (fixed):
```
$ bun -e "console.log('%j %s', {foo: 'bar'}, 'hello')"
{"foo":"bar"} hello
```

Closes #24234

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-28 22:57:55 -08:00

73 lines
2.2 KiB
TypeScript

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);
});