Compare commits

...

5 Commits

Author SHA1 Message Date
Claude Bot
d12ca50334 refactor: use asUTF8() accessor and add comptime encoding guard
Address CodeRabbit review feedback:
- Use str.asUTF8() instead of str.latin1() for UTF-8 strings to use
  the semantically correct accessor
- Add comptime guard to writeQuotedString to reject unsupported
  encodings (only latin1 and utf8 are supported; use JSON path for UTF-16)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 03:56:03 +00:00
Claude Bot
d859f23ec2 fix: preserve encoding in writeQuotedString for UTF-8/Latin-1 strings
Address review feedback:
- Add encoding parameter to writeQuotedString instead of hardcoding latin1
- Detect UTF-8 vs Latin-1 strings in ConsoleObject and pass correct encoding
- Supports proper handling of non-ASCII characters in both encodings

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 03:43:34 +00:00
Claude Bot
bdd8d705bb refactor: reuse bestQuoteCharForString for console quote selection
Address review feedback:
- Remove custom bestQuoteCharForConsole function and reuse existing
  bestQuoteCharForString which counts quotes in a single SIMD pass
- Update tests to use toContain assertions instead of snapshots
  (snapshots go through Jest pretty printer, not ConsoleObject.zig)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 01:48:51 +00:00
Claude Bot
149ace4ba6 Merge remote-tracking branch 'origin/main' into claude/fix-console-string-quoting-25234 2025-11-30 17:23:02 +00:00
Claude Bot
1158d18784 fix(console): use single quotes for strings containing double quotes
When printing strings in console output, use single quotes if the string
contains double quotes but no single quotes. This avoids ugly escaped
output like `"{\"test\":\"value\"}"` and instead shows `'{"test":"value"}'`.

Closes #25234

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 16:56:01 +00:00
3 changed files with 84 additions and 2 deletions

View File

@@ -2151,11 +2151,16 @@ pub const Formatter = struct {
writer.writeAll(Output.prettyFmt("<r>", true));
if (str.isUTF16()) {
// Use JSON path for UTF-16 to properly handle surrogate pairs (emojis)
try this.printAs(.JSON, Writer, writer_, value, .StringObject, enable_ansi_colors);
return;
}
JSPrinter.writeJSONString(str.latin1(), Writer, writer_, .latin1) catch unreachable;
if (str.asUTF8()) |utf8| {
JSPrinter.writeQuotedString(utf8, Writer, writer_, .utf8) catch unreachable;
} else {
JSPrinter.writeQuotedString(str.latin1(), Writer, writer_, .latin1) catch unreachable;
}
return;
}
@@ -2170,9 +2175,12 @@ pub const Formatter = struct {
writer.print("[String: ", .{});
if (str.isUTF16()) {
// Use JSON path for UTF-16 to properly handle surrogate pairs (emojis)
try this.printAs(.JSON, Writer, writer_, value, .StringObject, enable_ansi_colors);
} else if (str.asUTF8()) |utf8| {
JSPrinter.writeQuotedString(utf8, Writer, writer_, .utf8) catch unreachable;
} else {
JSPrinter.writeJSONString(str.latin1(), Writer, writer_, .latin1) catch unreachable;
JSPrinter.writeQuotedString(str.latin1(), Writer, writer_, .latin1) catch unreachable;
}
writer.print("]", .{});

View File

@@ -353,6 +353,27 @@ pub fn writeJSONString(input: []const u8, comptime Writer: type, writer: Writer,
try writer.writeAll("\"");
}
/// Writes a quoted string for console output, choosing the best quote character
/// to minimize escaping. Supports latin1 and utf8 encodings (use JSON path for UTF-16).
pub fn writeQuotedString(input: []const u8, comptime Writer: type, writer: Writer, comptime encoding: strings.Encoding) !void {
comptime {
if (encoding != .latin1 and encoding != .utf8) {
@compileError("writeQuotedString only supports latin1 and utf8 encodings; use JSON path for UTF-16");
}
}
// Reuse existing bestQuoteCharForString which counts quotes in a single pass
const quote_char = bestQuoteCharForString(u8, input, false);
if (quote_char == '\'') {
try writer.writeAll("'");
try writePreQuotedString(input, Writer, writer, '\'', false, false, encoding);
try writer.writeAll("'");
} else {
try writer.writeAll("\"");
try writePreQuotedString(input, Writer, writer, '"', false, false, encoding);
try writer.writeAll("\"");
}
}
pub const SourceMapHandler = struct {
ctx: *anyopaque,
callback: Callback,

View File

@@ -0,0 +1,53 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe } from "harness";
// https://github.com/oven-sh/bun/issues/25234
// Console output should use single quotes when string contains double quotes
// to avoid ugly escaping like: "{\"test\":{\"pretty\":\"pretty\"}}"
test("console.log uses single quotes for strings containing double quotes", async () => {
await using proc = Bun.spawn({
cmd: [bunExe(), "-e", 'console.log({ test: JSON.stringify({ test: { pretty: "pretty" } }) });'],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
// Should use single quotes to avoid escaping double quotes
expect(stdout).toContain("'");
expect(stdout).not.toContain('\\"'); // no escaped double quotes
expect(exitCode).toBe(0);
});
test("console.log uses double quotes for strings containing single quotes", async () => {
await using proc = Bun.spawn({
cmd: [bunExe(), "-e", `console.log({ a: "hello 'world'" });`],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
// Should use double quotes to avoid escaping single quotes
expect(stdout).toContain("\"hello 'world'\"");
expect(stdout).not.toContain("\\'"); // no escaped single quotes
expect(exitCode).toBe(0);
});
test("console.log uses double quotes by default", async () => {
await using proc = Bun.spawn({
cmd: [bunExe(), "-e", 'console.log({ a: "hello world" });'],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
// Default should be double quotes
expect(stdout).toContain('"hello world"');
expect(exitCode).toBe(0);
});