IlmHamroh
JavaScript Full-stack/24-qism. Node.js12/55-dars18 daqiqa
Mundarija (28)

CJS va ESM birga: require(esm), interop va dual package hazard

Qisqacha: Node 24 da ikki modul tizimi ikki tomonga "gaplasha" oladi. ESM → CJS: import CommonJS faylni yuklaydi; module.exports doim default bo'ladi, nomli eksportlar esa faqat Node ularni statik taniy olsa (exports.x = …, module.exports = { x }) — module.exports = { x: 3 } dagi x tanilmadi (o'lchandi). CJS → ESM: require(esm) bayroqsiz ishlaydi (Node 20.19+ / 22.12+) va modul nomlar maydonini qaytaradi — lekin ESM ichida top-level await bo'lsa, ERR_REQUIRE_ASYNC_MODULE. ESM ichida require kerak bo'lsa — createRequire(import.meta.url). Paket ikki formatni alohida fayllarda bersa — dual package hazard: bitta klassning ikki nusxasi. Shu darsda valyuta paket tuzilmasini oladi va CommonJS loyihalar uni require qila oladi.

Bu darsda

  • ESM'dan CommonJS import qilganda qaysi nomli eksportlar ko'rinishini oldindan ayta olasiz.
  • require(esm) qachon ishlashini va qachon ERR_REQUIRE_ASYNC_MODULE berishini bilasiz.
  • createRequire bilan ESM ichida require, JSON va CommonJS-faqat paketlarni ishlatasiz.
  • Dual package hazard'ni Node'da o'lchab ko'rsatasiz va undan qochish yo'llarini tanlaysiz.
  • valyuta ni npm paket tuzilmasiga keltirasiz: bin, exports, files, prepack, kutubxona va CLI ajratilgan.

Oldin bilishingiz kerak: ES Modules Node'da, CommonJS ichidan, CommonJS va ESM bilan o'zaro ishlash, Modul formatlari: IIFE, UMD, CJS, ESM.

1. Nega bu kerak?

npm'da millionlab paket bor va ularning katta qismi hali ham CommonJS. Yangilari esa ko'pincha faqat ESM. Sizning loyihangiz qaysi tizimda bo'lmasin, ertami-kechmi ikkinchisidan paket kerak bo'ladi. 2024 yil oxirigacha (Node 22.12 gacha) bu og'riqli edi: CommonJS loyihada ESM paketni require qilsangiz — ERR_REQUIRE_ESM xatosi, yagona yo'l — asinxron import().

CommonJS va ESM bilan o'zaro ishlash darsida asoslarni ko'rdingiz. require(esm) Node 24 da ishlaydi, top-level await esa uni to'xtatadi. Modul formatlari darsida esa bundler nuqtai nazaridan default export tuzog'i va dual package hazard'ni o'rgandingiz. Bu dars — Node ichidagi mexanizm va uni o'z paketingizga qo'llash.

valyuta uchun savol aniq: u ESM paket, ichida top-level await bor (bin.ts). CommonJS'da yozilgan boshqa loyiha require("valyuta") qila oladimi? Kanon buni tekshirdi — javob "ha", lekin faqat bitta to'g'ri qaror tufayli.

O'xshatish: ikki tilli ofis. Bir xodim faqat o'zbekcha, boshqasi faqat ruscha yozadi. Tarjimon (Node interop) ko'p hujjatni o'giradi. Lekin ba'zi hujjatlarda "keyinroq to'ldiriladi" degan bo'sh joy bor (top-level await) — tarjimon ularni kutib o'tira olmaydi va qaytaradi.

Diagrammada ikki yo'nalishni va har birining chegarasini ko'ring — darsning qolgan qismi shu to'rt o'qni ochadi:

flowchart LR
  E["ESM fayl"] -->|"import: default = module.exports,<br/>nomlilar — lexer tanisa"| C["CommonJS fayl"]
  C -->|"require(esm): nomlar maydoni,<br/>TLA bo'lsa — xato"| E
  E -.->|"createRequire"| C
  C -.->|"await import()"| E

2. ESM'dan CommonJS: nomli eksportlar qayerdan keladi

2.1 Muammo

