02 β Modullar: CommonJS va ESM¶
β¬ οΈ Oldingi: 01 β Node.js nima va o'rnatish Β· π README Β· Keyingi: 03 β npm va package.json β‘οΈ
Bu bobda: kodni nega bitta ulkan faylga emas, ko'p kichik modullarga bo'lish kerakligini ko'ramiz: qayta ishlatish, izolyatsiya, tartib. Keyin Node'ning ikki modul tizimini o'rganamiz β CommonJS (an'anaviy
require/module.exports) va ESM (zamonaviy standartimport/export). Har biri qanday ishlashini, fayl scope'i va modul kesh nimaligini,__dirnameva uning ESM'dagi muqobiliimport.metani, top-level await'ni, built-in (node:) va uchinchi-tomon modullar farqini, Node modulni qanday topishini (resolution) va ikkalasini birga ishlatishdagi nozikliklarni ko'rib chiqamiz. Bitta matematik yordamchi modulni ham CommonJS, ham ESM uslubida yozib, boshqa fayldan chaqiramiz. Bu bobdan keyin kitobimizda asosiy uslub ESM bo'ladi.
Nega modullar kerak?¶
Tasavvur qiling, butun dasturingiz bitta index.js faylda β 3000 qator kod. Kalkulyator funksiyalari, fayl o'qish, server, ma'lumotlar tekshiruvi β hammasi aralash. Bunday faylda:
- Kerakli funksiyani topish qiyin.
- Bir o'zgaruvchi nomi ikki joyda ishlatilsa, ular bir-biriga xalaqit beradi (hammasi bitta global makonda).
- Bu kodning bir bo'lagini boshqa loyihada qayta ishlatib bo'lmaydi β hammasi bir-biriga yopishgan.
Modul β bu mustaqil bir fayl. Node'da har fayl alohida modul, va u o'z ichidagi o'zgaruvchilarni "yashirin" saqlaydi. Modul tashqariga faqat o'zi ataylab bergan narsalarni ko'rsatadi (eksport qiladi), boshqa fayl esa o'sha narsani so'rab oladi (import qiladi).
Modullar uchta katta foyda beradi:
- Kodni bo'lish β har vazifa o'z faylida.
matematika.js,server.js,fayllar.js. O'qish va tuzatish oson. - Qayta ishlatish β bir marta yozilgan
matematika.jsni o'nlab joyda chaqirish mumkin. - Izolyatsiya β bir modul ichidagi o'zgaruvchi boshqasiga sizib chiqmaydi. Bu xatolarning butun dasturga tarqalishini oldini oladi.
Node'da modullarni yozishning ikki usuli bor. Avval CommonJS ni (Node bilan tug'ilgan, eski paketlarda hali ham keng tarqalgan), keyin ESM ni (JavaScript'ning rasmiy zamonaviy standarti, biz asosan shuni ishlatamiz) ko'ramiz.
CommonJS β Node'ning an'anaviy tizimi¶
CommonJS β Node 2009-yilda paydo bo'lganidan beri mavjud bo'lgan tizim. Uning ikki asosiy kaliti bor: eksport uchun module.exports, import uchun require().
Birinchi modul: matematika yordamchisi¶
Keling, real bir narsa yozaylik β bir necha matematik amalni jamlagan yordamchi modul. Bu kelajakda bir necha joyda kerak bo'ladigan, qayta ishlatiladigan kod.
// matematika.js β CommonJS modul (yordamchi funksiyalar)
function qosh(a, b) {
return a + b;
}
function ayir(a, b) {
return a - b;
}
function kopaytir(a, b) {
return a * b;
}
function bol(a, b) {
if (b === 0) throw new Error("Nolga bo'lib bo'lmaydi");
return a / b;
}
// PI β modul ichidagi const, faqat eksport qilinsa tashqaridan ko'rinadi
const PI = 3.14159;
// module.exports β bu modul tashqariga nimani beradi
module.exports = { qosh, ayir, kopaytir, bol, PI };
module.exports β bu har modulda Node avtomatik beradigan maxsus obyekt. Modul tashqariga nimani ko'rsatishini shu obyektga yozasiz. Bu yerda biz to'rt funksiya va PI ni bitta obyekt ichida eksport qildik.
Endi boshqa fayldan bu modulni chaqiramiz:
// app.js β matematika modulini require bilan chaqiramiz
const matematika = require("./matematika.js");
console.log("qosh(2, 3) =", matematika.qosh(2, 3));
console.log("ayir(10, 4) =", matematika.ayir(10, 4));
console.log("kopaytir(6, 7) =", matematika.kopaytir(6, 7));
console.log("bol(20, 5) =", matematika.bol(20, 5));
console.log("PI =", matematika.PI);
// Faqat kerakli funksiyalarni ham olish mumkin (destructuring)
const { qosh, bol } = require("./matematika.js");
console.log("qosh(100, 1) =", qosh(100, 1));
require("./matematika.js") β bu funksiya matematika.js ni topib, ishga tushiradi va o'sha modulning module.exports obyektini qaytaradi. ./ β bu "shu papkadan" degani (nisbiy yo'l). Faylni node app.js bilan ishga tushiramiz:
Natija:
E'tibor bering: matematika.js ichidagi PI ham, funksiyalar ham module.exports ga qo'shilgani uchun tashqaridan ko'rindi. Agar biror narsani eksport qilmasak β u modul ichida yashirin qoladi, bu izolyatsiyaning asosi.
exports qisqartmasi va bitta tuzoq¶
module.exports o'rniga qisqacha exports deb ham yozish mumkin β bu bir xil obyektga ishora. Maydon qo'shganda ishlaydi:
// exports β bu module.exports ga ishora (shortcut). Maydon qo'shsa ishlaydi:
exports.a = 1;
exports.b = 2;
// β Lekin exports ni TO'LIQ almashtirib bo'lmaydi β bog'lanish uziladi:
// exports = { c: 3 }; // bu module.exports ni o'zgartirmaydi!
// To'liq almashtirish kerak bo'lsa module.exports ishlatiladi:
module.exports.c = 3;
console.log("Bu modul:", module.exports); // { a: 1, b: 2, c: 3 }
Eslab qoling: exports.nimadir = ... ishlaydi, lekin exports = {...} (to'liq almashtirish) ishlamaydi. Shubha bo'lsa, har doim module.exports yozing β u har vaqt to'g'ri ishlaydi.
__dirname va __filename¶
CommonJS modullarida Node ikkita tayyor o'zgaruvchi beradi: __filename (joriy faylning to'liq yo'li) va __dirname (joriy faylning papkasi). Bular fayllarni ishonchli ochish uchun juda kerak β chunki dastur qaysi papkadan ishga tushirilganidan qat'i nazar, kerakli fayl modulning yonida bo'ladi.
// CommonJS: __dirname va __filename tayyor mavjud
console.log("__filename =", __filename);
console.log("__dirname =", __dirname);
const path = require("node:path");
console.log("birga =", path.join(__dirname, "data", "a.txt"));
Natija (yo'llar sizning kompyuteringizga mos bo'ladi):
Modul kesh β modul bir marta yuklanadi¶
Muhim xususiyat: Node har modulni faqat bir marta yuklaydi va natijasini keshlaydi. Bir modulni necha marta require qilsangiz ham, modul tanasi (yuqori darajadagi kod) faqat bir marta ishlaydi, va doim bir xil obyekt qaytadi.
// hisoblagich.js β modul tanasi FAQAT bir marta ishlaydi
console.log("[hisoblagich.js ishga tushdi]");
let son = 0;
function oshir() {
son += 1;
return son;
}
module.exports = { oshir };
// kesh-test.js β bir necha bor require qilsak ham, modul keshlanadi, holat saqlanadi
const a = require("./hisoblagich.js");
const b = require("./hisoblagich.js");
console.log("a === b ?", a === b); // true β bir xil obyekt
console.log(a.oshir()); // 1
console.log(b.oshir()); // 2 (a va b bir xil modul!)
console.log(a.oshir()); // 3
Natija:
[hisoblagich.js ishga tushdi] faqat bir marta chiqdi, garchi ikki marta require qildik. a va b aynan bir xil obyekt β shuning uchun son hisobi ular orasida bo'linadi. Bu juda foydali: masalan, bitta ma'lumotlar bazasi ulanishini yoki sozlamani butun dastur bo'ylab "yagona nusxa" sifatida ulashish mumkin.
ESM β zamonaviy standart¶
ESM (ECMAScript Modules) β bu JavaScript tilining rasmiy modul tizimi. U brauzerda ham, Node'da ham bir xil ishlaydi. Bugun yangi loyihalar uchun standart tanlov shu. Kalitlari: eksport uchun export / export default, import uchun import.
Node'ga "bu fayl ESM" deb aytish¶
Node faylni standart holatda CommonJS deb hisoblaydi. ESM yoqishning ikki yo'li bor:
- Fayl kengaytmasini
.mjsqilish, yoki package.jsonichida"type": "module"yozish β shunda papkadagi barcha.jsfayllar ESM bo'ladi.
Biz kitobda ikkinchi yo'lni asosiy qilamiz. Shunchaki papkaga shunday package.json qo'yamiz:
Bir xil matematika moduli β ESM uslubida¶
Endi xuddi shu yordamchi modulni ESM bilan yozamiz. Bu yerda farq: har funksiya oldida export, va modulning "asosiy" narsasi uchun export default.
// matematika.js β ESM modul. Har funksiya oldida export.
export function qosh(a, b) {
return a + b;
}
export function ayir(a, b) {
return a - b;
}
export function kopaytir(a, b) {
return a * b;
}
export function bol(a, b) {
if (b === 0) throw new Error("Nolga bo'lib bo'lmaydi");
return a / b;
}
export const PI = 3.14159;
// Modulning "asosiy" eksporti β default
export default function kalkulyator(amal, a, b) {
switch (amal) {
case "+": return qosh(a, b);
case "-": return ayir(a, b);
case "*": return kopaytir(a, b);
case "/": return bol(a, b);
default: throw new Error("Noma'lum amal: " + amal);
}
}
Bu yerda ikki turdagi eksport bor:
- Named eksport (
export function qosh,export const PI) β nomi bilan eksport qilinadi, import qilganda xuddi shu nom bilan,{ }qavs ichida olinadi. - Default eksport (
export default ...) β modulda bittagina bo'ladi, modulning "asosiy qiymati". Import qilganda istalgan nom bilan, qavssiz olinadi.
Import tomoni:
// app.js β ESM. default va named importlar bir qatorda.
import kalkulyator, { qosh, ayir, PI } from "./matematika.js";
console.log("qosh(2, 3) =", qosh(2, 3));
console.log("ayir(10, 4) =", ayir(10, 4));
console.log("PI =", PI);
console.log('kalkulyator("*", 6, 7) =', kalkulyator("*", 6, 7));
// Hammasini bitta nom ostida yig'ish (namespace import)
import * as matematika from "./matematika.js";
console.log("matematika.kopaytir(4, 5) =", matematika.kopaytir(4, 5));
import kalkulyator, { qosh, ayir, PI } from "./matematika.js" qatorida: kalkulyator β default eksport (qavssiz), { qosh, ayir, PI } β named eksportlar. import * as matematika esa modulning hamma named eksportini bitta matematika obyektiga yig'adi.
node app.js natijasi:
qosh(2, 3) = 5
ayir(10, 4) = 6
PI = 3.14159
kalkulyator("*", 6, 7) = 42
matematika.kopaytir(4, 5) = 20
Diqqat: ESM'da import qilganda fayl nomini to'liq yozish kerak β
"./matematika.js",.jssiz emas. CommonJS'da.jsni tashlab ketsa ham ishlaydi, ESM'da esa to'liq nom talab qilinadi (bu standart talabi).
import.meta β ESM'da __dirname o'rni¶
ESM modullarida __dirname va __filename yo'q β bu CommonJS o'zgaruvchilari edi. Ularning o'rnida import.meta bor. import.meta.url joriy faylning URL manzilini beradi (file:///... ko'rinishida), undan klassik yo'lni ajratib olamiz:
// ESM: __dirname/__filename YO'Q. import.meta dan olamiz.
import { fileURLToPath } from "node:url";
import path from "node:path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
console.log("import.meta.url =", import.meta.url);
console.log("__filename =", __filename);
console.log("__dirname =", __dirname);
// Node 20.11+ / 21.2+ da tayyor mayyor variant ham bor:
console.log("import.meta.dirname =", import.meta.dirname);
Natija:
import.meta.url = file:///C:/.../dirname-esm.mjs
__filename = C:\...\dirname-esm.mjs
__dirname = C:\...\
import.meta.dirname = C:\...\
Zamonaviy Node'da (biz ishlatayotgan 24-versiya) import.meta.dirname va import.meta.filename to'g'ridan-to'g'ri tayyor β fileURLToPath bilan ovora bo'lmasdan ishlataversa bo'ladi. Eski versiyalarni qo'llab-quvvatlash kerak bo'lsa yuqoridagi fileURLToPath usulini biling.
Top-level await¶
ESM'ning yana bir kuchli imkoniyati β top-level await. Ya'ni await ni async funksiya ichiga o'rashsiz, to'g'ridan-to'g'ri fayl darajasida ishlatish mumkin. (CommonJS'da bu mumkin emas.) Node 24'da global fetch ham tayyor, demak hech qanday paketsiz internetdan ma'lumot olish mumkin:
// Top-level await β ESM da await ni funksiyasiz, fayl darajasida ishlatish mumkin
console.log("Ma'lumot so'ralmoqda...");
const javob = await fetch("https://httpbin.org/get");
const data = await javob.json();
console.log("Status:", javob.status);
console.log("Sizning IP (origin):", data.origin);
Natija (IP raqami sizda boshqacha bo'ladi):
Top-level await ayniqsa sozlama yuklash kabi vazifalarda qulay: dastur boshlanishidan oldin konfiguratsiya faylini yoki bazadan dastlabki ma'lumotni o'qib olib, keyin davom etish. Buni bob oxiridagi mashqlarda ishlatamiz.
CommonJS va ESM β farqlari va qaysi birini tanlash¶
Ikki tizimni yonma-yon ko'rib chiqaylik:
| Xususiyat | CommonJS | ESM |
|---|---|---|
| Eksport | module.exports = {...} |
export / export default |
| Import | const x = require("...") |
import x from "..." |
| Yuklash | sinxron β require chaqirilganda darrov |
asinxron β oldindan tahlil qilinadi |
| Fayl | .cjs yoki standart (type yo'q) |
.mjs yoki "type": "module" |
| Papka yo'li | __dirname tayyor |
import.meta.dirname |
| Top-level await | yo'q | bor |
| Fayl nomi importda | .js siz ham bo'ladi |
to'liq nom (./x.js) shart |
Asosiy texnik farq: require sinxron β u kod ishlayotgan paytda, o'sha qatorga yetganda modulni topadi va darrov yuklaydi. import esa asinxron va statik β Node faylni ishga tushirishdan oldin barcha import larni tahlil qiladi, kerakli modullarni oldindan yuklaydi. Aynan shu "oldindan tahlil" tufayli import doim faylning yuqorisida bo'lishi kerak (shart bilan import qilib bo'lmaydi β buning uchun dinamik import() bor, pastda).
Qaysi birini tanlash?
- Yangi loyiha β ESM. Bu standart, brauzer bilan bir xil, kelajak shu tomonda. Bizning kitobimiz ham 02-bobdan keyin ESM ni asosiy uslub qiladi.
- Eski kod yoki ba'zi eski npm paketlari β CommonJS bilan uchrashasiz. Shuning uchun uni o'qiy va tushuna olish kerak, ataylab yangidan yozmasangiz ham.
Built-in va uchinchi-tomon modullar¶
Node'da modullar uch turga bo'linadi, va Node ularni qayerdan topishini modul nomiga qarab ajratadi:
- Built-in (yadro) modullar β Node'ning o'zi bilan keladi, hech narsa o'rnatish shart emas:
fs(fayllar),path(yo'llar),http(server),os(operatsion tizim),url,cryptova boshqalar. Zamonaviy uslub β ularninode:prefiks bilan yozish:import fs from "node:fs". Bu Node'ga "bu aniq yadro moduli, papkalarda qidirmang" deb aytadi va boshqa paket bilan nom to'qnashuvidan saqlaydi.
// node: prefiks bilan built-in modul β bu Node ichidagi modul, npm dan emas
import { writeFile, readFile } from "node:fs/promises";
await writeFile("salom.txt", "Salom, Node!", "utf8");
const matn = await readFile("salom.txt", "utf8");
console.log("Faylda:", matn); // Faylda: Salom, Node!
-
Uchinchi-tomon modullar β npm orqali o'rnatasiz,
node_modulespapkasida yashaydi. Masalanexpress,lodash. Bularni prefikssiz, faqat nomi bilan import qilasiz:import express from "express". (npm vanode_moduleshaqida keyingi bobda batafsil.) -
O'z modullaringiz β siz yozgan fayllar. Bularni nisbiy yo'l bilan chaqirasiz:
import { qosh } from "./matematika.js"β./yoki../bilan boshlanadi.
Mana shu uchchala turni bitta serverda ko'ramiz (REAL KEYS qismida).
Modul resolution β Node modulni qanday topadi¶
Siz require("nimadir") yoki import ... from "nimadir" yozganingizda, Node "nimadir" ni qayerdan qidiradi? Tartib shunday:
- Yadro modulmi? Agar nom
node:bilan boshlansa yoki yadro moduli nomi bo'lsa (fs,path, ...) β Node uni ichidan oladi, fayl tizimiga umuman qaramaydi. Eng tez yo'l. - Nisbiy yo'lmi? Agar nom
./,../yoki/bilan boshlansa β Node uni fayl yo'li deb biladi va o'sha joydan qidiradi (./matematika.js). - Aks holda β paket nomi. Node
node_modulespapkasiga boradi: avval joriy papkadaginode_modules, topmasa bir yuqori papkadaginode_modules, va hokazo yuqoriga qarab β ildizgacha.
require.resolve modul aynan qayerdan topilganini ko'rsatadi β bu resolution'ni "ko'z bilan ko'rish" uchun foydali:
// Node modulni qayerdan topishini ko'rsatadi
console.log("path: ", require.resolve("node:path")); // core
console.log("hisob: ", require.resolve("./hisoblagich.js")); // nisbiy
Natija:
node:path o'zicha qaytdi (yadro), nisbiy fayl esa to'liq fizik yo'l bilan. Demak Node birinchi navbatda yadroni, keyin nisbiy yo'lni, oxirida node_modules ni tekshiradi.
REAL KEYS: yordamchi modulni serverga ulash¶
Endi hammasini birlashtiraylik. Real vazifa: matematik yordamchi modulimizni veb-serverga ulaymiz β foydalanuvchi http://localhost:3000/qosh/2/3 ga kirsa, server 5 ni qaytaradi. Bu yerda barcha uch turdagi modul birga ishlaydi: uchinchi-tomon express, bizning matematika.js, va Node yadrosi.
Avval papkada package.json (ESM uchun) yaratamiz va express ni o'rnatamiz:
Yordamchi modul (avvalgisining soddalashtirilgan ESM nusxasi):
// matematika.js β o'sha yordamchi modul, endi serverda ishlatiladi
export function qosh(a, b) { return a + b; }
export function ayir(a, b) { return a - b; }
export function kopaytir(a, b) { return a * b; }
export function bol(a, b) {
if (b === 0) throw new Error("Nolga bo'lib bo'lmaydi");
return a / b;
}
Server β express ni (uchinchi-tomon) va o'z modulimizni (nisbiy yo'l) import qiladi:
// server.js β ESM import bilan modulni serverga ulaymiz
import express from "express"; // uchinchi-tomon modul (node_modules)
import { qosh, kopaytir } from "./matematika.js"; // o'z modulimiz (nisbiy yo'l)
const app = express();
app.get("/qosh/:a/:b", (req, res) => {
const a = Number(req.params.a);
const b = Number(req.params.b);
res.json({ amal: "qosh", natija: qosh(a, b) });
});
app.get("/kopaytir/:a/:b", (req, res) => {
res.json({ amal: "kopaytir", natija: kopaytir(Number(req.params.a), Number(req.params.b)) });
});
app.listen(3000, () => console.log("Server: http://localhost:3000"));
node server.js bilan ishga tushiriladi, keyin brauzerda http://localhost:3000/qosh/2/3 ni ochsangiz {"amal":"qosh","natija":5} ko'rasiz. Tekshirishni avtomatlashtirish uchun serverni ko'tarib, o'sha jarayonning ichida global fetch bilan so'rov yuborib ko'ramiz:
// test-client.mjs β serverni ko'tarib, fetch bilan so'rov yuboramiz
import express from "express";
import { qosh, kopaytir } from "./matematika.js";
const app = express();
app.get("/qosh/:a/:b", (req, res) =>
res.json({ natija: qosh(Number(req.params.a), Number(req.params.b)) }));
app.get("/kopaytir/:a/:b", (req, res) =>
res.json({ natija: kopaytir(Number(req.params.a), Number(req.params.b)) }));
const server = app.listen(3000);
await new Promise(r => server.once("listening", r)); // top-level await!
const r1 = await (await fetch("http://localhost:3000/qosh/2/3")).json();
const r2 = await (await fetch("http://localhost:3000/kopaytir/6/7")).json();
console.log("qosh:", r1, " kopaytir:", r2);
server.close();
Natija:
Mana β bitta yordamchi modul yozdik, uni serverga uladik, uchinchi-tomon paket bilan birga ishlatdik va top-level await yordamida tekshirdik. Modullarning kuchi shunda: matematika.js ni endi xohlagan loyihada qayta ishlatish mumkin.
Ikkalasini birga ishlatish β nozikliklar¶
Real loyihalarda ba'zan CommonJS va ESM kodlari uchrashadi (masalan, ESM loyihangizda eski CommonJS paketdan foydalanasiz). Node bularni birga ishlata oladi, lekin bir nechta qoidani bilish kerak.
ESM ichidan CommonJS modulni import qilish β oson¶
Bu eng keng tarqalgan holat va u yaxshi ishlaydi. CommonJS modulning module.exports obyekti ESM tomonda default eksport bo'lib keladi:
// cjs-modul.cjs β CommonJS modul (.cjs kengaytma har doim CommonJS)
function salom(ism) {
return "Salom, " + ism + "!";
}
module.exports = { salom };
// esm-ishlatadi.mjs β ESM dan CommonJS modulni import qilish
import cjs from "./cjs-modul.cjs"; // module.exports => default
console.log(cjs.salom("Oqil")); // Salom, Oqil!
// Named import ham ko'pincha ishlaydi (Node static-analyzdan aniqlaydi):
import { salom } from "./cjs-modul.cjs";
console.log(salom("Ali")); // Salom, Ali!
CommonJS ichidan ESM modulni chaqirish¶
Bu yo'nalish murakkabroq edi, lekin zamonaviy Node'da (22+ va biz ishlatayotgan 24) ancha soddalashdi. Endi sinxron ESM modulni to'g'ridan-to'g'ri require qilsa bo'ladi:
// require-esm.cjs β Node 22+/24: require() endi sinxron ESM grafini ham yuklay oladi
const esm = require("./esm-modul.mjs");
console.log(esm.ism); // ESM modul
console.log(esm.hi()); // hi from esm
Ammo β agar ESM modulda top-level await bo'lsa (ya'ni modul asinxron), uni sinxron require qilib bo'lmaydi:
// esm-tla.mjs β top-level await li ESM (asinxron modul)
await Promise.resolve();
export const x = 1;
// β require-esm-tla.cjs β top-level await li ESM ni sinxron require qilib bo'lmaydi
const esm = require("./esm-tla.mjs");
console.log(esm.x);
Bu xato beradi:
Error [ERR_REQUIRE_ASYNC_MODULE]: require() cannot be used on an ESM graph
with top-level await. Use import() instead.
Yechim β dinamik import(). Bu funksiya CommonJS'da ham, ESM'da ham ishlaydi, Promise qaytaradi va har qanday ESM modulni (top-level await li bo'lsa ham) yuklay oladi:
// dynamic-import.js β CommonJS faylida ESM modulni yuklash uchun import() (Promise qaytaradi)
async function main() {
const esm = await import("node:os");
console.log("Platforma:", esm.platform()); // win32 / linux / darwin
console.log("CPU yadrolari:", esm.cpus().length);
}
main();
import() (qavs bilan) β bu dinamik import: oddiy import (qavssiz) faylning yuqorisida, statik bo'lishi shart edi; import() esa istalgan joyda, shart ostida, funksiya ichida ishlatiladi va natijani await qilasiz.
ESM ichida require kerak bo'lsa¶
ESM modulda require o'zgaruvchisi yo'q. Ba'zan eski CommonJS paketni require bilan chaqirish kerak bo'lsa, createRequire yordam beradi:
// createRequire.mjs β ESM ichida require kerak bo'lsa
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const cjs = require("./cjs-modul.cjs");
console.log(cjs.salom("Vali")); // Salom, Vali!
Amaliy maslahat: boshida bu nozikliklarni yodlab o'tirmang. Asosiy qoida β yangi kodni ESM'da yozing, va kerak bo'lganda CommonJS bilan ulashning bu usullarini eslang. Ko'pincha shunchaki import ... from "..." yetarli.
Mashqlar¶
Yechimga qaramasdan urinib ko'ring. Har birini
nodebilan ishga tushirib tekshiring. ESM uchun papkadapackage.jsonda"type": "module"borligiga ishonch hosil qiling (yoki.mjsishlating).
Oson¶
-
Bitta
salom.jsmodul yarating: usalomla(ism)funksiyasini eksport qilsin ("Salom, " + ism + "!"qaytarsin). Boshqaindex.jsfaylda uni chaqirib,salomla("Dunyo")natijasini chop eting. Avval CommonJS uslubida yozing. -
Xuddi shu
salom.jsni endi ESM uslubida qayta yozing (export function),index.jsdaimportqiling.package.jsonga"type": "module"qo'shing. -
node:osbuilt-in modulidan foydalanib, kompyuteringiz platformasini (os.platform()) va CPU yadrolari sonini (os.cpus().length) chop eting.node:prefiksini ishlating.
O'rta¶
-
harorat.jsmoduli yozing:cToF(c)(Selsiydan Farengeytga) vafToC(f)(teskari) funksiyalarini eksport qilsin. Ham CommonJS, ham ESM versiyasini alohida yozib, ikkalasini ham ishga tushirib tekshiring.cToF(100)β212,fToC(32)β0bo'lishi kerak. -
Modul kesh xulq-atvorini isbotlang: bir
holatlar.jsmoduli yozing, u ichidalet ochilganlar = 0saqlasin vaochil()funksiyasi har chaqirilganda uni oshirib qaytarsin. Boshqa faylda modulni ikki martarequire/importqiling, ikkalasidan hamochil()ni chaqiring va natija umumiy hisoblanishini (1, 2, 3...) ko'rsating. -
ESM modulda top-level await bilan
https://httpbin.org/getdan ma'lumot oling (fetch) va javobdagiorigin(sizning IP) ni chop eting.asyncfunksiya ishlatmang β to'g'ridan-to'g'ri fayl darajasidaawait.
Qiyin¶
-
Sozlama yuklovchi modul.
config.jsonfaylida{ "nom": "...", "port": 8080, "debug": true }saqlang.config-yukla.mjsmoduli yozing: u top-level await bilannode:fs/promisesorqali shu faylni o'qisin,JSON.parseqilsin va natijani default eksport qilsin. Yo'lniimport.metadan to'g'ri quring (dastur boshqa papkadan ishga tushirilsa ham fayl topilsin). Modulni import qilib,config.portni chop eting. -
Mini kalkulyator API. 4-mashqdagi
harorat.js(ESM) modulini Express serverga ulang:GET /c-to-f/:cSelsiyni Farengeytga,GET /f-to-c/:fteskarisini JSON qilib qaytarsin. Serverni ko'tarib, o'sha jarayonda globalfetchbilan/c-to-f/100ga so'rov yuborib, natija212ekanini tekshiring (in-process test).expressninpm installqiling.
Yechim β 1
// salom.js (CommonJS)
function salomla(ism) {
return "Salom, " + ism + "!";
}
module.exports = { salomla };
// index.js
const { salomla } = require("./salom.js");
console.log(salomla("Dunyo")); // Salom, Dunyo!
node index.js β Salom, Dunyo!
Yechim β 2
package.json:
Yechim β 3
import os from "node:os";
console.log("Platforma:", os.platform());
console.log("CPU yadrolari:", os.cpus().length);
(CommonJS bo'lsa: const os = require("node:os");)
Yechim β 4
ESM versiya:
// harorat.js (ESM)
export function cToF(c) { return c * 9 / 5 + 32; }
export function fToC(f) { return (f - 32) * 5 / 9; }
// harorat-app.js (ESM)
import { cToF, fToC } from "./harorat.js";
console.log("100C =", cToF(100), "F"); // 212
console.log("32F =", fToC(32), "C"); // 0
CommonJS versiya:
// harorat.js (CommonJS)
function cToF(c) { return c * 9 / 5 + 32; }
function fToC(f) { return (f - 32) * 5 / 9; }
module.exports = { cToF, fToC };
// harorat-app.js (CommonJS)
const { cToF, fToC } = require("./harorat.js");
console.log("100C =", cToF(100), "F"); // 212
console.log("32F =", fToC(32), "C"); // 0
Ikkalasi ham bir xil natija beradi β bu modul mantig'i bir xilligini, faqat eksport/import sintaksisi farq qilishini ko'rsatadi.
Yechim β 5
// holatlar.js
console.log("[holatlar.js yuklandi]");
let ochilganlar = 0;
function ochil() {
ochilganlar += 1;
return ochilganlar;
}
module.exports = { ochil };
// test.js
const a = require("./holatlar.js");
const b = require("./holatlar.js");
console.log("a === b ?", a === b); // true β kesh tufayli bir xil obyekt
console.log(a.ochil()); // 1
console.log(b.ochil()); // 2
console.log(a.ochil()); // 3
[holatlar.js yuklandi] faqat bir marta chiqadi: modul tanasi bir marta ishlaydi, holat (ochilganlar) a va b orasida umumiy.
Yechim β 6
package.json da "type": "module" bo'lsin (yoki fayl .mjs):
console.log("Ma'lumot so'ralmoqda...");
const javob = await fetch("https://httpbin.org/get"); // top-level await
const data = await javob.json();
console.log("Status:", javob.status); // 200
console.log("Sizning IP:", data.origin); // masalan 203.0.113.5
async function umuman yo'q β await to'g'ridan-to'g'ri fayl darajasida ishladi, bu faqat ESM'da mumkin.
Yechim β 7
config.json:
// config-yukla.mjs
import { readFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import path from "node:path";
// Yo'lni modul joylashgan papkaga nisbatan quramiz β dastur qayerdan ishga
// tushirilganidan qat'i nazar fayl topiladi.
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const xom = await readFile(path.join(__dirname, "config.json"), "utf8");
const config = JSON.parse(xom);
console.log("Ilova nomi:", config.nom);
console.log("Port:", config.port);
export default config; // top-level await tugagach eksport qilinadi
// index.mjs
import config from "./config-yukla.mjs";
console.log("Import qilingan port:", config.port); // 8080
Natija:
Diqqat qiling: config-yukla.mjs ichidagi await (fayl o'qish) modul eksport qilinishidan oldin tugaydi. Boshqa fayl uni import qilganida, config allaqachon tayyor bo'ladi β bu top-level await'ning kuchi.
Eslatma: zamonaviy Node'da JSON ni to'g'ridan-to'g'ri ham import qilsa bo'ladi:
import config from "./config.json" with { type: "json" };β bu yana ham qisqaroq, lekin fayl o'qishni "qo'lda" qilishni o'rganish ham foydali.
Yechim β 8
Tayyorgarlik:
// harorat.js (ESM)
export function cToF(c) { return c * 9 / 5 + 32; }
export function fToC(f) { return (f - 32) * 5 / 9; }
// server-test.mjs β serverni ko'tarib, in-process fetch bilan tekshiramiz
import express from "express"; // uchinchi-tomon
import { cToF, fToC } from "./harorat.js"; // o'z modulimiz
const app = express();
app.get("/c-to-f/:c", (req, res) =>
res.json({ natija: cToF(Number(req.params.c)) }));
app.get("/f-to-c/:f", (req, res) =>
res.json({ natija: fToC(Number(req.params.f)) }));
const server = app.listen(3000);
await new Promise(r => server.once("listening", r)); // top-level await
const c100 = await (await fetch("http://localhost:3000/c-to-f/100")).json();
const f32 = await (await fetch("http://localhost:3000/f-to-c/32")).json();
console.log("100C ->", c100, " (212 kutilgan)");
console.log("32F ->", f32, " (0 kutilgan)");
if (c100.natija !== 212) throw new Error("c-to-f xato!");
if (f32.natija !== 0) throw new Error("f-to-c xato!");
console.log("Hammasi to'g'ri!");
server.close();
Natija:
Bu yechim bobning hamma asosiy g'oyasini bir joyda ko'rsatadi: o'z modulimizni (harorat.js) yozdik, uchinchi-tomon modul (express) bilan ulagandik, ESM import ishlatib, top-level await bilan serverni ko'tarib, global fetch bilan tekshirdik. Server yopilishini unutmang (server.close()), aks holda jarayon yopilmay turaverishi mumkin.
β¬ οΈ Oldingi: 01 β Node.js nima va o'rnatish Β· π README Β· Keyingi: 03 β npm va package.json β‘οΈ