Skip to content

Commit

Permalink
Implement Bun.inspect.table (#14486)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jarred-Sumner authored Oct 12, 2024
1 parent d3323c8 commit 43a5c4a
Show file tree
Hide file tree
Showing 10 changed files with 614 additions and 67 deletions.
59 changes: 59 additions & 0 deletions docs/api/utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,65 @@ const foo = new Foo();
console.log(foo); // => "foo"
```

## `Bun.inspect.table(tabularData, properties, options)`

Format tabular data into a string. Like [`console.table`](https://developer.mozilla.org/en-US/docs/Web/API/console/table_static), except it returns a string rather than printing to the console.

```ts
console.log(
Bun.inspect.table([
{ a: 1, b: 2, c: 3 },
{ a: 4, b: 5, c: 6 },
{ a: 7, b: 8, c: 9 },
]),
);
//
// ┌───┬───┬───┬───┐
// │ │ a │ b │ c │
// ├───┼───┼───┼───┤
// │ 0 │ 1 │ 2 │ 3 │
// │ 1 │ 4 │ 5 │ 6 │
// │ 2 │ 7 │ 8 │ 9 │
// └───┴───┴───┴───┘
```

Additionally, you can pass an array of property names to display only a subset of properties.

```ts
console.log(
Bun.inspect.table(
[
{ a: 1, b: 2, c: 3 },
{ a: 4, b: 5, c: 6 },
],
["a", "c"],
),
);
//
// ┌───┬───┬───┐
// │ │ a │ c │
// ├───┼───┼───┤
// │ 0 │ 1 │ 3 │
// │ 1 │ 4 │ 6 │
// └───┴───┴───┘
```

You can also conditionally enable ANSI colors by passing `{ colors: true }`.

```ts
console.log(
Bun.inspect.table(
[
{ a: 1, b: 2, c: 3 },
{ a: 4, b: 5, c: 6 },
],
{
colors: true,
},
),
);
```

## `Bun.nanoseconds()`

Returns the number of nanoseconds since the current `bun` process started, as a `number`. Useful for high-precision timing and benchmarking.
Expand Down
9 changes: 9 additions & 0 deletions packages/bun-types/bun.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3023,6 +3023,7 @@ declare module "bun" {
colors?: boolean;
depth?: number;
sorted?: boolean;
compact?: boolean;
}

/**
Expand All @@ -3038,6 +3039,14 @@ declare module "bun" {
* That can be used to declare custom inspect functions.
*/
const custom: typeof import("util").inspect.custom;

/**
* Pretty-print an object or array as a table
*
* Like {@link console.table}, except it returns a string
*/
function table(tabularData: object | unknown[], properties?: string[], options?: { colors?: boolean }): string;
function table(tabularData: object | unknown[], options?: { colors?: boolean }): string;
}

interface MMapOptions {
Expand Down
67 changes: 66 additions & 1 deletion src/bun.js/ConsoleObject.zig
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ pub fn messageWithTypeAndLevel(
}
}

const TablePrinter = struct {
pub const TablePrinter = struct {
const Column = struct {
name: String,
width: u32 = 1,
Expand Down Expand Up @@ -666,6 +666,69 @@ pub const FormatOptions = struct {
ordered_properties: bool = false,
quote_strings: bool = false,
max_depth: u16 = 2,
single_line: bool = false,

pub fn fromJS(formatOptions: *FormatOptions, globalThis: *JSC.JSGlobalObject, arguments: []const JSC.JSValue) !void {
const arg1 = arguments[0];

if (arg1.isObject()) {
if (arg1.getTruthy(globalThis, "depth")) |opt| {
if (opt.isInt32()) {
const arg = opt.toInt32();
if (arg < 0) {
globalThis.throwInvalidArguments("expected depth to be greater than or equal to 0, got {d}", .{arg});
return error.JSError;
}
formatOptions.max_depth = @as(u16, @truncate(@as(u32, @intCast(@min(arg, std.math.maxInt(u16))))));
} else if (opt.isNumber()) {
const v = opt.coerce(f64, globalThis);
if (std.math.isInf(v)) {
formatOptions.max_depth = std.math.maxInt(u16);
} else {
globalThis.throwInvalidArguments("expected depth to be an integer, got {d}", .{v});
return error.JSError;
}
}
}
if (try arg1.getOptional(globalThis, "colors", bool)) |opt| {
formatOptions.enable_colors = opt;
}
if (try arg1.getOptional(globalThis, "sorted", bool)) |opt| {
formatOptions.ordered_properties = opt;
}

if (try arg1.getOptional(globalThis, "compact", bool)) |opt| {
formatOptions.single_line = opt;
}
} else {
// formatOptions.show_hidden = arg1.toBoolean();
if (arguments.len > 0) {
var depthArg = arg1;
if (depthArg.isInt32()) {
const arg = depthArg.toInt32();
if (arg < 0) {
globalThis.throwInvalidArguments("expected depth to be greater than or equal to 0, got {d}", .{arg});
return error.JSError;
}
formatOptions.max_depth = @as(u16, @truncate(@as(u32, @intCast(@min(arg, std.math.maxInt(u16))))));
} else if (depthArg.isNumber()) {
const v = depthArg.coerce(f64, globalThis);
if (std.math.isInf(v)) {
formatOptions.max_depth = std.math.maxInt(u16);
} else {
globalThis.throwInvalidArguments("expected depth to be an integer, got {d}", .{v});
return error.JSError;
}
}
if (arguments.len > 1 and !arguments[1].isEmptyOrUndefinedOrNull()) {
formatOptions.enable_colors = arguments[1].coerce(bool, globalThis);
if (globalThis.hasException()) {
return error.JSError;
}
}
}
}
}
};

pub fn format2(
Expand Down Expand Up @@ -694,6 +757,7 @@ pub fn format2(
.ordered_properties = options.ordered_properties,
.quote_strings = options.quote_strings,
.max_depth = options.max_depth,
.single_line = options.single_line,
};
const tag = ConsoleObject.Formatter.Tag.get(vals[0], global);

Expand Down Expand Up @@ -771,6 +835,7 @@ pub fn format2(
.globalThis = global,
.ordered_properties = options.ordered_properties,
.quote_strings = options.quote_strings,
.single_line = options.single_line,
};
var tag: ConsoleObject.Formatter.Tag.Result = undefined;

Expand Down
Loading

0 comments on commit 43a5c4a

Please sign in to comment.