(opts: HotStateReloadOptions<S>)
| 293 | } |
| 294 | |
| 295 | export function createHotStateReload<S>(opts: HotStateReloadOptions<S>): HotStateReloadController { |
| 296 | const reloadAdapter = createReloadAdapter(opts); |
| 297 | const entryLabel = isViewReloadOptions(opts) ? "viewModule" : "routesModule"; |
| 298 | const entryPath = toAbsolutePath(reloadAdapter.entryModule, entryLabel); |
| 299 | const moduleRoot = toAbsolutePath(opts.moduleRoot ?? dirname(entryPath), "moduleRoot"); |
| 300 | if (!isWithinOrEqual(moduleRoot, entryPath)) { |
| 301 | throw new Error(`${entryLabel} must be inside moduleRoot`); |
| 302 | } |
| 303 | |
| 304 | const watchTargets = new Set<string>([moduleRoot]); |
| 305 | for (const pathInput of opts.watchPaths ?? []) { |
| 306 | watchTargets.add(toAbsolutePath(pathInput, "watchPaths[]")); |
| 307 | } |
| 308 | const debounceMs = ensurePositiveInt("debounceMs", opts.debounceMs ?? DEFAULT_DEBOUNCE_MS); |
| 309 | const log = makeLogSink(opts.log); |
| 310 | const onError = opts.onError; |
| 311 | const targetLabel = reloadAdapter.targetLabel; |
| 312 | const hostNodeModulesPath = findNearestNodeModules(moduleRoot); |
| 313 | |
| 314 | let sessionRoot: string | null = null; |
| 315 | const watchers = new Map<string, FSWatcher>(); |
| 316 | let running = false; |
| 317 | let revision = 0; |
| 318 | let pendingChangePath: string | undefined; |
| 319 | let debounceTimer: ReturnType<typeof setTimeout> | null = null; |
| 320 | let refreshTimer: ReturnType<typeof setTimeout> | null = null; |
| 321 | let reloadChain: Promise<boolean> = Promise.resolve(false); |
| 322 | |
| 323 | function getSessionRoot(): string { |
| 324 | if (sessionRoot === null) { |
| 325 | sessionRoot = mkdtempSync(join(tmpdir(), "rezi-hsr-")); |
| 326 | } |
| 327 | return sessionRoot; |
| 328 | } |
| 329 | |
| 330 | function cleanupSessionRoot(): void { |
| 331 | if (sessionRoot === null) return; |
| 332 | rmSync(sessionRoot, { recursive: true, force: true }); |
| 333 | sessionRoot = null; |
| 334 | } |
| 335 | |
| 336 | function clearDebounce(): void { |
| 337 | if (debounceTimer === null) return; |
| 338 | clearTimeout(debounceTimer); |
| 339 | debounceTimer = null; |
| 340 | } |
| 341 | |
| 342 | function clearRefresh(): void { |
| 343 | if (refreshTimer === null) return; |
| 344 | clearTimeout(refreshTimer); |
| 345 | refreshTimer = null; |
| 346 | } |
| 347 | |
| 348 | function closeAllWatchers(): void { |
| 349 | for (const watcher of watchers.values()) { |
| 350 | try { |
| 351 | watcher.close(); |
| 352 | } catch { |
no test coverage detected