Skip to content

Await watcher shutdown and harden lifecycle cleanup #295

Description

@bcomnes

Problem

DomStack.stopWatching() calls Chokidar's asynchronous close() without awaiting its result.
It also disposes the esbuild context without explicitly waiting for the queued rebuilds to finish.
The shutdown promise therefore does not establish that all watcher and rebuild work has completed before callers remove fixtures or restart watching.

Watch startup also acquires resources in stages, so failures after acquiring an esbuild context or copy watcher need a reliable cleanup path.

Evidence and uncertainty

This was inspected on master at a522e60.
The installed Chokidar declaration returns Promise<void> from close(), and its documentation calls for awaiting closure.

A local Node test run on macOS with Node 24.20.0 passed 70 of 76 tests, with six watch cases failing with EMFILE.
The run occurred in a restricted execution environment.
The connection between those errors and the missing await has not been established, and this issue should not assume that awaiting closure alone fixes EMFILE.
Chokidar performs some closure work synchronously, so the resource failure needs independent verification if it persists.

Proposed fix

  • Define a shutdown order that stops new events, awaits Chokidar closure, closes copy watchers according to their actual contract, drains or safely cancels queued rebuilds, and then disposes esbuild and the development server.
  • Ensure a failure during one cleanup step does not prevent cleanup of other acquired resources.
  • Release partially acquired resources when watch startup fails.
  • Add focused lifecycle regression tests, including stopping while a rebuild is pending and repeated watch/stop cycles.
  • Recheck the local watch failures outside the restricted runner before attributing them to a library resource leak.

Acceptance criteria

  • Awaiting stopWatching() means DOMStack has completed its pending work and released its owned watch resources.
  • No DOMStack-triggered rebuild or output write occurs after shutdown resolves.
  • A failed startup releases resources it acquired.
  • Repeated watch/stop cycles and existing granular watch tests pass.
  • Test cleanup stops watchers before removing source fixtures.

Sequencing

Related to #294, which changes watch dependency invalidation but leaves this shutdown code unchanged at fedccb6.
A narrowly scoped teardown correction can accompany #294 if it is validated independently.
Broader lifecycle work can follow without blocking the dependency correctness fixes.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions