UNPKG

@open-utils/cli-colorize

Version:

A powerful and customizable library for terminal colors and styling. Add beautiful ANSI colors, progress bars, spinners, and formatted tables to your Node.js CLI applications.

598 lines 19.8 kB
"use strict"; /** * Console Text Colorizer Library * * A utility library for adding colors and styling to terminal output * with an object-oriented API for better developer experience. * * @version 1.0.0 * @license MIT */ Object.defineProperty(exports, "__esModule", { value: true }); exports.logger = exports.ColorizeLogger = exports.Spinner = exports.ProgressBar = exports.THEMES = exports.COLORS = void 0; exports.createWorkflow = createWorkflow; // ANSI color and style codes exports.COLORS = { // Styles reset: "\x1b[0m", bright: "\x1b[1m", dim: "\x1b[2m", underscore: "\x1b[4m", blink: "\x1b[5m", reverse: "\x1b[7m", hidden: "\x1b[8m", // Text colors black: "\x1b[30m", red: "\x1b[31m", green: "\x1b[32m", yellow: "\x1b[33m", blue: "\x1b[34m", magenta: "\x1b[35m", cyan: "\x1b[36m", white: "\x1b[37m", gray: "\x1b[90m", // Background colors bgBlack: "\x1b[40m", bgRed: "\x1b[41m", bgGreen: "\x1b[42m", bgYellow: "\x1b[43m", bgBlue: "\x1b[44m", bgMagenta: "\x1b[45m", bgCyan: "\x1b[46m", bgWhite: "\x1b[47m", bgGray: "\x1b[100m" }; // Pre-defined themes exports.THEMES = { default: { success: { color: 'green', style: null }, error: { color: 'red', style: null }, warning: { color: 'yellow', style: null }, info: { color: 'cyan', style: null }, debug: { color: 'magenta', style: null }, prompt: { color: 'white', style: 'bright' } }, dark: { success: { color: 'green', style: 'bright' }, error: { color: 'red', style: 'bright' }, warning: { color: 'yellow', style: 'bright' }, info: { color: 'blue', style: 'bright' }, debug: { color: 'magenta', style: 'dim' }, prompt: { color: 'white', style: 'underscore' } }, light: { success: { color: 'green', style: 'dim' }, error: { color: 'red', style: 'dim' }, warning: { color: 'yellow', style: 'dim' }, info: { color: 'blue', style: 'dim' }, debug: { color: 'magenta', style: 'dim' }, prompt: { color: 'black', style: 'bright' } }, minimal: { success: { color: 'green', style: null }, error: { color: 'red', style: null }, warning: { color: 'yellow', style: null }, info: { color: 'white', style: null }, debug: { color: 'white', style: 'dim' }, prompt: { color: 'white', style: null } }, vibrant: { success: { color: 'green', bgColor: 'bgBlack', style: 'bright' }, error: { color: 'white', bgColor: 'bgRed', style: 'bright' }, warning: { color: 'black', bgColor: 'bgYellow', style: null }, info: { color: 'white', bgColor: 'bgBlue', style: 'bright' }, debug: { color: 'white', bgColor: 'bgMagenta', style: null }, prompt: { color: 'black', bgColor: 'bgCyan', style: 'bright' } } }; /** * Validates that a color key exists in COLORS * @param color - The color key to validate * @returns boolean indicating if color is valid */ function isValidColor(color) { if (!color) return false; return Object.keys(exports.COLORS).includes(color); } /** * ProgressBar class for displaying terminal progress bars */ class ProgressBar { /** * Creates a new ProgressBar instance * @param total - Total steps in the progress * @param logger - Parent logger instance * @param options - Progress bar configuration options */ constructor(total, logger, options = {}) { this.current = 0; this.total = Math.max(1, total); this.width = options.width || 30; this.completeChar = options.completeChar || '█'; this.incompleteChar = options.incompleteChar || '░'; this.style = options.style || { color: 'green', style: 'bright' }; this.logger = logger; } /** * Updates the progress bar with a new value * @param value - Current progress value * @param text - Optional text to display alongside progress * @returns The progress bar instance for chaining */ update(value, text) { // Ensure value is between 0 and total this.current = Math.max(0, Math.min(value, this.total)); // Calculate percentage and bar representation const percent = this.current / this.total; const completeWidth = Math.round(this.width * percent); const incompleteWidth = this.width - completeWidth; // Create the bar string const bar = this.completeChar.repeat(completeWidth) + this.incompleteChar.repeat(incompleteWidth); // Clear the line and write the progress process.stdout.write('\r\x1b[K'); // Format the progress bar with style const progressText = `[${bar}] ${Math.round(percent * 100)}%`; process.stdout.write(this.logger._colorize(progressText, this.style.color, this.style.bgColor, this.style.style || undefined)); // Add optional text if (text) { process.stdout.write(' ' + text); } return this; } /** * Completes the progress bar and adds a newline * @param text - Optional completion text * @returns The progress bar instance for chaining */ complete(text) { this.update(this.total, text); process.stdout.write('\n'); return this; } } exports.ProgressBar = ProgressBar; /** * Spinner class for displaying terminal spinners */ class Spinner { /** * Creates a new Spinner instance * @param text - Text to display alongside spinner * @param logger - Parent logger instance * @param options - Spinner configuration options */ constructor(text, logger, options = {}) { this.intervalId = null; this.currentFrame = 0; this.text = text; this.frames = options.frames || ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; this.interval = options.interval || 80; this.style = options.style || { color: 'cyan', style: 'bright' }; this.logger = logger; } /** * Starts the spinner animation * @returns The spinner instance for chaining */ start() { if (this.intervalId) { return this; } // Hide cursor process.stdout.write('\x1b[?25l'); this.intervalId = setInterval(() => { const frame = this.frames[this.currentFrame]; process.stdout.write('\r\x1b[K'); process.stdout.write(this.logger._colorize(frame, this.style.color, this.style.bgColor, this.style.style || undefined)); process.stdout.write(' ' + this.text); this.currentFrame = (this.currentFrame + 1) % this.frames.length; }, this.interval); return this; } /** * Updates the spinner text * @param text - New text to display * @returns The spinner instance for chaining */ update(text) { this.text = text; return this; } /** * Stops the spinner with success indication * @param text - Optional success text * @returns The spinner instance for chaining */ success(text) { const successText = text || this.text; this.stop(); this.logger.success(successText); return this; } /** * Stops the spinner with error indication * @param text - Optional error text * @returns The spinner instance for chaining */ error(text) { const errorText = text || this.text; this.stop(); this.logger.error(errorText); return this; } /** * Stops the spinner * @param text - Optional text to display when stopped * @returns The spinner instance for chaining */ stop(text) { if (!this.intervalId) { return this; } clearInterval(this.intervalId); this.intervalId = null; // Show cursor and clear line process.stdout.write('\r\x1b[K'); process.stdout.write('\x1b[?25h'); if (text) { console.log(text); } return this; } } exports.Spinner = Spinner; /** * ColorizeLogger class for enhanced console output with colors */ class ColorizeLogger { /** * Creates a new ColorizeLogger instance * @param options - Configuration options */ constructor(options = {}) { this.options = { theme: options.theme || 'default', enabled: options.enabled !== undefined ? options.enabled : true, customTheme: options.customTheme || null }; // Set the theme this.theme = this.setTheme(this.options.theme || 'default', this.options.customTheme).theme; } /** * Sets the current theme * @param theme - Theme name or custom theme object * @param customTheme - Custom theme definition if theme is 'custom' * @returns The logger instance for chaining */ setTheme(theme, customTheme = null) { if (typeof theme === 'string') { if (theme === 'custom' && customTheme) { this.theme = customTheme; } else if (exports.THEMES[theme]) { this.theme = exports.THEMES[theme]; } else { this.theme = exports.THEMES.default; } } else if (typeof theme === 'object' && theme !== null) { this.theme = theme; } else { this.theme = exports.THEMES.default; } return this; } /** * Creates a custom theme * @param name - Theme name * @param themeConfig - Theme configuration * @returns The logger instance for chaining */ createTheme(name, themeConfig) { if (typeof name === 'string' && typeof themeConfig === 'object') { exports.THEMES[name] = themeConfig; } return this; } /** * Enables or disables color output * @param enabled - Whether color output is enabled * @returns The logger instance for chaining */ setEnabled(enabled) { this.options.enabled = !!enabled; return this; } /** * Creates a styled message with the specified color * This method is used internally but can also be accessed directly * @param text - The text to colorize * @param color - The color to apply (from COLORS object) * @param bgColor - Optional background color * @param style - Optional text style * @returns The styled text string */ _colorize(text, color, bgColor, style) { if (!this.options.enabled) { return text; } let result = ''; // Apply style if provided and valid if (style && isValidColor(style)) { result += exports.COLORS[style]; } // Apply background if provided and valid if (bgColor && isValidColor(bgColor)) { result += exports.COLORS[bgColor]; } // Apply text color if provided and valid if (color && isValidColor(color)) { result += exports.COLORS[color]; } // Add the text and reset result += text + exports.COLORS.reset; return result; } /** * Logs a colorized message to the console * @param text - The text to log * @param color - The color to apply * @param bgColor - Optional background color * @param style - Optional text style * @returns The logger instance for chaining */ log(text, color, bgColor, style) { console.log(this._colorize(text, color, bgColor, style)); return this; } /** * Logs a success message based on theme * @param text - The text to log * @param customStyle - Optional style override * @returns The logger instance for chaining */ success(text, customStyle) { const { color, bgColor, style } = this.theme.success; this.log(text, color, bgColor, customStyle || (style || undefined)); return this; } /** * Logs an error message based on theme * @param text - The text to log * @param customStyle - Optional style override * @returns The logger instance for chaining */ error(text, customStyle) { const { color, bgColor, style } = this.theme.error; this.log(text, color, bgColor, customStyle || (style || undefined)); return this; } /** * Logs a warning message based on theme * @param text - The text to log * @param customStyle - Optional style override * @returns The logger instance for chaining */ warning(text, customStyle) { const { color, bgColor, style } = this.theme.warning; this.log(text, color, bgColor, customStyle || (style || undefined)); return this; } /** * Logs an info message based on theme * @param text - The text to log * @param customStyle - Optional style override * @returns The logger instance for chaining */ info(text, customStyle) { const { color, bgColor, style } = this.theme.info; this.log(text, color, bgColor, customStyle || (style || undefined)); return this; } /** * Logs a debug message based on theme * @param text - The text to log * @param customStyle - Optional style override * @returns The logger instance for chaining */ debug(text, customStyle) { const { color, bgColor, style } = this.theme.debug; this.log(text, color, bgColor, customStyle || (style || undefined)); return this; } /** * Display data in a table with optional title * @param data - Object or array to display * @param title - Optional table title * @param options - Table display options * @returns The logger instance for chaining */ table(data, title, options = {}) { // Show title if provided if (title) { this.info(''); this.info(title); } // If data is empty, show a message if (!data || (Array.isArray(data) && data.length === 0) || Object.keys(data).length === 0) { this.warning(' No data to display'); return this; } const headerStyle = options.headerStyle || { color: 'white', style: 'bright' }; // Format based on data type if (Array.isArray(data)) { // Handle array of objects if (data.length > 0 && typeof data[0] === 'object') { // Extract column headers from first object const headers = Object.keys(data[0]); // Print headers const headerRow = headers.join('\t'); console.log(this._colorize(headerRow, headerStyle.color, headerStyle.bgColor, headerStyle.style)); // Print separator console.log(this._colorize('-'.repeat(headerRow.length), headerStyle.color, headerStyle.bgColor, headerStyle.style)); // Print data rows data.forEach(item => { const row = headers.map(header => { const value = item[header]; return value === undefined || value === null ? '' : String(value); }).join('\t'); console.log(row); }); } else { // Handle simple array data.forEach((item, index) => { console.log(`${index}\t${item}`); }); } } else if (typeof data === 'object') { // Handle single object Object.entries(data).forEach(([key, value]) => { console.log(`${this._colorize(key, headerStyle.color, headerStyle.bgColor, headerStyle.style)}\t${value}`); }); } this.info(''); return this; } /** * Format text without logging * @param text - The text to format * @param color - The color to apply * @param bgColor - Optional background color * @param style - Optional text style * @returns The formatted text string */ format(text, color, bgColor, style) { return this._colorize(text, color, bgColor, style); } /** * Format text using theme's prompt style * @param text - The text to format * @returns The formatted prompt string */ formatPrompt(text) { const { color, bgColor, style } = this.theme.prompt; return this._colorize(text, color, bgColor, style || undefined); } /** * Creates a progress bar * @param total - Total steps * @param options - Progress bar options * @returns A new progress bar instance */ createProgressBar(total, options = {}) { return new ProgressBar(total, this, options); } /** * Creates a spinner * @param text - Spinner text * @param options - Spinner options * @returns A new spinner instance */ createSpinner(text, options = {}) { return new Spinner(text, this, options); } } exports.ColorizeLogger = ColorizeLogger; /** * LogWorkflow class for structured logging workflows */ class Workflow { /** * Creates a new LogWorkflow instance * @param options - Logger options */ constructor(options = {}) { this.steps = 0; this.logger = new ColorizeLogger(options); } /** * Logs the start of a workflow * @param message - Start message * @returns The workflow instance for chaining */ start(message) { this.logger.info(''); this.logger.info(`◆ ${message}`, 'bright'); this.logger.info(''); this.steps = 0; return this; } /** * Logs a step in the workflow * @param message - Step message * @returns The workflow instance for chaining */ step(message) { this.steps++; this.logger.info(` ${this.steps}. ${message}`); return this; } /** * Logs a success message in the workflow * @param message - Success message * @returns The workflow instance for chaining */ success(message) { this.logger.success(` ✓ ${message}`); return this; } /** * Logs an error message in the workflow * @param message - Error message * @returns The workflow instance for chaining */ error(message) { this.logger.error(` ✗ ${message}`); return this; } /** * Logs a warning message in the workflow * @param message - Warning message * @returns The workflow instance for chaining */ warning(message) { this.logger.warning(` ⚠ ${message}`); return this; } /** * Logs the end of a workflow * @param message - End message * @returns The workflow instance for chaining */ end(message) { this.logger.info(''); this.logger.info(`◆ ${message}`, 'bright'); this.logger.info(''); return this; } } /** * Creates a new workflow logger * @param options - Logger options * @returns A new workflow instance */ function createWorkflow(options = {}) { return new Workflow(options); } // Export singleton instance exports.logger = new ColorizeLogger(); // For CommonJS compatibility exports.default = { ColorizeLogger, ProgressBar, Spinner, logger: exports.logger, COLORS: exports.COLORS, THEMES: exports.THEMES, createWorkflow }; //# sourceMappingURL=colorize.js.map