Lompat ke konten
Kembali ke modul

Node.js, Package, dan Ekosistemnya · 4/7

Bagaimana sebuah import diselesaikan

Ikuti cara Node mencari module, supaya "Cannot find module" jadi masalah yang bisa diselesaikan.

Baca 22 menit

Setelah pelajaran ini kamu bisa

  • Melacak bagaimana import biasa diubah jadi sebuah file di disk
  • Menjelaskan kenapa folder `node_modules` bisa bersarang
  • Mendiagnosis "Cannot find module" secara metodis
  • Menjelaskan apa yang sebenarnya dilakukan alias import seperti `@/`

import { z } from "zod" tidak mengandung path. Ada sesuatu yang harus mengubah nama itu jadi file sungguhan. Memahami algoritmanya mengubah "Cannot find module" dari misteri jadi daftar periksa.

Tiga jenis import

BentuknyaDisebutDiselesaikan dengan
./format, ../lib/xRelatifLangsung dari folder file saat ini
zod, reactBare / packageMencari node_modules ke atas
node:fs, node:pathBawaanNode sendiri, tanpa pencarian di disk

Pencarian ke atas

Untuk import bare, Node mencari di node_modules folder saat ini. Tidak ada? Naik satu tingkat lalu cari lagi. Ulangi sampai root filesystem, lalu gagal.

text
Importing "zod" from /home/you/app/features/booking/create.ts
Node checks, in this order:

  /home/you/app/features/booking/node_modules/zod
  /home/you/app/features/node_modules/zod
  /home/you/app/node_modules/zod              <- normally found here
  /home/you/node_modules/zod
  /home/node_modules/zod
  /node_modules/zod
  -> Error: Cannot find module 'zod'
Coba sendiri

Algoritmanya sendiri, dalam dua belas baris, terhadap filesystem tiruan. Ubah `from` dan lihat jalur pencariannya berubah.

Hasil

Tekan Jalankan untuk melihat hasilnya.

Ini jalan di browser kamu, di dalam sandbox. Apa pun yang kamu tulis di sini tidak bisa merusak situs.

Perhatikan kedua pemanggilan menemukan salinan React yang berbeda. Itu sebabnya penyarangan ada: kalau dua package butuh versi yang tidak kompatibel, masing-masing mendapat salinannya sendiri di dekatnya. Itu juga sebabnya kamu bisa berakhir dengan dua salinan React dan error "invalid hook call" yang terkenal membingungkan.

File mana di dalam package-nya?

Menemukan foldernya baru separuh pekerjaan. package.json milik package itu sendiri menyatakan file mana yang seharusnya didapat sebuah import.

json
{
  "name": "some-library",
  "main": "./dist/index.js",        // the old field, still widely used
  "types": "./dist/index.d.ts",     // where TypeScript looks
  "exports": {                      // the modern field, and it WINS
    ".": "./dist/index.js",
    "./server": "./dist/server.js",
    "./package.json": "./package.json"
  }
}
exports itu dinding sekaligus peta: kalau sebuah path tidak terdaftar, kamu tidak bisa mengimpornya, meski file-nya jelas ada di disk.

Alias

@/features/i18n/locales bukan sebuah package. Ia alias yang dikonfigurasi di tsconfig.json, ditulis ulang jadi path sungguhan oleh alat build-mu. Ia menyelamatkanmu dari ../../../features/i18n/locales, yang rusak begitu kamu memindahkan sebuah file.

json
// tsconfig.json in this project
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./*"]       // "@/features/x" -> "./features/x"
    }
  }
}
Type checker dan bundler dua-duanya harus memahami alias-nya. Itu sebabnya vitest.config.mts di sini menyalakan resolve.tsconfigPaths — kalau tidak, test akan gagal pada import yang sebenarnya lolos compile.

Mendiagnosis "Cannot find module"

  1. Apakah ejaannya benar, termasuk huruf besar-kecilnya? Di Linux, React dan react itu file berbeda; di macOS tidak, dan itu sebabnya bug ini hanya muncul di CI.
  2. Apakah ada di package.json? Kalau tidak, bun add dulu.
  3. Apakah ada di node_modules? ls node_modules/nama-nya.
  4. Apakah proses install-nya benar-benar selesai? Jalankan ulang dan baca output-nya.
  5. Apakah itu alias yang tidak dikenali alat build-mu? Cek paths dan konfigurasi bundler-nya.
  6. Apakah itu subpath yang tidak diizinkan peta exports package-nya?
  7. Pilihan terakhir: rm -rf node_modules && bun install.

Tugas praktik

Di proyek ini, jalankan cat node_modules/next/package.json | head -40 dan temukan field exports-nya. Lalu coba bun run -e 'import("next/dist/server/lib/some-internal.js")' dan baca kegagalannya. Terakhir, buka tsconfig.json, ubah "@/*" jadi "~/*", jalankan bunx tsc --noEmit, dan lihat semua import rusak sekaligus — lalu kembalikan.

Hasil yang diharapkan

Puluhan error 'Cannot find module', semuanya beres dengan mengembalikan satu baris.