UNPKG

neex

Version:

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

163 lines (162 loc) 8.66 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.addStartCommands = addStartCommands; const start_manager_js_1 = require("../start-manager.js"); const logger_manager_js_1 = require("../logger-manager.js"); const chalk_1 = __importDefault(require("chalk")); const figures_1 = __importDefault(require("figures")); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); function addStartCommands(program) { let startManager = null; // Production start command program .command('start [file]') .description('Start production application') .option('-d, --dir <directory>', 'Working directory', process.cwd()) .option('-e, --env <file>', 'Environment file to load', '.env') .option('-p, --port <port>', 'Port number', parseInt) .option('-w, --workers <count>', 'Number of worker processes', parseInt, 1) .option('-v, --verbose', 'Verbose output') .option('--watch', 'Watch for changes and restart (development mode)') .option('--no-health', 'Disable health check endpoint') .option('--health-port <port>', 'Health check port', parseInt, 3001) .option('--max-memory <limit>', 'Maximum memory before restart (e.g., 1G)') .option('--graceful-timeout <ms>', 'Graceful shutdown timeout (ms)', parseInt, 30000) .option('--inspect', 'Enable Node.js inspector') .option('--inspect-brk', 'Enable Node.js inspector with break') .option('--node-args <args>', 'Additional Node.js arguments') .action(async (file, options) => { try { const targetFile = file || 'dist/server.js'; let resolvedFile = path_1.default.resolve(options.dir, targetFile); // Auto-detect main file if not found if (!fs_1.default.existsSync(resolvedFile)) { const packageJsonPath = path_1.default.join(options.dir, 'package.json'); if (fs_1.default.existsSync(packageJsonPath)) { try { const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8')); const mainFile = packageJson.main || 'index.js'; const alternativeFile = path_1.default.resolve(options.dir, mainFile); if (fs_1.default.existsSync(alternativeFile)) { resolvedFile = alternativeFile; if (options.verbose) { logger_manager_js_1.loggerManager.printLine(`Using main file: ${mainFile}`, 'info'); } } else { // Try common locations const commonLocations = [ 'dist/server.js', 'dist/app.js', 'dist/index.js', 'build/server.js', 'build/app.js', 'build/index.js', 'server.js', 'app.js', 'index.js' ]; let found = false; for (const location of commonLocations) { const testPath = path_1.default.resolve(options.dir, location); if (fs_1.default.existsSync(testPath)) { resolvedFile = testPath; found = true; if (options.verbose) { logger_manager_js_1.loggerManager.printLine(`Found application file: ${location}`, 'info'); } break; } } if (!found) { throw new Error(`Application file not found. Tried: ${targetFile}, ${mainFile}, and common locations.`); } } } catch (parseError) { throw new Error(`Failed to parse package.json: ${parseError.message}`); } } else { throw new Error(`Application file not found: ${resolvedFile}`); } } // Environment detection const isDevelopment = options.watch || process.env.NODE_ENV === 'development'; const isProduction = !isDevelopment; const healthCheck = options.health !== false; const defaultPort = parseInt(process.env.PORT || '8000'); const port = options.port || defaultPort; // Set NODE_ENV if not already set if (!process.env.NODE_ENV) { process.env.NODE_ENV = isProduction ? 'production' : 'development'; } // Startup logging const mode = isDevelopment ? 'development' : 'production'; const workerText = options.workers === 1 ? 'worker' : 'workers'; const clusterInfo = `(${options.workers} ${workerText})`; logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.green(figures_1.default.play)} Starting ${mode} server ${clusterInfo}`, 'info'); if (options.verbose) { logger_manager_js_1.loggerManager.printLine(`File: ${path_1.default.relative(process.cwd(), resolvedFile)}`); logger_manager_js_1.loggerManager.printLine(`Working Directory: ${options.dir}`); logger_manager_js_1.loggerManager.printLine(`Environment: ${process.env.NODE_ENV}`); logger_manager_js_1.loggerManager.printLine(`Port: ${port}`); logger_manager_js_1.loggerManager.printLine(`Workers: ${options.workers}`); if (healthCheck) { logger_manager_js_1.loggerManager.printLine(`Health Check: http://localhost:${options.healthPort}/health`); } } startManager = new start_manager_js_1.StartManager({ file: resolvedFile, workingDir: options.dir, envFile: options.env, port: options.port, workers: options.workers, memoryLimit: options.maxMemory, logLevel: options.verbose ? 'verbose' : 'info', color: true, // Assuming color is always enabled verbose: options.verbose, watch: options.watch, maxCrashes: 5, // Default max crashes restartDelay: 2000, // Default restart delay healthCheck, healthPort: options.healthPort, gracefulTimeout: options.gracefulTimeout, inspect: options.inspect, inspectBrk: options.inspectBrk, nodeArgs: options.nodeArgs }); // --- Signal Handlers for Start --- const cleanupAndExit = (signal) => { if (startManager) { logger_manager_js_1.loggerManager.printLine(`\n${chalk_1.default.yellow('⏹')} Received ${signal}, shutting down...`, 'info'); startManager.stop().then(() => process.exit(0)); } else { process.exit(0); } }; const sigintHandler = () => cleanupAndExit('SIGINT'); const sigtermHandler = () => cleanupAndExit('SIGTERM'); process.on('SIGINT', sigintHandler); process.on('SIGTERM', sigtermHandler); await startManager.start(); } catch (error) { if (error instanceof Error) { logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} ${error.message}`, 'error'); } else { logger_manager_js_1.loggerManager.printLine(`${chalk_1.default.red(figures_1.default.cross)} Startup failed`, 'error'); } process.exit(1); } }); // Cleanup function is no longer needed here as it's handled within the command const cleanupStart = async () => { }; return { cleanupStart }; }