Mundarija (28)
- Bu darsda
- 1. Nega bu kerak?
- 2. Node fayl turini qanday aniqlaydi
- 2.1 Qoidalar jadvali
- 2.2 "type" yozilmasa: sintaksis bo'yicha aniqlash
- 3. ESM'ning uch qat'iy qoidasi
- 3.1 Uch xato — bitta tajribada
- 3.2 JSON import
- 4. Bajarilish tartibi va kesh
- 4.1 Statik importlar — tanadan oldin
- 4.2 import.meta — Node'dagi to'liq to'plam
- 4.3 ESM keshi va so'rov satri
- 5. Top-level await va dinamik import Node'da
- 6. CLI qadami: valyuta ESM paket sifatida (holat 12, 1-qism)
- 6.1 Loyiha allaqachon ESM
- 6.2 Kirish va kutubxonani ajratish
- 6.3 exports — paket chegarasi
- 6.4 Self-reference: paketni o'z nomi bilan import qilish
- 6.5 Build: .ts importlar .js ga
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): ESM yoki CommonJS?
- 2-mashq (o'rta): uch xatoni tuzating
- 3-mashq (qiyin): ham kutubxona, ham buyruq
- 4-mashq: CLI qadami — bin.ts va index.ts
- 9. Real ishda
- Xulosa
- Manbalar
ES Modules Node'da: "type": "module", kengaytmalar, import.meta va top-level await
Qisqacha: Node 24 da ESM — asosiy modul tizimi. Fayl ESM bo'ladi, agar u
.mjsbo'lsa yoki eng yaqinpackage.jsonda"type": "module"bo'lsa (.cjs— doim CommonJS). ESM qoidalari CommonJS'dan qat'iy: nisbiy importda kengaytma majburiy (./kurs.js,./kursemas), papka importi yo'q, JSON faqatwith { type: "json" }bilan. Modul haqida ma'lumot —import.meta:url,filename,dirname,resolve()va Node 24 damain. Modul darajasidaawaitishlaydi. Statikimportlar modul tanasidan oldin bajariladi — buni o'lchab ko'rsatamiz.
Bu darsda
- Node faylni qachon ESM, qachon CommonJS deb o'qishini aniq aytasiz — va
"type"yozilmasa nima bo'lishini. - ESM'ning uchta tipik xatosini (
ERR_MODULE_NOT_FOUND,ERR_UNSUPPORTED_DIR_IMPORT,ERR_IMPORT_ATTRIBUTE_MISSING) taniysiz va tuzatasiz. import.meta.resolve,import.meta.mainbilan ishlay olasiz.- Statik va dinamik importning bajarilish tartibini bilasiz, ESM keshini qanday "aylanib o'tish" mumkinligini ko'rasiz.
valyutada paketni o'z nomi bilan import qilasiz vaexportsichki fayllarni qanday yopishini ko'rasiz.
Oldin bilishingiz kerak: CommonJS ichidan, Dinamik import() va top-level await, Modul semantikasi, package.json IV: kirish nuqtalari.
1. Nega bu kerak?
CommonJS ichidan darsida require qanchalik "kechirimli" ekanini ko'rdingiz: kengaytmasiz yozsangiz — o'zi topadi, papka bersangiz — index.js ni oladi, JSON'ni ham to'g'ridan yuklaydi. ESM'da bularning hech biri yo'q. Aziz valyuta ga import { konvert } from "./kurs" deb yozdi va birinchi ishga tushirishdayoq ERR_MODULE_NOT_FOUND oldi.
Nega ESM shunchalik qat'iy? Chunki u Node uchun emas, veb uchun yaratilgan standart. Brauzer "kengaytmani o'zi topish" uchun serverga beshta so'rov yubora olmaydi — har bir taxmin tarmoqda vaqt. Shuning uchun ESM'da manzil aniq bo'lishi shart. Node shu standartga amal qiladi, natijada bir xil kod brauzerda ham, Node'da ham ishlaydi.
ESM import va Dinamik import darslarida ESM sintaksisini brauzerda o'rgangansiz. Bu dars Node'ga xos qismi: fayl turini aniqlash, Node'dagi xatolar, import.meta ning Node xususiyatlari va paket chegaralari.
O'xshatish: pochta. CommonJS — mahalla pochtachisi: "Aziz akaga" deb yozsangiz ham, u kimligini biladi va topadi. ESM — xalqaro pochta: ko'cha, uy, xonadon raqami aniq yozilmasa, xat qaytadi.
2. Node fayl turini qanday aniqlaydi
2.1 Qoidalar jadvali
Node har fayl uchun bitta savolga javob beradi: "bu ESM'mi yoki CommonJS?" Javob kengaytma va eng yaqin package.json dagi "type" maydonidan:
| Fayl | "type": "module" |
"type": "commonjs" yoki yo'q |
|---|---|---|
.mjs |
ESM | ESM |
.cjs |
CommonJS | CommonJS |
.js |
ESM | CommonJS (sintaksis tekshiriladi — pastda) |
.ts, .mts, .cts |
.js, .mjs, .cjs kabi |
.js, .mjs, .cjs kabi |
"Eng yaqin" — fayl papkasidan boshlab yuqoriga qarab birinchi topilgan package.json. Shuning uchun loyihaning ichki papkasiga alohida package.json ({ "type": "commonjs" }) qo'yib, uning ichini CommonJS qilish mumkin. .mjs va .cjs esa hech qachon "ikkilanmaydi" — shuning uchun bu darsdagi tajribalar ko'pincha shu kengaytmalarda.
Diagrammada qaror yo'liga qarang:
flowchart TD
F["fayl"] --> M{"kengaytma?"}
M -->|".mjs"| E["ESM"]
M -->|".cjs"| C["CommonJS"]
M -->|".js"| T{"eng yaqin<br/>package.json type?"}
T -->|"module"| E
T -->|"commonjs"| C
T -->|"yo'q"| S{"import/export<br/>sintaksisi?"}
S -->|"ha (ogohlantirish)"| E
S -->|"yo'q"| C2.2 "type" yozilmasa: sintaksis bo'yicha aniqlash
Node 22.7 dan beri "type" bo'lmasa ham .js fayl ESM bo'lishi mumkin. Node avval uni CommonJS deb o'qiydi, import/export ni ko'rsa — ESM deb qayta o'qiydi. Biz ishga tushirdik (package.json da faqat "name", t.js ichida export):
(node:6260) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///C:/Users/…/t.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\…\package.json.
ESM sintaksis, .js, type yoqTarjimasi: "Fayl turi ko'rsatilmagan va u CommonJS sifatida o'qilmadi. ES modul sintaksisi topilgani uchun qayta o'qilmoqda — bu qo'shimcha vaqt oladi. Ogohlantirishni yo'qotish uchun package.json ga "type": "module" qo'shing". Kod ishladi, lekin ikki marta tahlil qilindi. Xulosa: har loyihada "type" ni aniq yozing. valyuta da u birinchi kundan bor (process obyekti darsidagi package.json).
Tekshirib ko'ring:
"type": "module"li loyihada eski CommonJS skriptni (requirebilan) qanday saqlab qolasiz?
Javob
Uni .cjs kengaytmasi bilan nomlang (masalan migratsiya.cjs). .cjs har doim CommonJS — "type" ga qaramaydi. Ikkinchi yo'l — shu fayl turgan papkaga { "type": "commonjs" } li package.json qo'yish.
3. ESM'ning uch qat'iy qoidasi
3.1 Uch xato — bitta tajribada
Quyidagi blok kichik papka yaratadi va CommonJS'da ishlaydigan uch xil importni ESM'da sinaydi. Xabarlarda yo'l bor, u kompyuterga bog'liq — shuning uchun ostida haqiqiy chiqish (yo'llar qisqartirilgan):
import { writeFileSync, mkdirSync } from "node:fs";
mkdirSync("lib", { recursive: true });
writeFileSync("lib/kurs.js", "export const kurs = 11903.15;\n");
writeFileSync("lib/index.js", "export const x = 1;\n");
writeFileSync("kurs.json", '{ "USD": 11903.15 }');
writeFileSync("package.json", '{ "type": "module" }');
for (const spec of ["./lib/kurs", "./lib", "./kurs.json"]) {
try {
await import(spec);
} catch (e) {
console.log(e.code, "|", e.message.split("\n")[0]);
}
}Biz ishga tushirdik (Node 24.21.0):
ERR_MODULE_NOT_FOUND | Cannot find module 'C:\Users\…\lib\kurs' imported from C:\Users\…\a.mjs
ERR_UNSUPPORTED_DIR_IMPORT | Directory import 'C:\Users\…\lib' is not supported resolving ES modules imported from C:\Users\…\a.mjs
ERR_IMPORT_ATTRIBUTE_MISSING | Module "file:///C:/Users/…/kurs.json" needs an import attribute of "type: json"| Xato | Tarjimasi | Tuzatish |
|---|---|---|
ERR_MODULE_NOT_FOUND |
"lib/kurs moduli topilmadi" |
./lib/kurs.js |
ERR_UNSUPPORTED_DIR_IMPORT |
"papkani import qilish qo'llanmaydi" | ./lib/index.js |
ERR_IMPORT_ATTRIBUTE_MISSING |
"JSON modulga type: json atributi kerak" |
with { type: "json" } |
Uchala holatda require muammosiz ishlardi (CommonJS ichidan darsidagi resolution). ESM'da resolution — URL qoidalari: ./lib/kurs — aniq manzil, Node unga hech narsa qo'shmaydi. Yalang'och nomlar ("valyuta") uchun esa ESM ham node_modules zanjirini qidiradi — lekin paket ichida exports va main ga qaraydi, kengaytma taxmin qilmaydi.
3.2 JSON import
To'g'ri yo'l — import attributes:
import { writeFileSync } from "node:fs";
writeFileSync("kurs.json", '{ "USD": 11903.15 }');
const data = await import("./kurs.json", { with: { type: "json" } });
console.log(data.default.USD);Konsolda:
11903.15Statik shakli: import kurslar from "./kurs.json" with { type: "json" };. JSON modulda faqat default eksport bor — import { USD } ishlamaydi. Nega atribut shart? Xavfsizlik: .json deb so'ralgan manzildan server JS qaytarsa, atributsiz u kod sifatida bajarilib ketardi. Atribut "bu faqat ma'lumot" deb kafolatlaydi.
Bu blokda dinamik import() ishlatildi. Statik import … with ham shu natijani beradi, lekin u fayl yaratilishidan oldin bajarilardi — sababi keyingi bo'limda.
Maslahat: ESM'da ham JSON keshlanadi (CommonJS ichidan darsidagi
requirekabi): ikkinchi import o'sha obyektni beradi. O'zgaruvchan ma'lumot uchunreadFile+JSON.parseishlating.
4. Bajarilish tartibi va kesh
4.1 Statik importlar — tanadan oldin
Dinamik import va top-level await darsida o'qigansiz: statik import lar faylning qayerida yozilganidan qat'i nazar, modul tanasidan oldin yuklanadi. Node'da buni o'z ko'zimiz bilan ko'ramiz. ord2.mjs:
console.log("1: ord2 tanasi boshi");
import "./dep.mjs";
console.log("3: ord2 tanasi oxiri");dep.mjs da bitta qator: console.log("2: dep.mjs bajarildi");. Biz ishga tushirdik:
2: dep.mjs bajarildi
1: ord2 tanasi boshi
3: ord2 tanasi oxiriimport ikkinchi qatorda yozilgan, lekin dep.mjs birinchi bajarildi. ESM avval butun bog'liqlik grafini tahlil qiladi va bog'laydi, keyin bajaradi — bargdan ildizga qarab. CommonJS'da esa require oddiy funksiya: u yozilgan joyida, ketma-ket bajariladi.
Bu farq amalda muhim. Kanon valyuta da shunday topilma bo'ldi: bin.ts boshida source map sozlamasini yoqib, keyin statik import qilingan modullar sozlamadan oldin yuklanib bo'lgan edi. Yechim — kerakli modulni dinamik import() bilan yuklash. Bu topilmani Stack trace, source map darsida to'liq ko'rasiz.
4.2 import.meta — Node'dagi to'liq to'plam
| Xususiyat | Nima | Qachondan |
|---|---|---|
import.meta.url |
file:///… manzil |
doim |
import.meta.filename / dirname |
yo'l, papka | Node 20.11 (Global obyektlar) |
import.meta.resolve(spec) |
specifier → to'liq URL (yuklamasdan) | Node 20.6 (sinxron) |
import.meta.main |
shu fayl dasturga kirish nuqtasimi | Node 24 |
import.meta.resolve — require.resolve ning ESM'dagi akasi. U faylni yuklamaydi, faqat qayerda ekanini aytadi:
console.log(import.meta.resolve("node:fs"));
const url = import.meta.resolve("./kurs.js");
console.log(url.startsWith("file:///"), url.endsWith("/kurs.js"));Konsolda:
node:fs
true true./kurs.js mavjud bo'lmasa ham URL qaytdi — nisbiy manzil shunchaki joriy faylga nisbatan hisoblanadi. Paket nomi topilmasa esa xato tashlaydi.
import.meta.main — CommonJS ichidan darsidagi require.main === module ning o'rni. Kirish faylida true, import qilingan modulda false. Biz ishga tushirdik — ord.mjs dyn.mjs ni dinamik import qiladi, dyn.mjs esa o'z qiymatini chiqaradi:
ord.mjs tanasi
dyn.mjs yuklandi, main: falseBu "fayl ham kutubxona, ham buyruq" naqshi uchun: if (import.meta.main) { await main(); }. Eski Node'da bu xususiyat undefined beradi.
4.3 ESM keshi va so'rov satri
ESM modullari ham bir marta yuklanadi va keshlanadi — lekin require.cache kabi ochiq obyekt yo'q, keshdan o'chirib bo'lmaydi. Kalit — to'liq URL. Shuning uchun URL'ga so'rov satri qo'shilsa, bu boshqa modul hisoblanadi:
import { writeFileSync } from "node:fs";
writeFileSync("son.mjs", "export const n = 1;");
const a = await import("./son.mjs");
writeFileSync("son.mjs", "export const n = 2;");
const b = await import("./son.mjs");
const c = await import("./son.mjs?v=2");
console.log(a.n, b.n, c.n, a === b);Konsolda:
1 1 2 trueFayl diskda o'zgardi, lekin ikkinchi import keshdan eski modulni berdi. ?v=2 — yangi URL, shuning uchun fayl qayta o'qildi. Bu hiyla dev vositalarida uchraydi. Lekin xotira bo'shamaydi: har yangi ?v= — keshda yana bitta modul. Dasturni qayta yuklash uchun node --watch to'g'ri yo'l (--watch, --env-file va node --run).
Tekshirib ko'ring:
main.mjsbirinchi qatoridaconsole.log("start"), ikkinchi qatoridaimport "./config.mjs"(uconsole.log("config")qiladi). Nima chiqadi?
Javob
Avval config, keyin start. Statik import modul tanasidan oldin bajariladi — qaysi qatorda yozilganidan qat'i nazar. Tartib muhim bo'lsa (masalan, avval muhit sozlansin), await import("./config.mjs") yozing.
5. Top-level await va dinamik import Node'da
Dinamik import() va top-level await darsida top-level await (modul darajasidagi await) va uning narxini ko'rgansiz: uni import qilgan modullar ham kutadi. Node'ga xos uch fakt:
- Faqat ESM'da.
.cjsfaylda top-levelawait—SyntaxError. - Tugamasa — exit 13. process obyekti darsida ko'rdingiz: hech qachon hal bo'lmaydigan Promise'ni kutsa, Node
Detected unsettled top-level awaitbilan chiqadi. - TLA'li modulni
requireqilib bo'lmaydi —ERR_REQUIRE_ASYNC_MODULE. Bu keyingi darsning markaziy mavzusi.
Dinamik import() esa ikkala tizimda ham ishlaydi — .cjs fayldan ham ESM'ni yuklash mumkin (u Promise qaytaradi). Node'da uning odatiy ishlatilishlari: kam ishlatiladigan og'ir modulni faqat kerak bo'lganda yuklash (CLI'ning sovuq startini tejaydi) va shartli yuklash (masalan, --interaktiv bayrog'i bo'lsa).
6. CLI qadami: valyuta ESM paket sifatida (holat 12, 1-qism)
Kanon holat 12 ikki darsga bo'lingan: bu darsda ESM qismi, CJS va ESM birga darsida — CommonJS bilan moslik va commit. Hali commit qilmang.
6.1 Loyiha allaqachon ESM
valyuta boshidan bu darsning qoidalariga amal qiladi:
| Qoida | valyuta da |
|---|---|
"type": "module" |
package.json da (holat 08 dan) |
| kengaytma majburiy | import { … } from "./kurs.ts" |
import.meta |
new URL("../fixtures/…", import.meta.url) |
| top-level await | process.exitCode = await main(…) |
6.2 Kirish va kutubxonani ajratish
Holat 08 da cli.ts ham mantiqni, ham process bilan ishni bajarardi: oxirida top-level await va SIGINT ishlovchisi. Bunday faylni boshqa loyiha kutubxona sifatida import qilsa, u darhol ishga tushib ketadi — import qilishning o'zi yon ta'sir. Holat 12 uni uchga bo'ladi:
| Fayl | Vazifasi |
|---|---|
src/cli.ts |
faqat main(argv, env) — yon ta'sirsiz |
src/bin.ts |
kirish nuqtasi: shebang, SIGINT, process.exitCode |
src/index.ts |
kutubxona API: kurs.ts va manba.ts eksportlari |
src/bin.ts (kanon; uzun izohlar telefon uchun ikki qatorga bo'lingan, kod o'zgarmagan):
#!/usr/bin/env node
// Kirish nuqtasi (package.json "bin"): signal, exit kod.
// Mantiq — cli.ts da
import { main } from "./cli.ts";
process.once("SIGINT", () => {
process.stderr.write("\nTo'xtatildi\n");
process.exit(130);
});
process.exitCode = await main(process.argv.slice(2), process.env);src/index.ts (kanon):
// Kutubxona API'si (package.json "exports"): boshqa loyihalar
// kurslarni CLI'siz ishlatishi uchun
export { SOM, birlikKursi, konvert, royxatniOqi, sonniYoz, yozuvniOqi, type Kurs } from "./kurs.ts";
export { API, apidanOl, fixturedanOl } from "./manba.ts";cli.ts dan oxirgi ikki bo'lak (SIGINT va process.exitCode = …) olib tashlanadi va bin.ts ga ko'chadi. test/cli.test.ts dagi yo'l ../src/cli.ts dan ../src/bin.ts ga o'zgaradi — test endi kirish nuqtasini ishga tushiradi. package.json dagi start ham: node src/bin.ts.
6.3 exports — paket chegarasi
package.json ga ESM paketning "eshigi" qo'shiladi (to'liq faylni keyingi darsda ko'rasiz):
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./package.json": "./package.json"
}package.json IV darsidan: exports — paketning ochiq yuzi. "." — import "valyuta" qaysi faylni berishi, "types" — TypeScript uchun tiplar. Ro'yxatda yo'q fayl — yopiq.
6.4 Self-reference: paketni o'z nomi bilan import qilish
exports bor paket ichida o'zini o'z nomi bilan import qilish mumkin — bu "self-reference". node_modules kerak emas: Node eng yaqin package.json ning name ini taniydi. Kanon holat 12 da (build qilingandan keyin) biz ishga tushirdik:
const lib = await import("valyuta");
console.log(Object.keys(lib).sort().join(", "));
try {
await import("valyuta/dist/kurs.js");
} catch (e) {
console.log(e.code, "|", e.message.split("\n")[0]);
}API, SOM, apidanOl, birlikKursi, fixturedanOl, konvert, royxatniOqi, sonniYoz, yozuvniOqi
ERR_PACKAGE_PATH_NOT_EXPORTED | Package subpath './dist/kurs.js' is not defined by "exports" in C:\Users\…\valyuta\package.json imported from C:\Users\…\valyuta\self.mjsBirinchi qator — index.ts dagi 9 ta eksport (Kurs tipi ishlash vaqtida yo'q). Ikkinchisi — fayl diskda bor, lekin exports da yo'q, shuning uchun yopiq: "./dist/kurs.js yo'li exports da e'lon qilinmagan". Bu xato foydali: paket foydalanuvchisi ichki faylingizga bog'lanib qolmaydi va siz uni keyinroq bemalol o'zgartira olasiz.
Self-reference testlarda qulay: test kodi paketni foydalanuvchi qanday ko'rsa, shunday import qiladi. Paket nomi hozircha valyuta; nashrdan oldin O'z npm paketingiz darsida @login/valyuta ga o'zgartirasiz — shunda importlar ham @login/valyuta bo'ladi.
6.5 Build: .ts importlar .js ga
npm'ga .ts emas, JS chiqariladi (sababini O'z npm paketingiz darsida o'lchaymiz). tsconfig.build.json (kanon):
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false,
"allowImportingTsExtensions": false,
"rewriteRelativeImportExtensions": true,
"rootDir": "src",
"outDir": "dist",
"declaration": true,
"sourceMap": true
},
"include": ["src"]
}npm run build (tsc -p tsconfig.build.json) dan keyin dist/cli.js ning boshi — biz ishga tushirdik (izoh telefon uchun bo'lingan). Import qatorlaridagi kengaytmaga qarang:
// valyuta buyruqlari: main(argv, env) → exit kod (0 — muvaffaqiyat,
// 1 — ish vaqtidagi xato, 2 — noto'g'ri foydalanish)
import { SOM, konvert, sonniYoz } from "./kurs.js";
import { apidanOl, fixturedanOl } from "./manba.js";Manbada ./kurs.ts edi — rewriteRelativeImportExtensions uni ./kurs.js ga almashtirdi, type Kurs importi esa o'chdi. Natijada dist/ — oddiy ESM JS, kengaytmalari to'g'ri: ESM qoidasi buzilmaydi. Ishga tushiramiz:
$ VALYUTA_OFFLINE=1 node dist/bin.js 100 USD EUR
100.00 USD = 89.30 EURHali commit qilmang — holat 12 ning qolgan qismi (bin, files, prepack, CommonJS'dan require) keyingi darsda.
7. Ko'p uchraydigan xatolar
| Xato | Sabab | Davosi |
|---|---|---|
ERR_MODULE_NOT_FOUND (nisbiy yo'l) |
kengaytma yozilmagan | ./kurs.js |
ERR_UNSUPPORTED_DIR_IMPORT |
papka import qilindi | ./lib/index.js yoki exports |
ERR_IMPORT_ATTRIBUTE_MISSING |
JSON atributsiz | with { type: "json" } |
ERR_PACKAGE_PATH_NOT_EXPORTED |
paketning yopiq fayli | exports dagi ochiq yo'l |
SyntaxError: Cannot use import statement outside a module |
.js CommonJS deb o'qilgan |
"type": "module" yoki .mjs |
MODULE_TYPELESS_PACKAGE_JSON ogohlantirishi |
"type" yo'q |
"type" ni aniq yozing |
| Import tartibi "noto'g'ri" | statik importlar tanadan oldin | dinamik import() |
8. Mashqlar
Mashqlar kurs/mashqlar/24/11-esm/ da.
1-mashq (oson): ESM yoki CommonJS?
Loyiha package.json ida "type": "module" bor.
src/app.js—scripts/eski.cjs—tools/gen.mjs—legacy/old.js,legacy/package.jsonda{ "type": "commonjs" }—
Yechim
- ESM —
.jsva eng yaqinpackage.json—"module". - CommonJS —
.cjshar doim CommonJS. - ESM —
.mjshar doim ESM. - CommonJS — eng yaqin
package.json—legacy/dagi, unda"commonjs".
2-mashq (o'rta): uch xatoni tuzating
Quyidagi kod CommonJS'dan ko'chirilgan. Uni ESM'da ishlaydigan qiling: blok avval fayllarni yaratadi (o'zgartirmang), keyin uchta import keladi. Natija 11903.15 11903.15 1 bo'lsin.
import { writeFileSync, mkdirSync } from "node:fs";
mkdirSync("lib", { recursive: true });
writeFileSync("lib/kurs.mjs", "export const kurs = 11903.15;");
writeFileSync("lib/index.mjs", "export const versiya = 1;");
writeFileSync("kurs.json", '{ "USD": 11903.15 }');
const { kurs } = await import("./lib/kurs");
const json = await import("./kurs.json");
const { versiya } = await import("./lib");
console.log(kurs, json.default.USD, versiya);Yechim
import { writeFileSync, mkdirSync } from "node:fs";
mkdirSync("lib", { recursive: true });
writeFileSync("lib/kurs.mjs", "export const kurs = 11903.15;");
writeFileSync("lib/index.mjs", "export const versiya = 1;");
writeFileSync("kurs.json", '{ "USD": 11903.15 }');
const { kurs } = await import("./lib/kurs.mjs");
const json = await import("./kurs.json", { with: { type: "json" } });
const { versiya } = await import("./lib/index.mjs");
console.log(kurs, json.default.USD, versiya);Konsolda:
11903.15 11903.15 1Uch tuzatish: kengaytma (.mjs), JSON atributi va papka o'rniga aniq fayl. Fayllar ataylab .mjs — shunda bu papkada package.json bo'lmasa ham ular ESM.
3-mashq (qiyin): ham kutubxona, ham buyruq
kurs-lib.mjs faylini (blok ichida) yarating: u export function toUzs(amount, rate) eksport qilsin va faqat kirish fayli bo'lganda CLI rejimi deb chiqarsin (import.meta.main). Asosiy blok uni import qilib toUzs(100, 11903.15) ni chiqarsin (CLI xabari chiqmasligi kerak), keyin spawnSync(process.execPath, ["kurs-lib.mjs"]) bilan alohida ishga tushirib, uning stdout'ini chiqarsin.
Yechim
import { writeFileSync } from "node:fs";
import { spawnSync } from "node:child_process";
writeFileSync("kurs-lib.mjs", `
export function toUzs(amount, rate) {
return Math.round(amount * rate * 100) / 100;
}
if (import.meta.main) console.log("CLI rejimi");
`);
const { toUzs } = await import("./kurs-lib.mjs");
console.log(toUzs(100, 11903.15));
const r = spawnSync(process.execPath, ["kurs-lib.mjs"], {
encoding: "utf8",
});
console.log(r.stdout.trim());Konsolda:
1190315
CLI rejimiImport qilinganda import.meta.main — false, shuning uchun xabar chiqmadi. Alohida jarayonda u kirish fayli — true. valyuta bu masalani boshqacha hal qiladi: kutubxona (index.ts) va kirish (bin.ts) — alohida fayllar. Bu yo'l eski Node versiyalarida ham ishlaydi va exports bilan aniqroq.
4-mashq: CLI qadami — bin.ts va index.ts
«CLI qadami» bo'limidagi kabi: cli.ts dan kirish qismini bin.ts ga ko'chiring, index.ts yarating, package.json dagi start va test/cli.test.ts dagi yo'lni yangilang. npm run check — 10/10. Keyin node -e "import('valyuta').then(m => console.log(Object.keys(m).length))" — nechta? Commit hali yo'q.
Yechim
Self-reference exports ni talab qiladi, exports esa dist/ ga ishora qiladi. Shuning uchun avval tsconfig.build.json va npm run build kerak — bular keyingi darsda to'liq. Build'dan keyin natija — 9 (kanondagidek). Build'siz ERR_MODULE_NOT_FOUND olsangiz — bu to'g'ri: dist/index.js hali yo'q. node src/bin.ts kurs USD esa hozirning o'zida ishlashi kerak.
9. Real ishda
- Ekotizim ESM'ga o'tmoqda — ko'p mashhur paketlar (masalan
chalk5,node-fetch3,execa) faqat ESM chiqaradi. Ularni CommonJS loyihada ishlatish — keyingi darsning mavzusi. - Monorepo va TypeScript —
module: "NodeNext"aynan shu darsdagi qoidalarni ("type", kengaytma) tiplar darajasida tekshiradi;valyutaningtsconfig.jsonida shu. - CLI'larda dinamik import — og'ir bog'liqlikni faqat kerakli buyruqda yuklash sovuq startni tejaydi; buni Node'da CLI dastur anatomiyasi darsida o'lchaymiz.
- Intervyu savoli: "ESM va CommonJS'ning Node'dagi asosiy farqlari?" — statik tahlil va bog'lash (importlar tanadan oldin), kengaytma majburiyligi,
import.meta, top-level await, jonli bog'lanishlar (Modul semantikasi).
Xulosa
- Fayl turi:
.mjs— ESM,.cjs— CommonJS,.js— eng yaqinpackage.jsondagi"type"ga qarab;"type"ni doim aniq yozing. - ESM qat'iy: kengaytma majburiy, papka importi yo'q, JSON —
with { type: "json" }. - Statik importlar modul tanasidan oldin bajariladi; tartib kerak bo'lsa —
import(). import.meta:url,filename,dirname,resolve(),main(Node 24).- ESM keshini tozalab bo'lmaydi; so'rov satri yangi modul yaratadi.
exportspaket chegarasi: ichki fayl —ERR_PACKAGE_PATH_NOT_EXPORTED; self-reference bilan paket o'zini nomi bilan import qiladi.
Keyingi dars: CJS va ESM birga: interop va require(esm) — CommonJS loyiha valyuta ni require qila oladimi va nega top-level await bunga to'sqinlik qiladi.
Manbalar
- Node.js hujjati — Modules: ECMAScript modules (resolution,
import.meta, import attributes): https://nodejs.org/docs/latest-v24.x/api/esm.html - Node.js hujjati — Modules: Packages (
"type",exports, self-referencing, syntax detection): https://nodejs.org/docs/latest-v24.x/api/packages.html - TypeScript hujjati —
rewriteRelativeImportExtensions: https://www.typescriptlang.org/tsconfig/#rewriteRelativeImportExtensions - Kanon:
valyutaholat 12 (tekshiruv-12.txt; self-reference — 9 eksport,ERR_PACKAGE_PATH_NOT_EXPORTED), Node 24.21.0, Windows 11, 2026-10-23; ESM xatolari, import tartibi,import.meta.main, kesh bloklari — yozuvchi o'lchovi, xuddi shu mashina.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!