IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur64/65-dars20 daqiqa
Mundarija (35)

Node.js'da CommonJS va ESM: require, module.exports va o'zaro ishlash

Qisqacha: CommonJS — Node'ning eski modul tizimi: require("./fayl") bilan oladi, module.exports = {...} bilan beradi. U kod ishlayotganda, sinxron yuklanadi va qiymat nusxasini beradi; ESM esa statik va jonli bog'lanishli. Node faylni .mjs (ESM), .cjs (CommonJS) yoki .js + package.json dagi "type" bo'yicha taniydi. ESM CommonJS'ni import qila oladi, Node 24 da esa CommonJS ham ESM'ni require qila oladi — faqat unda top-level await bo'lmasa.

Bu darsda

  • CommonJS modulini require va module.exports bilan yoza va o'qiy olasiz.
  • exports = ... tuzog'ini, require keshini va qiymat nusxalanishini tushuntira olasiz.
  • Node .js faylni qaysi tizimda ishga tushirishini package.json va kengaytmaga qarab aniqlay olasiz.
  • ESM'dan CommonJS'ni va CommonJS'dan ESM'ni (require(esm)) ulay olasiz, cheklovlarini bilasiz.
  • vazifalar ga package.json qo'shib, ro'yxat mantiqini Node'da tekshirasiz va refactor/modullar ni main ga qo'shib, Pages'ga chiqarasiz.

Oldin bilishingiz kerak: Modul semantikasi, Strict mode, this: to'rt qoida, JSON asoslari.

1. Nega bu kerak?

Kursda kodni ESM'da yozamiz. Lekin dasturchi faqat o'z kodini yozmaydi — u boshqalarning kodini o'qiydi. Va u yerda siz tez-tez shunday qatorlarni uchratasiz:

js
const express = require("express");
const { readFileSync } = require("fs");

module.exports = { narxniTop };

Bu — CommonJS (qisqacha CJS) — Node'ning o'z modul tizimi. Node 2009-yilda paydo bo'lganda JavaScript'da modullar yo'q edi, shuning uchun Node o'zinikini yaratdi. ESM tilga faqat 2015-yilda qo'shildi. O'n yil davomida npm'ga yuz minglab paket CommonJS'da yozildi. Eski darsliklar, StackOverflow javoblari, ko'p sozlama fayllari — hammasi shunda.

Strict mode darsida .cjs va .mjs farqini ko'rib, "formatlarni keyin batafsil ko'ramiz" degan edik. this: to'rt qoida darsida esa .cjs faylning yuqori darajasidagi this — {} ekanini va bu "module.exports obyekti" ekanini aytgan edik. Bugun ikkalasining sababini ochamiz.

Hayotdan o'xshatish: ikki xil hujjat aylanishi. Eski idorada hujjat qog'oz nusxa bo'lib keladi: olgan zahoti u sizniki, asl hujjat o'zgarsa ham, qo'lingizdagi nusxa eskicha qoladi. Yangi idorada hamma bitta elektron hujjatga havola oladi: kimdir uni yangilasa, hamma yangisini ko'radi. CommonJS — qog'oz nusxa, ESM — umumiy elektron hujjat. Ikkala idora ham ishlayapti va ular bir-biriga hujjat yuborishni o'rganishi kerak.

2. CommonJS asoslari

2.1 require va module.exports

«Bahor» menyusi CommonJS'da:

js
// menyu.cjs
console.log("menyu.cjs yuklandi");
const narxlar = { osh: 35000, manti: 30000 };

function narxniTop(taom) {
  return narxlar[taom] ?? 0;
}

module.exports = { narxlar, narxniTop };
js
// asosiy.cjs
const { narxniTop } = require("./menyu.cjs");
const menyu = require("./menyu.cjs");

console.log(narxniTop("osh"));
console.log(menyu === require("./menyu.cjs"));
console.log(Object.keys(require.cache).length);

node asosiy.cjs:

text
menyu.cjs yuklandi
35000
true
2

Qatorma-qator:

  • module.exports = {...} — fayl tashqariga beradigan bitta qiymat. Odatda obyekt, lekin funksiya yoki klass ham bo'lishi mumkin.
  • require("./menyu.cjs") — faylni yuklaydi, bajaradi va o'sha module.exports ni qaytaradi. Oddiy funksiya chaqiruvi: natijani o'zgaruvchiga yozasiz.
  • const { narxniTop } = require(...) — bu oddiy destructuring. import { ... } ga o'xshaydi, lekin ESM'ning statik importi emas: qaytgan obyektdan xususiyat olinyapti.
  • "menyu.cjs yuklandi" bir marta. Uch require — bitta bajarilish. menyu === require(...) — true: hamma bitta obyektni oldi.
  • require.cache — yuklangan fayllar keshi. Ikkita yozuv: asosiy.cjs ning o'zi va menyu.cjs.