ESM statik: import { a } from "./x.js" ni bog'lash uchun Node x.js da a borligini bajarishdan oldin bilishi kerak (ES Modules Node'da darsidagi tartib). CommonJS esa dinamik: module.exports faqat fayl bajarilgandan keyin ma'lum. Bu ziddiyatni Node cjs-module-lexer bilan yechadi — CommonJS faylning matnini "tez ko'zdan kechirib", eksport nomlarini taxmin qiladigan kichik tahlilchi.

2.2 Kanon topilmasi va kengaytirilgan tajriba

Kanon tajribasida module.exports = { c: 3 }; module.exports.d = 4; faylini import() qilganda natija:

text
import(cjs): [Module: null prototype] {
  d: 4,
  default: { c: 3, d: 4 },
  'module.exports': { c: 3, d: 4 }
}

d nomli eksport bo'ldi, c — yo'q! Uch xil yozuvni solishtiramiz:

js
import { writeFileSync } from "node:fs";

writeFileSync("lit.cjs", "module.exports = { c: 3 };");
writeFileSync("short.cjs",
  "const c = 3; const d = 4; module.exports = { c, d };");
writeFileSync("dot.cjs", "exports.c = 3; exports.d = 4;");

for (const name of ["lit", "short", "dot"]) {
  const mod = await import(`./${name}.cjs`);
  console.log(name, Object.keys(mod).join(","));
}

Konsolda:

text
lit default,module.exports
short c,d,default,module.exports
dot c,d,default,module.exports
Yozuv Lexer taniydimi
exports.c = 3 / module.exports.c = 3 ha
module.exports = { c, d } (qisqa yozuv) ha
module.exports = { c: 3 } (qiymat — literal) yo'q

Lexer kodni bajarmaydi, faqat naqshlarni qidiradi. Obyekt literalida identifikator (c yoki c: c) bo'lsa — taniydi, qiymat murakkab bo'lsa — to'xtaydi. Natija: import { c } from "./lit.cjs" — xato, import lit from "./lit.cjs"; lit.c — ishlaydi.

Ikkita doimiy qoida:

  • default — doim module.exports ning o'zi. Shuning uchun CommonJS paketdan import pkg from "pkg" hech qachon yolg'on gapirmaydi.
  • 'module.exports' nomli eksport — Node 24 ning yangiligi: default bilan bir xil qiymat, lekin "bu CommonJS'ning module.exports i" degan aniq nom bilan.

Maslahat: CommonJS paketdan nomli import xato bersa (Named export 'c' not found. The requested module './lit.cjs' is a CommonJS module, which may not support all module.exports as named exports.), tuzatish oddiy: import pkg from "pkg"; const { c } = pkg; — xabarning o'zi shuni taklif qiladi (CommonJS va ESM bilan o'zaro ishlash darsidagi xato).

3. CommonJS'dan ESM: require(esm)

3.1 Qanday ishlaydi

Node 22.12 dan (20.19 ga ham qaytarilgan) va Node 24 da bayroqsiz: require("./x.mjs") ESM faylni sinxron yuklaydi va modul nomlar maydonini qaytaradi. Buni tekshirish uchun xususiyat: process.features.require_module. Kanon (Node 24, bayroqsiz):

text
require(esm): [Module: null prototype] { __esModule: true, a: 1, default: 'sukut' } | __esModule: true
process.features.require_module: true

__esModule: true — eski bundlerlar va TypeScript kelishuvi: "bu CommonJS ko'rinishidagi ESM, default ni alohida ko'r". Node uni ESM'da default eksport bo'lsa avtomatik qo'shadi — eski CommonJS kodi require(x).default ni to'g'ri tushunishi uchun.

3.2 Faqat default bo'lsa — va "module.exports" eksporti

Faqat default eksportli ESM'ni require qilsangiz, funksiya emas, obyekt olasiz — funksiya uning default xususiyatida. Bu — Modul formatlari darsidagi "default export tuzog'i"ning Node'dagi shakli. Node 24 da ESM muallifi require natijasini o'zi belgilay oladi: "module.exports" nomi bilan eksport:

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

writeFileSync("only.mjs",
  'export default function salom() { return "salom"; }');
