@prostojs/dye
Version:
Easy and light console styling tool
415 lines (414 loc) • 14.2 kB
JavaScript
//#region src/index.ts
const DyeColors = [
"black",
"red",
"green",
"yellow",
"blue",
"magenta",
"cyan",
"white"
];
const DyeGrayscale = [
"gray01",
"gray02",
"gray03",
"gray04",
"gray05",
"gray06",
"gray07",
"gray08",
"gray09",
"gray10",
"gray11",
"gray12",
"gray13",
"gray14",
"gray15",
"gray16",
"gray17",
"gray18",
"gray19",
"gray20",
"gray21",
"gray22"
];
const DyeModifier = [
"bold",
"dim",
"italic",
"underscore",
"inverse",
"hidden",
"crossed"
];
var Codes = /* @__PURE__ */ function(Codes) {
Codes[Codes["RESET"] = 0] = "RESET";
Codes[Codes["BOLD"] = 1] = "BOLD";
Codes[Codes["DIM"] = 2] = "DIM";
Codes[Codes["ITALIC"] = 3] = "ITALIC";
Codes[Codes["UNDERSCORE"] = 4] = "UNDERSCORE";
Codes[Codes["INVERSE"] = 7] = "INVERSE";
Codes[Codes["HIDDEN"] = 8] = "HIDDEN";
Codes[Codes["CROSSED"] = 9] = "CROSSED";
Codes[Codes["BOLD_OFF"] = 22] = "BOLD_OFF";
Codes[Codes["DIM_OFF"] = 22] = "DIM_OFF";
Codes[Codes["ITALIC_OFF"] = 23] = "ITALIC_OFF";
Codes[Codes["UNDERSCORE_OFF"] = 24] = "UNDERSCORE_OFF";
Codes[Codes["BLINK_SLOW_OFF"] = 25] = "BLINK_SLOW_OFF";
Codes[Codes["BLINK_RAPID_OFF"] = 26] = "BLINK_RAPID_OFF";
Codes[Codes["INVERSE_OFF"] = 27] = "INVERSE_OFF";
Codes[Codes["REVEAL"] = 28] = "REVEAL";
Codes[Codes["CROSSED_OFF"] = 29] = "CROSSED_OFF";
Codes[Codes["BLACK"] = 30] = "BLACK";
Codes[Codes["RED"] = 31] = "RED";
Codes[Codes["GREEN"] = 32] = "GREEN";
Codes[Codes["YELLOW"] = 33] = "YELLOW";
Codes[Codes["BLUE"] = 34] = "BLUE";
Codes[Codes["MAGENTA"] = 35] = "MAGENTA";
Codes[Codes["CYAN"] = 36] = "CYAN";
Codes[Codes["WHITE"] = 37] = "WHITE";
Codes[Codes["COLORS"] = 38] = "COLORS";
Codes[Codes["COLOR_OFF"] = 39] = "COLOR_OFF";
Codes[Codes["BG_BLACK"] = 40] = "BG_BLACK";
Codes[Codes["BG_RED"] = 41] = "BG_RED";
Codes[Codes["BG_GREEN"] = 42] = "BG_GREEN";
Codes[Codes["BG_YELLOW"] = 43] = "BG_YELLOW";
Codes[Codes["BG_BLUE"] = 44] = "BG_BLUE";
Codes[Codes["BG_MAGENTA"] = 45] = "BG_MAGENTA";
Codes[Codes["BG_CYAN"] = 46] = "BG_CYAN";
Codes[Codes["BG_WHITE"] = 47] = "BG_WHITE";
Codes[Codes["BG_COLORS"] = 48] = "BG_COLORS";
Codes[Codes["BG_COLOR_OFF"] = 49] = "BG_COLOR_OFF";
Codes[Codes["GRAY01"] = 233] = "GRAY01";
Codes[Codes["GRAY02"] = 234] = "GRAY02";
Codes[Codes["GRAY03"] = 235] = "GRAY03";
Codes[Codes["GRAY04"] = 236] = "GRAY04";
Codes[Codes["GRAY05"] = 237] = "GRAY05";
Codes[Codes["GRAY06"] = 238] = "GRAY06";
Codes[Codes["GRAY07"] = 239] = "GRAY07";
Codes[Codes["GRAY08"] = 240] = "GRAY08";
Codes[Codes["GRAY09"] = 241] = "GRAY09";
Codes[Codes["GRAY10"] = 242] = "GRAY10";
Codes[Codes["GRAY11"] = 243] = "GRAY11";
Codes[Codes["GRAY12"] = 244] = "GRAY12";
Codes[Codes["GRAY13"] = 245] = "GRAY13";
Codes[Codes["GRAY14"] = 246] = "GRAY14";
Codes[Codes["GRAY15"] = 247] = "GRAY15";
Codes[Codes["GRAY16"] = 248] = "GRAY16";
Codes[Codes["GRAY17"] = 249] = "GRAY17";
Codes[Codes["GRAY18"] = 250] = "GRAY18";
Codes[Codes["GRAY19"] = 251] = "GRAY19";
Codes[Codes["GRAY20"] = 252] = "GRAY20";
Codes[Codes["GRAY21"] = 253] = "GRAY21";
Codes[Codes["GRAY22"] = 254] = "GRAY22";
Codes[Codes["BLACK_BRIGHT"] = 90] = "BLACK_BRIGHT";
Codes[Codes["RED_BRIGHT"] = 91] = "RED_BRIGHT";
Codes[Codes["GREEN_BRIGHT"] = 92] = "GREEN_BRIGHT";
Codes[Codes["YELLOW_BRIGHT"] = 93] = "YELLOW_BRIGHT";
Codes[Codes["BLUE_BRIGHT"] = 94] = "BLUE_BRIGHT";
Codes[Codes["MAGENTA_BRIGHT"] = 95] = "MAGENTA_BRIGHT";
Codes[Codes["CYAN_BRIGHT"] = 96] = "CYAN_BRIGHT";
Codes[Codes["WHITE_BRIGHT"] = 97] = "WHITE_BRIGHT";
Codes[Codes["BG_BLACK_BRIGHT"] = 100] = "BG_BLACK_BRIGHT";
Codes[Codes["BG_RED_BRIGHT"] = 101] = "BG_RED_BRIGHT";
Codes[Codes["BG_GREEN_BRIGHT"] = 102] = "BG_GREEN_BRIGHT";
Codes[Codes["BG_YELLOW_BRIGHT"] = 103] = "BG_YELLOW_BRIGHT";
Codes[Codes["BG_BLUE_BRIGHT"] = 104] = "BG_BLUE_BRIGHT";
Codes[Codes["BG_MAGENTA_BRIGHT"] = 105] = "BG_MAGENTA_BRIGHT";
Codes[Codes["BG_CYAN_BRIGHT"] = 106] = "BG_CYAN_BRIGHT";
Codes[Codes["BG_WHITE_BRIGHT"] = 107] = "BG_WHITE_BRIGHT";
return Codes;
}(Codes || {});
const BG_OFFSET = 10;
const plain = (...n) => `\u001B[${n.join(";")}m`;
const gray = (n) => `\u001B[38;5;${n}m`;
const bgGray = (n) => `\u001B[48;5;${n}m`;
const rgb256 = (r, g, b, bg = false) => `\u001B[${38 + Number(bg) * BG_OFFSET};5;${16 + r * 36 + g * 6 + b}m`;
const tcRGB = (r, g, b, bg = false) => `\u001B[${38 + Number(bg) * BG_OFFSET};2;${r};${g};${b}m`;
const colors = {
"black": Codes.BLACK,
"red": Codes.RED,
"green": Codes.GREEN,
"yellow": Codes.YELLOW,
"blue": Codes.BLUE,
"magenta": Codes.MAGENTA,
"cyan": Codes.CYAN,
"white": Codes.WHITE,
"black-bright": Codes.BLACK_BRIGHT,
"red-bright": Codes.RED_BRIGHT,
"green-bright": Codes.GREEN_BRIGHT,
"yellow-bright": Codes.YELLOW_BRIGHT,
"blue-bright": Codes.BLUE_BRIGHT,
"magenta-bright": Codes.MAGENTA_BRIGHT,
"cyan-bright": Codes.CYAN_BRIGHT,
"white-bright": Codes.WHITE_BRIGHT
};
const grayColors = {
gray01: Codes.GRAY01,
gray02: Codes.GRAY02,
gray03: Codes.GRAY03,
gray04: Codes.GRAY04,
gray05: Codes.GRAY05,
gray06: Codes.GRAY06,
gray07: Codes.GRAY07,
gray08: Codes.GRAY08,
gray09: Codes.GRAY09,
gray10: Codes.GRAY10,
gray11: Codes.GRAY11,
gray12: Codes.GRAY12,
gray13: Codes.GRAY13,
gray14: Codes.GRAY14,
gray15: Codes.GRAY15,
gray16: Codes.GRAY16,
gray17: Codes.GRAY17,
gray18: Codes.GRAY18,
gray19: Codes.GRAY19,
gray20: Codes.GRAY20,
gray21: Codes.GRAY21,
gray22: Codes.GRAY22
};
const bgGrayColors = {
"bg-gray01": Codes.GRAY01,
"bg-gray02": Codes.GRAY02,
"bg-gray03": Codes.GRAY03,
"bg-gray04": Codes.GRAY04,
"bg-gray05": Codes.GRAY05,
"bg-gray06": Codes.GRAY06,
"bg-gray07": Codes.GRAY07,
"bg-gray08": Codes.GRAY08,
"bg-gray09": Codes.GRAY09,
"bg-gray10": Codes.GRAY10,
"bg-gray11": Codes.GRAY11,
"bg-gray12": Codes.GRAY12,
"bg-gray13": Codes.GRAY13,
"bg-gray14": Codes.GRAY14,
"bg-gray15": Codes.GRAY15,
"bg-gray16": Codes.GRAY16,
"bg-gray17": Codes.GRAY17,
"bg-gray18": Codes.GRAY18,
"bg-gray19": Codes.GRAY19,
"bg-gray20": Codes.GRAY20,
"bg-gray21": Codes.GRAY21,
"bg-gray22": Codes.GRAY22
};
const bgColors = {
"bg-black": Codes.BG_BLACK,
"bg-red": Codes.BG_RED,
"bg-green": Codes.BG_GREEN,
"bg-yellow": Codes.BG_YELLOW,
"bg-blue": Codes.BG_BLUE,
"bg-magenta": Codes.BG_MAGENTA,
"bg-cyan": Codes.BG_CYAN,
"bg-white": Codes.BG_WHITE,
"bg-black-bright": Codes.BG_BLACK_BRIGHT,
"bg-red-bright": Codes.BG_RED_BRIGHT,
"bg-green-bright": Codes.BG_GREEN_BRIGHT,
"bg-yellow-bright": Codes.BG_YELLOW_BRIGHT,
"bg-blue-bright": Codes.BG_BLUE_BRIGHT,
"bg-magenta-bright": Codes.BG_MAGENTA_BRIGHT,
"bg-cyan-bright": Codes.BG_CYAN_BRIGHT,
"bg-white-bright": Codes.BG_WHITE_BRIGHT
};
const modifiers = {
bold: Codes.BOLD,
dim: Codes.DIM,
italic: Codes.ITALIC,
underscore: Codes.UNDERSCORE,
inverse: Codes.INVERSE,
hidden: Codes.HIDDEN,
crossed: Codes.CROSSED
};
const modifiersClose = {
"color": Codes.COLOR_OFF,
"bg-color": Codes.BG_COLOR_OFF,
"bold": Codes.BOLD_OFF,
"dim": Codes.DIM_OFF,
"italic": Codes.ITALIC_OFF,
"underscore": Codes.UNDERSCORE_OFF,
"inverse": Codes.INVERSE_OFF,
"hidden": Codes.REVEAL,
"crossed": Codes.CROSSED_OFF
};
const dye = ((...args) => {
const openStack = [];
const openColorsStack = [];
const openBgColorsStack = [];
const openModifiersStack = [];
const closeModifiers = {};
if (args.length > 0) for (const c of args) if (c.indexOf(",") > 0 || c.includes("#")) {
let bg = false;
if (c.includes("#")) {
if (c.startsWith("bg")) bg = true;
let hex = c.split("#").pop();
if (!/^([0-9a-f]{3}|[0-9a-f]{6})$/iu.test(hex)) throw new Error(`[Dye] Wrong hex "#${hex}".`);
if (hex.length === 3) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
const r = Number.parseInt(hex.slice(0, 2), 16);
const g = Number.parseInt(hex.slice(2, 4), 16);
const b = Number.parseInt(hex.slice(4, 6), 16);
openStack.push(tcRGB(r, g, b, bg));
} else {
let mode256 = false;
let [r, g, b] = c.split(",");
if (r.startsWith("bg")) {
bg = true;
r = r.slice(2);
}
if (r.startsWith("*")) {
mode256 = true;
r = r.slice(1);
}
if (mode256) {
r = checkRGBNumber(r.trim(), "r", 5);
g = checkRGBNumber(g, "g", 5);
b = checkRGBNumber(b, "b", 5);
openStack.push(rgb256(r, g, b, bg));
} else {
r = checkRGBNumber(r.trim(), "r", 255);
g = checkRGBNumber(g, "g", 255);
b = checkRGBNumber(b, "b", 255);
openStack.push(tcRGB(r, g, b, bg));
}
}
if (bg) {
closeModifiers[Codes.BG_COLOR_OFF] = true;
openBgColorsStack.push(openStack[openStack.length - 1]);
} else {
closeModifiers[Codes.COLOR_OFF] = true;
openColorsStack.push(openStack[openStack.length - 1]);
}
} else if (colors[c]) {
openStack.push(colors[c]);
closeModifiers[Codes.COLOR_OFF] = true;
openColorsStack.push(openStack[openStack.length - 1]);
} else if (bgColors[c]) {
openStack.push(bgColors[c]);
closeModifiers[Codes.BG_COLOR_OFF] = true;
openBgColorsStack.push(openStack[openStack.length - 1]);
} else if (modifiers[c]) {
openStack.push(modifiers[c]);
closeModifiers[modifiersClose[c]] = true;
openModifiersStack.push(openStack[openStack.length - 1]);
} else if (grayColors[c]) {
openStack.push(gray(grayColors[c]));
closeModifiers[Codes.COLOR_OFF] = true;
openColorsStack.push(openStack[openStack.length - 1]);
} else if (bgGrayColors[c]) {
openStack.push(bgGray(bgGrayColors[c]));
closeModifiers[Codes.BG_COLOR_OFF] = true;
openBgColorsStack.push(openStack[openStack.length - 1]);
} else throw new Error(`[Dye] Color or modifier "${c}" is not supported.`);
function collapseStack(stack) {
let modStack = [];
let result = "";
for (const modifier of stack) if (typeof modifier === "number") modStack.push(modifier);
else {
if (modStack.length > 0) {
result += plain(...modStack);
modStack = [];
}
result += modifier;
}
if (modStack.length > 0) {
result += plain(...modStack);
modStack = [];
}
return result;
}
const open = collapseStack(openStack);
const openColors = collapseStack(openColorsStack);
const openBgColors = collapseStack(openBgColorsStack);
const openModifiers = collapseStack(openModifiersStack);
const closingCodes = Object.keys(closeModifiers);
return getStylist(open, closingCodes.length > 0 ? plain(...closingCodes) : "", "", "", {
openColors,
openBgColors,
openModifiers
});
});
dye.strip = (coloredText) => coloredText.replace(/\x1B\[[^m]+m/gu, "");
dye.reset = plain(Codes.RESET);
dye.bold_off = plain(Codes.BOLD_OFF);
dye.dim_off = plain(Codes.DIM_OFF);
dye.italic_off = plain(Codes.ITALIC_OFF);
dye.underscore_off = plain(Codes.UNDERSCORE_OFF);
dye.blink_slow_off = plain(Codes.BLINK_SLOW_OFF);
dye.blink_rapid_off = plain(Codes.BLINK_RAPID_OFF);
dye.inverse_off = plain(Codes.INVERSE_OFF);
dye.reveal = plain(Codes.REVEAL);
dye.crossed_off = plain(Codes.CROSSED_OFF);
function getStylist(open, close, prefix, suffix, parts, format) {
const resetOpen = dye.reset + open;
const getPrefix = (...texts) => {
const v = typeof prefix === "string" ? prefix : prefix(...texts);
return v ? open + v : "";
};
const getSuffix = (...texts) => {
const v = typeof suffix === "string" ? suffix : suffix(...texts);
return (v ? open + v : "") + close;
};
const formatDefault = (...texts) => texts.map(String).join(" ");
const stylist = (...texts) => {
const p = getPrefix(...texts) || "";
const s = getSuffix(...texts) || "";
return `${p}${open}${sanitizeString(format ? format(...texts) : formatDefault(...texts), parts)}${s}`;
};
stylist.open = open;
stylist.close = close;
stylist.format = (cb) => getStylist(open, close, prefix, suffix, parts, cb);
stylist.prefix = (v) => getStylist(open, close, v, suffix, parts, format);
stylist.suffix = (v) => getStylist(open, close, prefix, v, parts, format);
stylist.attachConsole = (...args) => {
const consoleInterface = args[1] || console;
let consoleMethod;
if (typeof args[0] === "string" || args[0] === void 0) consoleMethod = consoleInterface[args[0] || "log"];
else if (typeof args[0] === "function") consoleMethod = args[0];
let enabled = true;
const dyeConsole = (...consoleArgs) => {
if (enabled) {
const p = getPrefix(...consoleArgs);
const s = getSuffix(...consoleArgs);
const start = p ? dye.reset + p : "";
const end = s === close ? dye.reset : dye.reset + s;
if (format) consoleMethod(start + resetOpen + format(...consoleArgs) + end);
else {
let first = "";
if (typeof consoleArgs[0] === "string" || typeof consoleArgs[0] === "number") first = resetOpen + sanitizeString(String(consoleArgs.shift()), parts);
let last = "";
if (typeof consoleArgs[consoleArgs.length - 1] === "string") last = resetOpen + sanitizeString(consoleArgs.pop(), parts);
const newArgs = consoleArgs.flatMap((a) => {
if (typeof a === "string") return resetOpen + sanitizeString(a, parts);
else if (typeof a === "number") return resetOpen + a.toString();
else return [dye.reset, a];
});
if (newArgs.length > 0) consoleMethod(...[
start + first,
...newArgs,
last + end
].filter((e) => typeof e !== "string" || !!e));
else consoleMethod(start + first + last + end);
}
}
};
dyeConsole.enable = (v = true) => enabled = v;
dyeConsole.disable = () => enabled = false;
dyeConsole.asStylist = () => stylist;
return dyeConsole;
};
return stylist;
}
function sanitizeString(source, parts) {
let result = source;
if (parts.openColors) result = result.replace(/(\x1B\[[0-9;]*39[0-9;]*m)/gu, `$1${parts.openColors}`);
if (parts.openBgColors) result = result.replace(/(\x1B\[[0-9;]*49[0-9;]*m)/gu, `$1${parts.openBgColors}`);
if (parts.openModifiers) result = result.replace(/(\x1B\[(?:22|23|24)m)/gu, `$1${parts.openModifiers}`);
return result;
}
function checkRGBNumber(n, component, limit = 255) {
const _n = Number(n);
if (Number.isNaN(_n)) throw new TypeError(`[Dye] Color component "${component}" must be a number. Received "${n}".`);
if (_n > limit) throw new Error(`[Dye] Color component "${component}" must be less than ${limit + 1}. Received "${n}".`);
return _n;
}
//#endregion
export { DyeColors, DyeGrayscale, DyeModifier, dye };