@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
JavaScript
"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;