UNPKG

neex

Version:

Neex - Modern Fullstack Framework Built on Express and Next.js. Fast to Start, Easy to Build, Ready to Deploy

274 lines (273 loc) 12.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); // src/logger.ts const chalk_1 = __importDefault(require("chalk")); const figures_1 = __importDefault(require("figures")); const string_width_1 = __importDefault(require("string-width")); const utils_1 = require("./utils"); class Logger { constructor() { this.prefixLength = 0; this.outputBuffer = new Map(); this.commandColors = new Map(); this.startTimes = new Map(); this.spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; this.spinnerIndex = 0; this.spinnerIntervals = new Map(); this.isSpinnerActive = false; } static getInstance() { if (!Logger.instance) { Logger.instance = new Logger(); } return Logger.instance; } getSpinnerFrame() { const frame = this.spinnerFrames[this.spinnerIndex]; this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length; return frame; } showBanner() { console.log('\n' + chalk_1.default.bgHex('#0066FF').black(' Neex ') + '\n'); } setCommands(commands) { // Clear any existing spinner intervals this.stopAllSpinners(); // Show Neex banner this.showBanner(); // Calculate prefix length for aligning output this.prefixLength = Math.max(...commands.map(cmd => (0, string_width_1.default)(cmd))) + 3; // Initialize buffers and colors for each command commands.forEach(cmd => { this.outputBuffer.set(cmd, []); this.commandColors.set(cmd, this.generateColor(cmd)); }); // Log commands that will be executed console.log(chalk_1.default.dim('» Commands to execute:')); commands.forEach(cmd => { const color = this.commandColors.get(cmd) || chalk_1.default.white; console.log(chalk_1.default.dim(' ┌') + color(` ${cmd}`)); }); console.log(''); // Add a blank line after commands list } generateColor(command) { // Generate distinct colors for commands based on the command string const vibrantColors = [ '#00BFFF', // Deep Sky Blue '#32CD32', // Lime Green '#FF6347', // Tomato '#9370DB', // Medium Purple '#FF8C00', // Dark Orange '#20B2AA', // Light Sea Green '#0066FF', // Deep Pink '#4169E1', // Royal Blue '#FFD700', // Gold '#8A2BE2' // Blue Violet ]; let hash = 0; for (let i = 0; i < command.length; i++) { hash = (hash << 5) - hash + command.charCodeAt(i); hash |= 0; // Convert to 32bit integer } const colorIndex = Math.abs(hash) % vibrantColors.length; return chalk_1.default.hex(vibrantColors[colorIndex]); } formatPrefix(command) { const color = this.commandColors.get(command) || chalk_1.default.white; const prefix = `${command}:`.padEnd(this.prefixLength); return color(prefix); } bufferOutput(output) { const currentBuffer = this.outputBuffer.get(output.command) || []; currentBuffer.push(output); this.outputBuffer.set(output.command, currentBuffer); } printBuffer(command) { const buffer = this.outputBuffer.get(command) || []; const color = this.commandColors.get(command) || chalk_1.default.white; // Stop spinner for this command if running this.stopSpinner(command); buffer.forEach(output => { const prefix = this.formatPrefix(output.command); const content = output.data.trim(); if (content) { const lines = content.split('\n'); lines.forEach(line => { if (line.trim()) { const outputLine = `${prefix} ${line}`; // Show stderr in appropriate colors if (output.type === 'stderr') { // Not all stderr is an error, check for warning or info patterns if (line.toLowerCase().includes('warn') || line.toLowerCase().includes('warning')) { console.log(`${prefix} ${chalk_1.default.yellow(line)}`); } else if (line.toLowerCase().includes('error')) { console.log(`${prefix} ${chalk_1.default.red(line)}`); } else { console.log(`${prefix} ${line}`); } } else { console.log(outputLine); } } }); } }); // Clear buffer after printing this.outputBuffer.set(command, []); } clearBuffer(command) { this.outputBuffer.set(command, []); } printLine(message, level = 'info') { if (level === 'error') { console.error(chalk_1.default.red(`${figures_1.default.cross} ${message}`)); } else if (level === 'warn') { console.warn(chalk_1.default.yellow(`${figures_1.default.warning} ${message}`)); } else { console.log(chalk_1.default.blue(`${figures_1.default.info} ${message}`)); } } printStart(command) { // Record start time this.startTimes.set(command, new Date()); const prefix = this.formatPrefix(command); const color = this.commandColors.get(command) || chalk_1.default.white; // Stop any previous spinner for this command (e.g. if retrying) this.stopSpinner(command); // Clear the line before printing "Starting..." if (this.isSpinnerActive) { // Check if any spinner was active to avoid clearing unnecessarily process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r'); } console.log(`${prefix} ${color('Starting...')}`); // Start spinner for this command this.startSpinner(command); } startSpinner(command) { // Only create a spinner if one doesn't already exist for this command if (this.spinnerIntervals.has(command)) { return; } this.isSpinnerActive = true; const color = this.commandColors.get(command) || chalk_1.default.white; const prefix = this.formatPrefix(command); const interval = setInterval(() => { const frame = this.getSpinnerFrame(); process.stdout.write(`\r${prefix} ${color(frame)} ${chalk_1.default.dim('Running...')}`); }, 80); this.spinnerIntervals.set(command, interval); } stopSpinner(command) { const interval = this.spinnerIntervals.get(command); if (interval) { clearInterval(interval); this.spinnerIntervals.delete(command); // Clear the spinner line if (this.isSpinnerActive) { process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r'); } } } stopAllSpinners() { this.spinnerIntervals.forEach((interval, command) => { clearInterval(interval); }); this.spinnerIntervals.clear(); this.isSpinnerActive = false; // Clear the spinner line if any spinner was active if (this.isSpinnerActive) { process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r'); } } printSuccess(result) { const { command, duration } = result; this.stopSpinner(command); const prefix = this.formatPrefix(command); const color = this.commandColors.get(command) || chalk_1.default.white; const durationStr = duration ? ` ${chalk_1.default.dim(`(${(duration / 1000).toFixed(2)}s)`)}` : ''; console.log(`${prefix} ${chalk_1.default.green(figures_1.default.tick)} ${chalk_1.default.green('Completed')}${durationStr}`); } printError(result) { const { command, error, code, duration } = result; this.stopSpinner(command); const prefix = this.formatPrefix(command); const durationStr = duration ? ` ${chalk_1.default.dim(`(${(duration / 1000).toFixed(2)}s)`)}` : ''; const errorCode = code !== null ? ` ${chalk_1.default.red(`[code: ${code}]`)}` : ''; console.error(`${prefix} ${chalk_1.default.red(figures_1.default.cross)} ${chalk_1.default.red('Failed')}${errorCode}${durationStr}`); if (error) { console.error(`${prefix} ${chalk_1.default.red(error.message)}`); } } printEnd(result, minimalOutput) { this.stopSpinner(result.command); const prefix = this.formatPrefix(result.command); // Corrected to formatPrefix let durationDisplay = ''; if (result.duration !== null) { // Ensure result.duration is treated as a number here durationDisplay = `(${(0, utils_1.formatDuration)(result.duration)})`; } const duration = durationDisplay; if (minimalOutput) { if (!result.success) { const status = result.code !== null ? `failed (code ${result.code})` : 'failed'; this.printLine(`${prefix} ${chalk_1.default.red(figures_1.default.cross)} ${result.command} ${status} ${duration}`, 'error'); } } else { if (result.success) { this.printLine(`${prefix} ${chalk_1.default.green(figures_1.default.tick)} Command "${result.command}" finished successfully ${duration}`, 'info'); } else { const errorCode = result.code !== null ? ` (code ${result.code})` : ''; const errorMessage = result.error ? `: ${result.error.message}` : ''; this.printLine(`${prefix} ${chalk_1.default.red(figures_1.default.cross)} Command "${result.command}" failed${errorCode}${errorMessage} ${duration}`, 'error'); } } } printSummary(results) { // Stop any remaining spinners this.stopAllSpinners(); const successful = results.filter(r => r.success).length; const failed = results.length - successful; const totalDuration = results.reduce((sum, result) => sum + (result.duration || 0), 0); const totalSeconds = (totalDuration / 1000).toFixed(2); console.log('\n' + chalk_1.default.bgHex('#0066FF').black(' Execution Summary ') + '\n'); console.log(`${chalk_1.default.green(`${figures_1.default.tick} ${successful} succeeded`)}, ${chalk_1.default.red(`${figures_1.default.cross} ${failed} failed`)}`); console.log(`${chalk_1.default.blue(figures_1.default.info)} ${chalk_1.default.dim(`Total execution time: ${totalSeconds}s`)}`); if (successful > 0) { console.log('\n' + chalk_1.default.green.bold('Successful commands:')); results .filter(r => r.success) .forEach(result => { const color = this.commandColors.get(result.command) || chalk_1.default.white; const duration = result.duration ? chalk_1.default.dim(` (${(result.duration / 1000).toFixed(2)}s)`) : ''; console.log(` ${chalk_1.default.green(figures_1.default.tick)} ${color(result.command)}${duration}`); }); } if (failed > 0) { console.log('\n' + chalk_1.default.red.bold('Failed commands:')); results .filter(r => !r.success) .forEach(result => { const color = this.commandColors.get(result.command) || chalk_1.default.white; const duration = result.duration ? chalk_1.default.dim(` (${(result.duration / 1000).toFixed(2)}s)`) : ''; const code = result.code !== null ? chalk_1.default.red(` [code: ${result.code}]`) : ''; console.log(` ${chalk_1.default.red(figures_1.default.cross)} ${color(result.command)}${code}${duration}`); }); } } } exports.default = Logger.getInstance();