credl-parser-evaluator
Version:
TypeScript-based CREDL Parser and Evaluator that processes CREDL files and outputs complete Intermediate Representations
441 lines • 16.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProgressBar = exports.JSONFormatter = exports.TableFormatter = exports.OutputFormatter = void 0;
exports.createOutputFormatter = createOutputFormatter;
exports.createTableFormatter = createTableFormatter;
exports.createProgressBar = createProgressBar;
const chalk_1 = __importDefault(require("chalk"));
const ora_1 = __importDefault(require("ora"));
// Output formatting utilities for CLI commands
class OutputFormatter {
constructor(options = {}) {
this.quiet = false;
this.noColor = false;
this.quiet = options.quiet || false;
this.noColor = options.noColor || false;
}
// Basic logging methods
log(message) {
if (!this.quiet) {
console.log(message);
}
}
info(message) {
if (!this.quiet) {
console.log(this.color('blue', `ℹ️ ${message}`));
}
}
success(message) {
if (!this.quiet) {
console.log(this.color('green', `✅ ${message}`));
}
}
warning(message) {
if (!this.quiet) {
console.warn(this.color('yellow', `⚠️ ${message}`));
}
}
error(message) {
console.error(this.color('red', `❌ ${message}`));
}
debug(message, verbose = false) {
if (verbose && !this.quiet) {
console.log(this.color('gray', `🐛 ${message}`));
}
}
// Progress and status indicators
progress(message) {
if (!this.quiet) {
console.log(this.color('cyan', `🔄 ${message}`));
}
}
step(current, total, message) {
if (!this.quiet) {
const progress = `[${current}/${total}]`;
console.log(this.color('cyan', `🔄 ${progress} ${message}`));
}
}
// Spinner methods
spinner(message) {
return (0, ora_1.default)({
text: message,
color: 'cyan',
spinner: 'dots',
isEnabled: !this.quiet && !this.noColor
});
}
// Color utility (respects --no-color flag)
color(colorName, text) {
if (this.noColor) {
return text;
}
switch (colorName) {
case 'red': return chalk_1.default.red(text);
case 'green': return chalk_1.default.green(text);
case 'yellow': return chalk_1.default.yellow(text);
case 'blue': return chalk_1.default.blue(text);
case 'cyan': return chalk_1.default.cyan(text);
case 'gray': return chalk_1.default.gray(text);
case 'bold': return chalk_1.default.bold(text);
case 'dim': return chalk_1.default.dim(text);
default: return text;
}
}
// Header and section formatting
header(title) {
if (!this.quiet) {
console.log('');
console.log(this.color('bold', title));
console.log(this.color('dim', '='.repeat(Math.min(title.length, 50))));
}
}
section(title) {
if (!this.quiet) {
console.log('');
console.log(this.color('bold', title));
console.log(this.color('dim', '-'.repeat(Math.min(title.length, 30))));
}
}
// List formatting
bulletList(items, indent = 0) {
if (!this.quiet) {
const prefix = ' '.repeat(indent);
items.forEach(item => {
console.log(`${prefix}• ${item}`);
});
}
}
numberedList(items, indent = 0) {
if (!this.quiet) {
const prefix = ' '.repeat(indent);
items.forEach((item, index) => {
console.log(`${prefix}${index + 1}. ${item}`);
});
}
}
// Detailed formatting with icons and colors
fileStatus(filePath, status) {
if (!this.quiet) {
const icons = {
success: '✅',
error: '❌',
warning: '⚠️',
info: 'ℹ️'
};
const colors = {
success: 'green',
error: 'red',
warning: 'yellow',
info: 'blue'
};
const icon = icons[status];
const coloredPath = this.color(colors[status], filePath);
console.log(` ${icon} ${coloredPath}`);
}
}
// Code block formatting
codeBlock(code, language = '') {
if (!this.quiet) {
console.log('');
if (language) {
console.log(this.color('dim', `\`\`\`${language}`));
}
else {
console.log(this.color('dim', '```'));
}
console.log(this.color('gray', code));
console.log(this.color('dim', '```'));
console.log('');
}
}
// Statistics and metrics formatting
metric(label, value, unit) {
if (!this.quiet) {
const formattedValue = typeof value === 'number' ?
value.toLocaleString() : value;
const fullValue = unit ? `${formattedValue}${unit}` : formattedValue;
console.log(` ${this.color('dim', label + ':')} ${this.color('bold', fullValue)}`);
}
}
keyValue(key, value, indent = 0) {
if (!this.quiet) {
const prefix = ' '.repeat(indent);
console.log(`${prefix}${this.color('cyan', key)}: ${value}`);
}
}
}
exports.OutputFormatter = OutputFormatter;
class TableFormatter {
constructor(options = {}) {
this.noColor = false;
this.noColor = options.noColor || false;
}
// Create formatted table
formatTable(data, columns, options = {}) {
const { showHeaders = true, headerStyle = 'bold', borderStyle = 'simple', maxWidth = 120 } = options;
if (data.length === 0) {
return 'No data to display';
}
// Calculate column widths
const columnWidths = this.calculateColumnWidths(data, columns, maxWidth);
let output = [];
// Add headers
if (showHeaders) {
const headers = columns.map((col, index) => this.padCell(col.header, columnWidths[index] || 10, col.align || 'left'));
const headerRow = this.formatRow(headers, borderStyle);
output.push(this.styleText(headerRow, headerStyle));
// Add separator line
if (headerStyle !== 'none') {
const separator = columns.map((_, index) => '-'.repeat(columnWidths[index] || 10));
output.push(this.formatRow(separator, borderStyle));
}
}
// Add data rows
for (const row of data) {
const cells = columns.map((col, index) => {
let value = row[col.key];
if (col.formatter) {
value = col.formatter(value);
}
else if (value === null || value === undefined) {
value = '';
}
else {
value = String(value);
}
return this.padCell(value, columnWidths[index] || 10, col.align || 'left');
});
output.push(this.formatRow(cells, borderStyle));
}
return output.join('\n');
}
// Simple data table (key-value pairs)
formatDataTable(data, options = {}) {
const { indent = 0 } = options;
const prefix = ' '.repeat(indent);
const entries = Object.entries(data);
const maxKeyLength = Math.max(...entries.map(([key]) => key.length));
return entries.map(([key, value]) => {
const paddedKey = key.padEnd(maxKeyLength);
const formattedValue = value === null || value === undefined ? 'N/A' : String(value);
return `${prefix}${this.styleText(paddedKey, 'dim')}: ${formattedValue}`;
}).join('\n');
}
// Validation results table
formatValidationTable(results) {
const columns = [
{ header: 'File', key: 'file', align: 'left' },
{
header: 'Status',
key: 'status',
align: 'center',
formatter: (status) => {
switch (status) {
case 'valid': return this.styleText('✅ Valid', 'green');
case 'invalid': return this.styleText('❌ Invalid', 'red');
case 'error': return this.styleText('💥 Error', 'red');
default: return String(status);
}
}
},
{
header: 'Errors',
key: 'errors',
align: 'right',
formatter: (errors) => errors > 0 ? this.styleText(String(errors), 'red') : '0'
},
{
header: 'Warnings',
key: 'warnings',
align: 'right',
formatter: (warnings) => warnings > 0 ? this.styleText(String(warnings), 'yellow') : '0'
},
{
header: 'Time (ms)',
key: 'time',
align: 'right',
formatter: (time) => time ? time.toFixed(2) : 'N/A'
}
];
return this.formatTable(results, columns);
}
calculateColumnWidths(data, columns, maxWidth) {
const minWidths = columns.map(col => Math.max(col.header.length, col.width || 10));
const contentWidths = columns.map((col, index) => {
const values = data.map(row => {
let value = row[col.key];
if (col.formatter) {
value = col.formatter(value);
}
return String(value || '');
});
const maxContentLength = Math.max(...values.map(v => v.length));
return Math.max(minWidths[index] || 10, maxContentLength);
});
// Adjust widths if total exceeds maxWidth
const totalWidth = contentWidths.reduce((sum, width) => sum + width, 0);
const separatorWidth = (columns.length - 1) * 3; // " | " between columns
if (totalWidth + separatorWidth > maxWidth) {
const availableWidth = maxWidth - separatorWidth;
const ratio = availableWidth / totalWidth;
return contentWidths.map(width => Math.max(8, Math.floor(width * ratio)));
}
return contentWidths;
}
padCell(text, width, align) {
if (text.length > width) {
return text.substring(0, width - 3) + '...';
}
switch (align) {
case 'right':
return text.padStart(width);
case 'center':
const leftPad = Math.floor((width - text.length) / 2);
const rightPad = width - text.length - leftPad;
return ' '.repeat(leftPad) + text + ' '.repeat(rightPad);
case 'left':
default:
return text.padEnd(width);
}
}
formatRow(cells, borderStyle) {
switch (borderStyle) {
case 'simple':
return cells.join(' | ');
case 'rounded':
return '│ ' + cells.join(' │ ') + ' │';
case 'none':
default:
return cells.join(' ');
}
}
styleText(text, style) {
if (this.noColor) {
return text;
}
switch (style) {
case 'bold': return chalk_1.default.bold(text);
case 'dim': return chalk_1.default.dim(text);
case 'underline': return chalk_1.default.underline(text);
case 'red': return chalk_1.default.red(text);
case 'green': return chalk_1.default.green(text);
case 'yellow': return chalk_1.default.yellow(text);
case 'blue': return chalk_1.default.blue(text);
case 'cyan': return chalk_1.default.cyan(text);
default: return text;
}
}
}
exports.TableFormatter = TableFormatter;
// JSON formatting utilities
class JSONFormatter {
// Pretty print JSON with optional syntax highlighting
static prettyPrint(data, options = {}) {
const { indent = 2, sortKeys = false, colorize = true, maxLength = 10000 } = options;
try {
let jsonString = JSON.stringify(data, sortKeys ? this.sortedReplacer : undefined, indent);
// Truncate if too long
if (jsonString.length > maxLength) {
jsonString = jsonString.substring(0, maxLength) + '\n... (truncated)';
}
// Add syntax highlighting if requested
if (colorize && !process.env.NO_COLOR) {
return this.colorizeJSON(jsonString);
}
return jsonString;
}
catch (error) {
return `Error formatting JSON: ${error instanceof Error ? error.message : String(error)}`;
}
}
// Compact JSON (single line)
static compact(data) {
try {
return JSON.stringify(data);
}
catch (error) {
return `Error formatting JSON: ${error instanceof Error ? error.message : String(error)}`;
}
}
// Safe JSON stringify with circular reference handling
static safeStringify(data, options = {}) {
const { indent = 2, replacer } = options;
const seen = new WeakSet();
const circularReplacer = (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular Reference]';
}
seen.add(value);
}
return replacer ? replacer(key, value) : value;
};
try {
return JSON.stringify(data, circularReplacer, indent);
}
catch (error) {
return `Error formatting JSON: ${error instanceof Error ? error.message : String(error)}`;
}
}
static sortedReplacer(_key, value) {
if (value && typeof value === 'object' && !Array.isArray(value)) {
const sortedObj = {};
Object.keys(value).sort().forEach(k => {
sortedObj[k] = value[k];
});
return sortedObj;
}
return value;
}
static colorizeJSON(jsonString) {
return jsonString
.replace(/"([^"]+)":/g, chalk_1.default.blue('"$1"') + ':') // Keys
.replace(/: "([^"]+)"/g, ': ' + chalk_1.default.green('"$1"')) // String values
.replace(/: (\d+\.?\d*)/g, ': ' + chalk_1.default.yellow('$1')) // Numbers
.replace(/: (true|false)/g, ': ' + chalk_1.default.magenta('$1')) // Booleans
.replace(/: (null)/g, ': ' + chalk_1.default.gray('$1')) // Null
.replace(/([{}[\]])/g, chalk_1.default.white('$1')); // Brackets
}
}
exports.JSONFormatter = JSONFormatter;
// Progress bar utilities
class ProgressBar {
constructor(total, options = {}) {
this.total = total;
this.width = options.width || 40;
this.label = options.label || 'Progress';
this.startTime = Date.now();
}
update(current, message) {
const percentage = Math.min(Math.round((current / this.total) * 100), 100);
const filled = Math.round((this.width * current) / this.total);
const bar = '█'.repeat(filled) + '░'.repeat(this.width - filled);
const elapsed = (Date.now() - this.startTime) / 1000;
const rate = current / elapsed;
const eta = current > 0 ? Math.round((this.total - current) / rate) : 0;
const statusLine = `${this.label}: [${bar}] ${percentage}% (${current}/${this.total}) ETA: ${eta}s`;
const fullLine = message ? `${statusLine} - ${message}` : statusLine;
process.stdout.write(`\r${fullLine}`);
if (current >= this.total) {
process.stdout.write('\n');
}
}
complete(message) {
this.update(this.total, message || 'Complete');
}
}
exports.ProgressBar = ProgressBar;
// Export utility functions for direct use
function createOutputFormatter(options) {
return new OutputFormatter(options);
}
function createTableFormatter(options) {
return new TableFormatter(options);
}
function createProgressBar(total, options) {
return new ProgressBar(total, options);
}
//# sourceMappingURL=output.js.map