neex
Version:
Neex - Modern Fullstack Framework Built on Express and Next.js. Fast to Start, Easy to Build, Ready to Deploy
235 lines (234 loc) • 8.96 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DevRunner = void 0;
// src/dev-runner.ts - Development runner with file watching (nodemon functionality)
const watcher_1 = require("./watcher");
const runner_1 = require("./runner");
const chalk_1 = __importDefault(require("chalk"));
const figures_1 = __importDefault(require("figures"));
const logger_process_1 = require("./logger-process");
class DevRunner {
constructor(options) {
this.commands = [];
this.isRunning = false;
this.restartCount = 0;
this.startTime = new Date();
const defaultOptions = {
parallel: false,
printOutput: true,
color: true,
showTiming: true,
prefix: true,
stopOnError: false,
minimalOutput: false,
groupOutput: false,
isServerMode: false,
restartOnChange: true,
clearConsole: false,
signal: 'SIGTERM',
watch: ['./'],
ignore: [
'node_modules/**',
'.git/**',
'*.log',
'dist/**',
'build/**',
'coverage/**',
'.nyc_output/**',
'*.tmp',
'*.temp'
],
ext: ['js', 'mjs', 'json', 'ts', 'tsx', 'jsx'],
delay: 1000,
verbose: false,
showInfo: false,
runnerName: 'neex dev',
};
this.options = {
...defaultOptions,
...options
};
}
setupFileWatcher() {
const watchOptions = {
watch: this.options.watch || ['./'],
ignore: this.options.ignore,
ext: this.options.ext,
delay: this.options.delay,
verbose: this.options.verbose && this.options.showInfo // Only show verbose watcher logs if both verbose and showInfo are true
};
this.fileWatcher = new watcher_1.FileWatcher(watchOptions);
this.fileWatcher.on('change', (event) => {
if (this.options.restartOnChange && this.isRunning) {
this.handleFileChange(event);
}
});
}
async handleFileChange(event) {
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
if (this.options.showInfo) {
logger_process_1.logger.printLine(`${prefix} File changed: ${chalk_1.default.yellow(event.relativePath)}`, 'info');
}
if (this.options.clearConsole) {
console.clear();
}
await this.restart();
}
async runCommands() {
if (this.commands.length === 0) {
return [];
}
// Create a modified options object for the runner to clean up output
const runnerOptions = {
...this.options,
// Override prefix behavior to show clean command name
customPrefix: (command) => {
// Extract just the filename from the command
const match = command.match(/(?:npx ts-node|node)\s+(.+)/);
if (match) {
const filePath = match[1];
const fileName = filePath.split('/').pop() || filePath;
return `${fileName}`;
}
return command;
}
};
this.runner = new runner_1.Runner(runnerOptions);
try {
const results = await this.runner.run(this.commands);
return results;
}
catch (error) {
if (this.options.showInfo) {
logger_process_1.logger.printLine(`Execution failed: ${error.message}`, 'error');
}
return [];
}
}
printDevBanner() {
var _a, _b;
if (!this.options.showInfo) {
return; // Don't show banner if showInfo is false
}
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
const uptime = Math.floor((Date.now() - this.startTime.getTime()) / 1000);
const uptimeStr = this.formatUptime(uptime);
console.log('\n' + chalk_1.default.bgGreen.black(` ${(_a = this.options.runnerName) === null || _a === void 0 ? void 0 : _a.toUpperCase()} MODE `) + '\n');
if (this.restartCount > 0) {
console.log(`${prefix} ${chalk_1.default.green(`${figures_1.default.arrowUp} Restarted ${this.restartCount} times`)}`);
}
console.log(`${prefix} ${chalk_1.default.blue(`${figures_1.default.info} Uptime: ${uptimeStr}`)}`);
console.log(`${prefix} ${chalk_1.default.blue(`${figures_1.default.info} Watching: ${((_b = this.options.watch) === null || _b === void 0 ? void 0 : _b.join(', ')) || 'current directory'}`)}`);
if (this.options.ext && this.options.ext.length > 0) {
console.log(`${prefix} ${chalk_1.default.blue(`${figures_1.default.info} Extensions: ${this.options.ext.join(', ')}`)}`);
}
console.log('');
}
formatUptime(seconds) {
if (seconds < 60) {
return `${seconds}s`;
}
else if (seconds < 3600) {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return `${minutes}m ${remainingSeconds}s`;
}
else {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return `${hours}h ${minutes}m`;
}
}
async start(commands) {
this.commands = commands;
this.isRunning = true;
this.startTime = new Date();
// Setup file watcher
this.setupFileWatcher();
// Print development banner only if showInfo is true
this.printDevBanner();
// Start file watcher
if (this.fileWatcher) {
await this.fileWatcher.start();
}
// Run initial commands
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
if (this.options.showInfo) {
logger_process_1.logger.printLine(`${prefix} Starting development server...`, 'info');
}
await this.runCommands();
// Set up graceful shutdown
this.setupGracefulShutdown();
if (this.options.showInfo) {
logger_process_1.logger.printLine(`${prefix} Development server started. Watching for changes...`, 'info');
logger_process_1.logger.printLine(`${prefix} Press ${chalk_1.default.cyan('Ctrl+C')} to stop`, 'info');
}
}
async restart() {
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
if (!this.isRunning) {
return;
}
if (this.options.showInfo) {
logger_process_1.logger.printLine(`${prefix} Restarting due to file changes...`, 'info');
}
this.restartCount++;
// Stop current processes
if (this.runner) {
this.runner.cleanup(this.options.signal);
}
// Wait a moment before restarting
await new Promise(resolve => setTimeout(resolve, 500));
// Print restart banner only if showInfo is true
this.printDevBanner();
// Run commands again
await this.runCommands();
if (this.options.showInfo) {
logger_process_1.logger.printLine(`${prefix} Restart completed. Watching for changes...`, 'info');
}
}
async stop() {
const prefix = chalk_1.default.cyan(`[${this.options.runnerName}]`);
if (!this.isRunning) {
return;
}
this.isRunning = false;
// Stop file watcher
if (this.fileWatcher) {
this.fileWatcher.stop();
}
// Stop current processes
if (this.runner) {
this.runner.cleanup(this.options.signal);
}
const uptime = Math.floor((Date.now() - this.startTime.getTime()) / 1000);
const uptimeStr = this.formatUptime(uptime);
}
setupGracefulShutdown() {
const handleSignal = (signal) => {
this.stop().then(() => {
process.exit(0);
});
};
process.on('SIGINT', () => handleSignal('SIGINT'));
process.on('SIGTERM', () => handleSignal('SIGTERM'));
process.on('SIGQUIT', () => handleSignal('SIGQUIT'));
}
isActive() {
return this.isRunning;
}
getUptime() {
return Math.floor((Date.now() - this.startTime.getTime()) / 1000);
}
getRestartCount() {
return this.restartCount;
}
getWatchedFiles() {
var _a;
return ((_a = this.fileWatcher) === null || _a === void 0 ? void 0 : _a.getWatchedFiles()) || [];
}
}
exports.DevRunner = DevRunner;