Repository navigation
docs: correct what --hot watches and its sample output, document built-in npm package replacements - #42718
Conversation
…cument built-in package replacements - guides/http/hot.mdx: --hot reloads when the entry file or a file it imports changes. Files that no module imports (.env, bunfig.toml, tsconfig.json) do not trigger a reload. - runtime/watch-mode.mdx: the sample logs before it increments, so the first line is "Reloaded 0 times". - runtime/module-resolution.mdx: list the npm specifiers that Bun resolves to built-in modules (undici, node-fetch, isomorphic-fetch, @vercel/fetch, ws, utf-8-validate, abort-controller) and what that means for resolution and bun build.
|
Status: ready for review. This PR changes docs only. How I checked it: I ran each statement on a release build of main ( |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Essentials Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 10 included reviews per hour; 4 remain after this review. WalkthroughThe documentation clarifies hot reload watch scope, corrects the reload output example, and describes Bun’s built-in replacements for selected npm packages. ChangesRuntime documentation
Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to The PR documents runtime behavior and does not indicate a merge-blocking production change. 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
…t specifier ws/lib/websocket resolves to "ws", abort-controller/polyfill to "abort-controller", and next/dist/compiled/* to the target module name.
There was a problem hiding this comment.
LGTM — thanks for the follow-up commit; the require.resolve() wording now correctly says it returns the built-in module name (with the next/dist/compiled/undici → "undici" example), which was my only note.
What was reviewed:
- Cross-checked the specifier table and subpath/resolve claims against
src/resolve_builtins/HardcodedModule.rs— rows and alias-target semantics match. --hotscope narrowing and theReloaded 0/1/2sample match the log-then-increment code in the guide.- Prose follows the docs voice rules (active voice, present tense, no "will"/"just"); the
bun.com/issueslink matches existing usage innodejs-compat.mdx.
Extended reasoning...
Overview
Docs-only PR touching three MDX files: tightens the --hot description in docs/guides/http/hot.mdx to state that only the entry file and its imports are watched (naming .env/bunfig.toml/tsconfig.json as unwatched), corrects the --hot sample output in docs/runtime/watch-mode.mdx to start at Reloaded 0 times, and adds a "Built-in replacements for npm packages" section to docs/runtime/module-resolution.mdx documenting the third-party specifiers Bun overrides with built-in modules, their limitations, subpath fall-through, require.resolve() behavior, and per-target bun build handling.
Security risks
None. Pure documentation; no code, config, scripts, or CI changes. No external content is fetched at build time.
Level of scrutiny
Low-to-moderate — the main risk for docs changes is inaccuracy. I checked the alias table in src/resolve_builtins/HardcodedModule.rs against the documented specifiers and the resolve-returns-target-name claim; the follow-up commit 0a8a939a fixed the one inaccuracy I flagged last run (aliased subpaths resolve to the target name, not the input). The bun.com/issues link pattern already appears in neighboring docs. The prose conforms to the repo's docs voice conventions (active voice with Bun as subject, present tense, imperative remedy, no hedging adverbs).
Other factors
No CODEOWNERS entry covers docs/. No outstanding third-party CHANGES_REQUESTED reviews. The hunt exited on dry_streak with no findings. The only prior inline comment (mine) was addressed by the second commit, so approving is appropriate.
…t-in npm package replacements (oven-sh#42718) ### Problem - `docs/guides/http/hot.mdx:7` says "When any module or file changes, Bun re-runs the file." `bun --hot` watches only the entry file and the files it imports. An edit to `.env`, `bunfig.toml`, or `tsconfig.json` triggers no reload. - `docs/runtime/watch-mode.mdx:124` shows `Reloaded 1 times` first. The sample logs before it increments, so the first line is `Reloaded 0 times`. - No docs page says that Bun resolves `undici`, `node-fetch`, `ws`, and other npm specifiers to built-in modules, even when the package is installed. ### Fix - `hot.mdx`: say what `--hot` watches, name three files it does not watch, and say to restart Bun for those. - `watch-mode.mdx`: the output sample starts at `Reloaded 0 times`. - `module-resolution.mdx`: new section "Built-in replacements for npm packages". It lists every third-party specifier in the alias table (`src/resolve_builtins/HardcodedModule.rs:746-799`), what each loads, how subpaths and `require.resolve()` behave, and what `bun build` does per target. - Verified: I ran every claim on a release build of main (`1.4.3-canary.1+09bb54630`). Output is in Notes. `prettier --check` passes. ### Background - `--hot` adds a file to the watcher when the module loader loads it, and skips `node_modules`. Bun reads `.env` and `bunfig.toml` at startup, outside the module loader. - The alias table maps a specifier to a built-in module before the resolver looks at `node_modules`. It applies when the target is `bun`: the runtime and `bun build --target=bun`. - oven-sh#36102 (open) makes bare `undici` prefer the installed package. If it lands, the `undici` row changes with it. <details><summary>Notes</summary> No hunk overlaps oven-sh#30912 (`watch-mode.mdx` lines 91-99 and 146-153) or oven-sh#38408 (`module-resolution.mdx` line 309 and below). **`--hot` triggers.** `server.ts` is the sample from `watch-mode.mdx:104-116`, with `.env`, `tsconfig.json`, and `bunfig.toml` in the same directory. A driver script edits one file, waits, and counts the `Reloaded` lines. ``` [after start] lines=1 [after .env edit] lines=1 [after tsconfig edit] lines=1 [after bunfig edit] lines=1 [after server.ts edit 1] lines=2 [after server.ts edit 2] lines=3 --- out.log Reloaded 0 times Reloaded 1 times Reloaded 2 times ``` **Resolution.** `node_modules/{undici,node-fetch,ws,isomorphic-fetch}` each hold a stub package that exports `{ fromNodeModules: true }`. ``` $ bun b.cjs undici resolve: undici fromNodeModules: false node-fetch resolve: node-fetch fromNodeModules: false ws resolve: ws fromNodeModules: false isomorphic-fetch resolve: isomorphic-fetch fromNodeModules: false undici/package.json -> /tmp/dv/undici-check/node_modules/undici/package.json $ node b.cjs undici resolve: /tmp/dv/undici-check/node_modules/undici/index.js fromNodeModules: true ... ``` `require.resolve()` for every specifier in the table: ``` @vercel/fetch -> @vercel/fetch isomorphic-fetch -> isomorphic-fetch node-fetch -> node-fetch undici -> undici utf-8-validate -> utf-8-validate ws -> ws ws/lib/websocket -> ws abort-controller -> abort-controller abort-controller/polyfill -> abort-controller next/dist/compiled/ws -> ws next/dist/compiled/node-fetch -> node-fetch next/dist/compiled/undici -> undici undici/package.json -> /tmp/dv/undici-check/node_modules/undici/package.json ws/package.json -> /tmp/dv/undici-check/node_modules/ws/package.json ``` `import.meta.resolve("undici")` and `import.meta.resolve("node-fetch")` return `undici` and `node-fetch`. **What the built-in modules do.** A local `Bun.serve` answers `direct:<path>`. ``` undici.Request/Response/Headers/FormData/File/WebSocket === the globals: true (each) undici.fetch === Bun.fetch: true undici.fetch with dead ProxyAgent -> 200 direct:/x global fetch after setGlobalDispatcher(dead proxy) -> direct:/x Agent has dispatch/close/destroy/compose: undefined,undefined,undefined,undefined undici.request -> 200 body is Readable: true direct:/x undici.stream() / pipeline() / connect() / upgrade() -> This function is not yet implemented in Bun node-fetch res instanceof Response: true | body is Readable: true | buffer(): function | headers.raw(): function abort-controller default === AbortController: true, .AbortSignal === AbortSignal: true utf-8-validate: function, returns true for Buffer.from("hi") ``` `ws`: `WebSocket` wraps the global `WebSocket` (`src/js/thirdparty/ws.js:312`). `WebSocketServer` uses `node:http` and `server.upgrade()` (`ws.js:1373`, `ws.js:1566`). `utf-8-validate` exports `jsBufferConstructorFunction_isUtf8` (`src/jsc/modules/UTF8ValidateModule.h:16`), the function behind `isUtf8` in `node:buffer`. **`bun build`.** The entry imports `fetch` from `undici` and the default export of `node-fetch`, with the stub packages installed. ``` --target=bun keeps import { fetch as f1 } from "undici"; import f2 from "node-fetch"; --target=node bundles node_modules/undici/index.js and node_modules/node-fetch/index.js --target=browser bundles node_modules/undici/index.js and node_modules/node-fetch/index.js ``` The `bun test` rewrite of `@jest/globals` and `vitest` to `bun:test` uses the same table. It is already in `docs/guides/test/migrate-from-jest.mdx:19` and is not repeated here. </details>
Problem
docs/guides/http/hot.mdx:7says "When any module or file changes, Bun re-runs the file."bun --hotwatches only the entry file and the files it imports. An edit to.env,bunfig.toml, ortsconfig.jsontriggers no reload.docs/runtime/watch-mode.mdx:124showsReloaded 1 timesfirst. The sample logs before it increments, so the first line isReloaded 0 times.undici,node-fetch,ws, and other npm specifiers to built-in modules, even when the package is installed.Fix
hot.mdx: say what--hotwatches, name three files it does not watch, and say to restart Bun for those.watch-mode.mdx: the output sample starts atReloaded 0 times.module-resolution.mdx: new section "Built-in replacements for npm packages". It lists every third-party specifier in the alias table (src/resolve_builtins/HardcodedModule.rs:746-799), what each loads, how subpaths andrequire.resolve()behave, and whatbun builddoes per target.1.4.3-canary.1+09bb54630). Output is in Notes.prettier --checkpasses.Background
--hotadds a file to the watcher when the module loader loads it, and skipsnode_modules. Bun reads.envandbunfig.tomlat startup, outside the module loader.node_modules. It applies when the target isbun: the runtime andbun build --target=bun.undiciprefer the installed package. If it lands, theundicirow changes with it.Notes
No hunk overlaps #30912 (
watch-mode.mdxlines 91-99 and 146-153) or #38408 (module-resolution.mdxline 309 and below).--hottriggers.server.tsis the sample fromwatch-mode.mdx:104-116, with.env,tsconfig.json, andbunfig.tomlin the same directory. A driver script edits one file, waits, and counts theReloadedlines.Resolution.
node_modules/{undici,node-fetch,ws,isomorphic-fetch}each hold a stub package that exports{ fromNodeModules: true }.require.resolve()for every specifier in the table:import.meta.resolve("undici")andimport.meta.resolve("node-fetch")returnundiciandnode-fetch.What the built-in modules do. A local
Bun.serveanswersdirect:<path>.ws:WebSocketwraps the globalWebSocket(src/js/thirdparty/ws.js:312).WebSocketServerusesnode:httpandserver.upgrade()(ws.js:1373,ws.js:1566).utf-8-validateexportsjsBufferConstructorFunction_isUtf8(src/jsc/modules/UTF8ValidateModule.h:16), the function behindisUtf8innode:buffer.bun build. The entry importsfetchfromundiciand the default export ofnode-fetch, with the stub packages installed.The
bun testrewrite of@jest/globalsandvitesttobun:testuses the same table. It is already indocs/guides/test/migrate-from-jest.mdx:19and is not repeated here.