UNPKG

@travetto/registry

Version:

Patterns and utilities for handling registration of metadata and functionality for run-time use

78 lines (63 loc) 2.2 kB
import { ManifestModuleUtil } from '@travetto/manifest'; import { watchCompiler, WatchEvent, Runtime, RuntimeIndex } from '@travetto/runtime'; interface ModuleLoader { init?(): Promise<void>; load(file: string): Promise<void>; unload(file: string): Promise<void>; } type Handler = (ev: WatchEvent) => unknown; const VALID_FILE_TYPES = new Set(['js', 'ts']); /** * Listens to file changes, and provides a unified interface for watching file changes, reloading files as needed */ class $DynamicFileLoader { #handlers: Handler[] = []; #loader: ModuleLoader; #initialized = false; async dispatch(ev: WatchEvent): Promise<void> { if (ev.action === 'update' || ev.action === 'delete') { await this.#loader.unload(ev.output); } if (ev.action === 'create' || ev.action === 'delete') { RuntimeIndex.reinitForModule(Runtime.main.name); } if (ev.action === 'create' || ev.action === 'update') { await this.#loader.load(ev.output); } for (const handler of this.#handlers) { await handler(ev); } } onLoadEvent(handler: Handler): void { this.#handlers.push(handler); } async init(): Promise<void> { if (this.#initialized) { return; } this.#initialized = true; // TODO: ESM Support? const { DynamicCommonjsLoader } = await import('./commonjs-loader'); this.#loader = new DynamicCommonjsLoader(); await this.#loader.init?.(); const handle = (err: Error): void => { if (err && (err.message ?? '').includes('Cannot find module')) { // Handle module reloading console.error('Cannot find module', { error: err }); } else { throw err; } }; process .on('unhandledRejection', handle) .on('uncaughtException', handle); // Fire off, and let it run in the bg. Restart on exit (async (): Promise<void> => { for await (const ev of watchCompiler({ restartOnExit: true })) { if (ev.file && RuntimeIndex.hasModule(ev.module) && VALID_FILE_TYPES.has(ManifestModuleUtil.getFileType(ev.file))) { await this.dispatch(ev); } } })(); } } export const DynamicFileLoader = new $DynamicFileLoader();