2.2 Har fayl — funksiya ichida

require, module, __dirname qayerdan keladi? Ularni hech kim e'lon qilmadi. Sir shunda: Node har CommonJS faylni ishga tushirishdan oldin uni funksiyaga o'raydi — taxminan shunday:

js
(function (exports, require, module, __filename, __dirname) {
  // sizning faylingiz shu yerda
});

Beshta nom — shu funksiyaning parametrlari. Bu bir nechta sirni ochadi:

  • Fayl o'z muhitiga ega — funksiya muhiti, xuddi IIFE kabi. Shuning uchun .cjs dagi var global'ga tushmaydi.
  • this — module.exports. Node funksiyani this sifatida module.exports bilan chaqiradi. Boshida u bo'sh obyekt — this: to'rt qoida darsidagi {} aynan shu.
  • __dirname va __filename — fayl turgan papka va faylning to'liq yo'li.
js
// bosh.cjs
console.log(this === module.exports, this);
console.log(typeof __dirname, typeof __filename, typeof require);
text
true {}
string string function

Bu ikki qatorni bosh.cjs faylga yozib, node bosh.cjs bilan ishga tushiring — natija yuqoridagidek. Saytdagi tugma bilan emas: brauzerda module yo'q.

2.3 exports tuzog'i

Beshta parametrdan biri — exports. U boshida module.exports bilan bitta obyektga qaraydi. Shuning uchun xususiyat qo'shish ikkalasi bilan ham ishlaydi:

js
exports.sotuvQosh = (narx) => narx; // ishlaydi

Lekin exports ga yangi obyekt bersangiz:

js
// kassa.cjs
exports.sotuvQosh = (narx) => narx;
exports = { hisobot: () => "hisobot" };
js
// sinov.cjs
const kassa = require("./kassa.cjs");
console.log(kassa);
console.log(typeof kassa.hisobot);
text
{ sotuvQosh: [Function (anonymous)] }
undefined

hisobot yo'qoldi — xatosiz! Sabab Havola semantikasi darsida: exports = {...} faqat mahalliy o'zgaruvchini boshqa obyektga qaratdi. require esa doim module.exports ni qaytaradi — u hamon eski obyekt. Sxema:

flowchart LR
  ME["module.exports"] --> O1["{ sotuvQosh }"]
  E1["exports — oldin"] -.-> O1
  E2["exports — keyin"] --> O2["{ hisobot }"]
  R["require natijasi"] --> O1

Qoida: butun qiymatni almashtirish — faqat module.exports = .... exports.nom = ... — faqat xususiyat qo'shish uchun. Chalkashmaslik uchun ko'p jamoalar exports ni umuman ishlatmaydi.

2.4 Nusxa, jonli emas

Modul semantikasi darsidagi hisoblagich CommonJS'da:

js
// hisob.cjs
let soni = 0;

function oshir() {
  soni++;
}

module.exports = { soni, oshir };
js
// hisob-sinov.cjs
const hisob = require("./hisob.cjs");

hisob.oshir();
hisob.oshir();
console.log(hisob.soni);
text
0

ESM'da 2 bo'lardi. Bu yerda { soni, oshir } obyekt yaratilgan paytda soni ning qiymati — 0 — obyektga nusxalandi. Keyin oshir() modul ichidagi o'zgaruvchini o'zgartiradi, obyektdagi nusxani emas. «Nega bu kerak?» dagi qog'oz nusxa. CommonJS'da jonli qiymat kerak bo'lsa, funksiya (olish()) yoki getter eksport qilinadi.

2.5 Dinamik va sinxron

require — oddiy funksiya. Shuning uchun u ESM'ning statik qoidalaridan ozod:

  • Istalgan joyda — if ichida, funksiya ichida: if (sinov) { const x = require("./x.cjs"); }.
  • Hisoblangan manzil bilan — require("./tillar/" + til + ".cjs").
  • Sinxron — fayl diskdan o'qilib, bajarilib bo'lguncha keyingi qator kutadi. Disk uchun bu tez; tarmoq uchun esa imkonsiz. Brauzer CommonJS'ni to'g'ridan-to'g'ri ishlata olmasligining sababi shu.

Bu erkinlikning narxi — Modul semantikasi darsida ko'rdik: bundler kod ishlamasdan "nima kerak" deya olmaydi va tree-shaking ishlamaydi.

Kengaytma. require("./menyu") — Node o'zi .js, .json va .node (C++ da yozilgan qo'shimcha — kam uchraydi) kengaytmalarini qo'shib qidiradi, shuning uchun eski kodda kengaytmasiz yozuv ko'p. Lekin .cjs ni o'zi qo'shmaydi: papkada faqat menyu.cjs bo'lsa, require("./menyu") — Error: Cannot find module './menyu'. ESM'da esa kengaytma doim majburiy (ESM import).

2.6 Kesh va qayta yuklash

require.cache — oddiy obyekt, uni o'zgartirish mumkin. Kalitni o'chirsangiz, keyingi require faylni qayta bajaradi:

js
const m = require("./menyu.cjs");
m.narxlar.osh = 1;
console.log(require("./menyu.cjs").narxlar.osh);
delete require.cache[require.resolve("./menyu.cjs")];
console.log(require("./menyu.cjs").narxlar.osh);
text
menyu.cjs yuklandi
1
menyu.cjs yuklandi
35000

Ikkinchi require keshdan oldi — o'zgargan 1. Kesh tozalangach, fayl qayta bajarildi va narx yana 35000. require.resolve faylning to'liq yo'lini qaytaradi — kesh kaliti shu. ESM'da bunday qayta yuklash yo'q.

Tekshirib ko'ring: module.exports.narx = 35000; exports.narx = 40000; — require qilgan fayl narx ni qanday ko'radi?

Javob

40000. Ikkala qator ham bitta obyektning xususiyatini yozdi: exports hali module.exports bilan bir obyektga qaraydi. Muammo faqat exports = {...} — butun obyektni almashtirganda chiqadi.

3. Node faylni qaysi tizimda ishlatadi

3.1 Kengaytma va "type"

Bir xil .js fayl bir loyihada ESM, boshqasida CommonJS bo'lishi mumkin. Node buni shu tartibda hal qiladi:

flowchart TD
  F["Fayl"] --> X{"Kengaytma?"}
  X -- ".mjs" --> E["ESM"]
  X -- ".cjs" --> C["CommonJS"]
  X -- ".js" --> P{"Eng yaqin package.json<br/>da type?"}
  P -- "module" --> E
  P -- "commonjs" --> C
  P -- "yo'q" --> D{"import/export<br/>sintaksisi bormi?"}
  D -- "ha" --> E
  D -- "yo'q" --> C

package.json — loyiha sozlamalari fayli: nomi, bog'liqliklari, buyruqlari. Uni npm bilan frontend asboblari qismida to'liq o'rganamiz. Hozir undan bitta maydon kerak:

json
{
  "type": "module"
}

Node .js faylning papkasidan boshlab yuqoriga qarab eng yaqin package.json ni qidiradi. Unda "type": "module" bo'lsa — papkadagi hamma .js ESM. "commonjs" bo'lsa yoki maydon umuman bo'lmasa — CommonJS. .mjs va .cjs esa "type" dan qat'i nazar o'z tizimida ishlaydi.

3.2 Avtomatik aniqlash — va uning ogohlantirishi

Diagrammaning pastki qismi — Node'ning yangi xulqi. "type" aytilmagan .js faylda import yoki export bo'lsa, Node uni avval CommonJS deb o'qishga urinadi, bo'lmasa — ESM deb qayta o'qiydi. Bu sintaksis bo'yicha aniqlash Node 22.7 dan sukut bo'yicha yoqilgan. Nega modullar darsida node --check assets/js/vazifa.js aynan shu tufayli ishladi.

Atrofda hech qanday package.json bo'lmasa, Node buni jimgina qiladi. Lekin package.json bor-u, "type" yo'q bo'lsa — ogohlantiradi:

text
(node:10276) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///C:/Users/Aziz/bahor/menyu.js is not specified and it doesn't parse as CommonJS.
Reparsing as ES module because module syntax was detected. This incurs a performance overhead.
To eliminate this warning, add "type": "module" to C:\Users\Aziz\bahor\package.json.

