Skip to content

fix: report a directory that appeared while no watcher listened to the watcher attaching next - #361

Merged
alexander-akait merged 1 commit into
mainfrom
fix/directory-outdated-on-attach
Oct 18, 2026
Merged

alexander-akait merged 1 commit into
mainfrom
fix/directory-outdated-on-attach

Conversation

@alexander-akait

@alexander-akait alexander-akait commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Summary

webpack pauses its watcher during a build. When the build ends, it attaches a new watcher with the build's start time and closes the paused one. Anything the paused watcher saw in between is dropped. For files, the new watcher catches up by comparing the file's time with the start time ("outdated on attach"). Directories had no stored time, so a watched missing directory that appeared during the build, e.g. a nested node_modules, was never reported. The rebuild waited for some unrelated change.

This was found through webpack's runtimes (deno) job, which runs the watch tests with polling. WatchCacheUnaffectedTestCases › cache › managedPath failed about 1 run in 20 under load, on watchpack 2.6.1 and 2.6.2 alike. Native watching can hit it too; polling widens the window.

The fix:

  • DirectoryWatcher keeps, per directory, the time it was noticed (directorySafeTimes). watch() uses that time when the target is a known directory.
  • It uses the time noticed rather than the creation time. A poll can notice a directory created just before the next build started, and that build did not see it either: it read webpack's cached "missing" state.
  • Internal watchers attach with start time 1 to wait for a directory to (re)appear. They are excluded. Otherwise a removed directory that the parent still lists fires that watcher at once, and the retry loops forever.

Tests

test/DirectoryOutdatedOnAttach.test.js:

Case main This PR
Directory appears while the paused watcher listens (native) fails passes
Same, with polling fails passes
Created just before the start time, noticed after it fails passes
Removed directory still listed by its parent: no retry loop passes passes (fails without the start-time-1 guard)

The full suite passes natively (213) and with WATCHPACK_POLLING=200, file by file. npm run lint is clean.

Checked in webpack under Deno, 40 runs under load: the missed change and the hangs are gone. The remaining failure there is a separate webpack issue: experiments.cacheUnaffected does not re-resolve a module when only a missing directory appears. I'll take that up in webpack.


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Watchers now detect directories that appear while a previous watcher is paused, including when polling is enabled, rather than waiting for an unrelated change.
    • Removed directories no longer cause repeated retries when still listed by their parent.

@changeset-bot

changeset-bot Bot commented Oct 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b0ec512

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
watchpack Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@codspeed

codspeed Bot commented Oct 18, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ 1 benchmark spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

✅ 36 untouched benchmarks


Comparing fix/directory-outdated-on-attach (b0ec512) with main (8cc6635)

Open in CodSpeed

@coderabbitai

coderabbitai Bot commented Oct 18, 2026

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ada47a60-ed08-4537-89c9-48991aa46d3d
📥 Commits

Reviewing files that changed from the base of the PR and between 8cc6635 and b0ec512.

📒 Files selected for processing: 4
  • .changeset/directory-outdated-on-attach.md
  • lib/DirectoryWatcher.js
  • test/DirectoryOutdatedOnAttach.test.js
  • types/DirectoryWatcher.d.ts

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.

Walkthrough

DirectoryWatcher now records the safe time for each noticed directory and removes that record when the directory is marked missing. When a target has no file entry, a watcher with a start time greater than 1 can use the recorded directory time. Added tests cover directory reporting across paused and newly attached watchers, polling, and retries after directory removal.

Priority: ⬇️ Low

Severity of issue fixed: Low

Merge Risk: ⚪ Minimal · up to b0ec5

No actionable issue remains from the reviewed changes. Merge after normal checks.

Pre-merge checks: Passed 5
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly describes the main change: reporting a directory that appeared while no watcher was listening when the next watcher attaches.
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 3…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 3 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.76%. Comparing base (8cc6635) to head (b0ec512).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #361      +/-   ##
==========================================
- Coverage   92.87%   92.76%   -0.12%     
==========================================
  Files           8        8              
  Lines        1404     1409       +5     
  Branches      421      423       +2     
==========================================
+ Hits         1304     1307       +3     
- Misses         94       96       +2     
  Partials        6        6              
Flag Coverage Δ
integration 92.76% <100.00%> (-0.12%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Member Author

codecov/project is red at −0.12% (2 lines), but this PR doesn't cause it. All lines it changes are covered (codecov/patch passes).

A local coverage run of lib/, main vs this branch, leaves the same 197 statements uncovered on both. Every difference is the same statement shifted by the lines this PR adds. So the 2 lines come from timing-dependent code that some of the 42 CI jobs reach on one run and miss on another. No fix ported, since there is nothing in this PR to fix.


Generated by Claude Code

@alexander-akait
alexander-akait merged commit 31fa98c into main Oct 18, 2026
50 of 51 checks passed
@alexander-akait
alexander-akait deleted the fix/directory-outdated-on-attach branch October 18, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant