@asyncapi/generator
Version:
The AsyncAPI generator. It can generate documentation, code, anything!
148 lines (139 loc) • 4.42 kB
JavaScript
const fs = require('fs');
const chokidar = require('chokidar');
/**
* Class to watch for change in certain file(s)
*/
class Watcher {
constructor(paths, ignorePaths) {
if (Array.isArray(paths)) {
this.paths = paths;
} else {
this.paths = [paths];
}
//Ensure all backwards slashes are replaced with forward slash based on the requirement from chokidar
for (const pathIndex in this.paths) {
const path = this.paths[pathIndex];
this.paths[pathIndex] = path.replace(/[\\]/g, '/');
}
this.fsWait = false;
this.watchers = {};
this.filesChanged = {};
this.ignorePaths = ignorePaths;
}
/**
* Initiates watch on a path.
* @param {*} path The path the watcher is listening on.
* @param {*} changeCallback Callback to call when changed occur.
* @param {*} errorCallback Calback to call when it is no longer possible to watch a file.
*/
initiateWatchOnPath(path, changeCallback, errorCallback) {
const watcher = chokidar.watch(path, {ignoreInitial: true, ignored: this.ignorePaths});
watcher.on('all', (eventType, changedPath) => this.fileChanged(path, changedPath, eventType, changeCallback, errorCallback));
this.watchers[path] = watcher;
}
/**
* This method initiate the watch for change in all files
* @param {*} callback called when the file(s) change
*/
async watch(changeCallback, errorCallback) {
for (const index in this.paths) {
const path = this.paths[index];
this.initiateWatchOnPath(path, changeCallback, errorCallback);
}
}
/**
* Should be called when a file has changed one way or another.
* @param {*} listenerPath The path the watcher is listening on.
* @param {*} changedPath The file/dir that was changed
* @param {*} eventType What kind of change
* @param {*} changeCallback Callback to call when changed occur.
* @param {*} errorCallback Calback to call when it is no longer possible to watch a file.
*/
fileChanged(listenerPath, changedPath, eventType, changeCallback, errorCallback) {
try {
if (fs.existsSync(listenerPath)) {
const newEventType = this.convertEventType(eventType);
this.filesChanged[changedPath] = { eventType: newEventType, path: changedPath};
// Since multiple changes can occur at the same time, lets wait a bit before processing.
if (this.fsWait) return;
this.fsWait = setTimeout(async () => {
await changeCallback(this.filesChanged);
this.filesChanged = {};
this.fsWait = false;
}, 500);
}
} catch (e) {
// File was not, find all files that are missing..
const unknownPaths = this.getAllNonExistingPaths();
this.closeWatchers();
errorCallback(unknownPaths);
}
}
/**
* Convert the event type to a more usefull one.
* @param {*} currentEventType The current event type (from chokidar)
*/
convertEventType(currentEventType) {
let newEventType = currentEventType;
//Change the naming of the event type
switch (newEventType) {
case 'unlink':
case 'unlinkDir':
newEventType = 'removed';
break;
case 'addDir':
case 'add':
newEventType = 'added';
break;
case 'change':
newEventType = 'changed';
break;
case 'rename':
newEventType = 'renamed';
break;
default:
newEventType = `unknown (${currentEventType})`;
}
return newEventType;
}
/**
* Get all paths which no longer exists
*/
getAllNonExistingPaths() {
const unknownPaths = [];
for (const index in this.paths) {
const path = this.paths[index];
if (!fs.existsSync(path)) {
unknownPaths.push(path);
}
}
return unknownPaths;
}
/**
* Closes all active watchers down.
*/
closeWatchers() {
this.filesChanged = {};
for (const index in this.paths) {
const path = this.paths[index];
this.closeWatcher(path);
}
}
/**
* Closes an active watcher down.
* @param {*} path The path to close the watcher for.
*/
closeWatcher(path) {
// Ensure if called before `watch` to do nothing
if (path !== null) {
const watcher = this.watchers[path];
if (watcher !== null) {
watcher.close();
this.watchers[path] = null;
} else {
//Watcher not found for path
}
}
}
}
module.exports = Watcher;