Tarjimasi: "menyu.js ning modul turi ko'rsatilmagan va u CommonJS sifatida o'qilmadi. Modul sintaksisi topilgani uchun ES modul sifatida qayta o'qilmoqda — bu qo'shimcha vaqt oladi. Ogohlantirishni yo'qotish uchun package.json ga "type": "module" qo'shing". Node o'zi yechimni aytyapti. Qoida: ESM loyihada "type": "module" ni aniq yozing — taxminga tayanmang.

3.3 ESM loyihada require yo'q

"type": "module" li loyihadagi .js faylda eski odat bilan require yozilsa:

text
ReferenceError: require is not defined in ES module scope, you can use import instead
This file is being treated as an ES module because it has a '.js' file extension and 'C:\Users\Aziz\bahor\package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.

"ES modul muhitida require aniqlanmagan, uning o'rniga import ishlating. Bu fayl ES modul deb hisoblanmoqda, chunki u .js va package.json da "type": "module" bor. CommonJS skript sifatida ishlatish uchun uni .cjs ga qayta nomlang". Ikki yo'l: kodni import ga o'tkazish yoki faylni .cjs qilish.

Teskari holat — .cjs faylda export:

text
SyntaxError: Unexpected token 'export'

.cjs doim CommonJS — u export ni tanimaydi.

Tekshirib ko'ring: Loyihada "type": "module". eslint.config.js ESM'da, eski postcss.config.js esa module.exports = {...}. Nima qilish kerak?

Javob

Eng oson yo'l — postcss.config.js ni postcss.config.cjs ga qayta nomlash. Ikkinchi yo'l — uni export default {...} ga o'tkazish. Sababi: "type": "module" papkadagi hamma .js ni ESM qiladi, module.exports esa ESM'da yo'q. Kengaytma .cjs bitta faylni bu qoidadan chiqaradi. PostCSS darsida biz to'g'ridan-to'g'ri postcss.config.mjs + export default yozgan edik — bu tuzoqqa tushmaslik uchun.

4. ESM'dan CommonJS'ni import qilish

4.1 Default — doim ishlaydi

ESM fayl CommonJS faylni oddiy import bilan oladi. module.exports — default eksport bo'lib keladi:

js
// asosiy.mjs
import menyu from "./menyu.cjs";
import { narxniTop } from "./menyu.cjs";

console.log(menyu.narxlar.manti, narxniTop("osh"));
text
menyu.cjs yuklandi
30000 35000

Nomli import ({ narxniTop }) ham ishladi. Qanday qilib? Node CommonJS faylning matnini ishga tushirishdan oldin o'qib chiqadi va module.exports = { narxlar, narxniTop } yoki exports.nom = ... kabi oddiy qatorlardan nomlarni topadi. Tekshirib ko'ramiz: import * as m from "./menyu.cjs" qilib, console.log(Object.keys(m)) ni chiqarsak:

text
[ 'default', 'module.exports', 'narxlar', 'narxniTop' ]

default va 'module.exports' — ikkalasi ham butun module.exports obyekti; qolganlari — topilgan nomlar.

4.2 Nom topilmasa

Agar module.exports murakkabroq yo'l bilan yasalsa, Node nomlarni oldindan ko'ra olmaydi:

js
// dinamik.cjs
function yasash() {
  return { osh: 35000 };
}
module.exports = yasash();
js
import { osh } from "./dinamik.cjs";
text
SyntaxError: Named export 'osh' not found. The requested module './dinamik.cjs' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from './dinamik.cjs';
const { osh } = pkg;

"osh nomli eksporti topilmadi. So'ralgan modul — CommonJS, u module.exports ning hamma qismini nomli eksport sifatida bermasligi mumkin. CommonJS modulni doim default orqali olish mumkin" — va Node tayyor kodni ham yozib berdi. Qoida: CommonJS paketdan nomli import xato bersa — default bilan olib, destructuring qiling.

5. CommonJS'dan ESM'ni require qilish

5.1 require(esm) — Node 24 da ishlaydi

Uzoq vaqt bu taqiqlangan edi: CommonJS fayl ESM'ni require qila olmasdi, faqat dinamik import() bilan (u Promise qaytaradi — 11-qismda). Endi — mumkin. require(esm) Node 22.12 dan sukut bo'yicha yoqilgan. Biz Node 24.21 va 22.17 da sinadik — ikkalasida ham ogohlantirishsiz ishladi:

js
// vazifa.mjs
export class Vazifa {
  constructor(matn) {
    this.matn = matn;
  }
}
export default function salom() {
  return "salom";
}
js
// sinov.cjs
const modul = require("./vazifa.mjs");
console.log(modul);
console.log(new modul.Vazifa("Non olish"));
console.log(modul.default());
text
[Module: null prototype] {
  Vazifa: [class Vazifa],
  __esModule: true,
  default: [Function: salom]
}
Vazifa { matn: 'Non olish' }
salom

require ESM'ning modul obyektini qaytaradi (ESM import dagi import * as). Default — .default ichida. __esModule: true — eski asboblar bilan kelishuv belgisi: "bu obyekt ESM'dan keldi, default'ni .default dan ol". Bunday asboblarga Babel (yangi JS'ni eski brauzerlar uchun eski JS'ga o'giradigan dastur) va TypeScript kiradi — ikkalasini keyingi qismlarda ko'ramiz, hozir bilish shart emas.

5.2 Cheklov: top-level await

ESM'da await ni funksiyadan tashqarida, eng yuqori darajada yozish mumkin (top-level await — 11-qismda). Bunday modul asinxron — u bajarilishi uchun kutishi kerak. require esa sinxron, kuta olmaydi:

text
Error [ERR_REQUIRE_ASYNC_MODULE]: require() cannot be used on an ESM graph with top-level await. Use import() instead. To see where the top-level await comes from, use --experimental-print-required-tla.

"require() ni top-level await bor ESM grafigida ishlatib bo'lmaydi. Uning o'rniga import() ishlating". Grafikning istalgan joyidagi bitta top-level await butun grafikni require uchun yopiq qiladi.

Diqqat: Eski Node versiyalarida (22.12 dan oldin, 20.19 dan oldin) require(esm) ERR_REQUIRE_ESM xatosini berardi. Internetdagi eski javoblar "ESM paketni CommonJS'dan ishlatib bo'lmaydi" deydi — bu endi to'g'ri emas, lekin loyihangiz qaysi Node versiyasida ishlashini bilib qo'ying: node -v.

5.3 ESM'da CommonJS nomlari

ESM fayl ichida __dirname, require yo'q (Modul semantikasi):

text
ReferenceError: __dirname is not defined in ES module scope

O'rnini bosuvchilar:

