Skip to content

feat(turbopack): add LocalPathOrProjectPath PostCSS config resolution#91338

Merged
sokra merged 10 commits into
canaryfrom
claude/test-turbopack-postcss-DhRev
Apr 11, 2026
Merged

feat(turbopack): add LocalPathOrProjectPath PostCSS config resolution#91338
sokra merged 10 commits into
canaryfrom
claude/test-turbopack-postcss-DhRev

Conversation

@sokra

@sokra sokra commented Mar 13, 2026

Copy link
Copy Markdown
Member

What?

Adds a new experimental.turbopackLocalPostcssConfig option and corresponding PostCssConfigLocation::LocalPathOrProjectPath variant so Turbopack can resolve postcss.config.js starting 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.js files 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.js files in different directories no longer collide.

Why?

Currently Turbopack uses ProjectPathOrLocalPath which checks the project root first, then falls back to the local directory. This means per-directory postcss.config.js files 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):

  • Added PostCssConfigLocation::LocalPathOrProjectPath enum variant with doc comments on all variants
  • Refactored find_config_in_location() to express the search order as a Vec<FileSystemPath> built from a single match, making each variant's strategy immediately clear

Evaluate pool asset file naming (turbopack/crates/turbopack-node/src/evaluate.rs):

  • Replaced file-name-based output path derivation with a deterministic xxh3 hash of the full module ident
  • Adds turbo-tasks-hash dependency to turbopack-node
  • Eliminates name collisions when multiple entries share the same file name (e.g. per-directory postcss.config.js files)

Next.js config flag (packages/next/src/server/config-shared.ts, config-schema.ts):

  • Added experimental.turbopackLocalPostcssConfig: boolean option (opt-in, default false)
  • Wired through crates/next-core/src/next_config.rs → both client and server contexts (next_client/context.rs, next_server/context.rs)
  • When true, uses LocalPathOrProjectPath; when false/unset, uses the existing ProjectPathOrLocalPath (no behavior change by default)

E2E test (test/e2e/app-dir/turbopack-postcss-multiple-configs/):

  • Runs in both Turbopack dev and production modes — skips webpack only (webpack does not support function-valued PostCSS plugins and the turbopackLocalPostcssConfig flag is Turbopack-specific)
  • 5 style directories, each with its own postcss.config.js passing 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 transform
  • Root postcss.config.js is a no-op — if only the root config were used, CSS would remain unmodified
  • Test validates all 15 elements (5 dirs × 3 files) render with CSS module classes
  • Test collects all CSS (inline <style> tags for dev, linked .css files for production) and asserts each directory's expected color is present and color: red is absent

Documentation (docs/01-app/03-api-reference/05-config/01-next-config-js/turbopackLocalPostcssConfig.mdx, docs/01-app/03-api-reference/08-turbopack.mdx):

  • New dedicated reference page for experimental.turbopackLocalPostcssConfig covering usage, behavior table, and a directory-tree example
  • Added to the experimental options table in the Turbopack API reference

Test plan

  • cargo check -p turbo-tasks-backend passes
  • cargo check -p turbopack-node and cargo check -p next-core pass
  • E2E test turbopack-postcss-multiple-configs validates per-directory PostCSS config resolution across 5 directories with distinct per-directory colors (Turbopack dev and production modes)
  • Test correctly skips in webpack mode and deployment
  • Documentation page and experimental options table updated

@nextjs-bot nextjs-bot added created-by: Turbopack team PRs by the Turbopack team. tests Turbopack Related to Turbopack with Next.js. labels Mar 13, 2026
@nextjs-bot

nextjs-bot commented Mar 13, 2026

Copy link
Copy Markdown
Contributor

Failing test suites

Commit: 3b328b8 | About building and testing Next.js

pnpm test-dev-turbo test/development/app-dir/instant-navs-devtools/instant-navs-devtools.test.ts (turbopack) (job)

  • instant-nav-panel > should show loading skeleton during SPA navigation after clicking Start (DD)