writeFileSync("me.mjs",
  'const api = { v: 1 };\nexport { api as "module.exports" };\n');

const require = createRequire(import.meta.url);
const only = require("./only.mjs");
console.log(typeof only, typeof only.default, only.__esModule);
console.log(require("./me.mjs"));

Konsolda:

text
object function true
{ v: 1 }

createRequire haqida — pastda; hozir u shunchaki ESM ichida require beradi. Birinchi qator: require funksiyani emas, obyektni berdi. Ikkinchisi: "module.exports" eksporti bo'lsa, require aynan uni qaytaradi — CommonJS foydalanuvchisi require("pkg")() deb to'g'ridan chaqira oladi.

3.3 To'siq: top-level await

require — sinxron. Top-level await li ESM esa yuklanishi uchun vaqt kutishi kerak. Ikkalasini birlashtirib bo'lmaydi:

js
const { writeFileSync } = require("node:fs");
writeFileSync("tla.mjs",
  "await Promise.resolve();\nexport const b = 2;\n");

try {
  require("./tla.mjs");
} catch (e) {
  console.log(e.code);
}

Konsolda:

text
ERR_REQUIRE_ASYNC_MODULE

To'liq xabar (kanon): 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. — "top-level await bor ESM grafiga require() ishlatib bo'lmaydi. import() ishlating. Await qayerdan kelayotganini ko'rish uchun — --experimental-print-required-tla bayrog'i". Graf so'ziga e'tibor bering: await require qilingan faylning o'zida bo'lmasa ham, u import qiladigan istalgan modulda bo'lsa — xato.

Tekshirib ko'ring: index.mjs da TLA yo'q, lekin u config.mjs ni import qiladi, config.mjs da esa const sozlama = await readFile(...). CommonJS loyiha require("./index.mjs") qila oladimi?

Javob

Yo'q — ERR_REQUIRE_ASYNC_MODULE. Tekshiruv butun import grafiga qaraydi. Yechim: config.mjs dagi o'qishni funksiyaga o'rash (export async function sozlamaniOl()), shunda graf sinxron bo'ladi. Yoki CommonJS tarafida await import("./index.mjs").

4. createRequire — ESM ichida require

ESM'da require yo'q (Global obyektlar). Lekin ba'zan kerak: faqat CommonJS'ga mo'ljallangan paket, require.resolve, yoki JSON'ni atributsiz sinxron o'qish. Buning uchun node:module dagi createRequire:

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

writeFileSync("sozlama.json", '{ "til": "uz" }');
const require = createRequire(import.meta.url);

console.log(require("./sozlama.json").til);
console.log(require.resolve("node:fs"));
console.log(typeof require.cache);

Konsolda:

text
uz
node:fs
object

createRequire(import.meta.url) — "joriy faylga nisbatan ishlaydigan require yasab ber". Nisbiy yo'llar shu faylga nisbatan hisoblanadi, kesh — umumiy require.cache. Yangi kodda kam kerak bo'ladi: JSON uchun import attributes, ESM paketlar uchun import bor. Lekin eski kutubxonalar bilan ishlaganda — eng toza ko'prik.

5. Dual package hazard — Node'da o'lchab

5.1 Ikki nusxa

Modul formatlari darsida bundler misolida ko'rgansiz: paket exports da import uchun bitta fayl, require uchun boshqa fayl bersa, bitta dasturda kutubxonaning ikki nusxasi yuklanishi mumkin. Node'ning o'zida tekshiramiz — blok node_modules da kichik ikki formatli paket yaratadi:

js
import { writeFileSync, mkdirSync } from "node:fs";
import { createRequire } from "node:module";

const dir = "node_modules/kurs-lib";
mkdirSync(dir, { recursive: true });
writeFileSync(`${dir}/package.json`, JSON.stringify({
  name: "kurs-lib",
  exports: { import: "./index.mjs", require: "./index.cjs" },
}));
writeFileSync(`${dir}/index.mjs`,
  "export class Kurs {}\nexport const counter = { n: 0 };\n");
writeFileSync(`${dir}/index.cjs`,
  "class Kurs {}\nmodule.exports = { Kurs, counter: { n: 0 } };\n");

