UNPKG

@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
#!/usr/bin/env node "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;