@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
407 lines (406 loc) • 16.3 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CLIApp = void 0;
const commander_1 = require("commander");
const chalk_1 = __importDefault(require("chalk"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs-extra"));
const command_registry_1 = require("./command-registry");
const logger_1 = require("./logger");
const analytics_1 = require("./analytics");
const config_manager_1 = require("./config-manager");
class CLIApp {
constructor(options) {
this.options = options;
this.initialized = false;
this.logger = options.logger || (0, logger_1.getGlobalLogger)();
this.analytics = options.analytics || (0, analytics_1.getGlobalAnalytics)();
this.config = options.config || (0, config_manager_1.getGlobalConfig)();
this.plugins = options.plugins;
// Create commander program
this.program = new commander_1.Command();
this.program
.name(options.name)
.version(options.version)
.description(options.description);
// Create command registry
this.registry = new command_registry_1.CommandRegistry(this.logger, this.analytics, this.config, this.plugins || {});
this.setupGlobalOptions();
this.setupGlobalMiddleware();
this.setupEventHandlers();
}
setupGlobalOptions() {
this.program
.option('--debug', 'Enable debug mode', false)
.option('--verbose', 'Enable verbose output', false)
.option('--quiet', 'Suppress output', false)
.option('--yes', 'Skip interactive prompts', false)
.option('--no-color', 'Disable colored output', false)
.option('--profile', 'Enable performance profiling', false)
.option('--log-level <level>', 'Set log level (debug, info, warn, error)', 'info')
.option('--config <path>', 'Specify config file path')
.option('--no-analytics', 'Disable analytics tracking', false);
}
setupGlobalMiddleware() {
// Pre-execution middleware
this.registry.addMiddleware({
name: 'global-setup',
phase: 'pre',
handler: async (context) => {
// Configure logger based on options
if (context.options.debug) {
this.logger.setLevel(logger_1.LogLevel.DEBUG);
}
else if (context.options.quiet) {
this.logger.setLevel(logger_1.LogLevel.ERROR);
}
else if (context.options.verbose) {
this.logger.setLevel(logger_1.LogLevel.DEBUG);
}
else {
const level = this.parseLogLevel(context.options.logLevel || 'info');
this.logger.setLevel(level);
}
// Disable color if requested
if (context.options.noColor) {
chalk_1.default.level = 0;
}
// Disable analytics if requested
if (context.options.noAnalytics) {
this.analytics.disable();
}
// Load custom config if specified
if (context.options.config) {
this.config.setProjectPath(context.options.config);
}
this.logger.debug('Command execution started', {
command: context.command.name,
args: context.args,
options: context.options
});
}
});
// Performance profiling middleware
this.registry.addMiddleware({
name: 'profiling',
phase: 'pre',
handler: async (context) => {
if (context.options.profile) {
context.analytics.timeStart(`command_${context.command.name}`);
}
}
});
this.registry.addMiddleware({
name: 'profiling-end',
phase: 'post',
handler: async (context) => {
if (context.options.profile) {
context.analytics.timeEnd(`command_${context.command.name}`);
}
}
});
// Error handling middleware
this.registry.addMiddleware({
name: 'error-handler',
phase: 'post',
handler: async (context) => {
this.logger.debug('Command execution completed', {
command: context.command.name
});
}
});
}
parseLogLevel(level) {
const levelMap = {
debug: logger_1.LogLevel.DEBUG,
info: logger_1.LogLevel.INFO,
warn: logger_1.LogLevel.WARN,
error: logger_1.LogLevel.ERROR,
silent: logger_1.LogLevel.SILENT
};
return levelMap[level.toLowerCase()] || logger_1.LogLevel.INFO;
}
setupEventHandlers() {
// Handle registry events
this.registry.on('command:executing', (context) => {
this.analytics.track('command_start', {
command: context.command.name,
args: Object.keys(context.args),
options: Object.keys(context.options)
});
});
this.registry.on('command:executed', (context) => {
this.analytics.track('command_success', {
command: context.command.name
});
});
this.registry.on('command:error', ({ context, error }) => {
this.logger.error(`Command ${context.command.name} failed`, error);
this.analytics.track('command_error', {
command: context.command.name,
error: error.name,
message: error.message
});
});
// Handle process signals
process.on('SIGINT', () => {
this.logger.info('Received SIGINT, shutting down gracefully...');
this.shutdown();
});
process.on('SIGTERM', () => {
this.logger.info('Received SIGTERM, shutting down gracefully...');
this.shutdown();
});
process.on('uncaughtException', (error) => {
this.logger.error('Uncaught exception', error);
this.analytics.track('uncaught_exception', {
error: error.name,
message: error.message,
stack: error.stack
});
this.shutdown(1);
});
process.on('unhandledRejection', (reason, promise) => {
this.logger.error('Unhandled promise rejection', reason);
this.analytics.track('unhandled_rejection', {
reason: String(reason)
});
});
}
async initialize() {
if (this.initialized)
return;
try {
this.logger.debug('Initializing CLI application...');
// Initialize plugins if available
if (this.plugins && typeof this.plugins === 'object' && 'executeHook' in this.plugins) {
await this.plugins.executeHook('cli:init', {});
}
// Load built-in commands
await this.loadBuiltinCommands();
// Load plugin commands
if (this.plugins) {
await this.loadPluginCommands();
}
this.initialized = true;
this.logger.debug('CLI application initialized');
this.analytics.track('cli_init', {
version: this.options.version,
commandCount: this.registry.getAll().length
});
}
catch (error) {
this.logger.error('Failed to initialize CLI application', error);
throw error;
}
}
async loadBuiltinCommands() {
const commandsDir = path.join(__dirname, '..', 'commands');
if (!fs.existsSync(commandsDir)) {
this.logger.warn('Commands directory not found:', commandsDir);
return;
}
const commandFiles = fs.readdirSync(commandsDir)
.filter(file => file.endsWith('.ts') || file.endsWith('.js'))
.filter(file => !file.endsWith('.d.ts'));
for (const file of commandFiles) {
try {
const commandPath = path.join(commandsDir, file);
const commandModule = await Promise.resolve(`${commandPath}`).then(s => __importStar(require(s)));
if (commandModule.default && typeof commandModule.default === 'object') {
const definition = commandModule.default;
await this.registry.register(definition);
}
}
catch (error) {
this.logger.warn(`Failed to load command ${file}:`, error.message);
}
}
}
async loadPluginCommands() {
// TODO: Load commands from active plugins
this.logger.debug('Loading plugin commands...');
}
registerCommand(definition) {
this.registry.register(definition);
}
unregisterCommand(name) {
this.registry.unregister(name);
}
getCommand(name) {
return this.registry.get(name);
}
getAllCommands() {
return this.registry.getAll();
}
async run(argv) {
try {
await this.initialize();
// Register all commands with commander
for (const definition of this.registry.getAll()) {
this.registry.createCommand(this.program, definition);
}
// Add help command
this.addHelpCommand();
// Parse and execute
await this.program.parseAsync(argv || process.argv);
}
catch (error) {
this.logger.error('CLI execution failed', error);
this.analytics.track('cli_error', {
error: error.name,
message: error.message
});
process.exit(1);
}
}
addHelpCommand() {
this.program
.command('help [command]')
.description('Display help for a command')
.action((command) => {
if (command) {
const cmd = this.registry.get(command);
if (cmd) {
this.displayCommandHelp(cmd);
}
else {
console.log(chalk_1.default.red(`Command '${command}' not found`));
process.exit(1);
}
}
else {
this.displayGeneralHelp();
}
});
}
displayCommandHelp(command) {
console.log(chalk_1.default.bold(`\n${command.name} - ${command.description}\n`));
if (command.alias && command.alias.length > 0) {
console.log(chalk_1.default.gray(`Aliases: ${command.alias.join(', ')}\n`));
}
if (command.arguments && command.arguments.length > 0) {
console.log(chalk_1.default.bold('Arguments:'));
for (const arg of command.arguments) {
const argName = arg.variadic ? `${arg.name}...` : arg.name;
const required = arg.required ? chalk_1.default.red('required') : chalk_1.default.gray('optional');
console.log(` ${chalk_1.default.cyan(argName)} - ${arg.description} (${required})`);
}
console.log();
}
if (command.options && command.options.length > 0) {
console.log(chalk_1.default.bold('Options:'));
for (const opt of command.options) {
const required = opt.required ? chalk_1.default.red(' [required]') : '';
console.log(` ${chalk_1.default.cyan(opt.flag)} - ${opt.description}${required}`);
}
console.log();
}
if (command.examples && command.examples.length > 0) {
console.log(chalk_1.default.bold('Examples:'));
for (const example of command.examples) {
console.log(` ${example.description}`);
console.log(` ${chalk_1.default.gray('$')} ${example.command}\n`);
}
}
}
displayGeneralHelp() {
console.log(chalk_1.default.bold(`\n${this.options.name} v${this.options.version}\n`));
console.log(`${this.options.description}\n`);
const categories = this.registry.getCategories();
if (categories.length > 0) {
for (const category of categories) {
const commands = this.registry.getByCategory(category);
if (commands.length > 0) {
console.log(chalk_1.default.bold(`${category.toUpperCase()} COMMANDS:`));
for (const cmd of commands) {
if (!cmd.hidden) {
console.log(` ${chalk_1.default.cyan(cmd.name.padEnd(20))} ${cmd.description}`);
}
}
console.log();
}
}
}
else {
console.log(chalk_1.default.bold('COMMANDS:'));
for (const cmd of this.registry.getAll()) {
if (!cmd.hidden) {
console.log(` ${chalk_1.default.cyan(cmd.name.padEnd(20))} ${cmd.description}`);
}
}
console.log();
}
console.log(chalk_1.default.bold('GLOBAL OPTIONS:'));
console.log(' --debug Enable debug mode');
console.log(' --verbose Enable verbose output');
console.log(' --quiet Suppress output');
console.log(' --yes Skip interactive prompts');
console.log(' --no-color Disable colored output');
console.log(' --profile Enable performance profiling');
console.log(' --log-level <level> Set log level (debug, info, warn, error)');
console.log(' --config <path> Specify config file path');
console.log(' --no-analytics Disable analytics tracking');
console.log();
console.log(`Use "${this.options.name} help <command>" for more information about a command.`);
}
shutdown(exitCode = 0) {
this.logger.debug('Shutting down CLI application...');
// Flush analytics
this.analytics.flush();
// Close logger
this.logger.close();
// Exit
process.exit(exitCode);
}
getLogger() {
return this.logger;
}
getAnalytics() {
return this.analytics;
}
getConfig() {
return this.config;
}
getRegistry() {
return this.registry;
}
}
exports.CLIApp = CLIApp;