webpack / webpack/watchpack

change event is fired for directories and files that were created shortly before startTime, when startTime is set

Open
#295 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
400
Forks
111
Avg merge
11h 16m
Merged PRs (30d)
5

Description

Have you used AI?

No

Bug Description

When Watchpack is initialized with startTime: Date.now(), it fires the change event for directories / files that were created shortly before.

When startTime is not set, the change event is not fired as desired (fixed with test coverage in #150).

README.md documents startTime as being an optional number, and "Watching can be started in the past. This way watching can start after file reading.". The observed behavior of getting unwanted events is surprising.

Moreover, the source shows that FS_ACCURACY (up to two seconds!) is added to the start time. Any file/directory older than this time will be flagged as changed. This is not documented and surprising (and contributing to this bug):
https://github.com/webpack/watchpack/blob/59adaaa473f0e164c7d88b8b8184b01693902d05/lib/DirectoryWatcher.js#L430

The above source also shows the use of birthtime/btime instead of ctime. This is surprising and not documented in README.
I tried using fs.utimes with a past date as a work-around. This works on macOS but failed on Linux and Windows, because fs.utimes updates mtime but not btime. The use of btime appears to be introduced in #183. The work-around works in watchpack@2.0.1 (which is before 2.1.0 that includes #183).

Link to Minimal Reproduction and step to reproduce

npm install watchpack ( tested with latest version watchpack@2.5.1 and Node 25.9.0)
Save following file as test.mjs and run node test.mjs:

import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import Watchpack from "watchpack";

const base = path.join(os.tmpdir(), "watchpack_repro");
await fs.rm(base, { recursive: true }).catch(() => {}); // ignore ENOENT
await fs.mkdir(base);
await fs.mkdir(path.join(base, "subdir"));

const dateInPast = Date.now() / 1000 - 10; // 10 seconds older should be plenty
// await fs.utimes(path.join(base, 'subdir'), dateInPast, dateInPast);
// ^ Works on macOS (changes btime), not on Linux or Windows. Also works in watchpack<=2.0.1

const w = new Watchpack({});
w.on("change", console.log);
w.watch({
  directories: [base],
  missing: [],
  startTime: Date.now(),
});
console.log("Waiting 2 seconds. Should not log anything.");
await new Promise(r => setTimeout(r, 2000));
w.close();
Expected Behavior

Should not log change events for files/directories that were created shortly before Watchpack is initialized.

Actual Behavior

Logs a change event for the subdirectory that was created immediately before:

/tmp/watchpack_repro/subdir 1777218474762 scan (dir)

(the minimal STR uses a directory, but this also reproduces for files)

Environment
Node: v25.9.0 (and also tested v20.20.2)
Linux: Ubuntu 24.04.4 LTS
macOS: 15.7.5
Is this a regression?

No

Last Working Version

No response

Additional Context

The fs.utimes work-around to get the test to pass works on macOS because it apparently changes the btime as well:

$ mkdir d;node -e 'require("fs/promises").utimes("d", 1234, 5678)';stat -x d
...
Access: Thu Jan  1 01:20:34 1970
Modify: Thu Jan  1 02:34:38 1970
Change: Sun Apr 26 18:19:50 2026
 Birth: Thu Jan  1 02:34:38 1970

On Linux, the Birth time is not modified when I run the same command.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the reproduction in test.mjs and inspect lib/DirectoryWatcher.js around line 430, where FS_ACCURACY and birthtime are used with startTime. Verify the behavior on the reported platforms and check the existing watcher tests, including the coverage referenced in #150. Done means recently created files and directories do not emit unwanted change events when startTime is set, with any relevant semantics documented in README.md.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.