Files
botpress--botpress/packages/cli/src/utils/file-watcher.ts
T
2026-07-13 13:34:48 +08:00

62 lines
1.7 KiB
TypeScript

import type watcher from '@parcel/watcher'
import { debounceAsync } from './concurrency-utils'
import { EventEmitter } from './event-emitter'
export type FileChangeHandler = (events: watcher.Event[]) => Promise<void>
type EmptyObject = Record<never, never>
type FileWatcherEvent = {
error: unknown
close: EmptyObject
}
type FileWatcherOptions = watcher.Options & {
debounceMs?: number
}
/**
* Wraps the Parcel file watcher to ensure errors can be catched in an async/await fashion
*/
export class FileWatcher {
public static async watch(dir: string, fn: FileChangeHandler, opt?: FileWatcherOptions) {
const eventEmitter = new EventEmitter<FileWatcherEvent>()
let subscriptionHandler = async (err: Error | null, events: watcher.Event[]) => {
if (err) {
eventEmitter.emit('error', err)
return
}
try {
await fn(events)
} catch (thrown) {
eventEmitter.emit('error', thrown)
}
}
if (opt?.debounceMs) {
subscriptionHandler = debounceAsync(subscriptionHandler, opt.debounceMs)
}
const watcher = await import('@parcel/watcher')
const subscription = await watcher.subscribe(dir, subscriptionHandler, opt)
return new FileWatcher(subscription, eventEmitter)
}
private constructor(
private _subscription: watcher.AsyncSubscription,
private _errorEmitter: EventEmitter<FileWatcherEvent>
) {}
public async close() {
await this._subscription.unsubscribe()
this._errorEmitter.emit('close', {})
}
public wait = () =>
new Promise((resolve, reject) => {
this._errorEmitter.once('error', reject)
this._errorEmitter.once('close', resolve)
})
}