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
JavaScript
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