← ./articles-ja

next devだけCSSが壊れる原因はNODE_ENV=productionだった

Next.jsで next build は成功するのに、next dev だけCSSで落ちることがあります。

このとき globals.css の構文、PostCSS、Turbopack、webpack設定を疑いたくなりますが、原因がCSSではない場合があります。今回の原因は、親プロセスから継承された NODE_ENV=production でした。

起きた症状

ローカル開発で起きた症状は次の通りです。

  • npm run build は成功する
  • npm run dev だけ失敗する
  • globals.css の普通のCSSで Module parse failed: Unexpected token が出る
  • エラー位置は *, のような正常なCSSセレクタ付近に見える

代表的には、次のような見え方になります。

Module parse failed: Unexpected token (2:0)

このエラーだけを見ると、CSSローダーやPostCSS設定が壊れているように見えます。しかし、next build が通っているなら「CSSの構文そのものが間違っている」と決め打ちしない方が安全です。

最初に疑ったもの

まず疑う候補は自然にいくつかあります。

  • globals.css の構文ミス
  • postcss.config.js の不足や形式ミス
  • Tailwind CSSやCSS Modulesの設定
  • Next.jsのwebpack/Turbopack切り替え
  • キャッシュや .next/ の破損

これらは調べる価値があります。ただし、今回のように next build は通るのに next dev だけ落ちる場合は、設定ファイルより先に環境変数を確認した方が早いことがあります。

本当の原因はNODE_ENV=productionだった

原因は、シェルまたはユーザー環境に NODE_ENV=production が残っていたことでした。

next dev は開発サーバーを起動するコマンドですが、子プロセスは親プロセスの環境変数を継承します。そのため、呼び出し側の環境に NODE_ENV=production が入っていると、開発用コマンドのつもりでもproduction向けの挙動が混ざります。

この状態では、CSS処理の初期化や依存関係の扱いが期待とずれて、結果として「正常なCSSがJavaScriptとしてパースされている」ようなエラーに見えることがあります。

Next.js公式ドキュメントも、NODE_ENV はReactエコシステムで特別扱いされる値であり、想定外の値や混ざった設定は確認対象だと説明しています。

参考: Next.js: Non-Standard NODE_ENV

npm installにも影響する

NODE_ENV=production の影響は next dev だけではありません。

npmの公式ドキュメントでは、NODE_ENVproduction の場合、omit の既定値に dev が入ると説明されています。つまり、ローカルで普通に npm install したつもりでも、devDependenciesが物理的に node_modules に入らないことがあります。

参考: npm-install: omit / include

この状態になると、次のような二次障害が起きます。

  • cross-env が見つからない
  • TypeScriptやESLintなどの開発用パッケージが見つからない
  • npm install --save-dev <package> したはずなのに実行時に解決できない
  • ビルドや開発サーバーの失敗原因が依存関係ミスに見える

CSSエラーとdevDependencies欠落が同時期に出ているなら、かなり強く NODE_ENV 汚染を疑ってよいです。

PowerShellで確認する

WindowsのPowerShellでは、まず現在のセッション値を確認します。

$env:NODE_ENV

何も表示されなければ、現在のPowerShellセッションには直接設定されていません。

次に、ユーザー環境変数とマシン環境変数を確認します。

[Environment]::GetEnvironmentVariable("NODE_ENV", "User")
[Environment]::GetEnvironmentVariable("NODE_ENV", "Machine")

ここで production が出る場合、ターミナルを開き直しても子プロセスに継承される可能性があります。

一時的に確認するだけなら、次のように開発モードを明示して実行します。

$env:NODE_ENV = "development"
npm run dev

ただし、この方法はそのPowerShellセッションにだけ効きます。再発防止には、スクリプト側で明示する方が安定します。

package.jsonで直す

ローカル環境に依存させないために、package.json のスクリプトで NODE_ENV を明示します。

Windows、macOS、Linuxで同じ書き方にしたい場合は cross-env を使います。cross-env は、OSごとの環境変数指定の差を吸収するためのパッケージです。

参考: cross-env README

例:

{
  "scripts": {
    "dev": "cross-env NODE_ENV=development next dev",
    "build": "cross-env NODE_ENV=production next build",
    "start": "cross-env NODE_ENV=production next start"
  }
}

devDependenciesが抜けている可能性がある場合は、明示的にdevDependenciesも入れ直します。

npm install --include=dev

cross-env 自体が入っていない場合は、先にdevDependencies込みでインストールしてください。

npm install --include=dev
npm install --save-dev cross-env

CSSやPostCSSを見る前に切り分ける

同じ Module parse failed でも、原因は複数あります。次の順番で切り分けると、遠回りを減らせます。

1. buildとdevの差を見る

npm run build
npm run dev

build だけ通るなら、CSSファイルの構文ミスよりも「開発サーバー固有の状態」を疑います。

2. NODE_ENVを見る

$env:NODE_ENV
[Environment]::GetEnvironmentVariable("NODE_ENV", "User")
[Environment]::GetEnvironmentVariable("NODE_ENV", "Machine")

どこかで production が出たら、まずそこを潰します。

3. 依存関係が入っているか見る

npm ls cross-env
npm ls typescript

devDependenciesが入っていないなら、npm install --include=dev で入れ直します。

4. それでも残るならCSS設定を見る

ここまで確認しても直らない場合に、PostCSS、Tailwind CSS、Next.js設定、.next/ キャッシュを見ます。

この順番にすると、「CSSエラーに見える環境変数の問題」を設定変更で無理に直そうとする事故を避けられます。

なぜこの問題は見つけにくいのか

この問題が厄介なのは、エラーメッセージに NODE_ENV=production が出ないことです。

画面上はCSS parse errorに見えます。npm側でも、devDependenciesが入らない理由が毎回大きく警告されるわけではありません。結果として、次のような誤診が起きやすくなります。

  • Turbopackの問題だと思い込む
  • postcss.config.js を何度も書き換える
  • globals.css の書き方を疑う
  • .next/ を消して直ったかどうかだけを見る
  • パッケージのインストール失敗を別問題として扱う

しかし、親環境の NODE_ENV がproductionなら、これらは同じ根から出た症状かもしれません。

再発防止のチェックリスト

  • dev スクリプトでは NODE_ENV=development を明示する
  • build / start では NODE_ENV=production を明示する
  • Windowsでは User / Machine scope の環境変数も確認する
  • npm install でdevDependenciesが怪しいときは --include=dev を使う
  • CSS parse errorでも、next build が通るなら環境変数を先に見る

まとめ

next dev だけCSSで落ちると、CSSやPostCSSを直したくなります。

ただ、next build が通るなら、ファイル内容そのものではなく、開発サーバーの実行環境が壊れている可能性があります。特に NODE_ENV=production が親プロセスから継承されていると、next devnpm install の挙動が静かに変わります。

まずは次の3つを確認してください。

$env:NODE_ENV
[Environment]::GetEnvironmentVariable("NODE_ENV", "User")
[Environment]::GetEnvironmentVariable("NODE_ENV", "Machine")

ここで production が出るなら、CSSを直す前に環境変数を直す方が先です。

関連記事: Next.jsのoutput: exportで詰まる5つの罠と回避策