CommonJS ESM
__dirname import.meta.dirname (Node 20.11+)
__filename import.meta.filename
require("./x.cjs") import x from "./x.cjs"
require (zarur bo'lsa) createRequire(import.meta.url)

Oxirgi qator — kam uchraydigan holat uchun: ESM ichida baribir require kerak bo'lsa (masalan, eski kod require.cache ga tayansa):

js
import { createRequire } from "node:module";

const require = createRequire(import.meta.url);
const eski = require("./menyu.cjs");
console.log(eski.narxniTop("manti"));
text
menyu.cjs yuklandi
30000

createRequire — shu modulga nisbatan ishlaydigan haqiqiy require yasaydi.

Tekshirib ko'ring: hisobot.cjs ichida require("./kassa.mjs") bor. kassa.mjs ning o'zida top-level await yo'q, lekin u import qiladigan kurs.mjs da await eng yuqori darajada yozilgan. node hisobot.cjs nima beradi?

Javob

ERR_REQUIRE_ASYNC_MODULE xatosi. require bitta faylni emas, butun grafikni sinxron yuklashi kerak. Grafikning istalgan joyidagi top-level await uni asinxron qiladi — require kuta olmaydi. Yechim: hisobot ni ESM'ga o'tkazish yoki import() (11-qismda).

6. Ikki tizim yonma-yon

CommonJS ESM
Yozuv require / module.exports import / export
Qachon yuklanadi kod ishlayotganda, sinxron oldindan, statik
Qiymat nusxa (obyekt havolasi) jonli bog'lanish
Yuqori this module.exports undefined
Rejim (Strict mode) oddiy qat'iy
Kengaytma ixtiyoriy majburiy
Qayerda faqat Node Node va brauzer

Yangi kod — ESM. CommonJS'ni o'qiy olish shart, yozish esa faqat eski loyihada yoki faqat CommonJS'ni tushunadigan asbob sozlamasida. Ikkala tizim uchun ham kod chiqaradigan paketlar ("dual package") bor — ularni frontend asboblari qismida ko'ramiz.

7. Ko'p uchraydigan xatolar

7.1 require is not defined in ES module scope

ESM faylda require. Tuzatish: import ga o'ting yoki faylni .cjs qiling.

7.2 exports = {...}

Xato yo'q, eksport — bo'sh. Tuzatish: module.exports = {...}.

7.3 Named export '...' not found

CommonJS paketdan nomli import. Tuzatish: import pkg from "..."; const { nom } = pkg; — Node xabarning o'zida yozib bergan kod.

7.4 .js faylda export, lekin xato

Unexpected token 'export' va ogohlantirish Failed to load the ES module ... Make sure to set "type": "module" in the nearest package.json file or use the .mjs extension. — fayl .cjs yoki "type": "commonjs" muhitida. Tuzatish: kengaytma yoki "type".

7.5 ERR_REQUIRE_ASYNC_MODULE

require qilingan ESM grafigida top-level await. Tuzatish: chaqiruvchini ESM'ga o'tkazing yoki import() ishlating (11-qism).

8. Mashqlar

1-mashq (oson): Qaysi tizim?

Har fayl uchun ayting: ESM yoki CommonJS?

  1. kassa.mjs, package.json da "type": "commonjs".
  2. kassa.js, package.json da "type": "module".
  3. kassa.cjs, package.json da "type": "module".
  4. kassa.js, package.json bor, "type" yo'q, faylda require va module.exports.
  5. kassa.js, hech qayerda package.json yo'q, faylda export.
Yechim
  1. ESM — .mjs har doim ESM, "type" ga qaramaydi.
  2. ESM — .js + "type": "module".
  3. CommonJS — .cjs har doim CommonJS.
  4. CommonJS — "type" yo'q, kod CommonJS sifatida o'qiladi.
  5. ESM — sintaksis bo'yicha aniqlash; package.json yo'q, demak ogohlantirishsiz.

2-mashq (o'rta): CommonJS'dan ESM'ga

Eski «Bahor» faylini ESM'ga o'tkazing (yetkazish.mjs) va uni ishlatadigan asosiy.mjs yozing. Xulq o'zgarmasin.

js
// yetkazish.cjs
const YAQIN_MASOFA = 5;

function yetkazishNarxi(km) {
  if (km <= YAQIN_MASOFA) {
    return 10000;
  }
  return km * 2000;
}

module.exports = { yetkazishNarxi, YAQIN_MASOFA };
js
// asosiy.cjs
const { yetkazishNarxi } = require("./yetkazish.cjs");
console.log(yetkazishNarxi(3), yetkazishNarxi(8));
Yechim
js
// yetkazish.mjs
export const YAQIN_MASOFA = 5;

export function yetkazishNarxi(km) {
  if (km <= YAQIN_MASOFA) {
    return 10000;
  }
  return km * 2000;
}
js
// asosiy.mjs
import { yetkazishNarxi } from "./yetkazish.mjs";
console.log(yetkazishNarxi(3), yetkazishNarxi(8));
text
10000 16000

Uch o'zgarish: module.exports = {...} o'rniga e'lonlar oldida export; require o'rniga import; kengaytma .mjs. Endi xato nomni Node ishga tushirishdan oldin topadi. YAQIN_MASOFA ham eksport qilindi — CommonJS versiyada ham u tashqariga chiqqan edi.

3-mashq (qiyin): Aralash loyiha

Papkada "type": "module" li package.json bor. Ikki fayl kerak:

  • eski-hisobot.cjs — CommonJS: module.exports = function hisobot(tushum) { ... } — "Tushum: N so'm" qaytaradi.
  • asosiy.js — ESM: eski-hisobot.cjs ni import qilib, hisobot(40000) ni chiqarsin; o'z papkasining nomini ham chiqarsin (CommonJS'dagi __dirname o'rniga).

So'ng teskari yo'lni ham sinang: sinov.cjs da asosiy.js ni require qilib ko'ring. Nima bo'ladi va nega?

Yechim
js
// eski-hisobot.cjs
module.exports = function hisobot(tushum) {
  return `Tushum: ${tushum} so'm`;
};
js
// asosiy.js
import hisobot from "./eski-hisobot.cjs";
import path from "node:path";

console.log(hisobot(40000));
console.log(path.basename(import.meta.dirname));

node asosiy.js (papka nomi bahor bo'lsa):

text
Tushum: 40000 so'm
bahor
  • module.exports funksiya — u to'g'ridan-to'g'ri default bo'ldi.
  • import.meta.dirname — __dirname ning ESM'dagi o'rni.
  • .cjs kengaytmasi tufayli module.exports "type": "module" papkasida ham ishladi.

sinov.cjs da require("./asosiy.js") — Node 24 da ishlaydi: asosiy.js bajariladi (ikki qator chiqadi) va require uning modul obyektini qaytaradi. asosiy.js da top-level await yo'q, shuning uchun require(esm) cheklovi tegmaydi. Agar u yerda await bo'lsa — ERR_REQUIRE_ASYNC_MODULE.

4-mashq: Vazifalar qadami — package.json, Node tekshiruvi va main ga

refactor/modullar ning oxirgi qadami.

  1. Repo ildiziga package.json qo'shing: nomi vazifalar, "private": true va "type": "module".
  2. tekshiruv/royxat.js faylida VazifalarRoyxati ni brauzersiz — Node'da tekshiring: qolgan vazifalar soni (qoldi), yangi id, takror matn, belgilash, filtr, o'chirishdan keyingi id.
  3. render.js ni Node'da import qilib ko'ring. Nima bo'ladi?
  4. Branch'ni main ga qo'shing, GitHub'ga yuboring va Pages'da tekshiring.

Ishora: tekshiruv uchun kichik tekshir(nomi, haqiqiy, kutilgan) funksiyasini yozing — u === bilan solishtirib, yoki chiqarsin. Haqiqiy test kutubxonalari (Birinchi avtomatik test) 12-qismda — hozir qo'lda.

Yechim

1. package.json:

json
{
  "name": "vazifalar",
  "private": true,
  "type": "module"
}

"private": true — "bu paket npm'ga chiqarilmaydi" (tasodifiy nashrdan himoya). "type": "module" — papkadagi hamma .js ES modul: Node ham, kodni o'qigan odam ham buni taxmin qilmaydi. Brauzerga ta'siri yo'q — u type="module" atributiga qaraydi.

2. tekshiruv/royxat.js:

js
// tekshiruv/royxat.js — VazifalarRoyxati ni brauzersiz tekshirish
// Ishga tushirish: node tekshiruv/royxat.js
import { VazifalarRoyxati } from "../assets/js/royxat.js";

function tekshir(nomi, haqiqiy, kutilgan) {
  const belgi = haqiqiy === kutilgan ? "✅" : "❌";
  console.log(`${belgi} ${nomi}: ${haqiqiy}`);
}

const royxat = VazifalarRoyxati.dan([
  { id: 1, matn: "Non olish", bajarildi: false },
  { id: 2, matn: "DOM darsini takrorlash", bajarildi: true },
]);

tekshir("boshida qoldi", royxat.qoldi, 1);
tekshir("yangi id", royxat.qosh("Choy damlash").id, 3);
tekshir("qo'shgandan keyin qoldi", royxat.qoldi, 2);
tekshir("takror matn", royxat.matnBormi("non olish", null), true);
royxat.almashtir(1);
tekshir("belgilagandan keyin qoldi", royxat.qoldi, 1);
const bajarilganlar = royxat.korinadiganlar("bajarilgan");
tekshir("bajarilganlar", bajarilganlar.length, 2);
royxat.ochir(3);
tekshir("o'chirgandan keyin id", royxat.qosh("Kitob o'qish").id, 4);
bash
node tekshiruv/royxat.js
text
✅ boshida qoldi: 1
✅ yangi id: 3
✅ qo'shgandan keyin qoldi: 2
✅ takror matn: true
✅ belgilagandan keyin qoldi: 1
✅ bajarilganlar: 2
✅ o'chirgandan keyin id: 4

Manzil ../assets/js/royxat.js — tekshiruv faylidan bir pog'ona yuqoriga. royxat.js o'zi vazifa.js ni import qiladi — Node grafikni o'zi yig'di. Oxirgi qator muhim: o'chirilgan 3 qayta berilmadi, closure'dagi hisoblagich ishlayapti (Closure amaliyotda). qoldi — getter, u statistika() ga tayanadi: hisob bitta joyda.

3. render.js Node'da:

text
export const forma = document.getElementById("yangi-forma");
                     ^
ReferenceError: document is not defined

Kutilgan natija: render.js — brauzer qatlami, u DOM'siz yashay olmaydi. vazifa.js, royxat.js, paket.js va holat.js esa DOM'ga tegmaydi — ular Node'da ham ishlaydi. Modullarga bo'lishning yashirin sovg'asi shu: mantiq ekrandan ajraldi va uni alohida tekshirish mumkin. 12-qismdagi avtomatik testlar aynan shu ajratishga tayanadi.

bash
git add package.json tekshiruv/royxat.js
git commit -m "Vazifalar: package.json va ro'yxat tekshiruvini qo'sh"

4. main ga va Pages'ga:

bash
git switch main
git merge refactor/modullar
git branch -d refactor/modullar
git push

Pages bir-ikki daqiqada yangilanadi (Yakuniy loyiha). https://login.github.io/vazifalar/ ni oching va DevTools bilan tekshiring:

  • Console — toza, qizil xato yo'q.
  • Network (JS filtri, "Disable cache") — oltita .js fayl, hammasi 200, modulepreload tufayli deyarli bir vaqtda.
  • Ilova — uch vazifa, "2 ta vazifa qoldi, 1 ta bajarildi"; belgilash, filtr, qo'shish, tahrirlash, o'chirish, teglar, eksport va import — avvalgidek.
  • Telefonda ham oching.

Biror modul 404 bersa — Brauzerda ESM darsidagi ikki tuzoqni tekshiring: / bilan boshlangan manzil va katta-kichik harf. Lokal Windows'da ishlagan, lekin Pages'da buzilgan modul ko'pincha shulardan biri.

Besh darsdagi ish:

Dars refactor/modullar da
Nega modullar reja, vazifa.js
ESM import qolgan beshta modul, type="module", "use strict" olib tashlandi
Modul semantikasi README — modullar xaritasi
Brauzerda ESM modulepreload
shu dars package.json, Node tekshiruvi, merge

Foydalanuvchi hech qanday farq ko'rmaydi. Ichkarida esa: global'da nol nom, oltita mustaqil fayl va brauzersiz tekshiriladigan mantiq. Bu tuzilma Yakuniy loyiha: vazifalar v2 ning asosi bo'ladi.

9. Real ishda

  • Eski kod bazalari. Ko'p kompaniyalarning Node serverlari hamon CommonJS'da. Ularni ESM'ga bosqichma-bosqich ko'chirishda "type", .cjs, createRequire va require(esm) ishlatiladi.
  • Sozlama fayllari. eslint.config.js, vite.config.js, postcss.config.mjs, jest.config.cjs — fayl nomidagi kengaytma qaysi tizimni kutayotganini aytadi. Asbob sozlamasi "ishlamayapti" desa, birinchi tekshiruv — "type" va kengaytma mosligi.
  • npm paketlari. Hujjatda "ESM-only" ("faqat ESM") yozilgan kutubxona eski Node'da require bilan ishlamasdi; Node 22.12+ da esa ishlaydi (top-level await bo'lmasa).
  • Intervyu. "CommonJS va ESM farqi?", "module.exports va exports farqi?", "ESM'da __dirname qanday olinadi?" — Node intervyularining doimiy savollari. Bugungi «Ikki tizim yonma-yon» jadvali — tayyor javob.

Xulosa

  • CommonJS: require() faylni sinxron yuklab, module.exports ni qaytaradi; har fayl funksiyaga o'ralgan — shuning uchun __dirname, require va this === module.exports.
  • exports.nom = ... — mumkin; exports = {...} — jim xato. Butun qiymat — faqat module.exports.
  • require keshlaydi (bir marta bajariladi), qiymatlarni nusxalaydi (jonli emas), istalgan joyda chaqiriladi — shuning uchun tree-shaking qiyin.
  • Node: .mjs — ESM, .cjs — CommonJS, .js — eng yaqin package.json dagi "type"; yo'q bo'lsa — sintaksis bo'yicha (ogohlantirish bilan). ESM loyihada "type": "module" ni aniq yozing.
  • ESM CommonJS'ni import qiladi (module.exports — default); Node 24 da CommonJS ESM'ni require qiladi — top-level await siz grafiklar uchun.
  • Yangi kod — ESM; CommonJS — o'qish va eski loyihalar uchun.

Keyingi dars: Yakuniy loyiha: vazifalar v2 — 10-qismning hamma bilimini — HOF, closure, klasslar, xatolar va modullarni — bitta toza arxitekturaga yig'amiz.

Manbalar

  • Node.js 24 hujjatlari: "Modules: CommonJS modules" (require(esm), "The module wrapper", "exports shortcut"), "Modules: Packages — Determining module system", "Modules: ECMAScript modules — Interoperability with CommonJS" — nodejs.org/api
  • MDN: "JavaScript modules" — developer.mozilla.org
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Node.js'da CommonJS va ESM: require, module.exports va o'zaro ishlash — IlmHamroh