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
JavaScript
;
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 };
}