fix(docs): stop the docs watcher polling 9.5k files every 100ms - #1937
Merged
Conversation
`usePolling: true` made chokidar `fs.watchFile` every watched path, so it re-`stat`ed all of them every 100ms forever — ~80% of a CPU core while the dev server sat completely idle. The tree is local APFS and emits native change events, so polling bought nothing here; the flag was present in the script's first commit rather than added to work around a filesystem that needed it. Also stop watching build output and generated icon components. Neither feeds the docs — routes come from `.mdx` under `src/`, types are parsed from `packages/nimbus/src/index.ts` — and watching `dist/` meant every build fired thousands of `change` events that each re-triggered a full type re-parse. Idle CPU 82% -> 0% of a core; watched files 9,491 -> 1,924; startup 1.01s -> 0.35s. Generated output is unchanged: 178 route JSON files and 367 type files byte-identical, search index identical, manifest identical after deep sort (only scan-order differs). Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
Bundle Size ReportLast updated: 2026-08-19 08:03:42 UTC
Baseline source: comment-chain |
tylermorrisford
approved these changes
Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The dev watcher burned ~80% of a CPU core while completely idle. On a running dev server the process had consumed 79 minutes of CPU in 100 minutes
of wall clock, doing nothing but watching files.
apps/docs/scripts/watcher.tspassedusePolling: trueto chokidar. Nobehavior change to generated output — this is purely a dev-loop cost fix.
Root cause
chokidar v4 honors
usePollingby callingfs.watchFileon every watchedpath, which re-
stats all of them everyinterval(100 ms) forever. Thepredicate matched 9,491 files under
packages/, so an idle watcher wasissuing roughly 95,000
statsyscalls per second.Two things made this pure waste:
apfs, local, journaled) and emits native changeevents. Polling is only needed for filesystems that don't — network mounts,
some container bind mounts.
work around a filesystem that needed it. It reads as an unexamined default.
Separately, 5,444 of those 9,491 files were in
dist/and 2,123 weregenerated
material-icons. Neither feeds the docs: routes come from.mdxunder
src/, and types are parsed frompackages/nimbus/src/index.ts. Watchingdist/also meant everypnpm buildfired thousands ofchangeevents thateach re-triggered a full type re-parse.
Changes
usePolling: false— stay on nativefs.watch. Commented with why, and withthe condition under which polling would legitimately be needed, so it doesn't
get reinstated by reflex.
/dist/).material-icons).Measured effect
Same tree, same ignore semantics otherwise:
Verification
change,addandunlinkalldetected on this case-sensitive APFS volume (working tree left clean).
packages/tree in an isolated sandbox and diffed against the live baseline:TypeScript internal symbol ids (
__@iterator@1709564vs@1070), whichvary between program instances and are unrelated to watching
search-index.jsonbyte-identicalroute-manifest.jsonidentical after a deep sort — same 178 routes, samecategories, same navigation; only entry order differs
prettier --checkandeslintclean on the changed file.Regression test
None added. There's no existing harness for asserting watcher CPU or syscall
volume, and a wall-clock CPU assertion would be flaky in CI. The guard here is
the comment on the flag explaining when polling is and isn't appropriate. Happy
to add a test if reviewers want one.
Not in scope
route-manifest.jsonentry order follows filesystem scan order, so it canlegitimately differ between runs. Pre-existing and unaffected by this change
(content is identical), but worth knowing if anyone ever diffs that file.
apps/docs/tsconfig.node.jsonreportsvite.config.ts(114,24): error TS18048: 'assetInfo.name' is possibly 'undefined'. Confirmed pre-existing on a cleanmain; left untouched.