You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
postcss plugin re-transforms every included file on each rebuild under Turbopack (minutes-long HMR in large projects) #520
In a Next.js 16 app using react-strict-dom/postcss-plugin, every incremental rebuild under Turbopack (the default dev bundler) re-transforms all files matched by the plugin's include globs, not just the changed file. In a large monorepo (~4,000 included source files) this makes a one-line edit take ~80 seconds to rebuild, while the same edit under next dev --webpack rebuilds in ~4 seconds.
postcss.config.js: react-strict-dom/postcss-plugin with include globs covering the app + shared workspace packages (~4,000 .ts/.tsx files), useLayers: true, and the documented babelConfig with react-strict-dom/babel-preset
Measurements (same machine, same session)
Scenario
Time
Cold compile of one route (Turbopack)
~50s
One-line edit to any file matched by include (Turbopack)
~80s
Edit to a file NOT matched by include (Turbopack)
0.35s
One-line edit, next dev --webpack
4.4s
Creating a new untracked file that matches the globs — even one that nothing imports — also triggers the full ~80s rebuild, since the generated CSS has a dir-dependency on the glob roots.
Analysis
The plugin already has an incremental path: postcss-react-strict-dom/src/builder.js keeps a fileModifiedMap of mtimes and skips unchanged files. But that cache lives in the plugin instance's memory:
Under webpack, Next keeps the PostCSS config module (and the plugin closure) alive across rebuilds, so only the changed file is re-transformed → ~4s HMR.
Under Turbopack, the PostCSS transform runs in a turbopack-node worker whose module state does not survive the invalidation, so fileModifiedMap is empty on every rebuild and all ~4,000 files are re-parsed and babel-transformed serially in a single worker on every edit.
Profiling during a rebuild shows one Node process (the turbopack-node PostCSS transform) pegged at ~100% CPU for the entire rebuild while everything else idles. A native sample of that process lands almost entirely in Array.prototype.includes via postcss's MapGenerator.previous() (previousMaps.includes(map) per AST node — quadratic when the generated CSS has many nodes with distinct input maps), with the repeated Babel transform pass as the underlying volume of work.
Suggested fixes
A persistent (disk-backed) cache for the per-file transform results, keyed by path + mtime/content hash (similar to Tailwind's approach), so extraction stays incremental regardless of the host process lifecycle.
Alternatively/additionally: a note in the Next.js setup docs that large projects should currently prefer next dev --webpack, since Turbopack defeats the plugin's in-memory cache.
Repro
I can't share the affected monorepo, but the behavior scales with the number of files matched by include: generating a few thousand small components using css.create from react-strict-dom and pointing the plugin's include at them reproduces the per-edit full re-extraction under next dev (Turbopack) vs the fast incremental path under next dev --webpack. Happy to put together a synthetic repro repo if useful.
Activity
feat(postcss): keep extracted styles in a disk cache with Turbopack