SIGN IN SIGN UP
oven-sh / bun UNCLAIMED

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one

0 0 150 Rust

node:fs: move fs.watch's FSWatcher into a lazily loaded internal module (#31428)

### What

Moves the `fs.watch` implementation (the `FSWatcher` class) out of
`node:fs` into a new lazily loaded internal module, as requested.
`fs.watch` in `fs.ts` is now just a thin wrapper that `require`s the
internal module and calls its `watch()`.

- **`src/js/internal/fs/watch.ts` (new):** `FSWatcher` plus a `watch()`
wrapper, moved from `fs.ts`. Gets the native binding from
`internal/fs/binding`, the same way the other lazily loaded
`internal/fs/*` modules do. Only evaluated on the first `fs.watch` call.
- **`src/js/node/fs.ts`:** `watch` delegates to
`require("internal/fs/watch").watch(...)`; the `FSWatcher` class, its
`setName` call, and the now-unused `node:events` import are gone (−75
lines). `watchFile`/`unwatchFile` (`StatWatcher`) are untouched — that
was the counterpart refactor #31424.
- **`src/jsc/bindings/ProcessBindingNatives.cpp`:** list
`internal/fs/watch` in `process.binding('natives')`, like the other
internal fs modules.

No `src/jsc/lib.rs` change: since #31424 the specifier →
`ResolvedSourceTag` table is code-generated
(`generated_resolved_source_tag.rs`, emitted by `bundle-modules.ts` and
`include!`'d by `lib.rs`), so adding the module and rebuilding
regenerates it — `internal:fs/watch` lands at tag 537 and later ids
shift automatically.

### Verification

- Rebuilt with the debug build;
`build/debug/codegen/generated_resolved_source_tag.rs` now contains
`b"internal:fs/watch" => ResolvedSourceTag(537)` with all later entries
shifted.
- All-builtins sanity check on the debug build: every
`require("node:module").builtinModules` entry (76) loads, and identity
checks (`fs.promises === require("node:fs/promises")`, `stream.Readable
=== require("node:_stream_readable")`, `path.posix ===
require("node:path/posix")`, `util.types ===
require("node:util/types")`, `dns.promises ===
require("node:dns/promises")`, …) pass — confirms the id shift didn't
misalign any builtin.
- `process.binding('natives')` includes `internal/fs/watch`.
- `test/js/node/watch/fs.watch.test.ts` on the debug build: 34 pass / 3
platform-skips. The only 2 failures are the "no permission to watch"
tests, which fail identically on the stock release build in this
environment because the container runs as root (chmod can't block root)
— unrelated to this change.
- New tests pin the behaviors that live in the moved code: the watcher
is an `EventEmitter` named `FSWatcher` with `ref`/`unref`/`start`, and
watch errors keep the `path`/`filename` properties. Behavior is
unchanged, so they pass both before and after.

### Notes

- Rebased onto main after #31424 merged; adopts its
`internal/fs/binding` native-binding module and its code-generated tag
table, so this diff no longer touches `lib.rs`.
- `internal/fs/watch.ts` is a verbatim move, apart from rewording the
comment that documents the watchpack `EACCES` → `EPERM` workaround.
R
robobun committed
b668561c31f848430f9ef5bafd761b83cdedd5c3
Parent: db928c2
Committed by GitHub <noreply@github.com> on 5/27/2026, 9:21:52 PM