import { ArrayPrototypeUnshift, Error, ErrorCaptureStackTrace, ErrorGetStack, Map, MapPrototypeDelete, MapPrototypeGet, MapPrototypeHas, MapPrototypeSet, MathMin, NumberPrototypeToFixed, ObjectCreate, ObjectDefineProperty, ObjectGetOwnPropertyDescriptor, ObjectPrototype, ObjectSetPrototypeOf, StringPrototypeRepeat, StringPrototypeReplaceAll, SymbolToStringTag, TypeError, } from "ext:core/primordials"; import { op_console_clear, op_console_colors, op_console_now, op_console_print, } from "ext:core/ops"; import { onRuntimeStart } from "ext:core/bootstrap"; import { inspect } from "./inspect.js"; import { format, labelString } from "./format.js"; import { table } from "./table.js"; const counters = new Map(); const timers = new Map(); let groupDepth = 0; let stdoutColors: boolean | undefined, stderrColors: boolean | undefined; onRuntimeStart(() => { stdoutColors = op_console_colors(false); stderrColors = op_console_colors(true); }); function print(stderr: boolean, text: unknown): void { const indent = StringPrototypeRepeat(" ", MathMin(groupDepth, 20)); op_console_print([ stderr, `${indent}${StringPrototypeReplaceAll(text, "\n", `\n${indent}`)}\n`, ]); } function logger(stderr: boolean, args: unknown[]): void { if (args.length) { print(stderr, format(args, stderr ? stderrColors : stdoutColors)); } } function elapsed(label: string): string | null { if (!MapPrototypeHas(timers, label)) { print(true, `Timer '${label}' does not exist`); return null; } const duration = op_console_now() - MapPrototypeGet(timers, label)!; return `${label}: ${NumberPrototypeToFixed(duration, 3)} ms`; } const console = { log(...args: unknown[]) { logger(false, args); }, debug(...args: unknown[]) { logger(false, args); }, info(...args: unknown[]) { logger(false, args); }, warn(...args: unknown[]) { logger(true, args); }, error(...args: unknown[]) { logger(true, args); }, assert(condition: unknown = false, ...args: unknown[]) { if (condition) return; if (typeof args[0] === "string") args[0] = `Assertion failed: ${args[0]}`; else ArrayPrototypeUnshift(args, "Assertion failed"); logger(true, args); }, clear() { groupDepth = 0; op_console_clear(); }, count(value: unknown = "default") { const label = labelString(value); const count = (MapPrototypeGet(counters, label) || 0) + 1; MapPrototypeSet(counters, label, count); print(false, `${label}: ${count}`); }, countReset(value: unknown = "default") { const label = labelString(value); if (MapPrototypeHas(counters, label)) MapPrototypeSet(counters, label, 0); else print(true, `Count for '${label}' does not exist`); }, dir(value: unknown = undefined, options: unknown = undefined) { if ( options !== undefined && options !== null && typeof options !== "object" && typeof options !== "function" ) { throw new TypeError("Console dir options must be an object"); } const depth: unknown = options ? ObjectGetOwnPropertyDescriptor(options, "depth")?.value : undefined; print(false, inspect(value, { depth, colors: stdoutColors })); }, dirxml(...args: unknown[]) { logger(false, args); }, group(...args: unknown[]) { print(false, args.length ? format(args, stdoutColors) : "group"); groupDepth++; }, groupCollapsed(...args: unknown[]) { print(false, args.length ? format(args, stdoutColors) : "group"); groupDepth++; }, groupEnd() { if (groupDepth) groupDepth--; }, table(data: unknown = undefined, columns: unknown = undefined) { table(data, columns, print); }, time(value: unknown = "default") { const label = labelString(value); if (MapPrototypeHas(timers, label)) { print(true, `Timer '${label}' already exists`); } else MapPrototypeSet(timers, label, op_console_now()); }, timeLog(label: unknown = "default", ...args: unknown[]) { const text = elapsed(labelString(label)); if (text !== null) { print( false, args.length ? `${text} ${format(args, stdoutColors)}` : text, ); } }, timeEnd(value: unknown = "default") { const label = labelString(value); const text = elapsed(label); if (text !== null) { MapPrototypeDelete(timers, label); print(false, text); } }, trace(...args: unknown[]) { const error = new Error(args.length ? format(args) : ""); ObjectDefineProperty(error, "name", { value: "Trace", configurable: true }); ErrorCaptureStackTrace(error, traceMethod); print(true, ErrorGetStack(error)); }, }; const traceMethod = console.trace; ObjectSetPrototypeOf(console, ObjectCreate(ObjectPrototype)); ObjectDefineProperty(console, SymbolToStringTag, { value: "console", configurable: true, }); ObjectDefineProperty(globalThis, "console", { value: console, writable: true, configurable: true, });