import * as plugins from './smartchok.plugins.js'; import { Stringmap } from '@push.rocks/lik'; export type TSmartchokStatus = 'idle' | 'starting' | 'watching'; export type TFsEvent = | 'add' | 'addDir' | 'change' | 'error' | 'unlink' | 'unlinkDir' | 'ready' | 'raw'; /** * Smartchok allows easy wathcing of files */ export class Smartchok { public watchStringmap = new Stringmap(); public status: TSmartchokStatus = 'idle'; private watcher: plugins.watcher; private watchingDeferred = plugins.smartpromise.defer(); // used to run things when watcher is initialized private eventObservablemap = new plugins.smartrx.Observablemap(); // register one observable per event /** * constructor of class smartchok */ constructor(watchArrayArg: string[]) { this.watchStringmap.addStringArray(watchArrayArg); } private getGlobBase(globPattern: string) { // Characters that mark the beginning of a glob pattern const globChars = ['*', '?', '[', ']', '{', '}']; // Find the index of the first glob character const firstGlobCharIndex = globPattern.split('').findIndex((char) => globChars.includes(char)); // If no glob characters are found, return the entire string if (firstGlobCharIndex === -1) { return globPattern; } // Extract the substring up to the first glob character const basePathPortion = globPattern.substring(0, firstGlobCharIndex); // Find the last slash before the glob pattern starts const lastSlashIndex = basePathPortion.lastIndexOf('/'); // If there is no slash, return the basePathPortion as is if (lastSlashIndex === -1) { return basePathPortion; } // Return the base path up to and including the last slash return basePathPortion.substring(0, lastSlashIndex + 1); } /** * adds files to the list of watched files */ public add(pathArrayArg: string[]) { this.watchStringmap.addStringArray(pathArrayArg); } /** * removes files from the list of watched files */ public remove(pathArg: string) { this.watchStringmap.removeString(pathArg); } /** * gets an observable for a certain event */ public getObservableFor( fsEvent: TFsEvent ): Promise> { const done = plugins.smartpromise.defer>(); this.watchingDeferred.promise.then(() => { const eventObservable = this.eventObservablemap.getSubjectForEmitterEvent( this.watcher, fsEvent ); done.resolve(eventObservable); }); return done.promise; } /** * starts the watcher * @returns Promise */ public start(): Promise { const done = plugins.smartpromise.defer(); this.status = 'starting'; this.watcher = new plugins.watcher( this.watchStringmap.getStringArray().map((string) => { const result = this.getGlobBase(string); console.log(`Watching ${result} for changes`); return result; }), { depth: 4, recursive: true, } ); this.watcher.on('ready', () => { this.status = 'watching'; this.watchingDeferred.resolve(); done.resolve(); }); return done.promise; } /** * stop the watcher process if watching */ public async stop() { const closeWatcher = async () => { await this.watcher.close(); }; if (this.status === 'watching') { console.log('closing while watching'); await closeWatcher(); } else if (this.status === 'starting') { await this.watchingDeferred.promise; await closeWatcher(); } } }