Berkas package.json adalah metadata utama untuk paket Node.js dan proyek JavaScript. Topik ini membahas bidang-bidang penting di dalamnya, mulai dari identitas paket, titik masuk yang dibaca oleh require dan bundler, hingga skrip npm yang dapat dipanggil. Setelah membaca topik ini, struktur package.json dapat disusun dengan sengaja, bukan sekadar hasil npm init -y yang diterima apa adanya.
Peran package.json#
Setiap direktori yang berisi package.json dianggap sebagai sebuah paket oleh npm, tanpa perlu mendaftar ke registry manapun. Berkas ini dibaca saat instalasi dependensi, eksekusi skrip, dan publikasi. Struktur package.json berbentuk JSON dengan bidang-bidang yang sebagian wajib (name, version) dan sebagian opsional sesuai kebutuhan paket.
Bukan hanya npm yang membaca package.json. Bundler seperti esbuild dan webpack membaca bidang exports dan module untuk menentukan titik masuk yang tepat. Editor dan alat analisis statis membaca type untuk memilih parser yang sesuai. Layanan CI sering memeriksa engines untuk memutuskan versi Node yang dipasang pada runner. Akibatnya, kesalahan pada package.json tidak hanya berdampak saat publikasi, melainkan juga pada pengalaman pengembangan sehari-hari.
npm init -y menghasilkan kerangka package.json dengan bidang standar, lalu isinya disesuaikan secara manual. Cara ini cepat untuk memulai proyek baru, tetapi pemahaman atas setiap bidang tetap diperlukan agar konfigurasi tidak menyesatkan.
# buat direktori demo dan inisialisasi package.json
rm -rf /tmp/menyelami-pj-init
mkdir -p /tmp/menyelami-pj-init
(cd /tmp/menyelami-pj-init && npm init -y)Wrote to /tmp/menyelami-pj-init/package.json:
{
"name": "menyelami-pj-init",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs"
}Bidang metadata#
Beberapa bidang pada package.json menyatakan identitas dan informasi paket. Bidang name berisi nama paket berupa huruf kecil tanpa spasi, version berisi versi mengikuti semver, description berisi deskripsi singkat, license berisi jenis lisensi, dan private bila diatur true mencegah publikasi tidak sengaja ke registry. Bidang engines menyatakan versi Node atau npm yang dibutuhkan, sehingga npm dapat memperingatkan ketika dipasang pada lingkungan yang tidak memenuhi.
{
"name": "demo-sapa",
"version": "1.0.0",
"description": "Paket contoh untuk skrip npm.",
"main": "sapa.js",
"license": "MIT",
"private": true
}Bidang type mengatur sistem modul untuk berkas .js di direktori paket. Nilai "module" membuat .js diperlakukan sebagai ESM, sedangkan "commonjs" atau ketiadaan bidang ini membuat .js diperlakukan sebagai CommonJS. Pengaruh type tidak berlaku pada berkas .mjs dan .cjs, yang selalu mengikuti ekstensinya.
Bidang files berupa daftar pola yang menentukan berkas apa saja yang disertakan saat paket dipublikasikan ke registry. Tanpa files, npm menyertakan seluruh berkas di dalam direktori paket kecuali yang diabaikan oleh .npmignore atau .gitignore. Penetapan files secara eksplisit menjaga ukuran paket tetap kecil dan mencegah berkas sensitif terbawa.
Titik masuk#
Titik masuk menyatakan berkas mana yang dimuat ketika paket diimpor dengan nama saja, tanpa jalur berkas eksplisit. Tiga bidang saling melengkapi, yaitu main untuk titik masuk CommonJS, module untuk titik masuk ESM yang dipakai bundler, dan exports sebagai pengganti modern yang lebih ekspresif.
exports mendukung pemetaan multi-kondisi, sehingga paket dapat menyajikan ESM ke bundler, CommonJS ke require, dan berkas khusus ke Node versi tertentu. Pemetaan ini juga membatasi berkas apa saja yang dapat diimpor dari luar, sehingga API publik paket menjadi eksplisit.
{
"name": "paket-modern",
"version": "1.0.0",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}Bidang bin digunakan untuk paket yang menyediakan CLI. Nilainya adalah pemetaan dari nama perintah ke berkas yang menjadi titik masuk CLI. Saat paket dipasang secara global atau dipanggil lewat npx, nama perintah tersebut dapat dieksekusi dari terminal.
Skrip npm#
Bidang scripts mendefinisikan perintah yang dapat dipanggil dengan npm run <nama>. Setiap entri adalah pasangan nama dan perintah shell yang dijalankan npm di dalam direktori paket. Beberapa nama skrip memiliki lifecycle khusus, seperti test, start, install, dan prepare, yang dipicu otomatis oleh npm pada momen tertentu.
Berikut adalah sapa.js, sebuah skrip kecil yang membaca variabel lingkungan NAMA dan mencetak salam.
const nama = process.env.NAMA || 'Node';
console.log(`Halo, ${nama}!`);
console.log(`Versi Node: ${process.version}`);Halo, Node!
Versi Node: v24.22.2Paket berikut mendaftarkan dua skrip, yaitu sapa yang memanggil sapa.js apa adanya, dan sapa-ali yang menetapkan variabel lingkungan NAMA=Ali sebelum perintah node. Skrip dijalankan dari direktori demo di /tmp agar node_modules tidak tercipta di dalam direktori konten.
# siapkan direktori demo di /tmp
rm -rf /tmp/menyelami-pj-demo
mkdir -p /tmp/menyelami-pj-demo
cp sapa.js /tmp/menyelami-pj-demo/
(cd /tmp/menyelami-pj-demo && cat > package.json <<'EOF'
{
"name": "demo-sapa",
"version": "1.0.0",
"main": "sapa.js",
"scripts": {
"sapa": "node sapa.js",
"sapa-ali": "NAMA=Ali node sapa.js"
}
}
EOF
npm run sapa)
> [email protected] sapa
> node sapa.js
Halo, Node!
Versi Node: v24.22.2# jalankan skrip sapa-ali yang menetapkan NAMA=Ali
(cd /tmp/menyelami-pj-demo && npm run sapa-ali)
> [email protected] sapa-ali
> NAMA=Ali node sapa.js
Halo, Ali!
Versi Node: v24.22.2Skrip npm mewarisi variabel lingkungan dari shell pemanggil, dan variabel yang ditulis sebelum perintah (seperti NAMA=Ali) hanya berlaku untuk perintah tersebut. Pola NAMA=nilai perintah adalah cara standar Unix untuk menetapkan variabel lingkungan sementara, dan menjadi jembatan antara konfigurasi proses dengan kode yang membaca process.env.
Kesalahan umum#
Penulisan name dengan huruf kapital atau spasi ditolak oleh npm. Gunakan huruf kecil dan tanda hubung sebagai pemisah kata, contoh demo-sapa, bukan Demo Sapa.
Pemakaian module tanpa exports sering menyesatkan bundler lama yang membaca module lebih dahulu, lalu menyajikan ESM ke lingkungan CommonJS. Penyajian ESM ke CommonJS bermasalah sebab require tidak dapat memuat modul ESM secara sinkron, sehingga kode yang mengimpor paket akan gagal pada runtime. Bila paket menyajikan kedua sistem, gunakan exports dengan kondisi "import" dan "require" sebagai pengganti main dan module.
Penyebutan nama berkas pada scripts tanpa prefiks node (misalnya "sapa": "sapa.js") hanya bekerja jika berkas memiliki baris shebang dan ditandai executable. Untuk skrip portabel, awali dengan node agar tidak bergantung pada hak eksekusi berkas.
Ringkasan#
package.jsonmenyatakan identitas paket (name,version,license,private), titik masuk (main,module,exports,bin), dan skrip npm (scripts).- Bidang
typemengatur sistem modul berkas.jsdi direktori paket:"module"untuk ESM,"commonjs"untuk CommonJS. exportsmendukung pemetaan multi-kondisi yang menggantikanmaindanmodulesekaligus membatasi API publik paket.- Skrip npm dipanggil dengan
npm run <nama>dan mewarisi variabel lingkungan dari shell pemanggil, sehingga konfigurasi dapat diteruskan ke kode lewatprocess.env.
Referensi#
- package.json — npm Docs
— Daftar lengkap bidang
package.jsonbeserta perilakunya menurut dokumentasi resmi npm. - Conditional exports — Node.js
— Penjelasan mekanisme
exportsdengan kondisi"import","require", dan"node". - npm scripts — npm Docs
— Daftar skrip lifecycle dan cara pemanggilan skrip kustom via
npm run.