Manifes package.json di chapter sebelumnya hanya berupa daftar nama: "ms": "^2.1.3". Berkas library itu sendiri tidak ikut disalin saat proyek dikirim — dan memang tidak perlu, karena ukurannya bisa membengkak. Pertanyaannya, siapa yang mengunduh library tersebut, menempatkan berkasnya di folder yang benar, dan memastikan versi yang terpasang identik di mesin semua anggota tim?

Jawabannya npm, package manager yang terpasang bersama Node.js. Chapter ini menuntaskan siklus kerjanya di folder latihan: membuat manifes, memasang package, membaca node_modules dan package-lock.json, membedakan npm ci dari npm install, serta mengeksekusi package lewat npx yang selama ini kita pakai untuk ESLint dan Prettier. Semua keluaran di chapter ini direkam nyata dengan npm 11.19.0.

Membuat Manifes dengan npm init #

Folder latihan dimulai kosong. Perintah npm init membuatkan manifes pertama, dan opsi -y menerima semua nilai bawaan tanpa pertanyaan:

bash
npm init -y

Output yang Dihasilkan:

text
Wrote to /home/z/my-project/scripts/npm-demo/package.json:

{
  "name": "npm-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "commonjs"
}

Beberapa hal di keluaran ini layak dicatat:

  1. Baris 1 (Wrote to ...): nama folder otomatis menjadi nama package — folder latihan kita bernama npm-demo. Field yang wajib (name dan version) langsung terisi.
  2. Bagian scripts: npm mengisi script test dengan penanda temporer yang berhenti dengan pesan kesalahan. Kita menggantinya begitu test nyata tersedia.
  3. Baris "type": "commonjs": npm 11 menuliskan mode module secara eksplisit. Nilai ini menegaskan perilaku bawaan Node — berkas .js dibaca sebagai CommonJS, sehingga contoh require() di chapter ini sah tanpa berkas .mjs.

npm install: Mengisi node_modules #

Perintah inti npm adalah npm install <nama-package>. npm mengunduh package dari registry, menempatkannya di folder node_modules, dan mencatatnya ke manifes. Kita pasang satu package runtime dan satu package pengembangan:

bash
npm install ms
npm install --save-dev picocolors

Output yang Dihasilkan:

text

added 1 package, and audited 2 packages in 435ms

found 0 vulnerabilities

Keluaran instalasi kedua (dengan --save-dev) nyaris sama: added 1 package, and audited 3 packages in 447ms. Arti tiap bagiannya:

  1. added 1 package: npm menempatkan satu folder baru di node_modules. Folder node_modules kini berisi ms dan picocolors — picocolors dipilih sebagai contoh devDependency karena juga berukuran kecil (pewarna teks terminal).
  2. audited 2 packages: npm memeriksa package terpasang terhadap basis data advisory keamanan — mekanisme yang dibedah tuntas di chapter dependency-security pada section Security.
  3. found 0 vulnerabilities: hasil pemeriksaan tersebut, bersih pada contoh ini.

Angka milidetik pada keluaran npm — 435ms, 447ms, dan 410ms pada contoh chapter ini — adalah durasi pemasangan yang terekam di lingkungan uji kita; nilainya wajar berbeda di mesin lain. Yang harus selalu sama adalah jumlah package: added 1 package untuk satu package, dan audited menghitung seluruh package yang diperiksa sampai saat itu.

Sebagai bukti bahwa package benar-benar terpasang dan siap dipakai, berkas berikut memanggil ms:

js uji-paket.js
// uji-paket.js — memakai package ms yang baru dipasang
const ms = require("ms");

console.log(ms("2 days"));
console.log(ms("1 h"));

Output yang Dihasilkan:

text
172800000
3600000

Perhatikan satu hal penting: kita tidak menulis require("./node_modules/ms"). Ketika bertemu require("ms"), Node mencari folder node_modules dari lokasi berkas ke atas, menemukan package itu, dan membaca titik masuknya. Dua angka keluaran adalah hasil konversi durasi: dua hari bernilai 172.800.000 milidetik dan satu jam bernilai 3.600.000 milidetik.

package-lock.json: Rekam Versi Persis #

