IlmHamroh
JavaScript Full-stack/18-qism. React chuqur32/42-dars17 daqiqa
Mundarija (32)

Katta frontend loyiha arxitekturasi: xususiyat papkalari, qatlamlar va import qoidalari

Qisqacha: Loyiha o'sganda fayllarni turi bo'yicha (komponentlar/, hooklar/) emas, xususiyat bo'yicha guruhlang: bitta xususiyatning komponenti, hooki, hisobi va testi — bitta papkada (xususiyatlar/statistika/). Har xususiyatning ochiq API'si bor — index.ts: tashqaridan faqat u orqali import qilinadi, ichki fayllar xususiyatning shaxsiy ishi. Bog'liqliklar bir yo'nalishda oqadi: umumiy (shared) qatlam xususiyatlarni bilmaydi, xususiyatlar bir-birini bilmaydi, ilova ularni yig'adi. Bu qoidalar so'z bilan emas, ESLint bilan himoyalanadi. Mashhur ikki yondashuv — bulletproof-react va Feature-Sliced Design.

Bu darsda

  • Tur bo'yicha tuzilma qayerda sinishini va xususiyat papkasi nimani yechishini tushuntira olasiz.
  • bulletproof-react va Feature-Sliced Design (qatlam, slice, segment) tuzilmalarini o'qiysiz.
  • Ochiq API (index.ts) va barrel fayl farqini bilasiz.
  • Import qoidalarini ESLint no-restricted-imports bilan majburlaysiz va haqiqiy xabarini o'qiysiz.
  • vazifalar dagi statistikani xususiyat papkasiga ko'chirasiz va umumiy modulni xususiyatdan "ajratasiz".

Oldin bilishingiz kerak: Kichik React loyihani tashkil qilish, Custom hooklar I, ESLint II: React va TypeScript loyiha uchun plaginlar.

1. Nega bu kerak?

Oldingi darsda bitta komponentning API'sini loyihaladik. Endi butun loyihaning "API'si" — fayllar qanday joylashgani va kim kimni import qilishi.

Kichik React loyihani tashkil qilish darsida vazifalar ga src/komponentlar/ papkasini qo'shgan edik. O'shanda u 8 fayl edi. Bugun komponentlar/ da 20 ta fayl, src/ ning ildizida esa 34 ta fayl va papka. «Batafsil statistika» xususiyati qayerda? Qidirib ko'ramiz:

  • komponentlar/StatistikaPaneli.tsx — panel va tugma;
  • komponentlar/BatafsilStatistika.tsx — jadval va menyu;
  • batafsil-statistika.ts — hisob va CSV;
  • statistika-ishchi.ts — Web Worker;
  • statistika-vadasi.ts — Promise keshi.

Beshta fayl, uch xil joyda, boshqa 50 ta fayl orasida. Statistikani o'zgartirmoqchi bo'lgan odam avval uni topishi kerak. Uni o'chirmoqchi bo'lsa — qaysi fayllar faqat unga tegishli ekanini taxmin qilishi kerak. xavfsiz-manzil.ts (umumiy xavfsizlik moduli) esa ishchi faylni to'g'ridan-to'g'ri import qiladi — umumiy modul bitta xususiyatga "yopishib" qolgan.

Hayotiy o'xshatish: supermarket. Kichik do'konda hamma narsa bitta javonda — muammo yo'q. Katta supermarketda mahsulotlar bo'limlarga ajratilgan: sut bo'limi, non bo'limi. Har bo'limda o'z sotuvchisi bor va sut bo'limining ombori non bo'limiga aralashmaydi. Xaridor esa bo'limga faqat old tomondan — peshtaxtadan — kiradi, omborga emas. Peshtaxta — ochiq API, ombor — ichki fayllar.

2. Tur bo'yicha yoki xususiyat bo'yicha

2.1 Ikki tuzilma

O'sha darsda ikki yondashuvni solishtirgan edik. Loyiha o'sganda farq keskinlashadi:

text
Tur bo'yicha                     Xususiyat bo'yicha
src/                             src/
  komponentlar/  (20)              xususiyatlar/
    StatistikaPaneli.tsx             statistika/
    BatafsilStatistika.tsx             StatistikaPaneli.tsx
    VazifaRoyxati.tsx …                BatafsilStatistika.tsx
  hooklar/                             batafsil-statistika.ts
  batafsil-statistika.ts               statistika-ishchi.ts
  statistika-ishchi.ts                 statistika-vadasi.ts
  statistika-vadasi.ts                 index.ts
  … (30 ta boshqa fayl)            komponentlar/  (18)
                                   ui/

Chap tomonda "bu fayl nima?" savoliga javob oson (komponent), "bu xususiyat qayerda?" ga — qiyin. O'ng tomonda aksincha: xususiyat bir joyda, ichida esa fayllar o'z vazifasi bo'yicha. Real ishda ikkinchi savol ko'proq beriladi: vazifalar "statistikaga CSV qo'sh", "to'lovni o'zgartir" deb keladi, "barcha hooklarni o'zgartir" deb emas.

2.2 O'lchov: bitta o'zgarish nechta papkaga tegadi

Arxitekturaning amaliy o'lchovi — bitta vazifa uchun o'zgaradigan papkalar soni. #26–#30 da statistikaga «Hisobotni yuklab olish» qo'shildi. O'zgargan fayllar: BatafsilStatistika.tsx (komponentlar/), batafsil-statistika.ts (src/), vazifalar.css va test. Xususiyat papkasida birinchi ikkitasi bitta joyda bo'lardi. Birga o'zgaradigan narsalar birga yashasin — bu colocation tamoyilining katta loyihadagi ko'rinishi.

3. bulletproof-react

3.1 Tuzilma

bulletproof-react — React loyihalari uchun ochiq "namuna arxitektura", GitHub'dagi eng mashhurlaridan biri. U qat'iy standart emas, lekin ko'p jamoalar boshlang'ich nuqta sifatida oladi:

text
src/
  app/          — ilova: marshrutlar, provider'lar, ildiz komponent
  features/     — xususiyatlar
    statistika/
      api/          — server so'rovlari
      components/   — xususiyat komponentlari
      hooks/
      types/
      utils/
      index.ts      — ochiq API
  components/   — umumiy UI (tugma, dialog)
  hooks/        — umumiy hooklar
  lib/          — tashqi kutubxonalar sozlamasi
  utils/        — umumiy yordamchi funksiyalar

3.2 Uchta qoida

  1. Xususiyat — mustaqil. features/statistika ichidagi narsalar faqat o'sha xususiyatga tegishli. Kichik xususiyatda ichki papkalar (api/, hooks/) shart emas — kerak bo'lganda paydo bo'ladi.
  2. Xususiyatlar bir-birini import qilmaydi. Statistika va eksport bir narsaga muhtoj bo'lsa, u narsa umumiy qatlamga (components/, lib/) chiqadi yoki ilova (app/) ularni yig'adi.
  3. Bir yo'nalish: umumiy → xususiyatlar → app. Umumiy kod xususiyatlarni bilmaydi, xususiyatlar ilovani bilmaydi.

Ikkinchi qoida eng ko'p buziladi va eng qimmatga tushadi. Xususiyatlar bir-birini import qila boshlasa, tez orada "hamma hammani biladi" — bitta xususiyatni o'chirib bo'lmaydi, chunki o'nta boshqasi unga tayanadi.

4. Feature-Sliced Design

4.1 Qatlam, slice, segment

Feature-Sliced Design (FSD) — rasmiy hujjatli, qat'iyroq metodologiya (MDH jamoalarida keng tarqalgan). U loyihani uch o'q bo'yicha bo'ladi:

O'q Nima Misol
Qatlam (layer) mas'uliyat darajasi app, pages, widgets, features, entities, shared
Slice biznes sohasi qatlam ichida entities/vazifa, features/vazifa-qoshish
Segment slice ichida texnik vazifa ui, model, api, lib, config