Expand output

● instant-nav-panel › should show loading skeleton during SPA navigation after clicking Start

expect(received).toBe(expected) // Object.is equality

Expected: true
Received: false

  164 |           '[data-testid="dynamic-skeleton"]'
  165 |         )
> 166 |         expect(skeleton).toBe(true)
      |                          ^
  167 |       },
  168 |       30000,
  169 |       500

  at toBe (development/app-dir/instant-navs-devtools/instant-navs-devtools.test.ts:166:26)
  at retry (lib/next-test-utils.ts:861:14)
  at Object.<anonymous> (development/app-dir/instant-navs-devtools/instant-navs-devtools.test.ts:161:5)

pnpm test-dev test/e2e/app-dir/turbopack-postcss-multiple-configs/turbopack-postcss-multiple-configs.test.ts (job)

  • turbopack-postcss-multiple-configs > should render all elements with CSS module classes applied (DD)
  • turbopack-postcss-multiple-configs > should apply per-directory PostCSS transforms (color: red → green) (DD)
Expand output

● turbopack-postcss-multiple-configs › should render all elements with CSS module classes applied

thrown: "Exceeded timeout of 120000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."

  31 |   // root config were used, the CSS would still contain red.
  32 |
> 33 |   it('should render all elements with CSS module classes applied', async () => {
     |   ^
  34 |     const $ = await next.render$('/')
  35 |
  36 |     for (let dir = 1; dir <= DIRS; dir++) {

  at it (e2e/app-dir/turbopack-postcss-multiple-configs/turbopack-postcss-multiple-configs.test.ts:33:3)
  at Object.describe (e2e/app-dir/turbopack-postcss-multiple-configs/turbopack-postcss-multiple-configs.test.ts:3:1)

● turbopack-postcss-multiple-configs › should apply per-directory PostCSS transforms (color: red → green)

thrown: "Exceeded timeout of 120000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."

  46 |   })
  47 |
> 48 |   it('should apply per-directory PostCSS transforms (color: red → green)', async () => {
     |   ^
  49 |     const cssContent = await collectCss(next)
  50 |
  51 |     // The per-directory PostCSS plugins transform `color: red` to `color: green`.

  at it (e2e/app-dir/turbopack-postcss-multiple-configs/turbopack-postcss-multiple-configs.test.ts:48:3)
  at Object.describe (e2e/app-dir/turbopack-postcss-multiple-configs/turbopack-postcss-multiple-configs.test.ts:3:1)

@codspeed-hq

codspeed-hq Bot commented Mar 13, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 17 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing claude/test-turbopack-postcss-DhRev (7782bd9) with canary (ba33b4e)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@nextjs-bot

nextjs-bot commented Mar 14, 2026

Copy link
Copy Markdown
Contributor

Stats from current PR

✅ No significant changes detected

📊 All Metrics
📖 Metrics Glossary

Dev Server Metrics:

  • Listen = TCP port starts accepting connections
  • First Request = HTTP server returns successful response
  • Cold = Fresh build (no cache)
  • Warm = With cached build artifacts

Build Metrics:

  • Fresh = Clean build (no .next directory)
  • Cached = With existing .next directory

Change Thresholds:

  • Time: Changes < 50ms AND < 10%, OR < 2% are insignificant
  • Size: Changes < 1KB AND < 1% are insignificant
  • All other changes are flagged to catch regressions

⚡ Dev Server

Metric Canary PR Change Trend
Cold (Listen) 455ms 456ms █▁██▁
Cold (Ready in log) 440ms 443ms █▄▄▇▃
Cold (First Request) 1.132s 1.126s ▁▇███
Warm (Listen) 457ms 456ms ▁█▁▁█
Warm (Ready in log) 441ms 443ms ▆▄▅██
Warm (First Request) 343ms 337ms ▆▇▅▇▅
📦 Dev Server (Webpack) (Legacy)

📦 Dev Server (Webpack)

Metric Canary PR Change Trend
Cold (Listen) 456ms 455ms ▁▁▁▅▁
Cold (Ready in log) 437ms 437ms ▅▄▄▃▄
Cold (First Request) 1.874s 1.856s ▂▂▇▁▂
Warm (Listen) 455ms 456ms █▁███
Warm (Ready in log) 437ms 437ms ▆▄▆▅▅
Warm (First Request) 1.874s 1.865s ▃▃▇▂▃

⚡ Production Builds

Metric Canary PR Change Trend
Fresh Build 3.905s 3.895s ▄▃█▂▃
Cached Build 3.889s 3.935s ▄▃█▁▁
📦 Production Builds (Webpack) (Legacy)

📦 Production Builds (Webpack)

Metric Canary PR Change Trend
Fresh Build 14.424s 14.469s ▅▄▁▂▃
Cached Build 14.604s 14.559s ▄▃▂▂▃
node_modules Size 492 MB 492 MB █████
📦 Bundle Sizes

Bundle Sizes

⚡ Turbopack

Client

Main Bundles
Canary PR Change
0-xemccipzit0.js gzip 13 kB N/A -
00-sfwc7hh0nr.js gzip 12.9 kB N/A -
07njffbag0w__.js gzip 156 B N/A -
0cz1d0mv5g_q7.js gzip 39.4 kB 39.4 kB
0lnp3l1q03nf6.js gzip 157 B N/A -
0lsxv4bg541pw.js gzip 65.5 kB N/A -
0p33svf597zhz.js gzip 156 B N/A -
0s8zkgq8tbel7.js gzip 70.8 kB N/A -
0vkt_1wbttko4.js gzip 153 B N/A -
13_2qj6sfhcpr.js gzip 8.51 kB N/A -
13q15tdry7-jw.js gzip 9.81 kB N/A -
163s91ld-ejxd.js gzip 154 B N/A -
16jdy7mb2hpzo.js gzip 2.28 kB N/A -
16lhqjoqbznyg.js gzip 220 B 220 B
17c6iioxxyq_7.js gzip 8.51 kB N/A -
1e9hak60wi8_q.js gzip 10.1 kB N/A -
1elt1qium-r2m.css gzip 115 B 115 B
1lti_nqcijcmi.js gzip 225 B N/A -
1lu_zu666m3ae.js gzip 160 B N/A -
1m9l9vnf18-38.js gzip 49 kB N/A -
1rmcmivcklfqn.js gzip 155 B N/A -
1zq04q8id1dsq.js gzip 8.59 kB N/A -
22jf6c01ubp3o.js gzip 159 B N/A -
246le60fytek6.js gzip 1.46 kB N/A -
28nzibfilon22.js gzip 156 B N/A -
2g5l7iy9vkloq.js gzip 152 B N/A -
2ipc3se0d9mja.js gzip 7.61 kB N/A -
2rm1ibbmjhlgi.js gzip 8.57 kB N/A -
2tmy32jxo7uy5.js gzip 154 B N/A -
2uqfz1rd1h-yi.js gzip 166 B N/A -
30z650ayitjz3.js gzip 5.67 kB N/A -
33ur7c3w36-m-.js gzip 8.56 kB N/A -
396buwq-nlhir.js gzip 8.59 kB N/A -
39mk1fjm58e-4.js gzip 8.62 kB N/A -
3cq10epinkxrc.js gzip 450 B N/A -
3wc1tgfurjhi-.js gzip 9.23 kB N/A -
3ze9s70gat6n_.js gzip 8.56 kB N/A -
41obdnb4lqdgs.js gzip 13.3 kB N/A -
44dza2th1-3p_.js gzip 155 B N/A -
454bom347xpxj.js gzip 13.8 kB N/A -
457x5n-k0jr1x.js gzip 10.4 kB N/A -
turbopack-00..5k8f.js gzip 4.17 kB N/A -
turbopack-03..7xjr.js gzip 4.17 kB N/A -
turbopack-14..0w66.js gzip 4.17 kB N/A -
turbopack-1a..1idr.js gzip 4.17 kB N/A -
turbopack-1e..y1sm.js gzip 4.17 kB N/A -
turbopack-1e..jwc3.js gzip 4.17 kB N/A -
turbopack-2_..0v01.js gzip 4.17 kB N/A -
turbopack-29..1ie-.js gzip 4.17 kB N/A -
turbopack-2e..unjt.js gzip 4.16 kB N/A -
turbopack-3g..e631.js gzip 4.17 kB N/A -
turbopack-3h..0jbu.js gzip 4.17 kB N/A -
turbopack-3q..nhhi.js gzip 4.17 kB N/A -
turbopack-41..2kti.js gzip 4.18 kB N/A -
turbopack-42..ltb0.js gzip 4.18 kB N/A -
0_1u_xrpzaeaj.js gzip N/A 8.52 kB -
0-ua_-urjvdtw.js gzip N/A 8.56 kB -
04o0z49-uugql.js gzip N/A 157 B -
05_r_-_rf4w-n.js gzip N/A 7.61 kB -
0eihfygkvyao-.js gzip N/A 1.46 kB -
0g_88ua4o_jp-.js gzip N/A 9.24 kB -
0ua91j3aes80c.js gzip N/A 8.58 kB -
0zwsxw6xkvw9p.js gzip N/A 8.62 kB -
1_fyx0hi94qc-.js gzip N/A 49 kB -
1_n6uuxm5qn11.js gzip N/A 170 B -
10mvvt3xn1_3j.js gzip N/A 8.59 kB -
1a5tis5_e07-2.js gzip N/A 70.8 kB -
1b75ishu64v5s.js gzip N/A 13 kB -
1b83ah3nflxjf.js gzip N/A 8.52 kB -
1fd23spooi5r7.js gzip N/A 225 B -
1gx5035rzzsj3.js gzip N/A 157 B -
1jbicyv-mme-i.js gzip N/A 156 B -
1nw99o32asytf.js gzip N/A 450 B -
1y6qa6xp0i1nz.js gzip N/A 13.3 kB -
1yd8qavjpi2n-.js gzip N/A 65.5 kB -
1zs0a63ov-hco.js gzip N/A 153 B -
2-moqrpvsput7.js gzip N/A 161 B -
26i99-ztb0xke.js gzip N/A 157 B -
2c3zssa2_hfne.js gzip N/A 158 B -
2kxdvc3gr7nt9.js gzip N/A 8.59 kB -
2sgg_sxyixu_p.js gzip N/A 13.8 kB -
2sk4gp5rmalb0.js gzip N/A 10.1 kB -
2u87ln5_zfir_.js gzip N/A 5.67 kB -
32k3-ntl-0f4s.js gzip N/A 163 B -
33602db0fe2xi.js gzip N/A 9.81 kB -
34l-6efot4df9.js gzip N/A 156 B -
3e-_gp4rum99w.js gzip N/A 157 B -
3j3snr-ce7e0q.js gzip N/A 10.4 kB -
3wy2l4mynculd.js gzip N/A 155 B -
3yby446qbgls0.js gzip N/A 8.56 kB -
3yypm2pwzx0mq.js gzip N/A 12.9 kB -
41e88xrrb9-u8.js gzip N/A 158 B -
42lado0_6oegq.js gzip N/A 2.28 kB -
turbopack-07..5x3d.js gzip N/A 4.18 kB -
turbopack-09..xjwl.js gzip N/A 4.16 kB -
turbopack-0i..szdq.js gzip N/A 4.18 kB -
turbopack-0x..wzj5.js gzip N/A 4.18 kB -
turbopack-0x..46wb.js gzip N/A 4.18 kB -
turbopack-1r..b2gv.js gzip N/A 4.18 kB -
turbopack-1s..7844.js gzip N/A 4.18 kB -
turbopack-1v..x7eh.js gzip N/A 4.18 kB -
turbopack-2s..1ss4.js gzip N/A 4.18 kB -
turbopack-2t..ntff.js gzip N/A 4.18 kB -
turbopack-37..gyrj.js gzip N/A 4.19 kB -
turbopack-3k..uuv_.js gzip N/A 4.18 kB -
turbopack-3t..5b--.js gzip N/A 4.18 kB -
turbopack-3w..vc63.js gzip N/A 4.18 kB -
Total 464 kB 465 kB ⚠️ +164 B

Server

Middleware
Canary PR Change
middleware-b..fest.js gzip 715 B 717 B
Total 715 B 717 B ⚠️ +2 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 435 B 436 B
Total 435 B 436 B ⚠️ +1 B

📦 Webpack

Client

Main Bundles
Canary PR Change
1011-HASH.js gzip 5.58 kB N/A -
2168.HASH.js gzip 169 B N/A -
2225-HASH.js gzip 4.64 kB N/A -
61a8f394-HASH.js gzip 62.8 kB N/A -
850-HASH.js gzip 60.6 kB N/A -
framework-HASH.js gzip 59.7 kB 59.7 kB
main-app-HASH.js gzip 257 B 252 B 🟢 5 B (-2%)
main-HASH.js gzip 39.3 kB 39.6 kB
webpack-HASH.js gzip 1.68 kB 1.68 kB
36c7d9a6-HASH.js gzip N/A 62.8 kB -
3967-HASH.js gzip N/A 4.63 kB -
5025-HASH.js gzip N/A 5.58 kB -
634-HASH.js gzip N/A 60.9 kB -
7586.HASH.js gzip N/A 170 B -
Total 235 kB 235 kB ⚠️ +608 B
Polyfills
Canary PR Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB
Total 39.4 kB 39.4 kB
Pages
Canary PR Change
_app-HASH.js gzip 194 B 194 B
_error-HASH.js gzip 182 B 181 B
css-HASH.js gzip 334 B 333 B
dynamic-HASH.js gzip 1.8 kB 1.81 kB
edge-ssr-HASH.js gzip 255 B 254 B
head-HASH.js gzip 352 B 352 B
hooks-HASH.js gzip 384 B 384 B
image-HASH.js gzip 580 B 581 B
index-HASH.js gzip 259 B 259 B
link-HASH.js gzip 2.52 kB 2.52 kB
routerDirect..HASH.js gzip 320 B 317 B
script-HASH.js gzip 386 B 386 B
withRouter-HASH.js gzip 315 B 315 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Total 7.98 kB 7.99 kB ⚠️ +4 B

Server

Edge SSR
Canary PR Change
edge-ssr.js gzip 125 kB 126 kB
page.js gzip 272 kB 273 kB
Total 398 kB 399 kB ⚠️ +1.05 kB
Middleware
Canary PR Change
middleware-b..fest.js gzip 614 B 617 B
middleware-r..fest.js gzip 156 B 156 B
middleware.js gzip 44.4 kB 44 kB
edge-runtime..pack.js gzip 842 B 842 B
Total 46 kB 45.6 kB ✅ -360 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 719 B 718 B
Total 719 B 718 B ✅ -1 B
Build Cache
Canary PR Change
0.pack gzip 4.38 MB 4.38 MB
index.pack gzip 113 kB 115 kB 🔴 +1.7 kB (+1%)
index.pack.old gzip 114 kB 114 kB
Total 4.61 MB 4.61 MB ✅ -1.03 kB

🔄 Shared (bundler-independent)

Runtimes
Canary PR Change
app-page-exp...dev.js gzip 346 kB 346 kB
app-page-exp..prod.js gzip 191 kB 191 kB
app-page-tur...dev.js gzip 345 kB 345 kB
app-page-tur..prod.js gzip 191 kB 191 kB
app-page-tur...dev.js gzip 342 kB 342 kB
app-page-tur..prod.js gzip 189 kB 189 kB
app-page.run...dev.js gzip 342 kB 342 kB
app-page.run..prod.js gzip 190 kB 190 kB
app-route-ex...dev.js gzip 76.9 kB 76.9 kB
app-route-ex..prod.js gzip 52.5 kB 52.5 kB
app-route-tu...dev.js gzip 77 kB 77 kB
app-route-tu..prod.js gzip 52.5 kB 52.5 kB
app-route-tu...dev.js gzip 76.6 kB 76.6 kB
app-route-tu..prod.js gzip 52.2 kB 52.2 kB
app-route.ru...dev.js gzip 76.5 kB 76.5 kB
app-route.ru..prod.js gzip 52.2 kB 52.2 kB
dist_client_...dev.js gzip 324 B 324 B
dist_client_...dev.js gzip 326 B 326 B
dist_client_...dev.js gzip 318 B 318 B
dist_client_...dev.js gzip 317 B 317 B
pages-api-tu...dev.js gzip 43.9 kB 43.9 kB
pages-api-tu..prod.js gzip 33.5 kB 33.5 kB
pages-api.ru...dev.js gzip 43.8 kB 43.8 kB
pages-api.ru..prod.js gzip 33.4 kB 33.4 kB
pages-turbo....dev.js gzip 53.3 kB 53.3 kB
pages-turbo...prod.js gzip 39.1 kB 39.1 kB
pages.runtim...dev.js gzip 53.2 kB 53.2 kB
pages.runtim..prod.js gzip 39 kB 39 kB
server.runti..prod.js gzip 62.8 kB 62.8 kB
Total 3.06 MB 3.06 MB ✅ -5 B
📎 Tarball URL
https://vercel-packages.vercel.app/next/commits/7782bd9cf5abb075746bea92f9057530aa225c08/next

@sokra sokra force-pushed the claude/test-turbopack-postcss-DhRev branch 2 times, most recently from 0241085 to 25f394d Compare March 17, 2026 20:46
@sokra sokra marked this pull request as ready for review March 19, 2026 19:44
@sokra sokra force-pushed the claude/test-turbopack-postcss-DhRev branch from b9cbab9 to e2c804f Compare March 19, 2026 19:54
@sokra sokra requested a review from lukesandberg March 20, 2026 13:34
@sokra sokra force-pushed the claude/test-turbopack-postcss-DhRev branch from e2c804f to 6e24884 Compare March 20, 2026 13:37
@sokra sokra force-pushed the claude/test-turbopack-postcss-DhRev branch 3 times, most recently from d88d4a9 to 3b328b8 Compare April 7, 2026 19:18
@nextjs-bot

nextjs-bot commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Tests Passed

@sokra sokra force-pushed the claude/test-turbopack-postcss-DhRev branch from 3b328b8 to c709c77 Compare April 9, 2026 11:25
Comment thread turbopack/crates/turbo-tasks-backend/src/backend/mod.rs Outdated
claude and others added 9 commits April 10, 2026 14:17
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.
@sokra sokra force-pushed the claude/test-turbopack-postcss-DhRev branch from c709c77 to eb7d1a6 Compare April 10, 2026 12:47
@sokra sokra requested a review from mischnic April 10, 2026 12:56
Add a dedicated API reference page for turbopackLocalPostcssConfig and
register it in the Turbopack experimental options table.

Co-Authored-By: Claude <noreply@anthropic.com>
@nextjs-bot nextjs-bot added the Documentation Related to Next.js' official documentation. label Apr 10, 2026
@sokra sokra merged commit 74cdcde into canary Apr 11, 2026
340 of 342 checks passed
@sokra sokra deleted the claude/test-turbopack-postcss-DhRev branch April 11, 2026 05:55
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Apr 25, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

created-by: Turbopack team PRs by the Turbopack team. Documentation Related to Next.js' official documentation. locked tests Turbopack Related to Turbopack with Next.js. type: next

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants