REPL memang nyaman untuk eksperimen, tetapi kode program sungguhan perlu disimpan agar bisa dijalankan berulang kali, dibagikan, dan dirawat bersama. Di Node.js, wadah kode itu adalah berkas JavaScript biasa yang kita eksekusi lewat satu perintah node diikuti nama berkasnya. Chapter ini membawa kita membuat berkas pertama, menjalankannya, lalu membedakan tiga ekstensi yang sering kita jumpai — .js, .mjs, dan .cjs — beserta dua gaya module yang menyertainya.

Berkas JavaScript Pertama #

Buat sebuah berkas bernama salam.mjs di folder kerja kita, lalu isi dengan kode berikut:

js
const nama = "Budi";
const bahasa = "JavaScript";

console.log(`Halo, ${nama}!`);
console.log(`Kode ${bahasa} ini dijalankan dari sebuah berkas.`);

Selanjutnya, buka terminal di folder yang sama dengan berkas tersebut, lalu jalankan:

bash
node salam.mjs

Output yang Dihasilkan:

text
Halo, Budi!
Kode JavaScript ini dijalankan dari sebuah berkas.

Mari kita telaah isi berkasnya baris per baris:

  1. Baris 1-2 (const nama = ...): dua variable dideklarasikan dengan const — satu menyimpan string "Budi", satu lagi "JavaScript".
  2. Baris 4-5 (console.log(...)): dua baris cetakan memakai template literal — string ber backtick yang menyisipkan nilai lewat ${...}. Tanda kurung kurawal di dalam backtick menunjuk nama variable yang nilainya ingin disisipkan.
  3. Susunan keseluruhan: tidak ada function, tidak ada main() khusus — Node.js membaca berkas dari baris pertama hingga terakhir dan mengeksekusinya berurutan. Struktur sederhana inilah bentuk paling dasar program Node.js.

Perintah node salam.mjs juga menerima path relatif, misalnya node latihan/salam.mjs, asalkan path tersebut benar dari posisi terminal kita berada. Node.js tidak peduli folder mana kode disimpan; yang dibutuhkan hanyalah path menuju berkasnya.

Tiga Ekstensi: .js, .mjs, dan .cjs #

Sejauh ini kita memakai .mjs. Lalu kapan muncul .js dan .cjs? Ketiga ekstensi itu menandai sistem module yang dipakai berkas tersebut — cara berkas saling berbagi kode. Ringkasannya seperti tabel berikut:

EkstensiSistem modulePerlakuan Node.js
.mjsES Modulesselalu diperlakukan sebagai ES Modules, apa pun konfigurasinya
.cjsCommonJSselalu diperlakukan sebagai CommonJS, apa pun konfigurasinya
.jstergantung package.jsonmengikuti field "type" pada package.json terdekat; tanpa package.json, berkas .js diperlakukan sebagai CommonJS

Dua sistem module tersebut punya dua kata kunci berbeda untuk saling memanggil kode: import milik ES Modules dan require milik CommonJS. Berikut penampilan pertama keduanya.

ES Modules: Mengimpor dengan import #

Pecah kode menjadi dua berkas. Berkas pertama, sapa.mjs, berisi function yang diekspor:

js
export function sapa(nama) {
  return `Selamat belajar, ${nama}!`;
}

Berkas kedua, utama.mjs, mengimpor function tersebut lalu memakainya:

js
import { sapa } from "./sapa.mjs";

console.log(sapa("Siti"));

Jalankan berkas utamanya:

bash
node utama.mjs

Output yang Dihasilkan:

text
Selamat belajar, Siti!

Dua baris kunci pada pola ini perlu dibedah:

  1. Baris 1 di utama.mjs (import { sapa } from "./sapa.mjs"): statement import mengambil nilai yang diekspor berkas lain. Kurung kurawal berisi nama yang sama persis dengan kata kunci export di sapa.mjs, dan path "./sapa.mjs" menunjuk berkas tersebut secara relatif — awalan ./ berarti “di folder yang sama”.
  2. Baris 1 di sapa.mjs (export function sapa(...)): kata kunci export menandai function agar bisa diimpor berkas lain. Function yang tidak diekspor tetap berjalan, tetapi hanya dikenal di dalam berkasnya sendiri.

Ketika node utama.mjs dijalankan, Node.js membaca utama.mjs, menemukan statement import, memuat sapa.mjs terlebih dahulu, baru mengeksekusi sisa kode. Urutan ini dijamin selesai sebelum baris console.log dieksekusi.

CommonJS: Mengimpor dengan require #

Sistem yang lebih lawas, CommonJS, memakai require dan module.exports. Berkas sapa.cjs berisi function yang sama, tetapi cara mengekspornya berbeda:

js
function sapa(nama) {
  return `Selamat belajar, ${nama}!`;
}

module.exports = { sapa };

Berkas pemanggilnya, utama.cjs, memuat function tersebut lewat require:

js
const { sapa } = require("./sapa.cjs");

console.log(sapa("Dewi"));

Jalankan seperti biasa:

bash
node utama.cjs

Output yang Dihasilkan:

text
Selamat belajar, Dewi!

Mekanismenya paralel dengan versi ES Modules: module.exports = { sapa } menyerahkan object berisi function sapa kepada sistem module, lalu require("./sapa.cjs") mengembalikan object yang sama di sisi pemanggil. Notasi const { sapa } = ... lalu mengambil property sapa dari object tersebut ke sebuah variable. Hasil akhirnya identik — string Selamat belajar, Dewi! tercetak — hanya bahasa pengantar antar-berkasnya yang berbeda.

Menjalankan Ulang Otomatis dengan –watch #

Saat mengembangkan program, kita pasti berulang kali menyimpan berkas lalu menjalankan ulang perintah node. Node.js menyediakan opsi --watch yang mengotomatiskan siklus itu:

bash
node --watch salam.mjs

Dengan opsi tersebut, Node.js menjalankan salam.mjs, lalu tetap berjalan di latar depan dan mengawasi perubahan berkas. Setiap kali kita menyimpan perubahan pada berkas yang berjalan, Node.js memuat ulang program secara otomatis sehingga versi terbaru langsung dieksekusi. Sesi pengawasan diakhiri dengan menekan Ctrl+C. Opsi ini paling terasa manfaatnya ketika kode kita tumbuh panjang dan sering diubah — kita akan memakainya rutin saat membangun proyek pada bagian akhir Course ini.

Kita kini bisa menyimpan kode dalam berkas, memecahnya menjadi module, dan menjalankannya dengan atau tanpa pengawasan otomatis. Pekerjaan selanjutnya adalah memilih tempat mengetik kode itu setiap hari — Chapter Memilih Code Editor memandu kita menimbang pilihan yang ada.