@roboplay/sage
Version:
Codemod for Robo.js
278 lines (276 loc) • 7.26 kB
JavaScript
import { inspect } from 'node:util';
import { color } from './color.js';
const DEBUG_MODE = process.env.NODE_ENV !== "production";
const ANSI_REGEX = /\x1b\[.*?m/g;
const pendingDrains = /* @__PURE__ */ new Set();
function consoleDrain(logger2, level, ...data) {
switch (level) {
case "trace":
case "debug":
case "info":
case "wait":
case "event":
return writeLog(process.stdout, ...data);
case "warn":
case "error":
return writeLog(process.stderr, ...data);
default:
return writeLog(process.stdout, ...data);
}
}
function writeLog(stream, ...data) {
return new Promise((resolve, reject) => {
const parts = data?.map((item) => {
if (typeof item === "object" || item instanceof Error || Array.isArray(item)) {
return inspect(item, { colors: true, depth: null });
}
return item;
});
stream.write(parts?.join(" ") + "\n", "utf8", (error) => {
if (error)
reject(error);
else
resolve();
});
});
}
const DEFAULT_MAX_ENTRIES = 100;
class LogEntry {
level;
timestamp;
data;
constructor(level, data) {
this.level = level;
this.data = data;
this.timestamp = /* @__PURE__ */ new Date();
}
message() {
const messageParts = this.data.map((item) => {
if (item instanceof Error) {
return item.message;
} else if (typeof item === "object") {
try {
return JSON.stringify(item);
} catch (error) {
return "[unserializable object]";
}
}
return item;
});
return messageParts.join(" ").replace(ANSI_REGEX, "");
}
}
class Logger {
_customLevels;
_enabled;
_level;
_levelValues;
_parent;
_prefix;
_currentIndex;
_drain;
_logBuffer;
constructor(options) {
const { customLevels, drain = consoleDrain, enabled = true, level = "info", parent, prefix } = options ?? {};
this._customLevels = customLevels;
this._drain = drain;
this._enabled = enabled;
this._level = level;
this._parent = parent;
this._prefix = prefix;
this._levelValues = {
...LogLevelValues,
...Object.fromEntries(Object.entries(this._customLevels ?? {}).map(([key, value]) => [key, value.priority]))
};
this._currentIndex = 0;
this._logBuffer = new Array(options?.maxEntries ?? DEFAULT_MAX_ENTRIES);
}
_log(level, ...data) {
if (this._parent) {
data.unshift(this._prefix);
return this._parent._log(level, ...data);
}
if (!this._enabled) {
return;
}
if (this._drain === consoleDrain && this._levelValues[this._level] > this._levelValues[level]) {
return;
}
if (level !== "other") {
const label = this._customLevels ? this._customLevels[level]?.label : colorizedLogLevels[level];
data.unshift((label ?? level.padEnd(5)) + " -");
}
if (DEBUG_MODE) {
this._logBuffer[this._currentIndex] = new LogEntry(level, data);
this._currentIndex = (this._currentIndex + 1) % this._logBuffer.length;
}
const promise = this._drain(this, level, ...data);
pendingDrains.add(promise);
promise.finally(() => {
pendingDrains.delete(promise);
});
}
/**
* Waits for all pending log writes to complete.
*/
async flush() {
if (this._parent) {
return this._parent.flush();
}
await Promise.allSettled([...pendingDrains]);
}
/**
* Creates a new logger instance with the specified prefix.
* This is useful for creating a logger for a specific plugin, big features, or modules.
*
* All writes and cached logs will be delegated to the parent logger, so debugging will still work.
*
* @param prefix The prefix to add to the logger (e.g. 'my-plugin')
* @returns A new logger instance with the specified prefix
*/
fork(prefix) {
return new Logger({
customLevels: this._customLevels,
enabled: this._enabled,
level: this._level,
parent: this,
prefix: this._prefix ? this._prefix + prefix : prefix
});
}
getLevel() {
if (this._parent) {
return this._parent.getLevel();
}
return this._level;
}
getLevelValues() {
if (this._parent) {
return this._parent.getLevelValues();
}
return this._levelValues;
}
getRecentLogs(count = 50) {
if (this._parent) {
return this._parent.getRecentLogs(count);
}
if (count <= 0) {
return [];
}
count = Math.min(count, this._logBuffer.length);
const startIndex = (this._currentIndex - count + this._logBuffer.length) % this._logBuffer.length;
let recentLogs;
if (startIndex < this._currentIndex) {
recentLogs = this._logBuffer.slice(startIndex, this._currentIndex);
} else {
recentLogs = this._logBuffer.slice(startIndex).concat(this._logBuffer.slice(0, this._currentIndex));
}
return recentLogs.reverse();
}
setDrain(drain) {
this._drain = drain;
}
trace(...data) {
this._log("trace", ...data);
}
debug(...data) {
this._log("debug", ...data);
}
info(...data) {
this._log("info", ...data);
}
wait(...data) {
this._log("wait", ...data);
}
log(...data) {
this._log("other", ...data);
}
event(...data) {
this._log("event", ...data);
}
ready(...data) {
this._log("ready", ...data);
}
warn(...data) {
this._log("warn", ...data);
}
error(...data) {
this._log("error", ...data);
}
custom(level, ...data) {
if (this._customLevels?.[level]) {
this._log(level, ...data);
}
}
}
const LogLevelValues = {
trace: 0,
debug: 1,
info: 2,
wait: 3,
other: 4,
event: 5,
ready: 6,
warn: 7,
error: 8
};
const colorizedLogLevels = {
trace: color.gray("trace".padEnd(5)),
debug: color.cyan("debug".padEnd(5)),
info: color.blue("info".padEnd(5)),
wait: color.cyan("wait".padEnd(5)),
event: color.magenta("event".padEnd(5)),
ready: color.green("ready".padEnd(5)),
warn: color.yellow("warn".padEnd(5)),
error: color.red("error".padEnd(5))
};
let _logger = null;
function logger(options) {
if (options) {
_logger = new Logger(options);
} else if (!_logger) {
_logger = new Logger();
}
return _logger;
}
logger.flush = async function() {
await logger().flush();
};
logger.fork = function(prefix) {
return logger().fork(prefix);
};
logger.getRecentLogs = function(count = 25) {
return logger().getRecentLogs(count);
};
logger.trace = function(...data) {
return logger().trace(...data);
};
logger.debug = function(...data) {
return logger().debug(...data);
};
logger.info = function(...data) {
return logger().info(...data);
};
logger.wait = function(...data) {
return logger().wait(...data);
};
logger.log = function(...data) {
return logger().log(...data);
};
logger.event = function(...data) {
return logger().event(...data);
};
logger.ready = function(...data) {
return logger().ready(...data);
};
logger.warn = function(...data) {
return logger().warn(...data);
};
logger.error = function(...data) {
return logger().error(...data);
};
logger.custom = function(level, ...data) {
return logger().custom(level, ...data);
};
export { ANSI_REGEX, DEBUG_MODE, Logger, logger };
//# sourceMappingURL=out.js.map
//# sourceMappingURL=logger.js.map