IlmHamroh
JavaScript Full-stack/24-qism. Node.js13/55-dars15 daqiqa
Mundarija (23)

Workspaces va monorepo asoslari: npm workspaces, workspace:* va Turborepo

Qisqacha: Monorepo — bitta Git repozitoriyda bir nechta paket (masalan core va cli). Workspaces ularni bog'laydi: ildiz package.json dagi "workspaces": ["packages/*"] bilan npm install har ichki paketni node_modules ga havola (Windows'da junction) qilib qo'yadi — registrdan yuklamaydi. Bitta buyruq hamma paketda ishlaydi: npm run test --workspaces. Diqqat: "workspace:*" — pnpm va Yarn yozuvi, npm uni bilmaydi (EUNSUPPORTEDPROTOCOL, o'lchandi). Katta monorepolarda vazifalarni keshlash va tartiblash uchun Turborepo yoki Nx qo'shiladi. Kanon valyuta ni ikkiga bo'lib sinadi va bitta paket bo'lib qolishni tanladi — sababi o'lchov bilan.

Bu darsda

  • Monorepo nima va u qaysi muammoni hal qilishini tushuntira olasiz (polyrepo bilan solishtirib).
  • npm workspaces bilan ikki paketli loyiha qurasiz va --workspaces, -w bayroqlarini ishlatasiz.
  • workspace:* qaysi paket menejerida ishlashini va npm'da nima yozilishini bilasiz.
  • Umumiy tsconfig ni extends bilan ulashni ko'rasiz.
  • Turborepo va Nx nima qilishini bilasiz — va qachon monorepo kerak emasligini o'lchov bilan asoslaysiz.

Oldin bilishingiz kerak: pnpm: tez va qat'iy paket menejeri, node_modules ichida, CJS va ESM birga, package.json IV: kirish nuqtalari.

1. Nega bu kerak?

Tasavvur qiling: valyuta muvaffaqiyatli bo'ldi va endi sizda uchta narsa bor — CLI, kurslarni ko'rsatadigan veb-sayt va Telegram bot. Uchalasi ham bir xil konvert va yozuvniOqi funksiyalarini ishlatadi. Qayerda saqlaysiz?

Birinchi yo'l — har birini alohida repoga, umumiy kodni esa npm'ga alohida paket qilib chiqarish. Shunda konvert dagi bitta xatoni tuzatish uchun: kutubxonada tuzatish, versiyani oshirish, nashr qilish, keyin uch repoda versiyani yangilash. To'rt joyda ish, to'rtta commit.

Ikkinchi yo'l — hammasi bitta repoda: packages/core, packages/cli, apps/web, apps/bot. Bitta commit hamma joyni birdan yangilaydi. Bu — monorepo. Uning qarama-qarshisi — polyrepo (har loyiha alohida repoda).

pnpm darsida «Bahor» monoreposini (@bahor/narx + @bahor/kassa) pnpm bilan qurgansiz va workspace:*, catalog: ni ko'rgansiz. Bu dars npm workspaces'ga qaratilgan — u Node bilan birga keladi — va real qaror: valyuta ga monorepo kerakmi?

O'xshatish: oila xo'jaligi. Polyrepo — har aka-uka alohida hovlida yashaydi: mustaqil, lekin bitta asbob kerak bo'lsa, qo'shnidan so'raydi. Monorepo — katta hovli: hamma asbob bitta omborda, lekin hovli kattalashgani sayin tartib kerak bo'ladi.

2. npm workspaces: ikki paketli tajriba

2.1 Tuzilma

Kanon tajribasidagi kabi valyuta ni ikkiga bo'lamiz: @valyuta/core (hisob) va @valyuta/cli (buyruq). Biz %TEMP% da qurdik va ishga tushirdik (Node 24.21.0, npm 11.19.0):

text
valyuta-monorepo/
├── package.json          ← ildiz: "workspaces"
└── packages/
    ├── core/
    │   ├── package.json  ← @valyuta/core
    │   ├── index.js
    │   └── index.test.js
    └── cli/
        ├── package.json  ← @valyuta/cli, core'ga bog'liq
        └── cli.js

Diagrammada bog'lanishga qarang — cli core ni registrdan emas, shu repodan oladi:

