fix(jest-haste-map): tolerate a locked file while indexing on Windows - #16358
Merged
Conversation
The agent notes called ChangeQueue's 30 ms interval a debounce; it is a periodic flush that emits whatever has queued, and it does not reset on activity. The same line merged two distinct guards — the event-key `Set` and the mtime comparison — into one claim. Add the two things a reader has to discover the hard way: that `ModuleMap`'s `toJSON`/`fromJSON` run for every test worker rather than only for the cache, and that overlapping roots are collapsed by watchman but traversed twice by the node crawler. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`includeDirs` and `WalkEntryKind`'s `dir` case have no caller — `find()` asks for files only — which reads as dead surface. Both exist for symlinked- directory support, where following a link means walking the directory behind it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
✅ Deploy Preview for jestjs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
`workerError` treated only `ENOENT` and `EACCES` as recoverable, so an `EPERM` from a file another process holds open failed the whole crawl. The watchers already survive that case via `isIgnorableFileError` (jestjs#16295); file processing did not. Move that helper from `watchers/common.ts` into `lib/`: both the watch path and the crawl path use it now, and `lib/FileProcessor.ts` should not reach sideways into `watchers/` for an errno predicate. `EACCES` stays a separate condition: an unreadable file should not tear a watcher down, but here it only means this one file cannot be indexed. Also correct the agent notes and add two comments from the same review pass: `ChangeQueue`'s 30 ms interval is a periodic flush rather than a debounce, its two dedup guards are distinct, `ModuleMap`'s `toJSON`/`fromJSON` run for every test worker, overlapping roots are collapsed by watchman but walked twice by `find()`, and `walk()`'s `includeDirs`/`dir` surface exists for symlinked directories. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Improves Windows crawl resilience by treating transient EPERM errors as ignorable during indexing.
Changes:
- Shares the ignorable-file-error predicate across crawlers and watchers.
- Adds worker error-path tests.
- Updates haste-map documentation and changelog.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
CHANGELOG.md |
Records the Windows indexing fix. |
packages/jest-haste-map/CLAUDE.md |
Updates architecture notes. |
packages/jest-haste-map/src/lib/FileProcessor.ts |
Ignores Windows EPERM during indexing. |
packages/jest-haste-map/src/lib/__tests__/FileProcessor.test.ts |
Expands worker-error tests. |
packages/jest-haste-map/src/lib/__tests__/isIgnorableFileError.test.ts |
Relocates helper tests. |
packages/jest-haste-map/src/lib/isIgnorableFileError.ts |
Houses the shared error predicate. |
packages/jest-haste-map/src/lib/walk.ts |
Documents directory-entry support. |
packages/jest-haste-map/src/watchers/ParcelWatcher.ts |
Imports the relocated helper. |
packages/jest-haste-map/src/watchers/WatchmanWatcher.js |
Imports the relocated helper. |
packages/jest-haste-map/src/watchers/common.ts |
Removes the former helper definition. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
babel-jest
babel-plugin-jest-hoist
babel-preset-jest
create-jest
@jest/diff-sequences
expect
@jest/expect-utils
jest
jest-changed-files
jest-circus
jest-cli
jest-config
@jest/console
@jest/core
@jest/create-cache-key-function
jest-diff
jest-docblock
jest-each
@jest/environment
jest-environment-jsdom
@jest/environment-jsdom-abstract
jest-environment-node
@jest/expect
@jest/fake-timers
@jest/get-type
@jest/globals
jest-haste-map
jest-jasmine2
jest-leak-detector
jest-matcher-utils
jest-message-util
jest-mock
@jest/pattern
jest-phabricator
jest-regex-util
@jest/reporters
jest-resolve
jest-resolve-dependencies
jest-runner
jest-runtime
@jest/schemas
jest-snapshot
@jest/snapshot-utils
@jest/source-map
@jest/test-result
@jest/test-sequencer
@jest/transform
@jest/types
jest-util
jest-validate
jest-watcher
jest-worker
pretty-format
commit: |
The added tests covered `EACCES`, `ENOENT` and `EISDIR`, all of which behave identically before and after the change, so reverting the fix outright left them green — the `EPERM`-on-Windows path they exist for was never asserted. Cover both platforms, loading `FileProcessor` through `jest.isolateModules` because the predicate reads the platform once at module load. Also fix two claims found in review. The comment cited `nodejs/node#4337`, which is an HTTP Upgrade change rather than anything about file locking; drop the reference and describe the behaviour on its own. And `statCache` spares repeated `lstat` calls only best-effort — the per-root walks run concurrently and can both miss a path before either caches it, as `walk.ts` already notes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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
One behavioural fix plus some minor documentation tweaks.
A file another process holds open aborts the crawl on Windows.
workerErrortreated exactly two codes as recoverable:Anything else fails the whole build. On Windows that includes
EPERM, which is whatgit maintenancetouching.git/index.lockor.git/objectslooks like from the outside — a transient condition that says nothing about the file being indexable. The watchers already survive it: #16295 addedisIgnorableFileErrorfor precisely this, but only on the watch path, so a crawl hitting the same file still died.That helper lived in
watchers/common.ts. Both the watch path and the crawl path want it now, so it moves tolib/isIgnorableFileError.tsinstead of havinglib/FileProcessor.tsreach sideways intowatchers/for an errno predicate.ParcelWatcherandWatchmanWatcherimport it from there, and its tests move with it.EACCESstays a separate condition rather than folding into the helper, because the two callers want different answers: an unreadable file should not tear a watcher down, but during indexing it only means this one file cannot be read. Collapsing them would silently dropEACCEStolerance.Docs. Three corrections to
packages/jest-haste-map/CLAUDE.md, and a comment inwalk.ts:ChangeQueue's 30 ms interval was described as a debounce. It is a periodic flush that emits whatever has queued and does not reset on activity.Setoftype:path:mtimekeys drops duplicates within a frame, and a directmtimecomparison dropschangeevents for files that were only accessed.ModuleMap'stoJSON/fromJSONrun for every test worker, not just for the on-disk cache — worth knowing before touching them, as fix(jest-haste-map): restore the nested duplicates index inModuleMap.fromJSON#16353 showed.rootsare collapsed by watchman viawatch-projectbut walked separately byfind(), so the shared subtree is traversed twice. The map stays correct, andstatCachespares some of the repeatedlstatcalls — best-effort only, since the walks run concurrently and can both miss a path before either caches it. The traversal is always duplicated.walk.ts:includeDirsand thedirentry kind have no caller and read as dead surface. They are there for symlinked-directory support.Test plan
lib/__tests__/FileProcessor.test.tsasserts the changed line on both platforms:EPERMis tolerated onwin32and still thrown onlinux. It loadsFileProcessorthroughjest.isolateModuleswithnode:osmocked, because the predicate reads the platform once at module load.That test is the guard. The error-path cases around it —
EACCESandENOENTdropping the file without throwing, an unrecognised code (EISDIR) propagating — behave the same before and after this change, so on their own they left a full revert green. RevertingworkerErrorto the original array check now fails:watchers/__tests__/common.test.ts→lib/__tests__/isIgnorableFileError.test.tsas a rename, keeping its platform matrix (ENOENTeverywhere,EPERMonly on win32,EACCESnever).Also green:
yarn lint,yarn typecheck:tests,yarn check-changelog,yarn check-copyright-headers.