UNPKG

neex

Version:

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

246 lines (245 loc) 9.29 kB
"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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __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.FileWatcher = void 0; // src/watcher.ts - File watcher for development (nodemon functionality) const fs = __importStar(require("fs")); const path = __importStar(require("path")); const events_1 = require("events"); const chalk_1 = __importDefault(require("chalk")); const logger_process_1 = require("./logger-process"); class FileWatcher extends events_1.EventEmitter { constructor(options) { super(); this.watchers = []; this.watchedFiles = new Set(); this.debounceTimer = null; this.isWatching = false; this.ignorePatterns = []; // Initialize with a copy of user-provided options. const processedOptions = { ...options }; // Apply defaults if properties were not set in the provided 'options'. if (processedOptions.watch === undefined) { processedOptions.watch = ['./']; } if (processedOptions.ignore === undefined) { processedOptions.ignore = [ 'node_modules/**', '.git/**', '*.log', 'dist/**', 'build/**', 'coverage/**', '.nyc_output/**', '*.tmp', '*.temp' ]; } if (processedOptions.ext === undefined) { processedOptions.ext = ['js', 'mjs', 'json', 'ts', 'tsx', 'jsx']; } if (processedOptions.delay === undefined) { processedOptions.delay = 1000; } if (processedOptions.verbose === undefined) { processedOptions.verbose = false; } if (processedOptions.legacyWatch === undefined) { processedOptions.legacyWatch = false; } if (processedOptions.pollingInterval === undefined) { processedOptions.pollingInterval = 1000; } // 'cwd' and 'env' are optional and don't have explicit defaults in this setup; // they will be taken from 'options' or remain undefined if not provided. this.options = processedOptions; this.setupIgnorePatterns(); } setupIgnorePatterns() { this.ignorePatterns = (this.options.ignore || []).map(pattern => { // Convert glob patterns to regex const regexPattern = pattern .replace(/\*\*/g, '.*') .replace(/\*/g, '[^/]*') .replace(/\?/g, '[^/]') .replace(/\./g, '\\.'); return new RegExp(regexPattern, 'i'); }); } shouldIgnoreFile(filePath) { const relativePath = path.relative(process.cwd(), filePath); // Check ignore patterns for (const pattern of this.ignorePatterns) { if (pattern.test(relativePath) || pattern.test(filePath)) { return true; } } // Check file extension if (this.options.ext && this.options.ext.length > 0) { const ext = path.extname(filePath).slice(1); if (!this.options.ext.includes(ext)) { return true; } } return false; } async isValidFile(filePath) { try { const stats = await fs.promises.stat(filePath); return stats.isFile(); } catch (_a) { return false; } } async watchDirectory(dirPath) { try { const absolutePath = path.resolve(dirPath); if (this.options.verbose) { logger_process_1.logger.printLine(`Watching directory: ${chalk_1.default.cyan(absolutePath)}`, 'info'); } const watcher = fs.watch(absolutePath, { recursive: true }, async (eventType, filename) => { if (!filename) return; const fullPath = path.join(absolutePath, filename); if (this.shouldIgnoreFile(fullPath)) { return; } if (!(await this.isValidFile(fullPath))) { return; } this.handleFileChange(fullPath, eventType); }); this.watchers.push(watcher); } catch (error) { if (this.options.verbose) { logger_process_1.logger.printLine(`Failed to watch directory ${dirPath}: ${error.message}`, 'warn'); } } } async watchFile(filePath) { try { const absolutePath = path.resolve(filePath); if (this.shouldIgnoreFile(absolutePath)) { return; } if (!(await this.isValidFile(absolutePath))) { return; } if (this.options.verbose) { logger_process_1.logger.printLine(`Watching file: ${chalk_1.default.cyan(absolutePath)}`, 'info'); } const watcher = fs.watch(absolutePath, (eventType) => { this.handleFileChange(absolutePath, eventType); }); this.watchers.push(watcher); this.watchedFiles.add(absolutePath); } catch (error) { if (this.options.verbose) { logger_process_1.logger.printLine(`Failed to watch file ${filePath}: ${error.message}`, 'warn'); } } } handleFileChange(filePath, eventType) { if (this.options.verbose) { logger_process_1.logger.printLine(`File ${eventType}: ${chalk_1.default.yellow(path.relative(process.cwd(), filePath))}`, 'info'); } // Debounce file changes if (this.debounceTimer) { clearTimeout(this.debounceTimer); } this.debounceTimer = setTimeout(() => { this.emit('change', { path: filePath, event: eventType, relativePath: path.relative(process.cwd(), filePath) }); }, this.options.delay); } async start() { if (this.isWatching) { return; } this.isWatching = true; logger_process_1.logger.printLine('Starting file watcher...', 'info'); for (const watchPath of this.options.watch) { const absolutePath = path.resolve(watchPath); try { const stats = await fs.promises.stat(absolutePath); if (stats.isDirectory()) { await this.watchDirectory(absolutePath); } else if (stats.isFile()) { await this.watchFile(absolutePath); } } catch (error) { logger_process_1.logger.printLine(`Cannot watch ${watchPath}: ${error.message}`, 'warn'); } } const watchedCount = this.watchers.length; logger_process_1.logger.printLine(`File watcher started. Monitoring ${chalk_1.default.green(watchedCount)} locations`, 'info'); if (this.options.ext && this.options.ext.length > 0) { logger_process_1.logger.printLine(`Watching extensions: ${chalk_1.default.cyan(this.options.ext.join(', '))}`, 'info'); } } stop() { if (!this.isWatching) { return; } logger_process_1.logger.printLine('Stopping file watcher...', 'info'); this.watchers.forEach(watcher => { try { watcher.close(); } catch (error) { // Ignore errors when closing watchers } }); this.watchers = []; this.watchedFiles.clear(); this.isWatching = false; if (this.debounceTimer) { clearTimeout(this.debounceTimer); this.debounceTimer = null; } logger_process_1.logger.printLine('File watcher stopped', 'info'); } isActive() { return this.isWatching; } getWatchedFiles() { return Array.from(this.watchedFiles); } getOptions() { return { ...this.options }; } } exports.FileWatcher = FileWatcher;