flowchart LR
  R["ildiz package.json<br/>workspaces: packages/*"] --> C["packages/core<br/>@valyuta/core"]
  R --> L["packages/cli<br/>@valyuta/cli"]
  L -->|"dependencies"| C
  NM["node_modules/@valyuta/core"] -.->|"junction"| C

2.2 Uch package.json

Ildiz — o'zi nashr qilinmaydi (private), faqat paketlarni yig'adi:

json
{
  "name": "valyuta-monorepo",
  "private": true,
  "type": "module",
  "workspaces": ["packages/*"],
  "scripts": { "test": "npm run test --workspaces --if-present" }
}

packages/core/package.json:

json
{
  "name": "@valyuta/core",
  "version": "0.1.0",
  "type": "module",
  "exports": "./index.js",
  "scripts": { "test": "node --test" }
}

packages/cli/package.json — core ga oddiy versiya bilan bog'liq:

json
{
  "name": "@valyuta/cli",
  "version": "0.1.0",
  "type": "module",
  "bin": { "valyuta-ws": "./cli.js" },
  "dependencies": { "@valyuta/core": "0.1.0" }
}

core/index.js — bitta funksiya; cli/cli.js uni paket nomi bilan import qiladi:

js
// packages/core/index.js
export const konvert = (miqdor, kurs) =>
  Math.round(miqdor * kurs * 100) / 100;
js
#!/usr/bin/env node
// packages/cli/cli.js
import { konvert } from "@valyuta/core";
console.log("cli -> core:", konvert(100, 11903.15));

2.3 npm install va natija

Ildizda bitta npm install:

text
added 2 packages in 595ms

"2 paket qo'shildi" — bu bizning ikki workspace'imiz, registrdan hech narsa yuklanmadi. npm ls --all:

text
valyuta-monorepo@ C:\Users\…\ws
+-- @valyuta/cli@0.1.0 -> .\packages\cli
| `-- @valyuta/core@0.1.0 deduped -> .\packages\core
`-- @valyuta/core@0.1.0 -> .\packages\core

-> belgisi — havola. node_modules/@valyuta/core — nusxa emas, packages/core papkasiga ishora. Windows'da uning turi — biz PowerShell'da tekshirdik:

text
PS> (Get-Item node_modules\@valyuta\core).LinkType
Junction

Junction — Windows'dagi papka havolasi (Linux/macOS'da npm symlink yasaydi — hujjatga ko'ra; sinalmagan). core dagi o'zgarish darhol cli ga ko'rinadi — build, nashr, versiya yangilash yo'q:

text
$ node packages/cli/cli.js
cli -> core: 1190315

