Kendala dan Solusi Saat Memindahkan Monorepo pnpm ke Nub
27 июля 2026 г.
0
Computing/SoftwareComments (0)
Log in to leave a comment
No posts yet
Log in to leave a comment
No posts yet
Ekosistem Node.js sudah sangat melelahkan. Lingkungan build yang ditumpuk secara berlapis dengan tsx, dotenv-cli, nvm, dan pnpm terasa berat, dan setiap kali ada yang diperbarui, skrip sering kali menjadi kacau. Ketika Nub, sebuah toolkit terintegrasi berbasis Rust, muncul dengan janji untuk menyatukan semua kekacauan paket ini ke dalam satu binary tunggal, jujur saja saya merasa senang sekaligus agak ragu.
Namun, setelah benar-benar memigrasikan monorepo pnpm-workspace di lingkungan produksi ke Nub, hasilnya terbukti cukup menarik. Meski begitu, tentu saja tidak ada proses yang berjalan se-fleksibel itu tanpa hambatan. Kendala-kendala yang langsung ditemui di lapangan mulai dari konflik dependensi, kebijakan penguncian keamanan 24 jam, hingga masalah pemisahan cache CI bermunculan.
Engine paket milik Nub, aube, dapat langsung mengenali pnpm-workspace.yaml yang ada serta properti workspaces di package.json. Sesuai dengan klaimnya bahwa ia kompatibel secara byte-level dengan skema pnpm-lock.yaml v9, struktur virtual Store bawaan (node_modules/.store/) tetap dimanfaatkan secara default.
Masalahnya terletak pada tooling lama yang secara implisit mengasumsikan struktur node_modules yang rata (hoisted). Jika Anda menjalankannya tepat setelah migrasi, aplikasi akan langsung crash dengan error MODULE_NOT_FOUND. Agar layanan bisa langsung berjalan saat itu juga, Anda perlu meratakan struktur paket terlebih dahulu menggunakan opsi --node-linker hoisted sebelum memulai.
Berdasarkan instalasi Warm yang memanfaatkan cache, mode GVS bawaan membutuhkan waktu 346 ms. Mode Hoisted membutuhkan 1461 ms—lebih lambat lebih dari 4 kali lipat—tetapi masih 2 kali lebih cepat dibandingkan pnpm v10+ yang memakan waktu 3453 ms. Pada awalnya, lebih nyaman untuk mengamankan kompatibilitas menggunakan mode Hoisted, lalu beralih ke mode GVS bawaan secara bertahap seiring dengan dihapusnya dependensi pada tooling lama.
`bash
#!/usr/bin/env bash
set -euo pipefail
echo "==> [1/4] Memeriksa binary Nub"
if ! command -v nub &> /dev/null; then
echo "Error: Nub tidak ditemukan. Silakan jalankan 'npm i -g @nubjs/nub' terlebih dahulu."
exit 1
fi
echo "==> [2/4] Memeriksa skema pnpm-lock.yaml"
if [ -f "pnpm-lock.yaml" ]; then
nub pm use nub
fi
echo "==> [3/4] Menyebutkan execution engine di package.json"
node -e '
const fs = require("fs");
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
pkg.devEngines = pkg.devEngines || {};
pkg.devEngines.packageManager = {
name: "nub",
version: "^0.4.0",
onFail: "warn"
};
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 2) + "\n");
'
echo "==> [4/4] Membuat Lockfile"
nub install --frozen-lockfile=false
`
Dengan mengonversi pnpm-lock.yaml menjadi nub.lock menggunakan skrip ini, Anda dapat menghemat waktu lebih dari 2 jam per minggu yang biasanya terbuang untuk memperbaiki skrip yang rusak.
Nub memiliki kebijakan keamanan yang cukup ketat. Skrip lifecycle diblokir secara default, dan jika tidak memiliki tanda tangan npm Provenance, Nub akan melemparkan error ERR_NUB_TRUST_DOWNGRADE. Hal yang paling membingungkan adalah opsi minimumReleaseAge. Versi paket yang dirilis kurang dari 24 jam lalu dianggap masih dalam status menunggu verifikasi keamanan, sehingga instalasinya akan diblokir.
Fitur ini merupakan benteng pertahanan yang luar biasa dalam kondisi normal, tetapi ketika kerentanan zero-day muncul dan Anda harus segera men-deploy versi patch yang baru dirilis 1 jam lalu, aturan ini menjadi tembok penghalang yang menyulitkan. Dalam situasi ini, Anda harus mendaftarkan paket yang diizinkan menjalankan skrip build di package.json tingkat root dan menembusnya menggunakan perintah khusus.
json { "name": "@org/monorepo-root", "private": true, "allowBuilds": { "esbuild": true, "sharp": true, "@fast-cve/patch-pkg": true } }
Berikut adalah alur untuk mengabaikan pembatasan dan menerapkan paket hotfix yang terblokir:
`bash
nub add --allow-build=@fast-cve/patch-pkg @fast-cve/patch-pkg@1.0.1-hotfix
nub approve-builds
`
Menjalankan nub approve-builds akan membuka daftar blokir dan langsung melanjutkan proses build. Namun, paket yang terdaftar sebagai kode berbahaya (MAL-*) di basis data OSV tidak akan bisa ditembus bahkan dengan perintah ini, sehingga dalam kasus tersebut Anda harus mencari versi alternatif tingkat atas.
Untuk meningkatkan kecepatan CI/CD, Anda perlu memahami jalur cache milik Nub. Binary Node disimpan di ~/.cache/nub/node/<version>/, sedangkan CAS (Content-Addressable Store) paket terbagi antara ~/.cache/nub/ dan node_modules/.store/.
Dalam multi-stage build Docker, Anda harus menggunakan cache mount BuildKit untuk mengisolasi layer instalasi secara penuh agar efek cache dapat dirasakan.
`dockerfile}
FROM ghcr.io/nubjs/nub:latest AS base
WORKDIR /app
FROM base AS dependencies
COPY package.json nub.lock pnpm-workspace.yaml ./
COPY packages/core/package.json ./packages/core/
COPY packages/api/package.json ./packages/api/
RUN --mount=type=cache,target=/root/.cache/nub
nub ci --prefer-offline
FROM dependencies AS builder
COPY . .
RUN nub run build --filter=@org/api
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/packages/api/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]
`
Pada GitHub Actions, gunakan nubjs/setup-nub@v0 alih-alih actions/setup-node.
`yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: nubjs/setup-nub@v0
with:
cache: true
- run: nub ci
- run: nub -r run build
- run: nub -r run test
`
Kecepatan nub ci yang dikombinasikan dengan setup-nub dan cache berada di kisaran 346 ms. Dibandingkan dengan pnpm bawaan (3453 ms), waktu instalasi paket pada pipeline berkurang secara drastis, sehingga total waktu build CI dapat dipangkas hingga setengahnya. Menerima tagihan infrastruktur cloud di akhir bulan dijamin akan membuat perasaan Anda jauh lebih lega.
Nub menyertakan transpiler memori berbasis oxc secara built-in, sehingga kode TypeScript dapat langsung dijalankan tanpa perlu tsx atau ts-node. File .env juga dibaca secara otomatis. Saat menggunakan pnpm run (442.7 ms), overhead bootstrap proses Node.js yang selalu muncul kini menghilang, sehingga waktu eksekusi skrip nub run turun drastis hingga 14.7 ms. Perbedaannya terasa sangat signifikan.
Untuk mencegah fragmentasi versi Node.js antar-anggota tim, Anda cukup menaruh file .node-version di tingkat root.
bash echo "22.15.0" > .node-version nub src/index.ts
Pada Node 22.15.0 ke atas, berkat module.registerHooks() yang bersifat sinkron, delay cold start benar-benar tereliminasi. Saat ada anggota tim baru yang bergabung, menyiapkannya dengan satu skrip otomatisasi adalah cara paling bersih agar lingkungan pengembang dapat diselaraskan sekaligus tanpa perlu penjelasan yang rumit.
`bash
#!/usr/bin/env bash
set -euo pipefail
echo "==> Memulai penyetelan lingkungan pengembang"
if ! command -v nub &> /dev/null; then
if command -v brew &> /dev/null; then
brew install nubjs/tap/nub
else
npm install -g --ignore-scripts=false @nubjs/nub
fi
fi
nub pm shim
nub node install
nub install
if [ ! -f ".env.local" ] && [ -f ".env.example" ]; then
cp .env.example .env.local
fi
echo "==> Penyetelan selesai. Jalankan dengan 'nub run dev'."
`
Jika Anda menjalankan nub pm shim, ketikan seperti pnpm install atau npm run yang dilakukan karena kebiasaan akan dicegat dan diproses oleh runner Nub. Selain overhead eksekusi CLI yang berkurang dari pnpm exec (191 ms) menjadi nubx (11 ms), Anda juga dapat menghemat waktu debugging setidaknya 2 jam per minggu dari keluhan seperti "di komputer saya kok tidak bisa, ya?".