Kamu pakai Tailwind CSS v4 di pnpm workspace dengan Next.js, terus muncul error ini:
Error: Cannot find native binding.
Caused by: Error: Cannot find module '@tailwindcss/oxide-darwin-arm64'
Caused by: Error: Cannot find module './tailwindcss-oxide.darwin-arm64.node'
Ini penyebabnya dan cara fixnya.
Kenapa @tailwindcss/oxide tidak bisa menemukan native binding-nya
Tailwind CSS v4 pakai @tailwindcss/oxide, engine berbasis Rust, untuk memproses CSS. Package ini dikirim sebagai wrapper yang platform-agnostic, dengan binary aslinya ada di optional package seperti @tailwindcss/oxide-darwin-arm64, @tailwindcss/oxide-linux-x64-gnu, dan sebagainya.
Saat runtime, oxide/index.js mencoba load binary native dengan dua cara:
// coba relative file dulu
require('./tailwindcss-oxide.darwin-arm64.node')
// fallback ke optional package
require('@tailwindcss/oxide-darwin-arm64')
Di setup npm atau yarn biasa ini jalan normal. Di pnpm workspace dengan Next.js Turbopack, keduanya gagal.
Masalahnya: pnpm + Turbopack
pnpm pakai isolated linker: setiap package dapat node_modules sendiri dengan symlink ke dependensinya. Jadi @tailwindcss/oxide-darwin-arm64 sudah di-symlink di bawah node_modules milik oxide, dan kalau kamu cd ke direktori package oxide lalu jalankan require('@tailwindcss/oxide-darwin-arm64'), resolusinya berjalan dengan benar.
Masalahnya ada di Turbopack. Turbopack mengkompilasi @tailwindcss/postcss dan @tailwindcss/oxide ke dalam worker bundle tersendiri di .next/dev/build/postcss.js, lalu menjalankannya di isolated worker context. Worker tersebut tidak mengikuti symlink chain milik pnpm dengan cara yang sama, sehingga kedua require path gagal, baik package require maupun relative file require.
Cara fix-nya
Step 1: pastikan optional dep-nya benar-benar terinstall. pnpm kadang skip. Jalankan dengan --force:
pnpm install --force
Step 2: copy binary .node native langsung ke direktori package oxide. Ini fix utamanya. Relative require ./tailwindcss-oxide.darwin-arm64.node akan jalan tanpa peduli module resolution context-nya dari mana.
cp node_modules/.pnpm/@tailwindcss+oxide-darwin-arm64@4.3.0/node_modules/@tailwindcss/oxide-darwin-arm64/tailwindcss-oxide.darwin-arm64.node \
node_modules/.pnpm/@tailwindcss+oxide@4.3.0/node_modules/@tailwindcss/oxide/
Sesuaikan nomor versi (4.3.0) dengan yang kamu punya.
Step 3: hapus cache Next.js. Turbopack men-cache state yang gagal, jadi meskipun sudah difix, error lama akan muncul lagi sampai cache-nya dihapus:
rm -rf apps/web/.next
Restart dev dan selesai.
Biar fix-nya bertahan setelah reinstall
Binary yang sudah dicopy akan hilang setiap kali pnpm install. Tambahkan postinstall script di root package.json:
{
"scripts": {
"postinstall": "node scripts/fix-oxide-binding.mjs"
}
}
// scripts/fix-oxide-binding.mjs
import { copyFileSync, existsSync, readdirSync } from 'node:fs'
import { join } from 'node:path'
const targets = {
'darwin-arm64': 'darwin-arm64',
'darwin-x64': 'darwin-x64',
'linux-x64': 'linux-x64-gnu',
'linux-arm64': 'linux-arm64-gnu',
}
const target = targets[`${process.platform}-${process.arch}`]
const store = 'node_modules/.pnpm'
if (!target || !existsSync(store)) process.exit(0)
const file = `tailwindcss-oxide.${target}.node`
for (const dir of readdirSync(store)) {
const match = dir.match(/^@tailwindcss\+oxide@([^_]+)/)
if (!match) continue
const version = match[1]
const src = join(store, `@tailwindcss+oxide-${target}@${version}`, 'node_modules/@tailwindcss', `oxide-${target}`, file)
const dest = join(store, dir, 'node_modules/@tailwindcss/oxide', file)
if (existsSync(src) && !existsSync(dest)) {
copyFileSync(src, dest)
console.log(`Copied ${file} into @tailwindcss/oxide@${version}`)
}
}
Script ini loop ke semua versi oxide yang terinstall, jadi lockfile dengan dua versi Tailwind tidak bikin install gagal. Platform yang ditangani: darwin-arm64, darwin-x64, linux-x64, dan linux-arm64. Tambahkan entry ke targets kalau butuh lebih.
TL;DR
pnpm mengisolasi package di balik symlink. Worker PostCSS milik Turbopack tidak bisa resolve optional native dep melalui symlink tersebut. Copy binary .node langsung ke direktori oxide membuat relative require berjalan, dan hapus .next untuk membuang cache kegagalan. Tambahkan postinstall script biar fix-nya otomatis reapply setiap install.