Diqqat: Junction va symlink'li papkalarni rm -rf bilan o'chirishda ehtiyot bo'ling: ba'zi vositalar havolaning ichidagi asl fayllarni ham o'chirib yuborishi mumkin. Avval node_modules ni o'chiring (npm o'zi to'g'ri qiladi) yoki havolani alohida olib tashlang.

Tekshirib ko'ring: cli ning package.json ida "@valyuta/core": "0.1.0" yozilgan. Agar core ning versiyasini 0.2.0 ga oshirsangiz va npm install qilsangiz, nima bo'ladi?

Javob

npm 0.1.0 ni repodagi 0.2.0 bilan moslay olmaydi va paketni registrdan qidirishga urinadi. Biz sinadik (oflayn rejimda): npm error request to https://registry.npmjs.org/@valyuta%2fcore failed — npm @valyuta/core ni internetdan so'radi. Workspace'dan bog'lash uchun versiya diapazoni repodagi versiyaga mos bo'lishi kerak: "^0.2.0" yoki "*". Shuning uchun monorepoda versiyalarni bir vaqtda yangilaydigan vositalar (changesets) ishlatiladi.

3. Hamma paketda bitta buyruq

3.1 --workspaces va -w

npm'ning workspace bayroqlari:

Buyruq Nima qiladi
npm run test --workspaces (-ws) har workspace'da test skripti
npm run test -ws --if-present skripti yo'q paketni jim o'tkazib yuboradi
npm run test -w @valyuta/core faqat bitta paketda
npm i zod -w @valyuta/core bog'liqlikni aniq paketga qo'shadi
npm ls --all hamma bog'lanishlar daraxti

Ildizdagi npm test (npm run test --workspaces --if-present) — biz ishga tushirdik:

text
> test
> npm run test --workspaces --if-present

> @valyuta/core@0.1.0 test
> node --test
ℹ tests 1
ℹ pass 1
ℹ fail 0

cli da test skripti yo'q — --if-present tufayli xato bermadi. Usiz npm "Missing script: test" deb to'xtardi.

Bog'liqliklar ham ildizda bitta node_modules ga "ko'tariladi" (node_modules ichida darsidagi hoisting) va bitta package-lock.json bo'ladi. Ya'ni uch paketda typescript bo'lsa ham, disk va o'rnatish vaqti bir marta sarflanadi.

3.2 workspace:* — npm'da emas

pnpm darsida "@bahor/narx": "workspace:*" yozgansiz — "registrdan emas, faqat shu repodan ol". Kanon tajribasi xuddi shu yozuvni npm'da sinadi:

text
$ npm install  (dependencies: "workspace:*")
npm error code EUNSUPPORTEDPROTOCOL
npm error Unsupported URL Type "workspace:": workspace:*

Tarjimasi: "Qo'llab-quvvatlanmaydigan URL turi workspace:". npm bu protokolni bilmaydi — u pnpm va Yarn'ga xos. npm'da oddiy versiya yoziladi ("0.1.0" yoki "*"), npm esa uni workspace'dan topadi. Natijada farq:

npm pnpm / Yarn
Ichki bog'liqlik "0.1.0", "^0.1.0", "*" "workspace:*", "workspace:^"
Versiya mos kelmasa registrdan qidiradi xato — faqat repodan
Nashrda o'zgarmaydi workspace:* aniq versiyaga almashtiriladi
Konfiguratsiya package.json → "workspaces" pnpm-workspace.yaml (pnpm)

workspace: protokolining afzalligi — xavfsizlik: tasodifan registrdagi begona paket o'rnatilib qolmaydi. npm'da esa ichki paket nomi registrdagi boshqa paket nomi bilan to'qnashmasligi uchun scope (@valyuta/) ishlatiladi.

4. Umumiy tsconfig

Monorepoda har paketning tsconfig.json i bor, lekin sozlamalarning 90 foizi bir xil. Ularni ildizdagi bitta faylga chiqarib, extends bilan ulash odatiy naqsh:

jsonc
// tsconfig.base.json (ildizda) — umumiy qoidalar
{
  "compilerOptions": {
    "target": "ES2024",
    "module": "NodeNext",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "erasableSyntaxOnly": true,
    "verbatimModuleSyntax": true
  }
}
jsonc
// packages/core/tsconfig.json — faqat farqlar
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": { "outDir": "dist", "rootDir": "src" },
  "include": ["src"]
}

Bu yerda valyuta ning tsconfig.build.json i bilan bir xil g'oya (ES Modules Node'da): u ham extends: "./tsconfig.json" bilan faqat farqlarni yozadi. Katta monorepolarda bunga project references qo'shiladi ("composite": true, "references": [...], tsc -b) — TypeScript paketlarni to'g'ri tartibda va faqat o'zgarganini qayta yig'adi. Biz uni kanonda sinamadik; TypeScript hujjatida batafsil.

ESLint va Prettier sozlamalari ham odatda ildizda bitta bo'ladi — flat config (eslint.config.js) butun repoga tatbiq etiladi.

5. Turborepo va Nx — tanishuv

