nextjs - ✅(Solved) Fix Turbopack passes "development" to SWC plugins during next build [2 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#92547Fetched 2026-04-09 07:49:52
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Author
Participants
Timeline (top)
labeled ×2issue_type_added ×1subscribed ×1

Root Cause

The root cause looks like a hardcoded "development" in turbopack:

PR fix notes

PR #92579: Turbopack: properly set NODE_ENV for SWC plugins

Description (problem / solution / changelog)

What?

Closes https://github.com/vercel/next.js/pull/92650 Closes https://github.com/vercel/next.js/issues/92547

When Turbopack runs SWC transform plugins, it passes a TransformPluginMetadataContext that includes the current NODE_ENV. Previously this value was hardcoded to "development" regardless of the actual build mode, causing SWC plugins to misbehave during production builds.

Why?

SwcPluginMetadataContext is used by SWC plugins (e.g. babel-plugin-styled-components, @swc/plugin-emotion) to conditionally produce development vs. production output — for instance, injecting display names and data attributes only in development. With NODE_ENV always set to "development", plugins could never produce their optimised production output, resulting in larger bundles and missing minification in next build.

Fixes https://github.com/vercel/next.js/issues/92547

How?

NODE_ENV is now derived from the process.env.NODE_ENV compile-time define that is already part of CompileTimeInfo (the same source of truth used for dead-code elimination). The resolution happens in EcmascriptModuleAsset::parse() via a new shared helper node_env_from_compile_time_info() in parse.rs, and the value is stored on TransformContext so every CustomTransformer implementation can read it from ctx.node_env without needing access to the broader compilation context.

Key design decisions:

  • TransformContext is the right place: it is the per-file context passed to every CustomTransformer::transform() call, so storing node_env there follows the existing pattern and avoids coupling the plugin struct to the build mode.
  • Webpack analysis path: compile_time_info is now threaded through webpack_runtime(), module_references(), and the webpack reference structs (WebpackModuleAsset, WebpackChunkAssetReference, WebpackEntryAssetReference, WebpackRuntimeAssetReference) so that webpack-bundled files with SWC plugin transforms also receive the correct NODE_ENV.
  • segment_config.rs: passes a placeholder but includes a comment explaining it is irrelevant since EcmascriptInputTransforms::empty() means no transforms run.
<!-- NEXT_JS_LLM_PR -->

Changed files

  • crates/next-core/src/segment_config.rs (modified, +3/-0)
  • test/e2e/swc-plugins-env/app/layout.tsx (added, +7/-0)
  • test/e2e/swc-plugins-env/app/page.tsx (added, +6/-0)
  • test/e2e/swc-plugins-env/index.test.ts (added, +18/-0)
  • test/e2e/swc-plugins-env/next.config.js (added, +6/-0)
  • test/e2e/swc-plugins-env/plugin/Cargo.lock (added, +2570/-0)
  • test/e2e/swc-plugins-env/plugin/Cargo.toml (added, +18/-0)
  • test/e2e/swc-plugins-env/plugin/src/lib.rs (added, +43/-0)
  • test/e2e/swc-plugins-env/swc_plugin_env_check.wasm (added, +0/-0)
  • turbopack/crates/turbopack-core/src/compile_time_info.rs (modified, +14/-1)
  • turbopack/crates/turbopack-ecmascript-plugins/src/transform/swc_ecma_transform_plugins.rs (modified, +1/-2)
  • turbopack/crates/turbopack-ecmascript/src/lib.rs (modified, +9/-0)
  • turbopack/crates/turbopack-ecmascript/src/parse.rs (modified, +15/-3)
  • turbopack/crates/turbopack-ecmascript/src/transform/mod.rs (modified, +3/-0)
  • turbopack/crates/turbopack-ecmascript/src/webpack/mod.rs (modified, +37/-10)
  • turbopack/crates/turbopack-ecmascript/src/webpack/parse.rs (modified, +11/-1)
  • turbopack/crates/turbopack-ecmascript/src/webpack/references.rs (modified, +13/-0)

PR #92650: fix(turbopack): use NODE_ENV for SWC plugins

Description (problem / solution / changelog)

Replaces hardcoded "development" with process.env.NODE_ENV or fallback.

Fixes #92547

Changed files

  • turbopack/crates/turbopack-ecmascript-plugins/src/transform/swc_ecma_transform_plugins.rs (modified, +2/-3)

Code Example

//[TODO]: Support env-related variable injection, i.e process.env.NODE_ENV
"development".to_string(),

---

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.3.0

Binaries:
  Node: 24.13.0
  npm: 11.6.2
  Yarn: 1.22.22
  pnpm: 10.17.1

Relevant Packages:
  next: 16.2.1-canary.28
  eslint-config-next: N/A
  react: 19.2.5
  react-dom: 19.2.5
  typescript: 6.0.2

Next.js Config:
  output: N/A
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/jantimon/repro-turbopack-swc-plugin-environment

To Reproduce

  1. Clone the repo (contains a minimal SWC plugin that reads TransformPluginMetadataContextKind::Env and replaces ENV_CHECK with the value)
  2. Build the SWC plugin: cd swc-plugin-env-check && cargo build --target wasm32-wasip1 --release && cd ..
  3. npm install
  4. Run next build (uses turbopack by default in Next 16)
  5. Check the output: grep -o 'strong>[^<]*</strong' .next/server/app/index.html
  6. You'll see development instead of production
  7. Compare with next build --webpack which correctly outputs production

Current vs. Expected behavior

Current: next build --turbopack passes "development" as the env to SWC plugins via TransformPluginMetadataContextKind::Env, regardless of whether it's a dev or prod build.

Expected: next build --turbopack should pass "production", same as webpack does.

ModeBundlerEnv receivedCorrect?
DevTurbopack"development"Yes
DevWebpack"development"Yes
ProdWebpack"production"Yes
ProdTurbopack"development"*No

The root cause looks like a hardcoded "development" in turbopack:

https://github.com/vercel/next.js/blob/b5da7b7f7d20802dd5f786c546c47a874612af13/turbopack/crates/turbopack-ecmascript-plugins/src/transform/swc_ecma_transform_plugins.rs#L240-L241

//[TODO]: Support env-related variable injection, i.e process.env.NODE_ENV
"development".to_string(),

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.3.0

Binaries:
  Node: 24.13.0
  npm: 11.6.2
  Yarn: 1.22.22
  pnpm: 10.17.1

Relevant Packages:
  next: 16.2.1-canary.28
  eslint-config-next: N/A
  react: 19.2.5
  react-dom: 19.2.5
  typescript: 6.0.2

Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Turbopack, SWC

Which stage(s) are affected? (Select all that apply)

next build (local)

Additional context

Tested on both 16.2.3 (stable) and 16.2.1-canary.28 (latest canary) — same behavior.

We hit this migrating galaxus.ch to turbopack. Our translation SWC plugin uses the env to split imports per-key in prod vs bundling all translations in dev. With turbopack always reporting dev, prod builds ship the full dictionary +1.4 MB JS transferred.

extent analysis

TL;DR

The most likely fix is to update the turbopack code to dynamically inject the NODE_ENV variable instead of hardcoding "development".

Guidance

  • The hardcoded "development" string in turbopack-ecmascript-plugins is the likely cause of the issue.
  • To verify, check the TransformPluginMetadataContextKind::Env value passed to the SWC plugin in the turbopack build process.
  • A potential workaround is to manually set the NODE_ENV environment variable before running next build --turbopack.
  • The issue can be mitigated by updating the turbopack code to support env-related variable injection, as indicated by the TODO comment in the swc_ecma_transform_plugins.rs file.

Example

No code snippet is provided as the issue is related to a specific turbopack implementation detail.

Notes

The issue is specific to the turbopack build process and does not affect the webpack build process. The problem is also specific to the next build stage and does not affect other stages.

Recommendation

Apply a workaround by setting the NODE_ENV environment variable before running next build --turbopack, as this is a simpler solution than updating the turbopack code. However, a more permanent fix would be to update the turbopack code to dynamically inject the NODE_ENV variable.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING