518 lines
14 KiB
JavaScript
518 lines
14 KiB
JavaScript
'use strict';
|
|
|
|
const LogLevels = {
|
|
silent: Number.NEGATIVE_INFINITY,
|
|
fatal: 0,
|
|
error: 0,
|
|
warn: 1,
|
|
log: 2,
|
|
info: 3,
|
|
success: 3,
|
|
fail: 3,
|
|
ready: 3,
|
|
start: 3,
|
|
box: 3,
|
|
debug: 4,
|
|
trace: 5,
|
|
verbose: Number.POSITIVE_INFINITY
|
|
};
|
|
const LogTypes = {
|
|
// Silent
|
|
silent: {
|
|
level: -1
|
|
},
|
|
// Level 0
|
|
fatal: {
|
|
level: LogLevels.fatal
|
|
},
|
|
error: {
|
|
level: LogLevels.error
|
|
},
|
|
// Level 1
|
|
warn: {
|
|
level: LogLevels.warn
|
|
},
|
|
// Level 2
|
|
log: {
|
|
level: LogLevels.log
|
|
},
|
|
// Level 3
|
|
info: {
|
|
level: LogLevels.info
|
|
},
|
|
success: {
|
|
level: LogLevels.success
|
|
},
|
|
fail: {
|
|
level: LogLevels.fail
|
|
},
|
|
ready: {
|
|
level: LogLevels.info
|
|
},
|
|
start: {
|
|
level: LogLevels.info
|
|
},
|
|
box: {
|
|
level: LogLevels.info
|
|
},
|
|
// Level 4
|
|
debug: {
|
|
level: LogLevels.debug
|
|
},
|
|
// Level 5
|
|
trace: {
|
|
level: LogLevels.trace
|
|
},
|
|
// Verbose
|
|
verbose: {
|
|
level: LogLevels.verbose
|
|
}
|
|
};
|
|
|
|
function isPlainObject$1(value) {
|
|
if (value === null || typeof value !== "object") {
|
|
return false;
|
|
}
|
|
const prototype = Object.getPrototypeOf(value);
|
|
if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) {
|
|
return false;
|
|
}
|
|
if (Symbol.iterator in value) {
|
|
return false;
|
|
}
|
|
if (Symbol.toStringTag in value) {
|
|
return Object.prototype.toString.call(value) === "[object Module]";
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function _defu(baseObject, defaults, namespace = ".", merger) {
|
|
if (!isPlainObject$1(defaults)) {
|
|
return _defu(baseObject, {}, namespace, merger);
|
|
}
|
|
const object = Object.assign({}, defaults);
|
|
for (const key in baseObject) {
|
|
if (key === "__proto__" || key === "constructor") {
|
|
continue;
|
|
}
|
|
const value = baseObject[key];
|
|
if (value === null || value === void 0) {
|
|
continue;
|
|
}
|
|
if (merger && merger(object, key, value, namespace)) {
|
|
continue;
|
|
}
|
|
if (Array.isArray(value) && Array.isArray(object[key])) {
|
|
object[key] = [...value, ...object[key]];
|
|
} else if (isPlainObject$1(value) && isPlainObject$1(object[key])) {
|
|
object[key] = _defu(
|
|
value,
|
|
object[key],
|
|
(namespace ? `${namespace}.` : "") + key.toString(),
|
|
merger
|
|
);
|
|
} else {
|
|
object[key] = value;
|
|
}
|
|
}
|
|
return object;
|
|
}
|
|
function createDefu(merger) {
|
|
return (...arguments_) => (
|
|
// eslint-disable-next-line unicorn/no-array-reduce
|
|
arguments_.reduce((p, c) => _defu(p, c, "", merger), {})
|
|
);
|
|
}
|
|
const defu = createDefu();
|
|
|
|
function isPlainObject(obj) {
|
|
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
}
|
|
function isLogObj(arg) {
|
|
if (!isPlainObject(arg)) {
|
|
return false;
|
|
}
|
|
if (!arg.message && !arg.args) {
|
|
return false;
|
|
}
|
|
if (arg.stack) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
let paused = false;
|
|
const queue = [];
|
|
class Consola {
|
|
options;
|
|
_lastLog;
|
|
_mockFn;
|
|
/**
|
|
* Creates an instance of Consola with specified options or defaults.
|
|
*
|
|
* @param {Partial<ConsolaOptions>} [options={}] - Configuration options for the Consola instance.
|
|
*/
|
|
constructor(options = {}) {
|
|
const types = options.types || LogTypes;
|
|
this.options = defu(
|
|
{
|
|
...options,
|
|
defaults: { ...options.defaults },
|
|
level: _normalizeLogLevel(options.level, types),
|
|
reporters: [...options.reporters || []]
|
|
},
|
|
{
|
|
types: LogTypes,
|
|
throttle: 1e3,
|
|
throttleMin: 5,
|
|
formatOptions: {
|
|
date: true,
|
|
colors: false,
|
|
compact: true
|
|
}
|
|
}
|
|
);
|
|
for (const type in types) {
|
|
const defaults = {
|
|
type,
|
|
...this.options.defaults,
|
|
...types[type]
|
|
};
|
|
this[type] = this._wrapLogFn(defaults);
|
|
this[type].raw = this._wrapLogFn(
|
|
defaults,
|
|
true
|
|
);
|
|
}
|
|
if (this.options.mockFn) {
|
|
this.mockTypes();
|
|
}
|
|
this._lastLog = {};
|
|
}
|
|
/**
|
|
* Gets the current log level of the Consola instance.
|
|
*
|
|
* @returns {number} The current log level.
|
|
*/
|
|
get level() {
|
|
return this.options.level;
|
|
}
|
|
/**
|
|
* Sets the minimum log level that will be output by the instance.
|
|
*
|
|
* @param {number} level - The new log level to set.
|
|
*/
|
|
set level(level) {
|
|
this.options.level = _normalizeLogLevel(
|
|
level,
|
|
this.options.types,
|
|
this.options.level
|
|
);
|
|
}
|
|
/**
|
|
* Displays a prompt to the user and returns the response.
|
|
* Throw an error if `prompt` is not supported by the current configuration.
|
|
*
|
|
* @template T
|
|
* @param {string} message - The message to display in the prompt.
|
|
* @param {T} [opts] - Optional options for the prompt. See {@link PromptOptions}.
|
|
* @returns {promise<T>} A promise that infer with the prompt options. See {@link PromptOptions}.
|
|
*/
|
|
prompt(message, opts) {
|
|
if (!this.options.prompt) {
|
|
throw new Error("prompt is not supported!");
|
|
}
|
|
return this.options.prompt(message, opts);
|
|
}
|
|
/**
|
|
* Creates a new instance of Consola, inheriting options from the current instance, with possible overrides.
|
|
*
|
|
* @param {Partial<ConsolaOptions>} options - Optional overrides for the new instance. See {@link ConsolaOptions}.
|
|
* @returns {ConsolaInstance} A new Consola instance. See {@link ConsolaInstance}.
|
|
*/
|
|
create(options) {
|
|
const instance = new Consola({
|
|
...this.options,
|
|
...options
|
|
});
|
|
if (this._mockFn) {
|
|
instance.mockTypes(this._mockFn);
|
|
}
|
|
return instance;
|
|
}
|
|
/**
|
|
* Creates a new Consola instance with the specified default log object properties.
|
|
*
|
|
* @param {InputLogObject} defaults - Default properties to include in any log from the new instance. See {@link InputLogObject}.
|
|
* @returns {ConsolaInstance} A new Consola instance. See {@link ConsolaInstance}.
|
|
*/
|
|
withDefaults(defaults) {
|
|
return this.create({
|
|
...this.options,
|
|
defaults: {
|
|
...this.options.defaults,
|
|
...defaults
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Creates a new Consola instance with a specified tag, which will be included in every log.
|
|
*
|
|
* @param {string} tag - The tag to include in each log of the new instance.
|
|
* @returns {ConsolaInstance} A new Consola instance. See {@link ConsolaInstance}.
|
|
*/
|
|
withTag(tag) {
|
|
return this.withDefaults({
|
|
tag: this.options.defaults.tag ? this.options.defaults.tag + ":" + tag : tag
|
|
});
|
|
}
|
|
/**
|
|
* Adds a custom reporter to the Consola instance.
|
|
* Reporters will be called for each log message, depending on their implementation and log level.
|
|
*
|
|
* @param {ConsolaReporter} reporter - The reporter to add. See {@link ConsolaReporter}.
|
|
* @returns {Consola} The current Consola instance.
|
|
*/
|
|
addReporter(reporter) {
|
|
this.options.reporters.push(reporter);
|
|
return this;
|
|
}
|
|
/**
|
|
* Removes a custom reporter from the Consola instance.
|
|
* If no reporter is specified, all reporters will be removed.
|
|
*
|
|
* @param {ConsolaReporter} reporter - The reporter to remove. See {@link ConsolaReporter}.
|
|
* @returns {Consola} The current Consola instance.
|
|
*/
|
|
removeReporter(reporter) {
|
|
if (reporter) {
|
|
const i = this.options.reporters.indexOf(reporter);
|
|
if (i !== -1) {
|
|
return this.options.reporters.splice(i, 1);
|
|
}
|
|
} else {
|
|
this.options.reporters.splice(0);
|
|
}
|
|
return this;
|
|
}
|
|
/**
|
|
* Replaces all reporters of the Consola instance with the specified array of reporters.
|
|
*
|
|
* @param {ConsolaReporter[]} reporters - The new reporters to set. See {@link ConsolaReporter}.
|
|
* @returns {Consola} The current Consola instance.
|
|
*/
|
|
setReporters(reporters) {
|
|
this.options.reporters = Array.isArray(reporters) ? reporters : [reporters];
|
|
return this;
|
|
}
|
|
wrapAll() {
|
|
this.wrapConsole();
|
|
this.wrapStd();
|
|
}
|
|
restoreAll() {
|
|
this.restoreConsole();
|
|
this.restoreStd();
|
|
}
|
|
/**
|
|
* Overrides console methods with Consola logging methods for consistent logging.
|
|
*/
|
|
wrapConsole() {
|
|
for (const type in this.options.types) {
|
|
if (!console["__" + type]) {
|
|
console["__" + type] = console[type];
|
|
}
|
|
console[type] = this[type].raw;
|
|
}
|
|
}
|
|
/**
|
|
* Restores the original console methods, removing Consola overrides.
|
|
*/
|
|
restoreConsole() {
|
|
for (const type in this.options.types) {
|
|
if (console["__" + type]) {
|
|
console[type] = console["__" + type];
|
|
delete console["__" + type];
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Overrides standard output and error streams to redirect them through Consola.
|
|
*/
|
|
wrapStd() {
|
|
this._wrapStream(this.options.stdout, "log");
|
|
this._wrapStream(this.options.stderr, "log");
|
|
}
|
|
_wrapStream(stream, type) {
|
|
if (!stream) {
|
|
return;
|
|
}
|
|
if (!stream.__write) {
|
|
stream.__write = stream.write;
|
|
}
|
|
stream.write = (data) => {
|
|
this[type].raw(String(data).trim());
|
|
};
|
|
}
|
|
/**
|
|
* Restores the original standard output and error streams, removing the Consola redirection.
|
|
*/
|
|
restoreStd() {
|
|
this._restoreStream(this.options.stdout);
|
|
this._restoreStream(this.options.stderr);
|
|
}
|
|
_restoreStream(stream) {
|
|
if (!stream) {
|
|
return;
|
|
}
|
|
if (stream.__write) {
|
|
stream.write = stream.__write;
|
|
delete stream.__write;
|
|
}
|
|
}
|
|
/**
|
|
* Pauses logging, queues incoming logs until resumed.
|
|
*/
|
|
pauseLogs() {
|
|
paused = true;
|
|
}
|
|
/**
|
|
* Resumes logging, processing any queued logs.
|
|
*/
|
|
resumeLogs() {
|
|
paused = false;
|
|
const _queue = queue.splice(0);
|
|
for (const item of _queue) {
|
|
item[0]._logFn(item[1], item[2]);
|
|
}
|
|
}
|
|
/**
|
|
* Replaces logging methods with mocks if a mock function is provided.
|
|
*
|
|
* @param {ConsolaOptions["mockFn"]} mockFn - The function to use for mocking logging methods. See {@link ConsolaOptions["mockFn"]}.
|
|
*/
|
|
mockTypes(mockFn) {
|
|
const _mockFn = mockFn || this.options.mockFn;
|
|
this._mockFn = _mockFn;
|
|
if (typeof _mockFn !== "function") {
|
|
return;
|
|
}
|
|
for (const type in this.options.types) {
|
|
this[type] = _mockFn(type, this.options.types[type]) || this[type];
|
|
this[type].raw = this[type];
|
|
}
|
|
}
|
|
_wrapLogFn(defaults, isRaw) {
|
|
return (...args) => {
|
|
if (paused) {
|
|
queue.push([this, defaults, args, isRaw]);
|
|
return;
|
|
}
|
|
return this._logFn(defaults, args, isRaw);
|
|
};
|
|
}
|
|
_logFn(defaults, args, isRaw) {
|
|
if ((defaults.level || 0) > this.level) {
|
|
return false;
|
|
}
|
|
const logObj = {
|
|
date: /* @__PURE__ */ new Date(),
|
|
args: [],
|
|
...defaults,
|
|
level: _normalizeLogLevel(defaults.level, this.options.types)
|
|
};
|
|
if (!isRaw && args.length === 1 && isLogObj(args[0])) {
|
|
Object.assign(logObj, args[0]);
|
|
} else {
|
|
logObj.args = [...args];
|
|
}
|
|
if (logObj.message) {
|
|
logObj.args.unshift(logObj.message);
|
|
delete logObj.message;
|
|
}
|
|
if (logObj.additional) {
|
|
if (!Array.isArray(logObj.additional)) {
|
|
logObj.additional = logObj.additional.split("\n");
|
|
}
|
|
logObj.args.push("\n" + logObj.additional.join("\n"));
|
|
delete logObj.additional;
|
|
}
|
|
logObj.type = typeof logObj.type === "string" ? logObj.type.toLowerCase() : "log";
|
|
logObj.tag = typeof logObj.tag === "string" ? logObj.tag : "";
|
|
const resolveLog = (newLog = false) => {
|
|
const repeated = (this._lastLog.count || 0) - this.options.throttleMin;
|
|
if (this._lastLog.object && repeated > 0) {
|
|
const args2 = [...this._lastLog.object.args];
|
|
if (repeated > 1) {
|
|
args2.push(`(repeated ${repeated} times)`);
|
|
}
|
|
this._log({ ...this._lastLog.object, args: args2 });
|
|
this._lastLog.count = 1;
|
|
}
|
|
if (newLog) {
|
|
this._lastLog.object = logObj;
|
|
this._log(logObj);
|
|
}
|
|
};
|
|
clearTimeout(this._lastLog.timeout);
|
|
const diffTime = this._lastLog.time && logObj.date ? logObj.date.getTime() - this._lastLog.time.getTime() : 0;
|
|
this._lastLog.time = logObj.date;
|
|
if (diffTime < this.options.throttle) {
|
|
try {
|
|
const serializedLog = JSON.stringify([
|
|
logObj.type,
|
|
logObj.tag,
|
|
logObj.args
|
|
]);
|
|
const isSameLog = this._lastLog.serialized === serializedLog;
|
|
this._lastLog.serialized = serializedLog;
|
|
if (isSameLog) {
|
|
this._lastLog.count = (this._lastLog.count || 0) + 1;
|
|
if (this._lastLog.count > this.options.throttleMin) {
|
|
this._lastLog.timeout = setTimeout(
|
|
resolveLog,
|
|
this.options.throttle
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
} catch {
|
|
}
|
|
}
|
|
resolveLog(true);
|
|
}
|
|
_log(logObj) {
|
|
for (const reporter of this.options.reporters) {
|
|
reporter.log(logObj, {
|
|
options: this.options
|
|
});
|
|
}
|
|
}
|
|
}
|
|
function _normalizeLogLevel(input, types = {}, defaultLevel = 3) {
|
|
if (input === void 0) {
|
|
return defaultLevel;
|
|
}
|
|
if (typeof input === "number") {
|
|
return input;
|
|
}
|
|
if (types[input] && types[input].level !== void 0) {
|
|
return types[input].level;
|
|
}
|
|
return defaultLevel;
|
|
}
|
|
Consola.prototype.add = Consola.prototype.addReporter;
|
|
Consola.prototype.remove = Consola.prototype.removeReporter;
|
|
Consola.prototype.clear = Consola.prototype.removeReporter;
|
|
Consola.prototype.withScope = Consola.prototype.withTag;
|
|
Consola.prototype.mock = Consola.prototype.mockTypes;
|
|
Consola.prototype.pause = Consola.prototype.pauseLogs;
|
|
Consola.prototype.resume = Consola.prototype.resumeLogs;
|
|
function createConsola(options = {}) {
|
|
return new Consola(options);
|
|
}
|
|
|
|
exports.Consola = Consola;
|
|
exports.LogLevels = LogLevels;
|
|
exports.LogTypes = LogTypes;
|
|
exports.createConsola = createConsola;
|