Paketlar ko'paygan sari ikkita muammo chiqadi:

  1. Tartib. cli ni build qilishdan oldin core build bo'lishi kerak. npm run build -ws paketlarni "workspaces" ro'yxatidagi tartibda yurgizadi (packages/* uchun — papka nomlari tartibida), bog'liqlik tartibida emas.
  2. Takror ish. 20 paketdan bittasi o'zgardi, lekin npm run test -ws hammasini qayta sinaydi.

Buni vazifa ishga tushiruvchilar (task runner) hal qiladi:

Vosita Asosiy g'oya
Turborepo turbo.json da vazifalar va ularning bog'liqligi; natijalar keshlanadi — o'zgarmagan paket qayta build/test qilinmaydi; masofaviy kesh (jamoa bilan ulashish)
Nx xuddi shu, ustiga kod generatorlari, bog'liqlik grafi vizualizatsiyasi, "faqat ta'sirlangan paketlar" (affected) buyruqlari

Ikkalasi ham npm, pnpm va Yarn workspaces ustida ishlaydi — ularning o'rnini bosmaydi, ustidan quriladi. Kichik monorepoda (2–3 paket) ular ortiqcha; o'nlab paket va CI vaqti muhim bo'lganda foyda beradi. Biz ikkalasini ham kanonda sinamadik — bu bo'lim hujjatlar bo'yicha tanishuv. Frontend monorepo misolini Monorepo'da frontend darsida ko'rasiz.

Tekshirib ko'ring: Monorepoda 3 paket: core, cli, web. Faqat web dagi CSS o'zgardi. Turborepo turbo run test da nima qiladi?

Javob

core va cli testlarini qayta ishga tushirmaydi — ularning kirishlari (fayllar, bog'liqliklar) o'zgarmagan, natija keshdan olinadi. Faqat web (va unga bog'liq paketlar bo'lsa — ular) qayta sinanadi. Oddiy npm run test -ws esa uchalasini ham yurgizardi.

6. CLI qadami: tajriba — valyuta bitta paket qoladi

Kanon valyuta ni ikki paketga bo'lib sinadi (packages/core + packages/cli) va kanonga kiritmadi. Bu darsda kod qo'shilmaydi, commit yo'q. Qaror o'lchovlarga asoslangan.

6.1 Tajriba nima ko'rsatdi

  • npm install — 1.4 s, node_modules/@valyuta/core — junction.
  • Workspace'dagi .ts fayl type stripping bilan ishladi (cli -> core: 1190315). Sabab: junction'ning haqiqiy yo'li node_modules dan tashqarida. npm'dan o'rnatilgan paketda esa bu ishlamaydi — O'z npm paketingiz darsida ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING ni ko'rasiz. Ya'ni workspace'da "ishlayapti" — nashrdan keyin ham ishlaydi degani emas.
  • Haqiqiy xato topildi. Tajribadagi cli.ts da shebang (#!/usr/bin/env node) yo'q edi. Natija:
text
$ node_modules/.bin/valyuta
…/cli.ts: line 1: import: command not found
…/cli.ts: line 2: syntax error near unexpected token `"cli -> core:",'

npm bin uchun Windows'da uchta "shim" (o'rab turuvchi kichik skript) yozadi: bash uchun, .cmd va .ps1. Bash shim'i interpretatorni fayldagi shebang'dan oladi. Shebang bo'lmasa, faylni shell skript deb bajaradi — import bash buyrug'i emas. npx valyuta esa 2 daqiqadan ko'p osilib qoldi. Saboq: bin faylida shebang shart. valyuta ning bin.ts ida u bor (process obyekti darsidan beri). Bizning tajribamizdagi cli.js da ham shebang bor — shuning uchun npx valyuta-ws darhol cli -> core: 1190315 chiqardi.

6.2 Nega bitta paket

Monorepo uchun Qarshi (kanon qarori)
core ni boshqa loyihalar alohida o'rnatadi exports bilan kutubxona API baribir ajratilgan (CJS va ESM birga)
Mustaqil versiyalar ikki paket = ikki marta nashr, versiyalarni sinxron tutish
Kelajakda veb va bot hozir ular yo'q; CLI kichik — 4 modul
junction, workspace tuzoqlari, ko'proq konfiguratsiya

Xulosa: monorepo — real muammoning yechimi (bir nechta ilova umumiy kod bilan). Muammo yo'q joyda u faqat murakkablik qo'shadi. valyuta ning kutubxona qismi exports orqali tashqariga ochiq — bu "alohida paket" ning foydasining katta qismini bepul beradi. Bir kun veb yoki bot qo'shilsa — o'shanda monorepoga o'tish oson, chunki index.ts chegarasi allaqachon bor.

7. Ko'p uchraydigan xatolar

Xato yoki belgi Sabab Davosi
EUNSUPPORTEDPROTOCOL … workspace:* pnpm/Yarn yozuvi npm'da npm'da oddiy versiya yoki "*"
ENOTCACHED / 404 ichki paketga versiya diapazoni repodagiga mos emas, npm registrga ketdi versiyani moslang
npm run x -ws — Missing script hamma paketda skript yo'q --if-present
import: command not found (bin) shebang yo'q #!/usr/bin/env node birinchi qator
npx osilib qoldi shim faylni noto'g'ri interpretator bilan ishga tushirdi xuddi shu — shebang
Workspace'da .ts ishlaydi, o'rnatilganda yo'q node_modules ichida type stripping taqiqlangan nashrga JS (build) chiqaring

8. Mashqlar

Mashqlar kurs/mashqlar/24/13-workspaces/ da.

1-mashq (oson): qaysi buyruq?

  • Hamma workspace'da build (yo'q joyda jim) — npm run build -ws --
  • Faqat @valyuta/core da test — npm test - @valyuta/core
  • npm'da ichki bog'liqlik yozuvi — "@valyuta/core": "" (workspace:* emas)
  • pnpm workspace konfiguratsiya fayli — pnpm-.yaml
Yechim
  • if-present — skripti yo'q paket xato bermaydi.
  • w — -w (--workspace) bitta paketni tanlaydi; -ws — hammasini.
  • 0.1.0 — yoki ^0.1.0, *; repodagi versiyaga mos bo'lsa, npm workspace'dan bog'laydi.
  • workspace — pnpm darsida ko'rgansiz.

2-mashq (o'rta): workspace bog'lanishini tekshiruvchi

Funksiya yozing: checkLinks(packages) — packages — { name, version, deps } massivi. Har ichki bog'liqlik uchun (nomi packages da bor) chiqaring: cli -> core: workspace (versiya aniq teng yoki "*"), cli -> core: registr! (mos emas — npm registrga ketadi), cli -> core: EUNSUPPORTEDPROTOCOL (workspace: bilan boshlansa). Tashqi bog'liqliklarni (nomi packages da yo'q) o'tkazib yuboring. Soddalik uchun ^ va boshqa diapazonlarni hisobga olmang.

Yechim
js
function checkLinks(packages) {
  const versions = new Map(packages.map((p) => [p.name, p.version]));
  const lines = [];
  for (const pkg of packages) {
    for (const [dep, range] of Object.entries(pkg.deps)) {
      if (!versions.has(dep)) continue; // tashqi paket
      let result = "registr!";
      if (range.startsWith("workspace:")) {
        result = "EUNSUPPORTEDPROTOCOL";
      } else if (range === "*" || range === versions.get(dep)) {
        result = "workspace";
      }
      lines.push(`${pkg.name} -> ${dep}: ${result}`);
    }
  }
  return lines;
}

const repo = [
  { name: "core", version: "0.2.0", deps: {} },
  {
    name: "cli",
    version: "0.1.0",
    deps: { core: "0.1.0", zod: "4.6.7" },
  },
  { name: "web", version: "0.1.0", deps: { core: "*" } },
  { name: "bot", version: "0.1.0", deps: { core: "workspace:*" } },
];
console.log(checkLinks(repo).join("\n"));

Konsolda:

text
cli -> core: registr!
web -> core: workspace
bot -> core: EUNSUPPORTEDPROTOCOL

cli eski versiyani (0.1.0) so'rayapti, repoda esa 0.2.0 — npm uni registrdan qidiradi (biz sinadik: oflayn rejimda ENOTCACHED). zod tashqi — o'tkazildi. Haqiqiy npm semver diapazonlarini ham tekshiradi (^0.2.0 mos keladi) — buni Semver darsidagi qoidalar bilan kengaytirish mumkin.

3-mashq (qiyin): o'z monorepongiz

%TEMP% (yoki istalgan bo'sh papka) da ikki paketli npm workspace quring: @sizning/core (sonniYoz(n) — n.toFixed(2) va minglik bo'sh joy bilan; testi bilan) va @sizning/cli (core ni import qilib sonniYoz(1190315) ni chiqaradi, bin va shebang bilan). Keyin: npm install, npm ls --all, npm test (ildizdan), npx <bin-nomi>. Oxirida shebang'ni olib tashlab, Git Bash'da node_modules/.bin/<bin-nomi> ni ishga tushiring va xatoni kuzating — keyin qaytaring.

Yechim

Tuzilma — «npm workspaces: ikki paketli tajriba» bo'limidagidek. core/index.js:

js
export function sonniYoz(n) {
  const [butun, kasr] = n.toFixed(2).split(".");
  return `${butun.replace(/\B(?=(\d{3})+(?!\d))/g, " ")}.${kasr}`;
}

Kutilgan natijalar: npm install — added 2 packages; npm ls --all — ikkala paket -> havola bilan; npm test — ℹ pass 1; npx <bin> — 1 190 315.00. Shebang'siz Git Bash'da: line 1: import: command not found (kanon xatosi). PowerShell'da natija boshqacha bo'lishi mumkin — .cmd/.ps1 shim'i faylni Windows fayl assotsiatsiyasi orqali ochishga urinadi. Papkani tozalashda avval node_modules ni o'chiring (junction).

4-mashq: CLI qadami — qaror yozing

valyuta uchun monorepo kerakmi? O'z loyihangiz holatiga qarab 3–5 qatorlik qaror yozing (README yoki docs/qarorlar.md — o'zingiz tanlang): qaysi muammo monorepoga undardi, hozir u bormi, qachon qaytadan ko'rib chiqasiz. Kod o'zgarmaydi.

Yechim

Namuna: "Monorepo hozircha kerak emas. valyuta — bitta CLI, 4 modul; kutubxona API exports orqali ochiq (index.ts), shuning uchun boshqa loyiha import { konvert } from "valyuta" qila oladi. Ikkinchi ilova (veb yoki bot) paydo bo'lsa, npm workspaces'ga o'tamiz. Tuzilma — packages/core va apps/*. bin faylida shebang, ichki bog'liqlik "*"." Bunday qisqa yozuv ADR (Architecture Decision Record — arxitektura qarori yozuvi) deyiladi; jamoalarda "nega shunday qildik?" savoliga javob beradi.

9. Real ishda

  • Katta ochiq loyihalar — Babel, Vite, Next.js, React repolari monorepo: yuzlab paket bitta repoda. Ularning package.json va pnpm-workspace.yaml ini o'qish — yaxshi mashq (Begona repoda orientatsiya).
  • Kompaniyalarda — "frontend + backend + umumiy tiplar" monoreposi juda keng tarqalgan: apps/web, apps/api, packages/shared. Umumiy Zod sxemalari va TypeScript tiplari ikkala tarafda bir xil bo'ladi.
  • CI tezligi — Turborepo/Nx keshlash bilan monorepo CI vaqtini ko'p marta qisqartiradi; "faqat o'zgargan paketlar" yondashuvi.
  • Intervyu savoli: "Monorepo va polyrepo — qachon qaysi biri?" — umumiy kod va atomik o'zgarishlar kerak bo'lsa monorepo; mustaqil jamoalar, alohida nashr sikllari va kichik loyihada polyrepo yoki bitta paket.

Xulosa

  • Monorepo — bitta repoda bir nechta paket; workspaces ularni node_modules dagi havola (Windows'da junction) bilan bog'laydi.
  • npm: ildizda "workspaces", buyruqlarda -ws, -w <nom>, --if-present; bitta node_modules va bitta lock fayl.
  • workspace:* — pnpm/Yarn; npm'da EUNSUPPORTEDPROTOCOL (o'lchandi) — oddiy versiya yoki "*".
  • Umumiy tsconfig.base.json + extends; ko'p paketda — project references.
  • Turborepo/Nx — vazifa tartibi va keshlash; workspaces ustidan ishlaydi.
  • valyuta bitta paket qoldi: CLI kichik, exports kutubxonani allaqachon ajratgan; tajriba shebang zarurligini haqiqiy xato bilan ko'rsatdi.

Keyingi dars: O'z npm paketingizni nashr qilish — npm pack, boshqa papkada o'rnatish, @login/valyuta va nega npm'ga faqat JS chiqariladi.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Workspaces va monorepo asoslari: npm workspaces, workspace: va Turborepo — IlmHamroh