willow-cli
Version:
CLI for installing Willow Design System components
200 lines (199 loc) • 6.83 kB
JavaScript
import chalk from 'chalk';
/**
* Enhanced logging utility with consistent formatting and context
*/
export class Logger {
static indent = 0;
static debugMode = process.env.WILLOW_DEBUG === 'true';
static setDebugMode(enabled) {
this.debugMode = enabled;
}
static increaseIndent() {
this.indent += 2;
}
static decreaseIndent() {
this.indent = Math.max(0, this.indent - 2);
}
static resetIndent() {
this.indent = 0;
}
static getIndent() {
return ' '.repeat(this.indent);
}
static getTimestamp() {
return new Date().toLocaleTimeString();
}
static info(message, options = {}) {
const { prefix, indent = 0, timestamp = false } = options;
const indentStr = ' '.repeat(this.indent + indent);
const prefixStr = prefix ? `${prefix} ` : '';
const timestampStr = timestamp ? `[${this.getTimestamp()}] ` : '';
console.log(`${indentStr}${timestampStr}${prefixStr}${message}`);
}
static success(message, options = {}) {
const { prefix = '✅', indent = 0, timestamp = false } = options;
const indentStr = ' '.repeat(this.indent + indent);
const timestampStr = timestamp ? `[${this.getTimestamp()}] ` : '';
console.log(chalk.green(`${indentStr}${timestampStr}${prefix} ${message}`));
}
static warning(message, options = {}) {
const { prefix = '⚠️', indent = 0, timestamp = false } = options;
const indentStr = ' '.repeat(this.indent + indent);
const timestampStr = timestamp ? `[${this.getTimestamp()}] ` : '';
console.log(chalk.yellow(`${indentStr}${timestampStr}${prefix} ${message}`));
}
static error(message, options = {}) {
const { prefix = '❌', indent = 0, timestamp = false } = options;
const indentStr = ' '.repeat(this.indent + indent);
const timestampStr = timestamp ? `[${this.getTimestamp()}] ` : '';
console.error(chalk.red(`${indentStr}${timestampStr}${prefix} ${message}`));
}
static debug(message, options = {}) {
if (!this.debugMode)
return;
const { prefix = '🐛', indent = 0, timestamp = true } = options;
const indentStr = ' '.repeat(this.indent + indent);
const timestampStr = timestamp ? `[${this.getTimestamp()}] ` : '';
console.log(chalk.gray(`${indentStr}${timestampStr}${prefix} ${message}`));
}
static step(message) {
console.log(chalk.blue(`⏳ ${message}...`));
}
static substep(message) {
this.info(chalk.gray(` ${message}`));
}
static title(message) {
console.log(chalk.blue.bold(`\n🎨 ${message}`));
}
static section(message) {
console.log(chalk.blue(`\n📦 ${message}`));
}
static spacer() {
console.log();
}
static divider() {
console.log(chalk.gray('─'.repeat(60)));
}
static table(headers, rows) {
// Simple table implementation
const colWidths = headers.map((header, i) => Math.max(header.length, ...rows.map(row => (row[i] || '').length)));
// Header
const headerRow = headers.map((header, i) => header.padEnd(colWidths[i])).join(' │ ');
console.log(chalk.blue(`│ ${headerRow} │`));
// Separator
const separator = colWidths.map(width => '─'.repeat(width)).join('─┼─');
console.log(chalk.blue(`├─${separator}─┤`));
// Rows
rows.forEach(row => {
const rowStr = row.map((cell, i) => (cell || '').padEnd(colWidths[i])).join(' │ ');
console.log(`│ ${rowStr} │`);
});
}
static list(items, options = {}) {
const { ordered = false, indent = 0 } = options;
const indentStr = ' '.repeat(this.indent + indent);
items.forEach((item, index) => {
const marker = ordered ? `${index + 1}.` : '•';
console.log(`${indentStr}${marker} ${item}`);
});
}
static group(title, callback) {
this.section(title);
this.increaseIndent();
try {
callback();
}
finally {
this.decreaseIndent();
}
}
}
/**
* Simple step logger for tracking progress
*/
export class StepLogger {
currentStep = 0;
totalSteps;
steps;
constructor(steps) {
this.steps = steps;
this.totalSteps = steps.length;
this.logProgress();
}
next(message) {
if (this.currentStep < this.totalSteps) {
Logger.success(message || this.steps[this.currentStep]);
this.currentStep++;
this.logProgress();
}
}
fail(message) {
Logger.error(message || `Failed: ${this.steps[this.currentStep]}`);
}
skip(message) {
Logger.warning(message || `Skipped: ${this.steps[this.currentStep]}`);
this.currentStep++;
this.logProgress();
}
logProgress() {
if (this.currentStep < this.totalSteps) {
Logger.step(`(${this.currentStep + 1}/${this.totalSteps}) ${this.steps[this.currentStep]}`);
}
}
complete() {
Logger.success(`All ${this.totalSteps} steps completed!`);
}
}
/**
* Utility functions for common logging patterns
*/
export function logFileOperation(operation, filePath) {
const icons = {
create: '📝',
update: '✏️',
delete: '🗑️',
};
const messages = {
create: 'Created',
update: 'Updated',
delete: 'Deleted',
};
Logger.info(`${icons[operation]} ${messages[operation]}: ${filePath}`);
}
export function logDependencyInstall(deps, manager = 'npm') {
Logger.info(`📦 Installing dependencies with ${manager}:`);
Logger.list(deps, { indent: 2 });
}
export function logComponentInstall(componentName, success) {
if (success) {
Logger.success(`Installed component: ${componentName}`);
}
else {
Logger.error(`Failed to install component: ${componentName}`);
}
}
export function logValidationResult(valid, issues = [], warnings = []) {
if (valid) {
Logger.success('Project validation passed');
}
else {
Logger.error('Project validation failed');
if (issues.length > 0) {
Logger.warning('Issues found:');
Logger.list(issues, { indent: 2 });
}
}
if (warnings.length > 0) {
Logger.warning('Warnings:');
Logger.list(warnings, { indent: 2 });
}
}
export function logEnvironmentInfo(info) {
Logger.section('Environment Information');
Object.entries(info).forEach(([key, value]) => {
Logger.info(`${key}: ${value}`, { indent: 2 });
});
}
// Convenience exports
export const log = Logger;
export { Logger as default };