UNPKG

igir

Version:

🕹 A zero-setup ROM collection manager that sorts, filters, extracts or archives, patches, and reports on collections of any size on any OS.

179 lines (178 loc) • 6.7 kB
import fs from "node:fs"; import chalk from "chalk"; import stripAnsi from "strip-ansi"; import terminalLink from "terminal-link"; import Package from "../globals/package.js"; import DateUtil from "../utils/dateUtil.js"; import { LogLevel, LogLevelInverted } from "./logLevel.js"; import PrefixedLogger from "./prefixedLogger.js"; import { terminal } from "./terminal.js"; class Logger { terminal; logLevel = LogLevel.TRACE; logFileHandle; lastPrintedFrozen = false; constructor(terminal2) { this.terminal = terminal2; process.once("exit", () => { if (this.logFileHandle !== void 0) { fs.closeSync(this.logFileHandle); } }); } getLogLevel() { return this.logLevel; } setLogLevel(logLevel) { this.logLevel = logLevel; } /** * Create a {@link PrefixedLogger} that prepends every message it logs with the given prefix. */ child(prefix) { return new PrefixedLogger(this, prefix); } /** * Open (or replace) a file that every message is additionally written to, regardless of log level. */ openLogFile(logFile) { if (this.logFileHandle !== void 0) { fs.closeSync(this.logFileHandle); } this.logFileHandle = fs.openSync(logFile, "a"); } /** * Print a frozen progress-bar snapshot (always, as a permanent line above the live region). The * line is marked as frozen so that consecutive snapshots stay visually adjacent and a following * non-frozen log line is separated from it by a blank line. */ printFrozenBar(message = "", prefix) { return this.write(LogLevel.ALWAYS, message, prefix, true); } write(logLevel, message, prefix, isFrozen = false) { const willPrint = this.logLevel <= logLevel; if (!willPrint && this.logFileHandle === void 0) { return false; } let messageString = String(message); if (this.lastPrintedFrozen) { if (isFrozen) { messageString = messageString.replace(/^\n+/, ""); } else { messageString = ` ${messageString}`; } } if (willPrint) { this.lastPrintedFrozen = isFrozen; } if (this.logFileHandle !== void 0) { const formattedMessage = stripAnsi( Logger.formatMessage(LogLevel.TRACE, logLevel, messageString, prefix) ); if (formattedMessage.trim()) { fs.writeSync(this.logFileHandle, `${formattedMessage} `); } } if (!willPrint) { return false; } this.terminal.writeLine(Logger.formatMessage(this.logLevel, logLevel, messageString, prefix)); return true; } /** * Print a newline. */ newLine() { this.write(LogLevel.ALWAYS, ""); } static formatMessage(currentLogLevel, messageLogLevel, message, prefix) { if (messageLogLevel >= LogLevel.ALWAYS) { return message; } const chalkFuncs = { [LogLevel.ALWAYS]: (msg) => msg, [LogLevel.TRACE]: chalk.grey, [LogLevel.DEBUG]: chalk.magenta, [LogLevel.INFO]: chalk.cyan, [LogLevel.WARN]: chalk.yellow, [LogLevel.ERROR]: chalk.red, [LogLevel.NOTICE]: chalk.underline, [LogLevel.NEVER]: (msg) => msg }; const chalkFunc = chalkFuncs[messageLogLevel]; const loggerTime = currentLogLevel <= LogLevel.TRACE ? `[${DateUtil.format("HH:mm:ss.SSS")}] ` : ""; const levelPrefix = `${chalkFunc(LogLevelInverted[messageLogLevel])}: `; const loggerPrefix = currentLogLevel <= LogLevel.TRACE && prefix ? chalk.dim(`${prefix}: `) : ""; return message.replace(/Error: /, "").replace(/(\r?\n)(\r?\n)+/, "$1").split("\n").map((m) => m.trim() ? loggerTime + levelPrefix + loggerPrefix + m : m).join("\n"); } trace = (message = "", prefix) => { this.write(LogLevel.TRACE, message, prefix); }; debug = (message = "", prefix) => { this.write(LogLevel.DEBUG, message, prefix); }; info = (message = "", prefix) => { this.write(LogLevel.INFO, message, prefix); }; warn = (message = "", prefix) => { this.write(LogLevel.WARN, message, prefix); }; error = (message = "", prefix) => { this.write(LogLevel.ERROR, message, prefix); }; notice = (message = "", prefix) => { this.write(LogLevel.NOTICE, message, prefix); }; /** * Print the CLI header. */ printHeader() { const logo = ` @@@@@@ @@@@@@ @@@@@@ @@@@@@@@ @@ @@ @@ @@ @@ @@ @@ @@ @@ @@ @@ @@ @@@@@@ @@ @@@@@@ @@@@@@@@@ @@@ @@@@ @@@ @@ @@ @@ @@ @@ @@ @@ @@ @@ @@ @@ @@ @@ @@@@@@@@@@@@ @@ @@ @@`.replace(/^[\r\n]+/, ""); const logoSplit = logo.split("\n"); const midLine = Math.min(Math.ceil(logoSplit.length / 2), logoSplit.length - 1); const maxLineLen = logoSplit.reduce((max, line) => Math.max(max, line.length), 0); logoSplit[midLine - 2] = `${logoSplit[midLine - 2].padEnd(maxLineLen, " ")} ROM collection manager`; logoSplit[midLine - 1] = `${logoSplit[midLine - 1].padEnd(maxLineLen, " ")} ${terminalLink(Package.HOMEPAGE, Package.HOMEPAGE, { fallback: false })}`; let runtime = `Node.js v${process.versions.node}`; if (process.versions.bun) { runtime = `Bun v${process.versions.bun}`; } logoSplit[midLine + 1] = `${logoSplit[midLine + 1].padEnd(maxLineLen, " ")} v${Package.VERSION} ${chalk.dim(`(${runtime})`)}`; this.write(LogLevel.ALWAYS, `${logoSplit.join("\n")} `); } /** * Print a colorized yargs help string. */ colorizeYargs(help) { this.write( LogLevel.ALWAYS, help.replace(/^(Usage:.+)/, chalk.bold("$1")).replaceAll(/(\[commands\.*\])/g, chalk.magenta("$1")).replaceAll( new RegExp(`(${Package.NAME}) (( ?[a-z0-9])+)`, "g"), `$1 ${chalk.magenta("$2")}` ).replaceAll(/(\[options\.*\])/g, chalk.cyan("$1")).replaceAll( /([^a-zA-Z0-9-])(-[a-zA-Z0-9]([a-zA-Z0-9]|\n[ \t]*)*)/g, `$1${chalk.cyanBright("$2")}` ).replaceAll( /(--[a-zA-Z0-9][a-zA-Z0-9-]+(\n[ \t]+)?[a-zA-Z0-9-]+) ((?:[^ -])[^"][^ \n]*|"(?:[^"\\]|\\.)*")/g, `$1 ${chalk.underline("$3")}` ).replaceAll(/(--[a-zA-Z0-9][a-zA-Z0-9-]+(\n[ \t]+)?[a-zA-Z0-9-]+)/g, chalk.cyan("$1")).replaceAll(/(<[a-zA-Z]+>)/g, chalk.blue("$1")).replaceAll(/(\[(array|boolean|count|number|string)\])/g, chalk.grey("$1")).replaceAll(/(\[default: ([^[\]]+(\[[^\]]+\])?)*\])/g, chalk.green("$1")).replaceAll(/(\[required\])/g, chalk.red("$1")).replaceAll(/(\{[a-zA-Z]+\})/g, chalk.yellow("$1")).replaceAll(new RegExp(` (${Package.NAME}) `, "g"), ` ${chalk.blueBright("$1")} `) ); } } const logger = new Logger(terminal); export { Logger as default, logger }; //# sourceMappingURL=logger.js.map