Di manifes kita menulis rentang ^2.1.3, tetapi yang benar-benar terpasang hari ini adalah 2.1.3. Versi persis itu direkam npm di berkas package-lock.json — lockfile yang menyimpan seluruh pohon dependensi beserta sidik jarinya:

json potongan package-lock.json
{
  "name": "npm-demo",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "npm-demo",
      "version": "1.0.0",
      "license": "ISC",
      "dependencies": {
        "ms": "^2.1.3"
      },
      "devDependencies": {
        "picocolors": "^1.1.1"
      }
    },
    "node_modules/ms": {
      "version": "2.1.3",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
      "license": "MIT"
    },
  1. "version": "2.1.3": versi persis yang terpasang — inilah yang membuat rentang ^2.1.3 menjadi konkrit.
  2. "resolved": alamat unduhan tempat package diambil.
  3. "integrity": hash SHA-512 dari isi package. Saat memasang ulang, npm menghitung hash hasil unduhan dan membandingkannya — isi package yang terjadi perubahan akan ditolak.
  4. "dev": true (pada entri picocolors): penanda bahwa package ini bagian dari devDependencies.

Hubungan kedua berkas bisa diringkas begini: package.json mencatat keinginan (rentang yang ditulis manusia), sedangkan package-lock.json mencatat kenyataan (versi persis yang sudah teruji). Karena itu lockfile wajib ikut disimpan di repository — dua mesin yang menjalankan pemasangan dari lockfile yang sama mendapatkan pohon dependensi yang identik. Sebaliknya, node_modules tidak pernah ikut disimpan: isinya bisa dipulihkan kapan saja dari lockfile.

npm ci: Pemasangan yang Tepat Sesuai Lockfile #

Untuk pemasangan dari lockfile, npm menyediakan perintah khusus. Skenario berikut menghapus node_modules terlebih dahulu, lalu memasang ulang dengan npm ci:

bash
rm -rf node_modules
npm ci

Output yang Dihasilkan:

text

added 2 packages, and audited 3 packages in 410ms

found 0 vulnerabilities

Hasilnya sama seperti npm install — dua package terpasang — tetapi kontrak keduanya berbeda:

Aspeknpm installnpm ci
Sumber keputusan versiManifes, lalu lockfileLockfile saja
Memperbarui lockfileYa, bila manifes berubahTidak pernah
Folder node_modules lamaDipakai ulang sebagianDihapus lalu dipasang ulang
Manifes dan lockfile tak sinkronDisesuaikan diam-diamBerhenti dengan kesalahan
Cocok untukPengembangan harianCI dan penerapan otomatis

Karena tegas dan dapat diprediksi, npm ci menjadi pilihan baku di lingkungan CI: setiap eksekusi pipeline memasang persis keadaan yang pernah teruji, tanpa kejutan versi baru.

npx: Menjalankan Package Tanpa Memasang #

Perintah terakhir menutup lingkaran dari chapter ESLint. npx mengeksekusi package sebagai program: bila package sudah ada di node_modules atau di cache npx, ia dipakai langsung; bila belum, npx mengunduhnya sementara. Versinya sama dengan npm:

bash
npx --version

Output yang Dihasilkan:

text
11.19.0

Perintah npx --yes eslint@latest kode.js yang kita jalankan di chapter ESLint kini terbaca utuh: npx mengambil ESLint (dengan opsi --yes untuk melewati konfirmasi), menjalankannya satu kali, dan menyimpannya di cache untuk pemanggilan berikutnya. Untuk alat yang dipakai tim setiap hari, pemasangan resmi sebagai devDependencies tetap lebih tepat — versi terkunci, tersedia luring, dan tercatat di lockfile.

Kini seluruh kebutuhan proyek — kode, alat, dan versinya — tercatat dan terpasang secara terkontrol. Chapter berikutnya berpindah ke sisi lain dari aplikasi nyata: bagaimana kode yang sama membaca nilai yang berbeda di tiap lingkungan tempat ia dijalankan.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

uji-paket.js

Unduh berkas
javascript
// uji-paket.js — memakai package ms yang baru dipasang
const ms = require("ms");

console.log(ms("2 days"));
console.log(ms("1 h"));