UNPKG

@hitomihiumi/filewatcher

Version:

A simple module that allows you to track changes in files of certain directories and certain extensions.

214 lines (213 loc) 7.82 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FileWatcher = void 0; const node_events_1 = require("node:events"); const chokidar_1 = __importDefault(require("chokidar")); const node_fs_1 = __importDefault(require("node:fs")); const node_path_1 = __importDefault(require("node:path")); /** * Class representing a file watcher. * @extends EventEmitter */ class FileWatcher extends node_events_1.EventEmitter { processId; baseDir; watchers; dirHandlers; ignoredDirectories; allowedExtensions; monitoredDirectories; initialFiles; /** * Create a FileWatcher. * @param {number} [processId] - The process ID. */ constructor(processId) { super(); this.processId = processId || process.pid; this.baseDir = process.cwd(); this.watchers = new Map(); this.dirHandlers = new Map(); this.ignoredDirectories = new Set(); this.allowedExtensions = new Set(); this.monitoredDirectories = new Set(); this.initialFiles = new Set(); } /** * Watch a directory for changes. * @private * @param {string} directory - The directory to watch. */ watchDirectory(directory) { if (this.watchers.has(directory)) return; const watcher = chokidar_1.default.watch(directory, { persistent: true, ignored: () => { for (const ignoredDir of this.ignoredDirectories) { if (this.baseDir + '/' + ignoredDir) { return true; } } return false; } }); watcher.on("add", (filePath) => this.handleEvent("add", directory, filePath)); watcher.on("change", (filePath) => this.handleEvent("change", directory, filePath)); watcher.on("unlink", (filePath) => this.handleEvent("unlink", directory, filePath)); this.watchers.set(directory, watcher); } /** * Find a handler for a specific event type in a directory. * @private * @param {string} directory - The directory to find the handler for. * @param {"add" | "change" | "unlink"} eventType - The event type. * @returns {Function | undefined} The handler function or undefined if not found. */ findHandler(directory, eventType) { let currentDir = directory; while (currentDir !== node_path_1.default.dirname(currentDir)) { if (this.dirHandlers.has(currentDir)) { const handlers = this.dirHandlers.get(currentDir); if (handlers && handlers.has(eventType)) { return handlers.get(eventType); } } currentDir = node_path_1.default.dirname(currentDir); } return undefined; } /** * Handle a file system event. * @private * @param {"add" | "change" | "unlink"} eventType - The event type. * @param {string} directory - The directory where the event occurred. * @param {string} filePath - The path of the file that triggered the event. */ handleEvent(eventType, directory, filePath) { const filename = node_path_1.default.basename(filePath); const extension = node_path_1.default.extname(filename).toLowerCase(); const relativePath = node_path_1.default.relative(this.baseDir, filePath).replace(new RegExp(`[\\\\/]${filename}$`), ''); if (this.allowedExtensions.size > 0 && !this.allowedExtensions.has(extension)) { return; } if (this.initialFiles.has(filePath) && eventType === "add") { return; } this.emit(eventType, directory, filename, `${this.baseDir}/` + relativePath); const handler = this.findHandler(directory, eventType); if (handler) handler(directory, filename, `${this.baseDir}/` + relativePath, eventType); } /** * Capture the initial files in a directory. * @private * @param {string} directory - The directory to capture initial files from. */ captureInitialFiles(directory) { const files = node_fs_1.default.readdirSync(directory); for (const file of files) { const fullPath = node_path_1.default.join(directory, file); if (node_fs_1.default.statSync(fullPath).isDirectory()) { this.captureInitialFiles(fullPath); } else { this.initialFiles.add(fullPath); } } } /** * Start watching the directories. */ startWatching() { const directories = this.monitoredDirectories.size > 0 ? this.monitoredDirectories : new Set([this.baseDir]); for (const dir of directories) { this.captureInitialFiles(dir); } for (const dir of directories) { this.watchDirectory(dir); } return this; } /** * Stop watching all directories. */ stopWatching() { for (const watcher of this.watchers.values()) { watcher.close(); } this.watchers.clear(); return this; } /** * Set a handler for a specific event type in a directory. * @param {string} directory - The directory to set the handler for. * @param {"add" | "change" | "unlink"} eventType - The event type. * @param {Function} callback - The handler function. */ setHandler(directory, eventType, callback) { if (!this.dirHandlers.has(directory)) { this.dirHandlers.set(directory, new Map()); } const handlers = this.dirHandlers.get(directory); if (handlers) { handlers.set(eventType, callback); } this.watchDirectory(directory); return this; } /** * Ignore specific directories. * @param {...string} directory - The directories to ignore. * @returns {FileWatcher} The current FileWatcher instance. */ ignoreDirectory(...directory) { for (const dir of directory) { if (!node_fs_1.default.existsSync(dir)) { console.warn(`Directory ${dir} doesr not exist, ignoring it will not work.`); } else { this.ignoredDirectories.add(dir); } } return this; } /** * Unignore specific directories. * @param {...string} directory - The directories to unignore. * @returns {FileWatcher} The current FileWatcher instance. */ unignoreDirectory(...directory) { for (const dir of directory) { if (this.ignoredDirectories.has(dir)) { this.ignoredDirectories.delete(dir); } else { console.warn(`Directory ${dir} is not ignored.`); } } return this; } /** * Set the allowed file extensions to watch. * @param {...string} extensions - The file extensions to allow. * @returns {FileWatcher} The current FileWatcher instance. */ setAllowedExtensions(...extensions) { this.allowedExtensions = new Set(extensions.map(ext => ext.toLowerCase())); return this; } /** * Set the directories to monitor. * @param {...string} directories - The directories to monitor. * @returns {FileWatcher} The current FileWatcher instance. */ setMonitoredDirectories(...directories) { this.monitoredDirectories = new Set(directories.map(dir => node_path_1.default.resolve(this.baseDir, dir))); return this; } } exports.FileWatcher = FileWatcher;