UNPKG

nhb-toolbox

Version:

A versatile collection of smart, efficient, and reusable utility functions, classes and types for everyday development needs.

297 lines (296 loc) 11.3 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.Stylog = exports.LogStyler = void 0; exports.detectColorSupport = detectColorSupport; exports.rgbToAnsi = rgbToAnsi; exports.hexToAnsi = hexToAnsi; exports.isCSSColor = isCSSColor; exports.isBGColor = isBGColor; exports.isTextStyle = isTextStyle; const convert_1 = require("../colors/convert"); const css_colors_1 = require("../colors/css-colors"); const guards_1 = require("../colors/guards"); const helpers_1 = require("../colors/helpers"); const primitives_1 = require("../guards/primitives"); const specials_1 = require("../guards/specials"); const console_log_1 = require("./console.log"); const constants_1 = require("./constants"); const helpers_2 = require("./helpers"); function detectColorSupport() { if ('NO_COLOR' in process.env) return 0; if ('FORCE_COLOR' in process.env) return 3; if (!process.stdout.isTTY) return 0; const term = process.env.TERM ?? process.env.COLORTERM ?? ''; if (term === 'dumb') return 0; if (/\b256(color)?\b/i.test(term)) return 2; if (/\btruecolor\b|\b24bit\b/i.test(term)) return 3; return 1; } function rgbToAnsi(r, g, b, isBg = false) { const open = `\x1b[${isBg ? 48 : 38};2;${r};${g};${b}m`; const close = `\x1b[${isBg ? 49 : 39}m`; return [open, close]; } function hexToAnsi(hex, isBg = false) { const rgb = ((0, convert_1.convertHexToRgb)(hex).match(/\d+/g) || []).map(parseFloat); return rgbToAnsi(...rgb, isBg); } function isCSSColor(value) { return value in css_colors_1.CSS_COLORS; } function isBGColor(value) { return value?.startsWith('bg') && isCSSColor(value.slice(2).toLowerCase()); } function isTextStyle(value) { return value in constants_1.CSS_TEXT_STYLES || value in constants_1.ANSI_TEXT_STYLES; } class LogStyler { #styles; constructor(styles = []) { this.#styles = styles; } #applyStyles(...style) { return createStylogProxy(new _a([...this.#styles, ...style])); } style(...style) { return this.#applyStyles(...style); } ansi16(color) { return this.#applyStyles(constants_1.ANSI_16_COLORS[color], `css-${color}`); } toCSS(input, stringify = false) { const stringified = stringify === true ? JSON.stringify(input) : `${input}`; const cssList = []; for (const style of this.#styles) { if ((0, primitives_1.isString)(style)) { if (isTextStyle(style)) { cssList.push(constants_1.CSS_TEXT_STYLES[style]); } else if (isBGColor(style)) { const color = css_colors_1.CSS_COLORS[(0, helpers_2._extractColorName)(style)]; cssList.push(`background: ${color}`); } else if (isCSSColor(style)) { const color = css_colors_1.CSS_COLORS[style]; cssList.push(`color: ${color}`); } else if (this.#isValidHexOrRGB(style)) { if (style.startsWith('bg-')) { cssList.push(`background: ${style?.replace('bg-', '')}`); } else { cssList.push(`color: ${style}`); } } else if ((0, helpers_2._isCSS16Color)(style)) { const color = (0, helpers_2._css16ToHex)(style); const colorValue = style.startsWith('css-bg') ? `background: ${color}` : `color: ${color}`; cssList.push(colorValue); } } } return [`%c${stringified}`, cssList]; } toANSI(input, stringify = false) { const stringified = stringify === true ? JSON.stringify(input) : `${input}`; let openSeq = '', closeSeq = ''; let fgOpenSeq = '', bgOpenSeq = ''; const reopenSequences = new Map(); for (const style of this.#styles) { if ((0, primitives_1.isString)(style)) { if (isTextStyle(style)) { const [open, close] = constants_1.ANSI_TEXT_STYLES[style]; openSeq += open; closeSeq = close + closeSeq; reopenSequences.set(close, (reopenSequences.get(close) ?? '') + open); } else if (isBGColor(style)) { const hex = css_colors_1.CSS_COLORS[(0, helpers_2._extractColorName)(style)]; const [open, close] = hexToAnsi(hex, true); openSeq += open; closeSeq = close + closeSeq; bgOpenSeq = open; } else if (isCSSColor(style)) { const hex = css_colors_1.CSS_COLORS[style]; const [open, close] = hexToAnsi(hex, false); openSeq += open; closeSeq = close + closeSeq; fgOpenSeq = open; } } else if ((0, helpers_2._isAnsiSequence)(style)) { openSeq += style[0]; closeSeq = style[1] + closeSeq; if (style[1] === '\x1b[49m') { bgOpenSeq = style[0]; } else if (style[1] === '\x1b[39m') { fgOpenSeq = style[0]; } } else if ((0, helpers_2._isAnsi16ColorValue)(style)) { const [open, close] = style.map((s) => `\x1b[${s}m`); openSeq += open; closeSeq = close + closeSeq; if (close === '\x1b[49m') { bgOpenSeq = open; } else if (close === '\x1b[39m') { fgOpenSeq = open; } } } if (!detectColorSupport()) { return stringified; } else { let nestedStr = stringified; if (nestedStr.includes('\x1b[')) { if (fgOpenSeq) { nestedStr = nestedStr.replaceAll('\x1b[39m', `\x1b[39m${fgOpenSeq}`); } if (bgOpenSeq) { nestedStr = nestedStr.replaceAll('\x1b[49m', `\x1b[49m${bgOpenSeq}`); } for (const [close, reopen] of reopenSequences) { nestedStr = nestedStr.replaceAll(close, `${close}${reopen}`); } if (openSeq) { nestedStr = nestedStr.replaceAll('\x1b[0m', `\x1b[0m${openSeq}`); } } return openSeq.concat(nestedStr, closeSeq); } } log(input, stringify = false) { if ((0, specials_1.isBrowser)()) { const [fmt, cssList] = this.toCSS(input, stringify); (0, console_log_1._logToConsole)(fmt, cssList.join(';')); } else { (0, console_log_1._logToConsole)(this.toANSI(input, stringify)); } } #isValidHexOrRGB(color) { const pure = color?.replace('bg-', ''); return (0, guards_1.isHex6)(pure) || (0, guards_1.isRGB)(pure); } #sanitizeHex(code) { return code?.trim()?.startsWith('#') ? code?.trim() : `#${code?.trim()}`; } #handleHex(code, isBg = false) { const sanitized = this.#sanitizeHex(code); if (!(0, guards_1.isHex6)(sanitized)) { return this.#applyStyles(); } const ansi = hexToAnsi(sanitized, isBg); return this.#applyStyles(isBg ? `bg-${sanitized}` : sanitized, ansi); } hex(code) { return this.#handleHex(code, false); } bgHex(code) { return this.#handleHex(code, true); } #extractColorValues(code) { const trimmed = code?.trim(); return (trimmed?.match(/[\d.]+%?/g) || []).map(parseFloat); } #isValidRGB(...value) { return value.every(helpers_1._isValidRGBComponent); } #handleRGB(code, green, blue, isBg = false) { if ((0, primitives_1.isString)(code)) { const rgb = this.#extractColorValues(code); if (this.#isValidRGB(...rgb)) { return this.#applyStyles(rgbToAnsi(...rgb, isBg), isBg ? `bg-rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})` : `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`); } else { return this.#applyStyles(); } } else if ((0, primitives_1.isNumber)(code) && (0, primitives_1.isNumber)(green) && (0, primitives_1.isNumber)(blue)) { if (this.#isValidRGB(code, green, blue)) { return this.#applyStyles(rgbToAnsi(code, green, blue, isBg), isBg ? `bg-rgb(${code}, ${green}, ${blue})` : `rgb(${code}, ${green}, ${blue})`); } else { return this.#applyStyles(); } } else { return this.#applyStyles(); } } rgb(code, green, blue) { return this.#handleRGB(code, green, blue, false); } bgRGB(code, green, blue) { return this.#handleRGB(code, green, blue, true); } #isValidHSL(h, s, l) { return (0, helpers_1._isValidHue)(h) && (0, helpers_1._isValidPercentage)(s) && (0, helpers_1._isValidPercentage)(l); } #handleHSL(code, saturation, lightness, isBg = false) { if ((0, primitives_1.isString)(code)) { const hsl = this.#extractColorValues(code); if (this.#isValidHSL(...hsl)) { return this.#handleRGB((0, convert_1.convertHslToRgb)(...hsl), undefined, undefined, isBg); } else { return this.#applyStyles(); } } else if ((0, primitives_1.isNumber)(code) && (0, primitives_1.isNumber)(saturation) && (0, primitives_1.isNumber)(lightness)) { if (this.#isValidHSL(code, saturation, lightness)) { return this.#handleRGB((0, convert_1.convertHslToRgb)(code, saturation, lightness), undefined, undefined, isBg); } else { return this.#applyStyles(); } } else { return this.#applyStyles(); } } hsl(code, saturation, lightness) { return this.#handleHSL(code, saturation, lightness, false); } bgHSL(code, saturation, lightness) { return this.#handleHSL(code, saturation, lightness, true); } } exports.LogStyler = LogStyler; _a = LogStyler; function createStylogProxy(styler) { return new Proxy(styler, { get(target, prop) { if (prop in target) { const value = target[prop]; if (typeof value === 'function') { return value.bind(target); } else { return value; } } if (isCSSColor(prop) || isBGColor(prop) || isTextStyle(prop)) { return createStylogProxy(target.style(prop)); } }, }); } exports.Stylog = createStylogProxy(new LogStyler());