Dependensi hantu (phantom dependencies) terjadi ketika kode mengimpor paket pihak ketiga yang tidak dideklarasikan di package.json, tetapi bisa diakses karena package manager menempatkannya di pohon direktori node_modules. Masalah ini sering tidak terdeteksi di lingkungan lokal, lalu menyebabkan kegagalan build atau runtime error mendadak di production saat dependency graph bergeser.
Akar Masalah Phantom Dependencies
Mekanisme resolusi modul Node.js bekerja secara rekursif menelusuri folder node_modules ke atas hingga menemukan package yang cocok. Manajer paket modern seperti npm dan Yarn v1 menerapkan teknik hoisting (perataan struktur dependensi) untuk menghemat ruang disk dan mencegah duplikasi.
Ketika paket A bergantung pada lodash, proses hoisting menempatkan lodash di level teratas node_modules. Aplikasi utama dapat mengeksekusi import _ from 'lodash' tanpa mendaftarkannya di dependencies milik aplikasi sendiri.
node_modules/
├── lodash/ <-- Di-hoist ke root
└── library-a/
└── package.json <-- library-a mendeklarasikan lodashRisiko muncul ketika pengelola paket A memperbarui dependensinya ke library lain atau mengubah versinya. Pada pembaruan package-lock.json berikutnya, lodash mungkin tidak lagi di-hoist ke root atau bahkan hilang sepenuhnya. Akibatnya, runtime aplikasi memunculkan eror Cannot find module 'lodash' secara tiba-tiba.
Struktur Symlink pnpm dan Batasannya
Secara default, pnpm memitigasi masalah ini dengan isolasi berbasis content-addressable storage dan struktur symlink. Aplikasi hanya memiliki akses ke modul yang secara eksplisit dicatat pada package.json. Namun, dalam arsitektur monorepo atau workspace pnpm, konfigurasi seperti shamefully-hoist=true atau ketergantungan antar-workspace dapat membuka kembali celah phantom dependency jika tidak diaudit secara ketat.
Audit Lokal via CLI
Pemeriksaan dependensi harus dilakukan sebelum perubahan didorong ke remote repository. Dua kakas yang umum digunakan untuk keperluan ini adalah depcheck dan Knip.
Opsi 1: depcheck
depcheck menganalisis import statement pada berkas sumber lalu mencocokkannya dengan daftar dependensi di package.json.
# Jalankan audit langsung tanpa instalasi global
npx depcheck --ignores="eslint,prettier,@types/*"Kelemahan depcheck: sering menghasilkan false positive pada import dynamic atau konfigurasi framework modern (seperti Vite, Next.js, dan Tailwind).
Opsi 2: Knip (Rekomendasi untuk TypeScript/Monorepo)
knip dirancang khusus untuk ekosistem TypeScript modern. Tool ini dapat mendeteksi dependensi yang tidak terdaftar, ekspor yang tidak digunakan, serta file dead code secara simultan.
# Instalasi dev dependency
pnpm add -D knip typescript
# Eksekusi audit phantom dependency
pnpm knip --productionKonfigurasi minimal pada knip.json untuk fokus pada dependensi tak terdaftar:
{
"$schema": "https://unpkg.com/knip@5/resources/knip-schema.json",
"entry": ["src/index.ts!", "src/server.ts!"],
"project": ["src/**/*.ts"],
"ignoreDependencies": ["@types/*"]
}Integrasi CI Pipeline: GitHub Actions
Otomatisasi pada continuous integration memastikan tidak ada Pull Request (PR) yang lolos jika menggunakan package yang belum dideklarasikan. Workflow berikut menjalankan knip dan menghentikan pipeline (exit code 1) jika ditemukan unresolved transitive package.
name: Dependency Audit
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
audit-phantom:
name: Audit Unlisted Dependencies
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Run Knip
run: pnpm exec knip --dependencies --include-libsMitigasi dan Best Practices
- Gunakan Strict Package Managers: Gunakan pnpm tanpa konfigurasi hoisting ekstrem, atau terapkan Yarn Berry dengan mode PnP (Plug'n'Play) yang secara bawaan melarang akses ke dependensi yang tidak dideklarasikan.
- Deklarasikan Dependensi Langsung: Jika proyek Anda mengimpor package tertentu (misal:
clsxataums), daftarkan langsung dipackage.jsonmeskipun dependensi lain di proyek sudah memuat package tersebut. - Kunci Versi Dependensi: Selalu commit
pnpm-lock.yamlataupackage-lock.jsonuntuk mencegah pergeseran pohon modul otomatis saat proses CI build.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!