Qatlamlar yuqoridan pastga:

  • app — ilovani ishga tushirish: provider'lar, global stil, marshrutlar.
  • pages — sahifalar (har marshrutga bitta).
  • widgets — sahifaning katta mustaqil bloklari (sarlavha, statistika paneli).
  • features — foydalanuvchi harakati (vazifa qo'shish, eksport qilish, tema almashtirish).
  • entities — biznes obyektlari (vazifa, foydalanuvchi): ularning modeli va kichik UI'si.
  • shared — biznesdan bexabar umumiy narsalar: UI kit, API mijozi, yordamchilar.

4.2 Asosiy qoida: faqat pastga

flowchart TD
  A[app] --> P[pages]
  P --> W[widgets]
  W --> F[features]
  F --> E[entities]
  E --> S[shared]
  F -. "taqiq: yonma-yon" .-x F2[boshqa feature]

Qatlam faqat o'zidan pastdagi qatlamlardan import qila oladi. features — entities va shared dan, lekin widgets yoki pages dan emas. Va bir qatlamdagi slice'lar bir-birini import qilmaydi: features/eksport features/import ni bilmaydi. Diagrammadagi kesilgan strelka shu taqiq.

Bu qoida nimani beradi? shared dagi Tugma ni o'zgartirsangiz — yuqoridagi hamma narsa ta'sirlanishi mumkin, shuning uchun u ehtiyotkorlik bilan o'zgaradi. features/eksport ni o'zgartirsangiz — faqat uni ishlatadigan widgets/pages ta'sirlanadi, yonidagi feature'lar emas. Har o'zgarishning "portlash radiusi" oldindan ma'lum.

4.3 vazifalar FSD bo'lsa

vazifalar FSD'ga ko'chirilmagan — buning uchun u hali kichik (bitta sahifa). Lekin qayerga nima tushishini ko'rish foydali:

vazifalar dagi FSD'da
main.tsx, App.tsx, TemaKonteksti app
xususiyatlar/statistika/ widgets/statistika
YangiVazifaForma, Eksport, Import features/…
vazifa.ts, VazifaQatori entities/vazifa
ui/ (Tugma, Menyu), api.ts, taymer.ts shared/ui, shared/api, shared/lib

Statistika widgets ga tushdi, chunki u bir nechta narsani (ro'yxat ma'lumoti, hisob, menyu) bitta mustaqil blokka yig'adi. FSD'dagi eng ko'p bahs — "bu feature mi yoki widget mi?" Javob kitobda emas, jamoa kelishuvida.

Tekshirib ko'ring: FSD'da entities/vazifa ichidagi VazifaQatori ga «O'chirish» tugmasi kerak. O'chirish — foydalanuvchi harakati, ya'ni features/vazifa-ochirish. entities uni import qila oladimi?

Javob

Yo'q — entities features dan pastda, pastki qatlam yuqoridagini import qilmaydi. FSD'ning yechimi — kompozitsiya: VazifaQatori tugma uchun slot qoldiradi (actions props'i yoki children). widgets yoki pages darajasida ikkalasi yig'iladi: <VazifaQatori actions={<VazifaniOchirish id={…} />} />. Compound komponentlar va kompozitsiya arxitektura darajasida ham ishlaydi.

5. Ochiq API: index.ts

5.1 Peshtaxta

Har xususiyat (FSD'da — har slice) tashqi dunyoga bitta eshik orqali ochiladi:

ts
// xususiyatlar/statistika — «Batafsil statistika» xususiyati
// (18/#32). Ochiq API (public API): tashqaridan FAQAT shu fayl
// orqali. Ichidagi fayllar (hisob, ishchi, Promise keshi) —
// xususiyatning ishi, ular o'zgarsa tashqaridagilar o'zgarmaydi.
// ESLint (no-restricted-imports) ichkariga to'g'ridan-to'g'ri
// importni to'xtatadi
export { StatistikaPaneli } from "./StatistikaPaneli.tsx";

Bu — vazifalar kanonidagi haqiqiy fayl, to'liq (izoh telefonga sig'ishi uchun qayta bo'lingan). Bitta qator eksport. Statistikaning boshqa beshta fayli tashqaridan ko'rinmaydi. Ertaga Web Worker o'rniga boshqa yechim tanlansa yoki batafsil-statistika.ts ikkiga bo'linsa, App.tsx buni bilmaydi va o'zgarmaydi. Ochiq qism (public API) (modul eksportlari) g'oyasi modul darajasidan papka darajasiga ko'tarildi.

5.2 Ochiq API va barrel fayl

index.ts barrel fayl ga o'xshaydi, lekin maqsadi boshqa:

Barrel (hamma narsa) Ochiq API
Ichida export * from "./A"; export * from "./B"; … faqat tashqariga kerakli 1–3 nom
Maqsad importni qisqartirish chegarani belgilash
Xavfi dev serverda hamma modul yuklanadi; aylanma importlar kam

Katta barrel'ning narxi real: components/index.ts 200 ta komponentni qayta eksport qilsa, bitta Tugma import qilinganda Vite dev serveri ko'pincha 200 ta faylni ham yuklaydi. Production'da tree shaking ularni olib tashlaydi, lekin yon ta'siri bor modullarni emas. Ochiq API esa kichik va ataylab tanlangan: "nimani va'da qilaman".

6. Qoidalarni majburlash

6.1 So'z bilan emas — ESLint bilan

"Xususiyatga faqat index.ts orqali" — yaxshi qoida. Lekin yangi dasturchi bilmasa yoki shoshilsa, muharrir avtomatik import qilib qo'yadi: from "./xususiyatlar/statistika/batafsil-statistika.ts". Qoida birinchi haftadayoq buziladi. Shuning uchun u vosita bilan himoyalanadi. ESLint'ning o'rnatilgan no-restricted-imports qoidasi (ESLint I) — vazifalar dagi sozlama:

js
  {
    // Xususiyat papkalari (18/#32): tashqaridan faqat ochiq API —
    // xususiyatlar/<nomi>/index.ts. Ichidagi fayl o'zgarsa yoki
    // ko'chsa, tashqi kod buzilmaydi. Xususiyat ichida — nisbiy
    // importlar erkin
    files: ["src/**/*.{ts,tsx}"],
    rules: {
      "no-restricted-imports": [
        "error",
        {
          patterns: [
            {
              regex: "xususiyatlar/[^/]+/(?!index[.]ts$)[^/]+$",
              message:
                "Xususiyatga faqat uning index.ts (ochiq API) orqali.",
            },
          ],
        },
      ],
    },
  },

Izoh telefonga sig'ishi uchun qayta bo'lindi, qolgani aynan kanon (message satri — 70 belgidan bir belgi uzun, o'zgartirilmadi). regex ni qismlarga ajratamiz (lookahead):

Qism Ma'nosi
xususiyatlar/ import yo'lida shu papka
[^/]+/ xususiyat nomi (statistika/)
(?!index[.]ts$) keyingi qism index.ts bo'lmasa (negative lookahead)
[^/]+$ va u oxirgi qism — fayl nomi

Xususiyat ichidagi importlar (./statistika-vadasi.ts) yo'lida xususiyatlar/ so'zi yo'q — ular erkin. Tashqaridan esa faqat …/statistika/index.ts.

6.2 Haqiqiy xabar

Qoida ishlashini kichik tajribada sinadik: ESLint 10.12.0, o'sha no-restricted-imports qoidasi (tajriba fayli .js bo'lgani uchun files ga js ham qo'shildi), src/App.js da ikki import — biri ochiq API orqali, biri ichkariga:

js
import { StatistikaPaneli } from "./xususiyatlar/statistika/index.ts";
import { batafsilHisobla } from "./xususiyatlar/statistika/batafsil-statistika.ts";

npx eslint src chiqishida uch narsaga qarang: qator va ustun (2:1), xabar matni va oxirida qoida nomi.

text
src\App.js
  2:1  error  './xususiyatlar/statistika/batafsil-statistika.ts' import is restricted from being used by a pattern. Xususiyatga faqat uning index.ts (ochiq API) orqali  no-restricted-imports

✖ 1 problem (1 error, 0 warnings)

"'./xususiyatlar/statistika/batafsil-statistika.ts' importi naqsh bo'yicha cheklangan." Ikkinchi qism — bizning message imiz: o'quvchi nima qilish kerakligini darhol biladi (oxiridagi nuqta yo'q — ESLint'ning standart chiqish formati uni olib tashlaydi). Birinchi qatordagi import xatosiz o'tdi. vazifalar da lint --max-warnings=0 bilan ishlaydi va commit oldidan (husky) hamda CI'da tekshiriladi. Buzilgan chegara main ga yetib bormaydi. Ikkinchi import qatori 70 belgidan uzun — bu yo'lning haqiqiy uzunligi.

6.3 Boshqa vositalar

Vosita Nima qiladi
no-restricted-imports (ESLint) aniq yo'l yoki naqshni taqiqlaydi — oddiy chegaralar uchun
eslint-plugin-boundaries qatlam va element turlari orasidagi ruxsatlar jadvali
Steiger (FSD linteri) FSD qoidalari: qatlam tartibi, ochiq API
dependency-cruiser butun bog'liqlik grafini tekshiradi va chizadi
knip ishlatilmagan fayl, eksport va paketlarni topadi

vazifalar ga #33 da knip qo'shildi. Birinchi ishga tushishida 5 ta topilma berdi: #03, #07, #23, #31 dan qolgan, hech kim import qilmaydigan eksportlar. Masalan, VIRTUAL_CHEGARA (VazifaRoyxati.tsx) va tablargaYubor (server-sinxron.ts) — ular faqat o'z faylida ishlatilardi, export kerak emas edi. Ortiqcha eksport — ochiq API'ning "sizib chiqishi": kimdir uni topib import qila boshlaydi va ichki tafsilot shartnomaga aylanadi.

7. Umumiy qatlam xususiyatni bilmasin

7.1 Muammo: teskari bog'liqlik

xavfsiz-manzil.ts — Trusted Types siyosati (Web Worker darsida qo'shilgan). U umumiy xavfsizlik moduli: Service Worker ham, ishchi ham undan o'tadi. Holat 30 da u shunday boshlanardi:

ts
import ishchiManzili from "./statistika-ishchi.ts?worker&url";

export const SW_MANZILI = "sw.js";
// Statistika ishchisi (18/#24): Vite build'da hash'li fayl
// (assets/statistika-ishchi-<hash>.js) — manzilini Vite o'zi beradi
export const ISHCHI_MANZILI: string = ishchiManzili;

const RUXSAT: ReadonlySet<string> = new Set([
  SW_MANZILI,
  ISHCHI_MANZILI,
]);

Oq ro'yxat aniq va qat'iy — xavfsizlik nuqtai nazaridan yaxshi. Lekin arxitektura nuqtai nazaridan bog'liqlik teskari: umumiy modul bitta xususiyatning fayliga qarab turibdi. Statistika xususiyatlar/ ga ko'chsa, bu import xususiyatning ichiga kiradi — ESLint qoidasini buzadi. Yangi ishchi qo'shilsa — umumiy modulni o'zgartirish kerak.

7.2 Yechim: ro'yxat o'rniga naqsh

Holat 33 da oq ro'yxat naqshga aylandi:

ts
function ruxsatmi(url: string): boolean {
  if (url === SW_MANZILI) {
    return true;
  }
  // Ilova papkasi (Pages'da /vazifalar/, lokal — /): base: "./"
  // (vite.config.ts) — sahifa manziliga nisbatan
  const assets = new URL("assets/", document.baseURI);
  const manzil = new URL(url, document.baseURI);
  // npm run dev: Vite ishchini manba manzilidan beradi
  // (/src/...ishchi.ts?worker_file&type=module) — faqat dev server'da
  if (import.meta.env.DEV && manzil.origin === location.origin) {
    return true;
  }
  return (
    manzil.origin === location.origin &&
    manzil.pathname.startsWith(assets.pathname) &&
    manzil.pathname.endsWith(".js") &&
    manzil.search === ""
  );
}

Ruxsat: sw.js va o'z saytimizdagi assets/ papkasidagi .js fayllar — Vite build chiqargan har qanday ishchi. Begona sayt, blob:, data:, boshqa papka, so'rov qatori (?…) — rad. Endi xavfsiz-manzil.ts statistika haqida hech narsa bilmaydi. Ishchi manzilini esa xususiyatning o'zi (statistika-vadasi.ts) import qiladi va siyosatdan o'tkazadi. Bog'liqlik to'g'ri yo'nalishda: xususiyat → umumiy.

Bu almashuvda xavfsizlik biroz kengaydi (bitta fayl o'rniga papka). Kanon buni qabul qildi: assets/ ga faqat build yozadi, va uchala shart (origin, papka, kengaytma) birga tekshiriladi. Brauzer tekshiruvi blob: va boshqa nomli siyosat bilan ishchi yaratishni sinab ko'radi — ikkalasi ham TypeError. Arxitektura qarori ham xavfsizlik qarori kabi o'lchab qilinadi.

8. Vazifalar qadami: xususiyat papkasi (holat 33)

refactor/tugma-tokenlar branch'ida davom etamiz. Ko'chirish:

text
src/komponentlar/StatistikaPaneli.tsx   → src/xususiyatlar/statistika/
src/komponentlar/BatafsilStatistika.tsx → src/xususiyatlar/statistika/
src/batafsil-statistika.ts              → src/xususiyatlar/statistika/
src/statistika-ishchi.ts                → src/xususiyatlar/statistika/
src/statistika-vadasi.ts                → src/xususiyatlar/statistika/
+ src/xususiyatlar/statistika/index.ts

git mv bilan ko'chiring — git tarixni fayl bilan birga saqlaydi (Yaxshi commit). Keyin importlar. Xususiyat ichida nisbiy yo'llar chuqurlashdi: ../turlar.ts → ../../turlar.ts. Tashqarida — faqat bitta joy, App.tsx:

diff
-import { StatistikaPaneli } from "./komponentlar/StatistikaPaneli.tsx";
+import { StatistikaPaneli } from "./xususiyatlar/statistika/index.ts";

ESLint qoidasi, xavfsiz-manzil.ts naqshi va knip — yuqoridagi bo'limlarda. Testlar o'zgardi faqat import yo'li bo'yicha (StatistikaPaneli.test.tsx, batafsil-statistika.test.js). Brauzer tekshiruvi — 159/159, e2e-18 — 50/50. Foydalanuvchi uchun hech narsa o'zgarmadi — sof refaktorlash. Holat 33 ning umumiy diff'i (Tugma, xususiyat papkasi, tokenlar): 21 fayl, +204 −93. Ko'chirishlar git'da "rename" sifatida ko'rinadi.

ui/ papkasi (Tugma, Menyu, useMenyu, useBoshqariladiganHolat) — FSD tilida shared/ui. Ular biznesdan bexabar: Menyu statistika haqida bilmaydi, uni statistika ishlatadi. Bu tasodif emas — #26–#31 dagi API qarorlari (children, ...rest, prop getters) aynan shuning uchun.

9. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Xususiyatni o'chirib bo'lmaydi boshqa xususiyatlar uning ichki fayllarini import qiladi ochiq API + ESLint chegarasi
Umumiy modulni har xususiyatda o'zgartirish kerak umumiy qatlam xususiyatni biladi (teskari bog'liqlik) naqsh, parametr yoki inversiya
Dev server sekin, aylanma import xatolari katta barrel index.ts (export *) kichik ochiq API, to'g'ridan-to'g'ri importlar
shared/ — "axlat qutisi" "qayerga qo'yishni bilmadim" fayllari faqat biznesdan bexabar narsalar
Arxitektura hujjatda bor, kodda yo'q qoida faqat so'zda lint qoidasi, CI
Kichik loyihada 6 qatlam, 40 bo'sh papka arxitektura "o'sish uchun" oldindan o'sganda qayta tuzing (qachon)

10. Mashqlar

Mashqlar kurs/mashqlar/18/32-arxitektura/ da. JavaScript yechimlarini Node'da ishga tushiring.

1-mashq (oson): FSD qatlami

Har biri qaysi qatlamga tushadi (app, pages, widgets, features, entities, shared)?

  • Router va provider'lar sozlamasi:
  • Tugma, Menyu (biznesdan bexabar):
  • «Taomni savatga qo'shish» harakati:
  • Taom obyekti modeli va uning kartochkasi:
Yechim

Provider va marshrutlar — ilovani yig'ish: app. UI kit biznesni bilmaydi: shared/ui. Foydalanuvchi harakati — features. Biznes obyekti va uning kichik ko'rinishi — entities. Agar «Menyu sahifasi» bo'lsa — pages, «Savat paneli» kabi katta mustaqil blok — widgets.

2-mashq (o'rta): chegara tekshiruvchisi

vazifalar dagi ESLint naqshini funksiya qiling: isAllowed(importPath) — yo'l xususiyatning ichki fayliga qarasa false, aks holda true. «Qoidalarni majburlash» bo'limidagi regex ni ishlating (RegExp).

js
function isAllowed(importPath) {
  // shu yerga yozing
  return true;
}

const paths = [
  "./xususiyatlar/statistika/index.ts",
  "./xususiyatlar/statistika/batafsil-statistika.ts",
  "../../turlar.ts",
  "./statistika-vadasi.ts",
];
for (const p of paths) console.log(isAllowed(p), p);

Konsolda:

text
true ./xususiyatlar/statistika/index.ts
true ./xususiyatlar/statistika/batafsil-statistika.ts
true ../../turlar.ts
true ./statistika-vadasi.ts

Yechimdan keyin ikkinchi qator false bo'lishi kerak.

Yechim
js
const RESTRICTED = /xususiyatlar\/[^/]+\/(?!index[.]ts$)[^/]+$/;

function isAllowed(importPath) {
  return !RESTRICTED.test(importPath);
}

const paths = [
  "./xususiyatlar/statistika/index.ts",
  "./xususiyatlar/statistika/batafsil-statistika.ts",
  "../../turlar.ts",
  "./statistika-vadasi.ts",
];
for (const p of paths) console.log(isAllowed(p), p);

Konsolda:

text
true ./xususiyatlar/statistika/index.ts
false ./xususiyatlar/statistika/batafsil-statistika.ts
true ../../turlar.ts
true ./statistika-vadasi.ts

Literal /…/ da / belgisi \/ bilan qochiriladi, ESLint sozlamasidagi satrda esa shart emas. To'rtinchi yo'l — xususiyat ichidagi nisbiy import: unda xususiyatlar/ so'zi yo'q, shuning uchun ruxsat. Bu naqshning zaif tomoni ham shu yerda: yo'l ../statistika/batafsil-statistika.ts bo'lsa (boshqa xususiyatdan "yon" import), u ham o'tadi. Shuning uchun katta loyihalarda yo'lni emas, faylning haqiqiy joyini tekshiradigan eslint-plugin-boundaries ishlatiladi.

3-mashq (qiyin): qatlam buzilishlarini topish

FSD qatlamlari tartibi: ["app", "pages", "widgets", "features", "entities", "shared"]. Har import — { from, to }, ikkalasi qatlam/slice ko'rinishida (shared da slice yo'q bo'lishi mumkin). findViolations(imports) yozing. U ikki xil buzilishni topsin: pastdan yuqoriga import (yuqoriga) va bir qatlamdagi boshqa slice'dan import (yonma-yon). Bir slice ichidagi import (features/savat → features/savat) — ruxsat.

Ishora: qatlam tartibi — massivdagi indeks (indexOf); split("/") (satr metodlari).

Kutilgan natija:

text
yuqoriga: entities/taom -> features/savat
yonma-yon: features/savat -> features/tolov
2 ta buzilish
Yechim
js
const LAYERS = [
  "app",
  "pages",
  "widgets",
  "features",
  "entities",
  "shared",
];

function findViolations(imports) {
  const found = [];
  for (const { from, to } of imports) {
    const [fromLayer, fromSlice] = from.split("/");
    const [toLayer, toSlice] = to.split("/");
    const a = LAYERS.indexOf(fromLayer);
    const b = LAYERS.indexOf(toLayer);
    if (b < a) {
      found.push(`yuqoriga: ${from} -> ${to}`);
    } else if (b === a && fromSlice !== toSlice) {
      found.push(`yonma-yon: ${from} -> ${to}`);
    }
  }
  return found;
}

const imports = [
  { from: "pages/menyu", to: "widgets/savat-paneli" },
  { from: "entities/taom", to: "features/savat" },
  { from: "features/savat", to: "features/tolov" },
  { from: "features/savat", to: "features/savat" },
  { from: "features/savat", to: "shared/ui" },
];
const violations = findViolations(imports);
for (const v of violations) console.log(v);
console.log(`${violations.length} ta buzilish`);

Konsolda:

text
yuqoriga: entities/taom -> features/savat
yonma-yon: features/savat -> features/tolov
2 ta buzilish

Indeks qanchalik kichik bo'lsa, qatlam shunchalik yuqori. b < a — import qilinayotgan qatlam yuqoriroq: taqiq. Teng indeks — bir qatlam: faqat bir xil slice bo'lsa ruxsat. shared/ui da slice ui — lekin shared eng pastki qatlam, unga hamma kira oladi. Steiger va eslint-plugin-boundaries aynan shu mantiqni haqiqiy import grafi ustida bajaradi.

4-mashq: vazifalar qadami

Statistikaning beshta faylini src/xususiyatlar/statistika/ ga git mv bilan ko'chiring. Keyin index.ts ni yarating va App.tsx dagi importni yangilang. ESLint sozlamasiga no-restricted-imports blokini qo'shing va sinab ko'ring: App.tsx ga ichki faylga import yozing — npm run lint xato berishi kerak. Umumiy xavfsiz-manzil.ts ni naqshga o'tkazing. Oxirida knip'ni o'rnating (npm i -D knip), package.json ga "knip": "knip" skriptini qo'shing va topilmalarni tuzating.

Yechim

Tekshirish tartibi: npm run tip (yo'llar to'g'rimi), npm run lint (chegara), npm run knip (bo'sh chiqishi kerak), npm test, npm run build. Build'dan keyin dist/assets/ da statistika-ishchi-<hash>.js bor — naqsh unga ruxsat beradi. npm run preview da statistika panelini oching: CSP ostida ishchi ishlashi kerak, konsolda TypeError bo'lmasin. Knip export larni olib tashlashni so'rasa — avval grep bilan haqiqatan hech kim ishlatmasligini tekshiring. Testlar ham import qilishi mumkin: knip ularni hisobga oladi, lekin sozlamasi to'g'ri bo'lsa. Commit — #33 da.

11. Real ishda

  • Yirik loyihalar: 10+ dasturchili frontend jamoalarida xususiyat papkalari yoki FSD — standart amaliyot. Yangi xodimga birinchi kuni beriladigan hujjatlardan biri — "arxitektura va import qoidalari".
  • Monorepo: keyingi bosqich — xususiyatlar va shared alohida paketlarga aylanadi (packages/ui, apps/web), chegaralarni esa paket tizimi o'zi qo'riqlaydi (Workspaces va monorepo).
  • Kod ko'rigi: "bu import chegarani buzadi", "bu shared ga tegishlimi?" — arxitektura muhokamalarining ko'pi PR'da bo'ladi.
  • Intervyu savollari: "Katta React loyihani qanday tuzasiz?", "Feature-Sliced Design qatlamlari?", "Barrel fayllarning muammosi?", "Arxitektura qoidalarini qanday majburlaysiz?".

Xulosa

  • Loyiha o'sganda — xususiyat bo'yicha tuzilma: birga o'zgaradigan fayllar bir papkada.
  • bulletproof-react: features/ + umumiy qatlam, xususiyatlar bir-birini import qilmaydi; FSD: 6 qatlam, slice va segment, import faqat pastga.
  • Ochiq API (index.ts) — xususiyatning yagona eshigi; katta barrel'dan farqli, kichik va ataylab tanlangan.
  • Qoidalar lint bilan majburlanadi: no-restricted-imports (yoki eslint-plugin-boundaries, Steiger), ishlatilmagan eksportlar — knip.
  • Umumiy qatlam xususiyatni bilmasin: vazifalar da xavfsiz-manzil.ts ro'yxatdan naqshga o'tdi.
  • vazifalar holat 33: xususiyatlar/statistika/ + index.ts, chuqur import — ESLint xatosi, knip 5 topilmani tuzatdi.

Keyingi dars: Dizayn tizimi asoslari: tokenlar va variantlar — ranglar, oraliqlar va komponent variantlarini tizimli boshqarish.

Manbalar

  • bulletproof-react: "Project Structure" — github.com/alan2207/bulletproof-react/blob/master/docs/project-structure.md
  • Feature-Sliced Design: "Overview", "Layers", "Public API" — feature-sliced.design/docs
  • ESLint: no-restricted-imports — eslint.org/docs/latest/rules/no-restricted-imports
  • knip — knip.dev
  • Kanon: sinov/18/QADAMLAR.md (#31–#33), holatlar/33/eslint.config.js, holatlar/33/src/xususiyatlar/statistika/, natijalar/tekshiruv-33.txt, natijalar/32-eslint.txt (ESLint xabari)
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Katta frontend loyiha arxitekturasi: xususiyat papkalari, qatlamlar va import qoidalari — IlmHamroh