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.
146 lines (145 loc) • 5.65 kB
JavaScript
import chalk from 'chalk';
import moment from 'moment';
import Package from '../globals/package.js';
import { LogLevel, LogLevelInverted } from './logLevel.js';
import MultiBar from './multiBar.js';
/**
* {@link Logger} is a class that deals with the formatting and outputting log messages to a stream.
*/
export default class Logger {
logLevel;
stream;
multiBar;
loggerPrefix;
constructor(logLevel, stream, multiBar, loggerPrefix) {
this.logLevel = logLevel;
this.stream = stream;
this.multiBar = multiBar ?? MultiBar.create({ writable: stream });
this.loggerPrefix = loggerPrefix;
}
getLogLevel() {
return this.logLevel;
}
setLogLevel(logLevel) {
this.logLevel = logLevel;
}
getStream() {
return this.stream;
}
print = (logLevel, message = '') => {
if (this.logLevel > logLevel) {
return;
}
this.stream.write(`${this.formatMessage(logLevel, String(message).toString())}\n`);
};
/**
* Print a newline.
*/
newLine() {
this.print(LogLevel.ALWAYS);
}
/**
* Format a log message for a given {@link LogLevelValue}.
*/
formatMessage(logLevel, message) {
// Don't format "ALWAYS" or "NEVER"
if (logLevel >= 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[logLevel];
const loggerTime = this.logLevel <= LogLevel.TRACE ? `[${moment().format('HH:mm:ss.SSS')}] ` : '';
const levelPrefix = `${chalkFunc(LogLevelInverted[logLevel])}: `;
const loggerPrefix = this.logLevel <= LogLevel.TRACE && this.loggerPrefix
? chalk.dim(`${this.loggerPrefix}: `)
: '';
return message
.replace(/Error: /, '') // strip `new Error()` prefix
.replace(/(\r?\n)(\r?\n)+/, '$1')
.split('\n')
.map((m) => (m.trim() ? loggerTime + levelPrefix + loggerPrefix + m : m))
.join('\n');
}
trace = (message = '') => {
this.print(LogLevel.TRACE, message);
};
debug = (message = '') => {
this.print(LogLevel.DEBUG, message);
};
info = (message = '') => {
this.print(LogLevel.INFO, message);
};
warn = (message = '') => {
this.print(LogLevel.WARN, message);
};
error = (message = '') => {
this.print(LogLevel.ERROR, message);
};
notice = (message = '') => {
this.print(LogLevel.NOTICE, message);
};
/**
* 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, ' ')} ${Package.HOMEPAGE}`;
logoSplit[midLine + 1] =
`${logoSplit[midLine + 1].padEnd(maxLineLen, ' ')} v${Package.VERSION}`;
this.print(LogLevel.ALWAYS, `${logoSplit.join('\n')}\n`);
}
/**
* Print a colorized yargs help string.
*/
colorizeYargs(help) {
this.print(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')} `));
}
/**
* Create a {@link ProgressBar} with a reference to this {@link Logger}.
*/
addProgressBar(options) {
return this.multiBar.addSingleBar(this, options);
}
/**
* Return a copy of this Logger with a new string prefix.
*/
withLoggerPrefix(prefix) {
return new Logger(this.logLevel, this.stream, this.multiBar, prefix);
}
}