62 lines
1.7 KiB
TypeScript
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)
|
|
})
|
|
}
|