feat(turbopack): add LocalPathOrProjectPath PostCSS config resolution#91338
Conversation
Failing test suitesCommit: 3b328b8 | About building and testing Next.js
Expand output● instant-nav-panel › should show loading skeleton during SPA navigation after clicking Start
Expand output● turbopack-postcss-multiple-configs › should render all elements with CSS module classes applied ● turbopack-postcss-multiple-configs › should apply per-directory PostCSS transforms (color: red → green) |
Merging this PR will not alter performance
Comparing Footnotes
|
Stats from current PR✅ No significant changes detected📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles
Server Middleware
Build DetailsBuild Manifests
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
📎 Tarball URL |
0241085 to
25f394d
Compare
b9cbab9 to
e2c804f
Compare
e2c804f to
6e24884
Compare
d88d4a9 to
3b328b8
Compare
Tests Passed |
3b328b8 to
c709c77
Compare
Verifies that Turbopack correctly handles multiple postcss.config.js files across separate directories with many CSS module files. Test setup: - 5 directories, each with its own postcss.config.js and plugin - 20 CSS module files per directory (100 total) - Custom PostCSS plugin that transforms color: red → green - Validates all 100 elements render with correct CSS module classes - Validates PostCSS transforms are applied in the CSS output https://claude.ai/code/session_01VjFd6129z6XbursoXTf9g7
The root postcss-plugin.js now does nothing, so only the per-directory postcss.config.js plugins can transform color: red → green. This ensures the test verifies that Turbopack resolves per-directory PostCSS configs rather than always falling back to the root config. Currently the test fails, confirming that Turbopack does not pick up per-directory postcss.config.js files. https://claude.ai/code/session_01VjFd6129z6XbursoXTf9g7
Add a new PostCssConfigLocation::LocalPathOrProjectPath variant that searches for postcss config starting from the CSS file's parent directory first, then falls back to the project root. This allows per-directory postcss.config.js files to override the project root config. Replace ProjectPathOrLocalPath with LocalPathOrProjectPath in both client and server contexts so user code benefits from local-first config resolution. Add doc comments to all PostCssConfigLocation variants. https://claude.ai/code/session_01VjFd6129z6XbursoXTf9g7
Gate the per-directory PostCSS config resolution behind an opt-in experimental flag. When turbopackLocalPostcssConfig is true, Turbopack searches for postcss.config.js starting from the CSS file's directory first, then falls back to the project root. Default behavior (flag absent/false) keeps the existing project-root-first resolution. Co-Authored-By: Claude <noreply@anthropic.com>
Test fixture: - Reduce CSS files from 100 (5×20) to 15 (5×3), sufficient to test per-directory PostCSS config resolution without excessive file count - Deduplicate postcss-plugin.js into a single shared file under styles/ - Use array.map() pattern in Dir components instead of 20 manual elements - Extract collectCss() helper in the test file - Remove 360s timeouts (no longer needed with fewer files) Rust: - Refactor find_config_in_location() to express search order as a vec of paths instead of scattered matches!/if-let blocks, making the strategy for each PostCssConfigLocation variant explicit Co-Authored-By: Claude <noreply@anthropic.com>
The test uses turbopackLocalPostcssConfig (Turbopack-only feature) and PostCSS plugins passed as require() functions (incompatible with webpack). Additionally, PostCSS evaluation in Turbopack production builds panics with "Dependency tracking is disabled so invalidation is not allowed". Skip webpack and production modes to fix all CI failures. Co-Authored-By: Claude <noreply@anthropic.com>
Remove the isNextDev restriction so the test also runs during next build (Turbopack production). The test was previously limited to dev mode out of caution after a production panic; the test now verifies per-directory PostCSS config resolution works in both dev and start modes. Co-Authored-By: Claude <noreply@anthropic.com>
Each directory's postcss.config.js now passes a unique color option (blue, purple, orange, cyan, magenta) to the shared plugin, proving that per-directory config resolution produces distinct results rather than all sharing the same transform.
Replace file-name-based output path derivation with a deterministic xxh3 hash of the full module ident. This avoids collisions when multiple entries share the same file name (e.g. per-directory postcss.config.js files) and removes the unused Cow import.
c709c77 to
eb7d1a6
Compare
Add a dedicated API reference page for turbopackLocalPostcssConfig and register it in the Turbopack experimental options table. Co-Authored-By: Claude <noreply@anthropic.com>
What?
Adds a new
experimental.turbopackLocalPostcssConfigoption and correspondingPostCssConfigLocation::LocalPathOrProjectPathvariant so Turbopack can resolvepostcss.config.jsstarting from the CSS file's directory first, falling back to the project root.Also fixes an issue that surfaces when multiple per-directory
postcss.config.jsfiles are used:Evaluate pool asset name collisions — output file names for the Node.js evaluate pool are now derived from an xxh3 hash of the full module ident rather than the bare file name, so multiple
postcss.config.jsfiles in different directories no longer collide.Why?
Currently Turbopack uses
ProjectPathOrLocalPathwhich checks the project root first, then falls back to the local directory. This means per-directorypostcss.config.jsfiles cannot override the root config — the root config always wins. For projects that need different PostCSS transforms in different directories (e.g. a monorepo with multiple apps or style directories), the more specific config should take precedence.How?
Turbopack PostCSS resolution (
turbopack/crates/turbopack-node/src/transforms/postcss.rs):PostCssConfigLocation::LocalPathOrProjectPathenum variant with doc comments on all variantsfind_config_in_location()to express the search order as aVec<FileSystemPath>built from a singlematch, making each variant's strategy immediately clearEvaluate pool asset file naming (
turbopack/crates/turbopack-node/src/evaluate.rs):turbo-tasks-hashdependency toturbopack-nodepostcss.config.jsfiles)Next.js config flag (
packages/next/src/server/config-shared.ts,config-schema.ts):experimental.turbopackLocalPostcssConfig: booleanoption (opt-in, defaultfalse)crates/next-core/src/next_config.rs→ both client and server contexts (next_client/context.rs,next_server/context.rs)true, usesLocalPathOrProjectPath; whenfalse/unset, uses the existingProjectPathOrLocalPath(no behavior change by default)E2E test (
test/e2e/app-dir/turbopack-postcss-multiple-configs/):turbopackLocalPostcssConfigflag is Turbopack-specific)postcss.config.jspassing a unique color option (blue,purple,orange,cyan,magenta) to a shared PostCSS plugin, proving that per-directory config resolution produces distinct results rather than all sharing the same transformpostcss.config.jsis a no-op — if only the root config were used, CSS would remain unmodified<style>tags for dev, linked.cssfiles for production) and asserts each directory's expected color is present andcolor: redis absentDocumentation (
docs/01-app/03-api-reference/05-config/01-next-config-js/turbopackLocalPostcssConfig.mdx,docs/01-app/03-api-reference/08-turbopack.mdx):experimental.turbopackLocalPostcssConfigcovering usage, behavior table, and a directory-tree exampleTest plan
cargo check -p turbo-tasks-backendpassescargo check -p turbopack-nodeandcargo check -p next-corepassturbopack-postcss-multiple-configsvalidates per-directory PostCSS config resolution across 5 directories with distinct per-directory colors (Turbopack dev and production modes)