| 49 | export type TableRow<Keys extends string> = Record<Keys, string>; |
| 50 | |
| 51 | export class Logger { |
| 52 | constructor() {} |
| 53 | |
| 54 | loggerLevel = getLoggerLevel(); |
| 55 | columns = process.stdout.columns; |
| 56 | |
| 57 | debug = (...args: unknown[]) => this.doLog("debug", args); |
| 58 | ignore = (...args: unknown[]) => {}; |
| 59 | debugWithSanitization = (label: string, ...args: unknown[]) => { |
| 60 | this.doLog("debug", [label, ...args]); |
| 61 | }; |
| 62 | info = (...args: unknown[]) => this.doLog("info", args); |
| 63 | log = (...args: unknown[]) => this.doLog("log", args); |
| 64 | warn = (...args: unknown[]) => this.doLog("warn", args); |
| 65 | error = (...args: unknown[]) => this.doLog("error", args); |
| 66 | table<Keys extends string>(data: TableRow<Keys>[], level?: Exclude<LoggerLevel, "none">) { |
| 67 | const keys: Keys[] = data.length === 0 ? [] : (Object.keys(data[0]!) as Keys[]); |
| 68 | const t = new CLITable({ |
| 69 | head: keys, |
| 70 | style: { |
| 71 | head: chalk.level ? ["blue"] : [], |
| 72 | border: chalk.level ? ["gray"] : [], |
| 73 | }, |
| 74 | }); |
| 75 | t.push(...data.map((row) => keys.map((k) => row[k]))); |
| 76 | return this.doLog(level ?? "log", [t.toString()]); |
| 77 | } |
| 78 | |
| 79 | private doLog(messageLevel: Exclude<LoggerLevel, "none">, args: unknown[]) { |
| 80 | const message = this.formatMessage(messageLevel, format(...args)); |
| 81 | |
| 82 | // only send logs to the terminal if their level is at least the configured log-level |
| 83 | if (LOGGER_LEVELS[this.loggerLevel] >= LOGGER_LEVELS[messageLevel]) { |
| 84 | console[messageLevel](message); |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | private formatMessage(level: Exclude<LoggerLevel, "none">, message: string): string { |
| 89 | const kind = LOGGER_LEVEL_FORMAT_TYPE_MAP[level]; |
| 90 | if (kind) { |
| 91 | // Format the message using the esbuild formatter. |
| 92 | // The first line of the message is the main `text`, |
| 93 | // subsequent lines are put into the `notes`. |
| 94 | const [firstLine, ...otherLines] = message.split("\n"); |
| 95 | const notes = otherLines.length > 0 ? otherLines.map((text) => ({ text })) : undefined; |
| 96 | return formatMessagesSync([{ text: firstLine, notes }], { |
| 97 | color: true, |
| 98 | kind, |
| 99 | terminalWidth: this.columns, |
| 100 | })[0]!; |
| 101 | } else { |
| 102 | return message; |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * A drop-in replacement for `console` for outputting logging messages. |
nothing calls this directly
no test coverage detected
searching dependent graphs…