const esm = await import("kurs-lib");
const cjs = createRequire(import.meta.url)("kurs-lib");
esm.counter.n++;
console.log(esm.Kurs === cjs.Kurs);
console.log(new esm.Kurs() instanceof cjs.Kurs);
console.log(esm.counter.n, cjs.counter.n);

Konsolda:

text
false
false
1 0

Bitta dasturda kurs-lib ning ikki Kurs klassi va ikki counter i bor. ESM tarafida yaratilgan obyekt CommonJS tarafdagi instanceof dan o'tmaydi, holat esa ikkiga bo'lindi. Real hayotda bu shunday ko'rinadi: kutubxona "agar xato mening ValidationError imda bo'lsa" deb tekshiradi — va tekshiruv sababsiz false beradi.

5.2 require(esm) bilan yechim

Bu xavf aynan "ikki format — ikki fayl" yondashuvidan kelib chiqadi. Node 24 da require(esm) ishlagani uchun eng sodda yechim — faqat ESM nashr qilish: bitta fayl, exports da bitta yo'l. CommonJS foydalanuvchisi require qiladi va o'sha modulni oladi. Shart bitta — ESM grafida top-level await bo'lmasin. Modul formatlari darsidagi ikkinchi yechim aynan shu edi; endi uning Node'dagi asosini — require(esm) va uning chegarasini — bilasiz.

Yondashuv Ikki nusxa xavfi Talab
ESM + CJS alohida fayllar bor hech narsa — eski Node ham
ESM "eshik" → CJS kod yo'q kod CommonJS'da
Faqat ESM yo'q Node 20.19+ / 22.12+, TLA yo'q

5.3 Modul yuklash hooklari haqida

Node'da modul yuklashni o'zgartiradigan hooklar ham bor (masalan, .yaml faylni import qilish yoki testlarda modulni almashtirish). Ikki API bor:

API Qanday Holati (Node hujjati, 2026-10)
module.register() asinxron, alohida oqimda eskirgan (deprecated, DEP0205): Node 24.15 dan hujjatda; Node 26 da ishlatsangiz ogohlantirish chiqadi
module.registerHooks() sinxron, shu oqimda Node 22.15 / 23.5 dan; "release candidate" — barqarorlikka yaqin

Node 24.21 da ikkalasi ham ishlaydi. Biz module.register() ni chaqirib ko'rdik — ogohlantirish chiqmadi, chunki 24-versiyada eskirish faqat hujjatda. Node 26 da u hali bor, lekin har chaqiruvda ogohlantirish beradi. Hujjat uni "kelgusi versiyada olib tashlanadi" deydi, aniq versiyani aytmaydi. Xulosa: yangi kod yozsangiz — faqat registerHooks. Bu chuqur mavzu, kundalik ishda test vositalari uni sizning o'rningizga ishlatadi.

6. CLI qadami: paket tuzilmasi (holat 12, 2-qism)

ES Modules Node'da darsida bin.ts, index.ts va self-reference'ni ko'rdingiz. Endi holat 12 ni yakunlaymiz va commit qilamiz.

6.1 Nega index.ts da TLA yo'q — bu darsning markaziy qarori

valyuta ning bin.ts faylida top-level await bor: process.exitCode = await main(…). Agar kutubxona kirishi ham shu fayl bo'lganida, CommonJS loyiha uni require qila olmasdi — ERR_REQUIRE_ASYNC_MODULE. Holat 12 da exports["."] — dist/index.js, u esa faqat kurs.ts va manba.ts ni qayta eksport qiladi. Ularda TLA yo'q. Biz kanon holat 12 da CommonJS fayldan tekshirdik:

js
// req.cjs — valyuta papkasida (self-reference), build'dan keyin
const v = require("valyuta");
console.log(Object.keys(v).length, typeof v.konvert);
try {
  require("./dist/bin.js");
} catch (e) {
  console.log(e.code);
}
text
9 function
ERR_REQUIRE_ASYNC_MODULE

