Does Grunt's internal file cache reliably invalidate when source timestamps change on virtual file systems?
0 reputation · 28 Jun 2021, 08:52 UTC
0 reputation · 28 Jun 2021, 08:52 UTC
Grunt caches processed files in an internal store and reuses them across runs unless explicitly cleared. The invalidation mechanism relies on file timestamps, which can behave inconsistently on networked or virtual file systems such as Docker volumes, WSL mounts, or NFS shares. This raises uncertainty about whether a modified source file will always produce a fresh build artifact without manual intervention.
Version differences between Grunt 1.x and 2.x may further affect reproducibility, and the --watch flag can trigger partial updates that leave stale entries in the cache. Node.js module resolution can also cause the same logical file to be cached under multiple paths, complicating invalidation.
No. Grunt's built-in cache invalidation does not reliably trigger rebuilds on virtual filesystems (Docker volumes, WSL mounts, NFS) when source timestamps fail to update. The mechanism depends entirely on the OS reporting a newer mtime; if the VFS clamps granularity to whole seconds or preserves the original timestamp, Grunt sees no change and serves stale output.
Grunt's newer and changed tasks (and the watch pipeline in 2.x) compare fs.stat mtime values. On many VFS implementations:
nocto or Docker bind mounts on macOS/Windows) report the original creation time until the container/host syncsWhen mtime doesn't change, Grunt assumes the file is unmodified regardless of actual content differences.
| Aspect | Grunt 1.x | Grunt 2.x |
|---|---|---|
| File watching | Built-in polling (grunt watch) | chokidar (native watchers + polling fallback) |
| Cache invalidation | fs.stat mtime comparison | Same mtime check; chokidar emits events but still validates via mtime |
| VFS reliability | Polling may catch changes if interval < mtime granularity | Native watchers often silent on VFS; falls back to polling with same limitation |
Neither version guarantees invalidation when the underlying filesystem fails to update mtime.
grunt <task> --force bypasses newer/changed checks for that task only. It does not clear Grunt's global file cache or affect other tasks/watch cycles.grunt clean (or deleting .grunt / node_modules/.cache/grunt) is the only way to fully reset the internal store.Grunt does not support content-hash cache keys natively. To implement this:
crypto.createHash('sha256').update(content).digest('hex')) for each source file..grunt/content-hashes.json).grunt-newer / grunt-changed logic with this hash check.Third-party plugins like grunt-contrib-uglify accept an options.hash for output naming, but they don't replace the input-file invalidation layer.
stat source-file.js — note Modify timestamp (nanosecond precision if shown)stat source-file.js again — confirm mtime changedgrunt <task> twice without --force; if output is identical on the second run, cache served stale resultWhat does stat report for mtime nanoseconds before and after a typical edit on your VFS? If the value is unchanged or clamped to whole seconds, no Grunt version or flag will fix this without a content-hash layer.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.