import { ArrayFrom, ArrayPrototypePush, Map, MapIteratorPrototypeNext, MapPrototype, MapPrototypeEntries, MapPrototypeGet, MapPrototypeHas, MapPrototypeSet, MathMin, ObjectGetOwnPropertyDescriptor, ReflectApply, ReflectOwnKeys, Set, SetIteratorPrototypeNext, SetPrototype, SetPrototypeValues, String, StringPrototypeRepeat, StringPrototypeSlice, SymbolIterator, TypeError, } from "ext:core/primordials"; import { inspect, intrinsicPrototype, property } from "./inspect.js"; import { format, labelString } from "./format.js"; function table( data: unknown, columns: unknown, print: (stderr: boolean, text: string) => void, ): void { if (data === null || typeof data !== "object") { print(false, format([data])); return; } // Keep data columns distinct from the synthetic index and value columns. const rows: Map[] = []; const headers = ["(index)"]; const columnNames = new Map(); let valuesColumn = 0; let selected: string[] | null = null; if (columns !== undefined) { if ( columns === null || (typeof columns !== "object" && typeof columns !== "function") ) { throw new TypeError("Console table properties must be iterable"); } const iterator = (columns as { [SymbolIterator]?: unknown })[SymbolIterator]; if (typeof iterator !== "function") { throw new TypeError("Console table properties must be iterable"); } selected = ArrayFrom({ [SymbolIterator]: () => ReflectApply(iterator, columns, []) as Iterator, }, labelString); } const setValue = (row: Map, text: string) => { if (!valuesColumn) valuesColumn = ArrayPrototypePush(headers, "Values") - 1; MapPrototypeSet(row, valuesColumn, text); }; try { const prototype = intrinsicPrototype(data); if (prototype === MapPrototype || prototype === SetPrototype) { const isMap = prototype === MapPrototype; const iterator = isMap ? MapPrototypeEntries(data as Map) : SetPrototypeValues(data as Set); const next = isMap ? MapIteratorPrototypeNext : SetIteratorPrototypeNext; headers[0] = "(iteration index)"; if (isMap) ArrayPrototypePush(headers, "Key"); valuesColumn = ArrayPrototypePush(headers, "Values") - 1; for (let index = 0; index < 50; index++) { const step = next(iterator); if (step.done) break; const row = new Map(); MapPrototypeSet(row, 0, String(index)); if (isMap) { MapPrototypeSet( row, 1, inspect((step.value as [unknown, unknown])[0], { depth: 2 }), ); } setValue( row, inspect(isMap ? (step.value as [unknown, unknown])[1] : step.value, { depth: 2, }), ); ArrayPrototypePush(rows, row); } } else { const keys = ReflectOwnKeys(data); for (let index = 0; index < keys.length && rows.length < 50; index++) { const key = keys[index]; const descriptor = ObjectGetOwnPropertyDescriptor(data, key); if (!descriptor?.enumerable) continue; const row = new Map(); MapPrototypeSet(row, 0, String(key)); const value: unknown = descriptor.value; if (value !== null && typeof value === "object") { const names = selected || ReflectOwnKeys(value); for (let index = 0; index < names.length && index < 50; index++) { const name = names[index]; const descriptor = ObjectGetOwnPropertyDescriptor(value, name); if (!descriptor?.enumerable) continue; const label = String(name); if (!MapPrototypeHas(columnNames, name)) { if (headers.length === 50) continue; MapPrototypeSet( columnNames, name, ArrayPrototypePush(headers, label) - 1, ); } MapPrototypeSet( row, MapPrototypeGet(columnNames, name)!, property(value, name, 2), ); } } else setValue(row, property(data, key, 2)); ArrayPrototypePush(rows, row); } } } catch { print(false, format([data])); return; } const widths: number[] = []; for (let column = 0; column < headers.length; column++) { let width = headers[column].length; for (let row = 0; row < rows.length; row++) { const value = MapPrototypeGet(rows[row], column) || ""; if (value.length > width) width = value.length; } ArrayPrototypePush(widths, MathMin(width, 80)); } let border = "+"; for (let index = 0; index < widths.length; index++) { border += `${StringPrototypeRepeat("-", widths[index] + 2)}+`; } const line = (row: string[]) => { let text = "|"; for (let column = 0; column < headers.length; column++) { const value = StringPrototypeSlice(row[column] || "", 0, widths[column]); text += ` ${value}${ StringPrototypeRepeat(" ", widths[column] - value.length) } |`; } print(false, text); }; print(false, border); line(headers); print(false, border); for (let index = 0; index < rows.length; index++) { const row: string[] = []; for (let column = 0; column < headers.length; column++) { ArrayPrototypePush(row, MapPrototypeGet(rows[index], column) || ""); } line(row); } print(false, border); } export { table };