@remotion/bundler: Suppress webpack rebuild on default props update#6889
Conversation
When the studio updates default props, the file write triggers a full webpack rebuild that isn't needed. This adds a WatchIgnoreNextChangePlugin that intercepts the watchFileSystem to suppress the change notification. The plugin snapshots the file/dir timestamps before the write, then when watchpack fires the aggregated callback, it restores the old timestamps, removes the file from changedFiles, and re-registers the watchpack listeners instead of calling the original callback (which would trigger _invalidate → compile). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Review: Suppress webpack rebuild on default props update
The approach of intercepting watchFileSystem and restoring timestamps to suppress watchpack callbacks is clever and well-documented. The plugin is scoped correctly, integrated cleanly via extraPlugins, and the call chain in update-default-props.ts is in the right order.
A few issues worth addressing:
Bug: snapshotDirTimestamps entries are never cleaned up for unchanged dirs
In wrappedCallback (lines 175–180), directory entries are only deleted from snapshotDirTimestamps when wasInChanged is true. If ignoreNextChange() is called but the directory is not detected as changed (e.g. extremely fast writes, filesystem granularity), the snapshot entry persists in memory forever. Unlike files, there is no unignoreNextChange() call that clears the dir snapshots. Consider either (a) unconditionally deleting the snapshot after the callback runs, or (b) adding a safety-cleanup in ignoreNextChange() before setting a new snapshot.
Type safety: collectTimeInfoEntries is an internal watchpack API
At line 249, the code casts to { collectTimeInfoEntries: ... } to call an undocumented method. This is fragile — watchpack may change or remove this method in a minor version. A safer alternative would be to re-read the filesystem timestamps directly (e.g. via fs.stat) rather than relying on internal watchpack state.
Minor: rspack-config.ts uses any[] for extraPlugins
The webpack config correctly types extraPlugins as webpack.WebpackPluginInstance[], but the rspack config falls back to any[]. This is understandable given rspack/webpack type differences, but could mask type errors at runtime. Consider adding a runtime type guard if the array is used in a way that could fail silently.
…TimeInfoEntries Fixes deprecation warnings when using rspack. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…sion - Replace console.log with Log.trace (only visible with --log=trace) - Clear watchpack.aggregatedChanges after unpausing to prevent re-triggering the same suppressed file change - Accept a trace log function in constructor to avoid bundler depending on @remotion/renderer Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Adds --log=trace to studio startup so trace logs are captured - Test updates default props via API and verifies no "Built in" log appears (rebuild was suppressed) - Then manually writes to Root.tsx and verifies webpack does rebuild - Uses Log.trace for plugin diagnostics instead of console.log Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Summary
WatchIgnoreNextChangePluginthat intercepts webpack'swatchFileSystemto suppress file change notifications for files written by the studio_invalidate()→compile()).once("aggregated")and.once("change")listeners and unpauses the watcher so the watch loop stays alive for subsequent real changesTest plan
🤖 Generated with Claude Code