Kutubxona — 9 eksport, CommonJS'dan bemalol. Kirish fayli (bin.js) esa require qilinmaydi — u buyruq sifatida ishga tushiriladi, kutubxona emas. Kanon hisobotida ham xuddi shu xulosa: "kutubxona kirishi index.ts da TLA yo'q, shuning uchun CJS loyihalar ham require("valyuta") qila oladi — tekshirildi: 9 eksport". Bitta paketda — ikki format, ikki nusxa xavfisiz.

6.2 To'liq package.json (holat 12)

json
{
  "name": "valyuta",
  "version": "0.2.0",
  "description": "O'zbekiston Markaziy banki valyuta kurslari terminalda",
  "keywords": ["valyuta", "kurs", "cbu", "cli"],
  "license": "MIT",
  "type": "module",
  "bin": {
    "valyuta": "dist/bin.js"
  },
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    },
    "./package.json": "./package.json"
  },
  "files": ["dist", "fixtures"],
  "engines": {
    "node": ">=24"
  },
  "scripts": {
    "start": "node src/bin.ts",
    "build": "tsc -p tsconfig.build.json",
    "prepack": "npm run build",
    "test": "node --test \"test/**/*.test.ts\"",
    "tip": "tsc --noEmit",
    "lint": "eslint . --max-warnings=0",
    "check": "npm run tip && npm run lint && npm test"
  },
  "devDependencies": {
    "@eslint/js": "10.0.1",
    "@types/node": "24.19.2",
    "eslint": "10.12.0",
    "typescript": "6.0.3",
    "typescript-eslint": "8.71.2"
  }
}

(Kanon faylida keywords va files massivlari har elementi alohida qatorda — mazmuni bir xil.) Holat 08 dan farqlar:

Maydon Nima Nega
"private" olib tashlandi paket nashr qilinishi mumkin O'z npm paketingiz
version 0.2.0, license, keywords pasport npm sahifasi uchun
bin valyuta buyrug'i → dist/bin.js o'rnatilganda terminal buyrug'i
exports ochiq yuz — faqat index va package.json ichki fayllar yopiq
files paketga faqat dist va fixtures test, src, tsconfig kirmaydi
build, prepack pack/publish oldidan avtomatik build dist doim yangi

"./package.json": "./package.json" — ba'zi vositalar paketning versiyasini o'qish uchun pkg/package.json ni import qiladi; exports bo'lgani uchun uni alohida ochish kerak.

6.3 Tekshiruv va o'lchov

npm run build && npm run check — kanon natijasi (tekshiruv-12.txt): build 2.1 s, tip 0, lint 0, test 10/10. npm pack --dry-run endi:

text
Tarball Contents
31B dist/bin.d.ts
333B dist/bin.js
2.8kB dist/cli.js
159B dist/index.d.ts
279B dist/index.js
2.0kB dist/kurs.js
681B dist/manba.js
25.5kB fixtures/kurslar-2026-10-23.json
978B package.json
package size: 10.1 kB
unpacked size: 40.9 kB
total files: 17

(Qisqartirilgan: kanon ro'yxatida yana .d.ts va .js.map fayllar — jami 17.) Holat 08 da paketda 9 fayl edi, jumladan testlar va tsconfig.json. Endi files tufayli faqat kerakli narsa: build natijasi, tiplar (.d.ts), source map (.map) va fixture.

Sovuq start (mediana, n=10): dist/bin.js --help — 122 ms, kurs USD — 133 ms. Holat 08 da .ts bilan 127 va 171 ms edi. Farq qayerdan — tiplarni o'chirishmi yoki boshqa sababmi — TypeScript'ni Node'da to'g'ridan darsida toza o'lchanadi.

6.4 Commit

bash
git add .
git commit -m "Paket tuzilmasi: tsc build (dist), bin, exports, files" \n  -m "Kutubxona API (index.ts)"

Kanon commit xabari bitta qator: Paket tuzilmasi: tsc build (dist), bin, exports, files; kutubxona API (index.ts). U 72 belgidan uzun, shuning uchun sarlavha va tanaga bo'ldik (commit xabarlari).

.gitignore da dist/ bor — build natijasi repoga tushmaydi, u prepack da qayta yasaladi.

7. Ko'p uchraydigan xatolar

Xato Sabab Davosi
SyntaxError: Named export 'c' not found. The requested module './lit.cjs' is a CommonJS module, which may not support all module.exports as named exports. lexer nomni tanimadi import pkg from …; const { c } = pkg;
ERR_REQUIRE_ASYNC_MODULE ESM grafida top-level await await import() yoki TLA'ni olib tashlash
ERR_REQUIRE_ESM eski Node (20.19 / 22.12 dan oldin) Node'ni yangilang yoki import()
require(esm) funksiya emas, obyekt berdi faqat default eksport .default yoki "module.exports" eksporti
instanceof sababsiz false dual package hazard faqat ESM yoki ESM "eshik"
require is not defined in ES module scope ESM'da require createRequire(import.meta.url)

8. Mashqlar

Mashqlar kurs/mashqlar/24/12-interop/ da.

1-mashq (oson): lexer nimani ko'radi?

ESM'dan import * as m from "./x.cjs" qilinadi. Qaysi holatda m.narx nomli eksport sifatida bor (ha/yo'q)?

  • exports.narx = 35000; —
  • module.exports = { narx: 35000 }; —
  • const narx = 35000; module.exports = { narx }; —
  • Har uchala holatda m.default.narx bormi —
Yechim
  • ha — exports.x = naqshi.
  • yo'q — obyekt literalida qiymat literal (35000), lexer to'xtaydi.
  • ha — qisqa yozuv ({ narx }) identifikator, lexer taniydi.
  • ha — default doim module.exports ning o'zi; u kod bajarilgandan keyin to'liq.

2-mashq (o'rta): require uchun qulay ESM

format.mjs faylini (blok ichida) yarating: u sonniYoz(n) funksiyasini (n.toFixed(2)) "module.exports" nomi bilan eksport qilsin, shunda CommonJS kodi require("./format.mjs")(5) deb to'g'ridan chaqira olsin. ESM tarafida esa import { sonniYoz } ham ishlasin. Ikkalasini sinang: natija 5.00 va 7.50.

Yechim
js
import { writeFileSync } from "node:fs";
import { createRequire } from "node:module";

writeFileSync("format.mjs", `
export function sonniYoz(n) {
  return n.toFixed(2);
}
export { sonniYoz as "module.exports" };
`);

const require = createRequire(import.meta.url);
const fromCjs = require("./format.mjs");
const { sonniYoz } = await import("./format.mjs");
console.log(fromCjs(5));
console.log(sonniYoz(7.5));

Konsolda:

text
5.00
7.50

Bitta funksiya ikki nom bilan eksport qilindi: oddiy nomli (ESM uchun) va "module.exports" (CommonJS require uchun). require endi nomlar maydonini emas, funksiyaning o'zini qaytaradi — eski CommonJS paketlaridagi module.exports = fn uslubi saqlanib qoladi, kod esa ESM'da.

3-mashq (qiyin): TLA'ni kutubxonadan chiqaring

Blok ichida uch fayl yarating. config.mjs: export const til = await Promise.resolve("uz"); (top-level await). lib.mjs: config.mjs dan til ni import qilib, salom() funksiyasini eksport qiladi. main.cjs: require("./lib.mjs") qiladi. Avval main.cjs ni bola jarayonda ishga tushiring va xato kodini ko'ring. Keyin config.mjs ni TLA'siz qilib qayta yozing (export function tilniOl() { return "uz"; }, lib.mjs ham shunga mos) va yana ishga tushiring.

Yechim
js
import { writeFileSync } from "node:fs";
import { spawnSync } from "node:child_process";

const run = () => {
  const r = spawnSync(process.execPath, ["main.cjs"], {
    encoding: "utf8",
  });
  return (r.stdout + r.stderr).match(/ERR_[A-Z_]+|salom: \w+/)?.[0];
};

writeFileSync("main.cjs",
  'const { salom } = require("./lib.mjs");\nconsole.log(salom());\n');

writeFileSync("config.mjs",
  'export const til = await Promise.resolve("uz");\n');
writeFileSync("lib.mjs", 'import { til } from "./config.mjs";\n' +
  "export const salom = () => `salom: ${til}`;\n");
console.log(run());

writeFileSync("config.mjs",
  'export function tilniOl() { return "uz"; }\n');
writeFileSync("lib.mjs", 'import { tilniOl } from "./config.mjs";\n' +
  "export const salom = () => `salom: ${tilniOl()}`;\n");
console.log(run());

Konsolda:

text
ERR_REQUIRE_ASYNC_MODULE
salom: uz

Birinchi urinishda await lib.mjs da emas, uning bog'liqligida — baribir xato: tekshiruv butun grafga qaraydi. TLA funksiyaga o'ralgach, graf sinxron va require ishladi. valyuta dagi qaror (TLA faqat bin.ts da, kutubxona kirishida emas) aynan shu.

4-mashq: CLI qadami — holat 12 ni yakunlang

package.json ni «To'liq package.json» bo'limidagidek yangilang, tsconfig.build.json qo'shing (ES Modules Node'da darsida ko'rsatilgan). Keyin:

  1. npm run build && npm run check — 10/10.
  2. npm pack --dry-run — nechta fayl? test/ va tsconfig.json bormi?
  3. Loyiha papkasida req.cjs yarating (yuqoridagi «Nega index.ts da TLA yo'q» blokidagidek) va node req.cjs — 9 function va ERR_REQUIRE_ASYNC_MODULE chiqdimi?
  4. Commit: Paket tuzilmasi: tsc build (dist), bin, exports, files; kutubxona API (index.ts).
Yechim
  1. prepack hozircha ishlamaydi (pack qilinmadi), lekin npm run build dist/ ni yaratadi — 15 fayl (5 modul × .js, .d.ts, .js.map).
  2. 17 fayl, 10.1 kB: dist/ (15), fixtures/ (1), package.json. Test va tsconfig yo'q — files ishladi.
  3. Ha. req.cjs ni commit qilmang — u tajriba fayli (yoki tekshirgach o'chiring).
  4. git log --oneline — ikki commit.

9. Real ishda

  • ESM-faqat paketlar — chalk 5, execa, nanoid kabi paketlar faqat ESM. Node 20.19+ / 22.12+ dan oldin CommonJS loyihalar ularni faqat import() bilan yuklay olardi; endi require ham ishlaydi (TLA bo'lmasa).
  • Kutubxona muallifi qarori — 2026 yilda yangi kutubxonalar odatda faqat ESM chiqaradi va CommonJS foydalanuvchilarni require(esm) ga qoldiradi. Eski Node'ni qo'llab-quvvatlash kerak bo'lsa — tsup yoki tsdown bilan ikki format (va dual hazard'ni hisobga olish).
  • NestJS va boshqa CommonJS freymvorklar — hali ko'p backend kodi CommonJS'da kompilyatsiya qilinadi. ESM paketni ulashda shu darsdagi qoidalar kerak bo'ladi.
  • Intervyu savoli: "require(esm) qachon ishlamaydi?" — ESM grafida top-level await bo'lsa (ERR_REQUIRE_ASYNC_MODULE) va eski Node versiyalarida.

Xulosa

  • ESM → CJS: default = module.exports; nomli eksportlar — faqat cjs-module-lexer tanigan naqshlar (exports.x, { x }), { x: 3 } — yo'q.
  • CJS → ESM: require(esm) Node 24 da bayroqsiz; natija — nomlar maydoni (__esModule: true), "module.exports" eksporti uni almashtiradi.
  • ESM grafida top-level await — ERR_REQUIRE_ASYNC_MODULE; TLA'ni kirish faylida qoldiring, kutubxonada emas.
  • createRequire(import.meta.url) — ESM ichida require.
  • Ikki formatli paket — ikki nusxa (instanceof → false, o'lchandi); require(esm) davrida yechim — faqat ESM.
  • valyuta: bin (TLA bilan) va exports (TLA'siz index) ajratildi, CommonJS'dan require — 9 eksport; paket 17 fayl, 10.1 kB.

Keyingi dars: Workspaces va monorepo asoslari — bir repoda bir nechta paket: npm workspaces, workspace:* va kanon tajribasi nega valyuta bitta paket bo'lib qolganini ko'rsatadi.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
CJS va ESM birga: require(esm), interop va dual package hazard — IlmHamroh