Mundarija (30)
- Bu darsda
- 1. Nega bu kerak?
- 2. Ekotizim: tayyor plaginlar
- 3. Plagin — hook'li obyekt
- 3.1 Eng kichik plagin
- 3.2 Hook'lar tartibi — o'lchab ko'ramiz
- 3.3 enforce — navbatdagi o'rin
- 3.4 apply — qachon yoqiladi
- 3.5 Umumiy hook'lar va Vite'ning o'z hook'lari
- 3.6 Hook filtri — tezlik uchun
- 4. O'z plaginlarimiz
- 4.1 Virtual modul va dev server yo'li
- 4.2 Fayl o'giruvchi: .csv → JS
- 5. Vazifalar qadami: qobiq plagini
- 5.1 Skriptdan funksiyaga
- 5.2 Plaginning o'zi
- 5.3 Ulash va tekshirish
- 6. Hujumchi nigohi
- 7. Ko'p uchraydigan xatolar
- 6.1 Plagin funksiyasini chaqirmaslik
- 6.2 transform da filtr yo'q
- 6.3 Natijani generateBundle da o'zgartirish va hash
- 6.4 console.log o'rniga logger
- 8. Mashqlar
- 1-mashq (oson): Kuzatuvchi
- 2-mashq (o'rta): virtual:bahor-info
- 3-mashq (qiyin): CSV plagini
- 9. Real ishda
- Xulosa
- Manbalar
Vite plaginlari va o'z plaginingizni yozish
Qisqacha: Vite plagini —
nameva bir nechta hook (Vite ma'lum paytda chaqiradigan funksiya) bor oddiy obyekt. Asosiy hook'lar:resolveId— import yo'lini topadi,load— modul matnini beradi,transform— matnni o'zgartiradi. Yana:configureServer— dev serverga yo'l qo'shadi,closeBundle— build tugagach ishlaydi. Sozlamaenforce: "pre"plaginni Vite'ning o'z plaginlaridan oldinga qo'yadi,apply: "build"— faqat build'da yoqadi. Ekotizimdagi plaginlarning (React, SVG, PWA) hammasi shu hook'lardan qurilgan.
Bu darsda
- Mashhur Vite plaginlari nima qilishini va qachon kerakligini bilasiz.
- Plagin hook'lari qaysi tartibda chaqirilishini o'z ko'zingiz bilan ko'rasiz.
- Virtual modul, fayl o'giruvchi (
transform) va dev server yo'li bo'lgan plaginlar yozasiz. enforcevaapplyfarqini tushuntira olasiz.vazifalarning Service Worker qobig'inipostbuildskriptidan Vite plaginiga ko'chirasiz.
Oldin bilishingiz kerak: Vite II: vite.config, Vite V: production build, scripts chuqur, Kesh strategiyalari va offline ishlash.
1. Nega bu kerak?
vazifalar ning package.json ida shunday qator bor:
{
"scripts": {
"postbuild": "node skriptlar/qobiq.js"
}
}Bundler nima darsida yozgan edik: build tugagach npm postbuild ni o'zi ishga tushiradi (pre va post skriptlar), skript esa dist/ dagi hash'li fayl nomlarini Service Worker'ning QOBIQ ro'yxatiga yozadi. Ishlaydi. Lekin bir kuni Sardor build'ni boshqacha boshladi — npx vite build (npm skriptisiz). Natija:
dist/sw.js: const VERSIYA = "manba"; const QOBIQ = ["./", "index.html"];postbuild ishlamadi — u faqat npm run build ga bog'langan. Service Worker endi faqat ikki faylni oldindan keshlaydi. Brauzer tekshiruvida:
❌ SW: qobiqdagi hamma fayl keshda: 7 (kutilgan 2)158 ta , 1 ta . Oflayn rejim baribir ishladi (network-first strategiya fayllarni ishlatilganda keshlaydi), shuning uchun xato ko'zga tashlanmasdi. Lekin birinchi tashrifdagi oldindan keshlash to'liq emas.
Muammo ildizi: build'ning bir qismi Vite'dan tashqarida. Yechim — uni ichkariga olish: Vite'ga "build tugaganda shu ishni ham qil" deb o'rgatish. Buni plagin qiladi.
Oshxona o'xshatishi: Vite — oshpaz, uning ish kuni bosqichlardan iborat (mahsulot olish, to'g'rash, pishirish, suzish). Plagin — oshpazga beriladigan qo'shimcha ko'rsatma: "to'g'rashdan keyin piyozni alohida qo'y", "suzgandan keyin idishga yorliq yopishtir". Har ko'rsatma ma'lum bosqichga bog'langan — bu bosqichlar hook'lar.
2. Ekotizim: tayyor plaginlar
O'z plaginingizni yozishdan oldin — tayyorlari. Haftalik yuklanishlar — npm statistikasidan (api.npmjs.org, 2026-09-29…10-05), versiyalar — npm view (2026-10).
| Plagin | Nima qiladi | Haftalik yuklanish |
|---|---|---|
@vitejs/plugin-react 6.1.2 |
JSX, React Fast Refresh (HMR) | 116.9 mln |
vite-tsconfig-paths 6.1.1 |
tsconfig dagi paths ni Vite'ga o'rgatadi |
36.2 mln |
@vitejs/plugin-react-swc 4.3.3 |
xuddi shu, SWC bilan | 13.4 mln |
vite-plugin-svgr 5.2.0 |
SVG'ni React komponenti qiladi | 6.7 mln |
vite-plugin-pwa 2.0.0 |
Service Worker va manifest (Workbox) | 5.1 mln |
vite-plugin-checker 0.14.5 |
tsc/ESLint'ni dev server yonida ishlatadi |
4.7 mln |
Ba'zilari haqida bir-ikki gap:
plugin-reactvaplugin-react-swc— React 17-qismda; hozir bilish shart emas. 6.x versiya (Vite 8 uchun) Babel'ni o'zi bilan olib yurmaydi:dependenciesda faqat@rolldown/pluginutils, Babel va React Compiler — ixtiyoriypeerDependencies. 5.x da esa@babel/coremajburiy edi. Ya'ni Vite 8 da JSX'ni Oxc o'giradi. SWC varianti esa@swc/coreni oladi — Vite 8 bilan uning afzalligi kamaydi.vite-tsconfig-paths—@/taxalluslari uchun (Path alias).vite-plugin-svgr— SVG va ikonka pipeline darsida.vite-plugin-pwa— Kesh strategiyalari darsida "self.__WB_MANIFESTga qobiq ro'yxatini Vite plagini yokiworkbox-buildqo'yadi" degan edik. Mana u. Biz esa xuddi shu ishni 30 qatorli o'z plaginimiz bilan qilamiz — bu darsning oxirida.vite-plugin-checker— Vite turlarni tekshirmaydi (Transpilyatsiya); bu plagintscxatolarini dev'da brauzerda ko'rsatadi.unplugin-*— unplugin (3.4.0) — bitta plaginni Vite, Rolldown, webpack, esbuild uchun bir yo'la yozish kutubxonasi.unplugin-icons,unplugin-auto-importkabi paketlar shu asosda.
Ulash har doim bir xil: npm i -D …, keyin vite.config.ts da plugins: [react()]. Plagin — funksiya chaqiruvi natijasi (obyekt), funksiyaning o'zi emas.
3. Plagin — hook'li obyekt
3.1 Eng kichik plagin
vazifalar ning vite.config.ts ida allaqachon bitta plagin bor — biz uni Bundler nima darsida yozgan edik:
{
name: "blocking-render",
transformIndexHtml: {
order: "post",
handler: (html) =>
html.replace(
'<script type="module" crossorigin',
'<script type="module" blocking="render" crossorigin',
),
},
},Plagin = obyekt. name — majburiy, xato xabarlarida va ogohlantirishlarda shu nom chiqadi. Qolgan har kalit — hook: Vite ma'lum paytda chaqiradigan funksiya. Hook transformIndexHtml — "index.html tayyor bo'lganda, matnini menga ber, men o'zgartirib qaytaraman". Kalit order: "post" — "Vite o'z o'zgarishlarini qilib bo'lgach" (aks holda Vite <script> ni keyin qayta yozib, atributni yo'qotardi).
3.2 Hook'lar tartibi — o'lchab ko'ramiz
Hook'lar ko'p. Qaysi biri qachon chaqirilishini ko'rish uchun "kuzatuvchi" plagin yozdik — har hook faqat o'z nomini chiqaradi:
// plaginlar/kuzatuvchi.ts — hook'lar qaysi tartibda chaqiriladi
import type { Plugin } from "vite";
export function watcher(): Plugin {
const log = (hook: string, extra = "") =>
console.log(`[kuzatuvchi] ${hook} ${extra}`.trimEnd());
return {
name: "kuzatuvchi",
enforce: "pre",
config() {
log("config");
},
configResolved(config) {
log("configResolved", config.command);
},
buildStart() {
log("buildStart");
},
resolveId(id) {
log("resolveId", id);
return null;
},
load(id) {
if (id.endsWith("menyu.ts")) log("load", "…/src/menyu.ts");
return null;
},
transform(code, id) {
if (id.endsWith("menyu.ts")) {
log("transform", `${code.length} belgi`);
}
return null;
},
transformIndexHtml() {
log("transformIndexHtml");
},
closeBundle() {
log("closeBundle");
},
};
}import type { Plugin } — plagin obyektining turi: muharrir har hook'ning argumentlarini biladi. return null — "men bu bilan ishlamayman, keyingi plaginga ber". bahor-menyu da npx vite build (Vite qatorlari olib tashlangan, yo'l qisqartirilgan):
[kuzatuvchi] config
[kuzatuvchi] configResolved build
[kuzatuvchi] buildStart
[kuzatuvchi] resolveId …\bahor-menyu\index.html
[kuzatuvchi] resolveId /src/main.ts
[kuzatuvchi] resolveId ./style.css
[kuzatuvchi] resolveId ./menyu.ts
[kuzatuvchi] load …/src/menyu.ts
[kuzatuvchi] transform 379 belgi
[kuzatuvchi] transformIndexHtml
[kuzatuvchi] closeBundleDiagramma — shu tartib, guruhlab:
flowchart TB
A["config, configResolved<br/>sozlama"] --> B["buildStart"]
B --> C["har import uchun:<br/>resolveId → load → transform"]
C --> D["chunk'lar yig'iladi"]
D --> E["transformIndexHtml"]
E --> F["fayllar diskka yoziladi"]
F --> G["closeBundle"]O'rtadagi uchlik — eng muhimi. Har import uchun Vite uch savol beradi:
| Hook | Savol | Misol javob |
|---|---|---|
resolveId(id) |
bu import qaysi fayl? | "./menyu.ts" → C:/…/src/menyu.ts |
load(id) |
uning matni qanday? | fayl tarkibi (diskdan) |
transform(code, id) |
matnni qanday o'zgartiray? | TS → JS |
Har savolga plaginlar navbat bilan javob beradi. Birinchi bo'lib javob (null emas) qaytargan plagin yutadi; transform esa zanjir — har plagin oldingisining natijasini oladi.
3.3 enforce — navbatdagi o'rin
Kuzatuvchida enforce: "pre" bor. Uni olib tashlasak, natija o'zgaradi: resolveId qatorlari umuman yo'qoladi, transform esa 379 belgi emas, 268 belgi ni ko'radi. Nega?
Vite'ning o'z ichki plaginlari bor (yo'l topuvchi, TS o'giruvchi, CSS…). Foydalanuvchi plaginlari sukut bo'yicha ulardan keyin turadi. Ichki yo'l topuvchi har importni o'zi topib, javob qaytaradi — navbat bizgacha yetmaydi. TS o'giruvchi esa bizdan oldin turlarni olib tashlaydi: 379 belgilik TypeScript 268 belgilik JavaScript'ga aylanadi.
enforce |
Navbat | Qachon kerak |
|---|---|---|
"pre" |
Vite'ning ichki plaginlaridan oldin | asl manbani ko'rish, importni o'zi topish |
| yo'q (sukut) | ichki plaginlardan keyin | JS bilan ishlash (eng ko'p uchraydi) |
"post" |
build plaginlaridan ham keyin | yakuniy natijani o'zgartirish |
3.4 apply — qachon yoqiladi
apply: "build" — plagin faqat vite build da ishlaydi, apply: "serve" — faqat dev'da. Funksiya ham bo'lishi mumkin: apply: (config, { command }) => …. Qobiq plaginimiz apply: "build" bo'ladi: dev'da dist/ yo'q.
3.5 Umumiy hook'lar va Vite'ning o'z hook'lari
Hook'lar ikki oiladan. Birinchisi — universal: ular Rollup'dan kelgan, Rolldown ham, Vite ham tushunadi. Shuning uchun ko'p Rollup plagini Vite'da o'zgarishsiz ishlaydi (rollup-plugin-visualizer ni oldingi darsda shunday ishlatdik). Ikkinchisi — faqat Vite'niki: ular dev server va HTML bilan bog'liq, bundler'da bunday tushuncha yo'q.
| Universal (Rollup/Rolldown) | Vite'ning o'zi |
|---|---|
buildStart, buildEnd |
config, configResolved |
resolveId, load, transform |
configureServer, configurePreviewServer |
renderChunk, generateBundle |
transformIndexHtml |
writeBundle, closeBundle |
handleHotUpdate (HMR) |
Dev rejimda bundler ishlamaydi, lekin Vite universal hook'larni o'zi chaqiradi: brauzer faylni so'raganda — resolveId, load, transform. Shuning uchun bitta plagin dev'da ham, build'da ham ishlaydi. Faqat chunk bilan bog'liqlari (renderChunk, generateBundle) dev'da chaqirilmaydi — u yerda chunk yo'q.
3.6 Hook filtri — tezlik uchun
Vite 8 da bundler Rust'da (Rolldown), plaginlar esa JavaScript'da. Har transform chaqiruvi — Rust va JavaScript orasida ma'lumot tashish. Plagin 100 ta moduldan bittasi bilan ishlasa ham, u 100 marta chaqiriladi va 99 marta return null qiladi. Buning oldini olish uchun hook obyekt bo'lishi mumkin — filter va handler bilan:
transform: {
filter: { id: /\.csv$/ },
handler(code) {
return `export default ${JSON.stringify(code.split("\n"))};`;
},
},Endi filtrni Rolldown o'zi (Rust tomonida) tekshiradi va handler faqat .csv fayllar uchun chaqiriladi. Sinadik: bahor-menyu da 7 ta modul bor, hisoblagich handler ni 1 marta chaqirilganini ko'rsatdi. Kichik loyihada farq sezilmaydi, minglab modulli loyihada — sekundlar. Yangi plagin yozsangiz, filtrdan foydalaning; ichidagi if (!id.endsWith(...)) tekshiruvini esa qoldirish mumkin — eski Vite versiyalari filtrni bilmaydi.
Tekshirib ko'ring:
.tsfayllardagi// TODOizohlarini sanaydigan plagin yozmoqchisiz.transformhook'ienforcesiz izohlarni ko'radimi?
Javob
Ishonib bo'lmaydi. enforce siz plagin Vite'ning TS o'giruvchisidan keyin turadi — u allaqachon o'girilgan JavaScript'ni oladi, unda izohlar saqlanishi kafolatlanmagan (o'giruvchi turlarni olib tashlaydi, kodni qayta yozadi). Asl manbani ko'rish uchun — enforce: "pre". Kuzatuvchidagi 379 va 268 belgi farqi aynan shuni ko'rsatadi.
4. O'z plaginlarimiz
4.1 Virtual modul va dev server yo'li
Virtual modul — diskda yo'q, lekin import qilinadigan modul: uning matnini plagin "o'ylab chiqaradi". Masalan, build sanasi:
// plaginlar/bahor-info.ts — virtual modul va dev server yo'li
import type { Plugin } from "vite";
const VIRTUAL_ID = "virtual:bahor-info";
// "\0" — boshqa plaginlar bu id'ni fayl deb o'ylamasin (kelishuv)
const RESOLVED_ID = "\0" + VIRTUAL_ID;
export function bahorInfo(): Plugin {
return {
name: "bahor-info",
resolveId(id) {
if (id === VIRTUAL_ID) return RESOLVED_ID;
return null;
},
load(id) {
if (id !== RESOLVED_ID) return null;
const date = new Date().toISOString().slice(0, 10);
return `export const buildDate = ${JSON.stringify(date)};`;
},
configureServer(server) {
// faqat npm run dev: /__holat — server tirikmi?
server.middlewares.use("/__holat", (_req, res) => {
res.setHeader("Content-Type", "text/plain; charset=utf-8");
res.end("«Bahor» dev server ishlayapti");
});
},
};
}Qatorma-qator:
resolveId—import … from "virtual:bahor-info"ni ko'rsa, "bu meniki" deb belgilangan id qaytaradi. Boshqa importlarga —null."\0"— nol belgi. Bu Rollup/Rolldown kelishuvi:\0bilan boshlangan id — "haqiqiy fayl emas". Boshqa plaginlar (masalan, CSS yoki rasm o'giruvchilar) unga tegmaydi.load— shu id uchun modul matnini qaytaradi: oddiy JavaScript satri.JSON.stringify—definedarsidagi sabab bilan.configureServer(server)— faqat dev'da chaqiriladi.server.middlewares.use(yo'l, funksiya)— dev serverga yangi manzil qo'shadi. Middleware — so'rov va javob orasida ishlaydigan funksiya (backend qismlarida Express bilan ko'p yozasiz). Bu yerda(_req, res)— Node'ning oddiy so'rov va javob obyektlari.
TypeScript virtual:bahor-info ni bilmaydi — turini declaration fayli bilan beramiz, src/virtual.d.ts:
// virtual:bahor-info — plaginlar/bahor-info.ts yasaydi
declare module "virtual:bahor-info" {
export const buildDate: string;
}Natija: tsc --noEmit toza; build ichida — var n=`2026-10-10`; dev'da curl http://localhost:5773/__holat — «Bahor» dev server ishlayapti. Import nomida xato bo'lsa (virtual:bahor-inf) — hech bir plagin javob bermaydi:
Error: [vite]: Rolldown failed to resolve import "virtual:bahor-inf" from "C:/…/src/xato.ts".
This is most likely unintended because it can break your application at runtime.Tarjimasi: "Rolldown virtual:bahor-inf importini topa olmadi. Bu, ehtimol, kutilmagan holat — ilovani ishga tushganda buzishi mumkin". Har resolveId null qaytardi.
4.2 Fayl o'giruvchi: .csv → JS
Jasur aka menyuni Excel'da yuritadi va CSV qilib saqlaydi. Uni to'g'ridan-to'g'ri import qilishni xohlaymiz: import menuRows from "./menyu.csv". Vite CSV'ni tanimaydi — o'rgatamiz:
// plaginlar/csv.ts — .csv faylni JS massivga aylantiradi
import type { Plugin } from "vite";
export function csv(): Plugin {
return {
name: "bahor-csv",
transform(code, id) {
if (!id.endsWith(".csv")) return null;
const [header, ...lines] = code.trim().split(/\r?\n/);
const keys = header!.split(",");
const rows = lines.map((line, index) => {
const cells = line.split(",");
if (cells.length !== keys.length) {
this.error(`${index + 2}-qator: ${cells.length} ta ustun`);
}
return Object.fromEntries(keys.map((k, i) => [k, cells[i]]));
});
return `export default ${JSON.stringify(rows)};`;
},
};
}transformhamma modulni ko'radi — birinchi qator "faqat.csv" filtri. Filtrsiz plagin har TS faylni ham buzardi.split(/\r?\n/)— Windows (\r\n) va Linux (\n) qatorlari (RegExp asoslari).this.error(...)— plagin kontekstidagi xato: build to'xtaydi, xabarda plagin nomi va fayl ko'rinadi.- Qaytgan qiymat — JavaScript matni:
export default [...].
src/menyu.csv:
nom,narx
Osh,35000
Lagmon,28000
Manti,30000Build ichida: [{nom:`Osh`,narx:`35000`},{nom:`Lagmon`,narx:`28000`},{nom:`Manti`,narx:`30000`}]. Narx — satr (CSV'da tur yo'q, .env dagi kabi). Faylga ortiqcha ustunli qator qo'shsak (Choy,5000,issiq):
[plugin bahor-csv] C:/…/src/menyu.csv
RolldownError: 5-qator: 3 ta ustunXato aniq: qaysi plagin, qaysi fayl, qaysi qator. Plagin yozganda xabarni foydalanuvchi (Jasur aka!) tushunadigan qiling.
Tekshirib ko'ring: CSV plagini
transformemas,loadhook'ida yozilsa ham ishlaydimi?
Javob
Ha. load(id) da id.endsWith(".csv") bo'lsa, faylni o'zingiz o'qiysiz (readFileSync(id, "utf8")) va JS matnini qaytarasiz. Farq: transform da Vite faylni allaqachon o'qib bergan (code), load da o'qish sizning zimmangizda. "Bu turdagi faylni men tushunaman" holatida ikkalasi ham to'g'ri; mavjud JS'ni o'zgartirishda esa faqat transform.
5. Vazifalar qadami: qobiq plagini
Branch: build/qobiq-plagini
5.1 Skriptdan funksiyaga
skriptlar/qobiq.js da avval hamma ish if (import.meta.main) { … } ichida edi — fayl to'g'ridan-to'g'ri ishga tushganda. Endi uni ikki funksiyaga ajratamiz: biri ishni qiladi, ikkinchisi — plagin. Birinchisi:
/**
* Build papkasidagi sw.js ni to'ldiradi
* @param {string} papka build papkasi (dist)
* @returns {{ soni: number, versiya: string }}
*/
export function qobiqniYoz(papka) {
const yol = (fayl) => resolve(papka, fayl);
// Windows'da ajratgich "\\" — URL va brauzer uchun "/"
const fayllar = readdirSync(papka, { recursive: true })
.map((fayl) => fayl.replaceAll("\\", "/"))
.filter((fayl) => statSync(yol(fayl)).isFile());
const royxat = qobiqRoyxati(fayllar);
// Tarkib o'zgarsa — VERSIYA ham (yangi kesh): index.html,
// manifest va ikonlar nomida hash yo'q, ular ham hisobga kiradi
const hash = createHash("sha256");
for (const fayl of royxat.slice(1)) {
hash.update(fayl).update(readFileSync(yol(fayl)));
}
const versiya = `v6-${hash.digest("hex").slice(0, 8)}`;
const sw = yol("sw.js");
writeFileSync(sw, swgaYoz(readFileSync(sw, "utf8"), royxat, versiya));
return { soni: royxat.length, versiya };
}Bu — avvalgi kod, faqat papka endi parametr (new URL("../dist/", import.meta.url) o'rniga) va natija qaytariladi. Nega? Plagin dist qayerdaligini Vite sozlamasidan biladi (outDir boshqa bo'lishi mumkin), test esa vaqtinchalik papka beradi. qobiqRoyxati va swgaYoz — o'zgarmagan. Fayl boshiga import { resolve } from "node:path"; qo'shiladi.
readdirSync(papka, { recursive: true })— ichki papkalar bilan hamma nom.createHash("sha256")— tarkibdan "barmoq izi" (Bundler nima dagi VERSIYA g'oyasi): bitta bayt o'zgarsa — yangi VERSIYA, brauzer yangi kesh ochadi.
5.2 Plaginning o'zi
/**
* Vite plagini: build oxirida (closeBundle — hamma fayl,
* public/ dan ko'chirilgan sw.js ham, diskka yozilgan) qobiqni
* yozadi. npm skriptiga (postbuild) bog'liq emas: "npx vite build"
* ham to'liq
* @returns {import("vite").Plugin}
*/
export function qobiqPlagini() {
/** @type {import("vite").ResolvedConfig} */
let sozlama;
return {
name: "vazifalar-qobiq",
apply: "build", // npm run dev da ishlamaydi
configResolved(tayyor) {
sozlama = tayyor;
},
closeBundle() {
const papka = resolve(sozlama.root, sozlama.build.outDir);
const { soni, versiya } = qobiqniYoz(papka);
sozlama.logger.info(
`sw.js: QOBIQ — ${soni} ta, VERSIYA — ${versiya}`,
);
},
};
}Qatorma-qator:
| Qator | Nima qiladi |
|---|---|
@returns {import("vite").Plugin} |
JSDoc tur: .js faylda ham muharrir plagin kalitlarini tekshiradi (JSDoc va @ts-check) |
let sozlama; |
yopilish (closure): ikki hook bitta o'zgaruvchini ulashadi |
name: "vazifalar-qobiq" |
xatolarda shu nom chiqadi |
apply: "build" |
dev'da dist/ yo'q — plagin o'chiq |
| Qator | Nima qiladi |
|---|---|
configResolved(tayyor) |
yakuniy sozlamani saqlab qo'yamiz (root, outDir, logger) |
closeBundle() |
build to'liq tugagach: JS, CSS va public/ dan ko'chgan sw.js — hammasi diskda |
resolve(sozlama.root, sozlama.build.outDir) |
dist ning to'liq yo'li — outDir o'zgarsa ham to'g'ri |
sozlama.logger.info(...) |
Vite'ning o'z logger'i: --logLevel silent ni hurmat qiladi (console.log — yo'q) |
Nega aynan closeBundle? Avvalroq hook'lar (generateBundle, writeBundle) paytida public/ dagi fayllar hali ko'chirilmagan bo'lishi mumkin, sw.js esa aynan public/ dan keladi. closeBundle — oxirgi nuqta, "oshxona yopildi".
5.3 Ulash va tekshirish
vite.config.ts:
import { defineConfig, loadEnv } from "vite";
import { qobiqPlagini } from "./skriptlar/qobiq.js"; plugins: [
// Service Worker qobig'i: hash'li fayl nomlari dist/sw.js ga
qobiqPlagini(),
{
name: "blocking-render",
…package.json dan "postbuild": "node skriptlar/qobiq.js" qatori o'chadi. Endi o'sha sinov:
npx vite build✓ built in 126ms
sw.js: QOBIQ — 12 ta, VERSIYA — v6-39c0ca88Oxirgi qatorni plagin yozdi. VERSIYA — postbuild skripti bergan qiymat bilan aynan bir xil: xulq o'zgarmadi, faqat qayerda ishlashi. Brauzer tekshiruvi — 159/159.
Testga bitta holat qo'shamiz (tekshiruv/qobiq.test.js) — qobiqniYoz vaqtinchalik papkada:
describe("qobiqniYoz (plagin build oxirida chaqiradi)", () => {
test("papkadagi fayllar QOBIQ ga, VERSIYA tarkibga bog'liq", (t) => {
const papka = mkdtempSync(join(tmpdir(), "qobiq-"));
t.after(() => rmSync(papka, { recursive: true, force: true }));
mkdirSync(join(papka, "assets"));
writeFileSync(join(papka, "sw.js"), SW);
writeFileSync(join(papka, "index.html"), "<!DOCTYPE html>");
writeFileSync(join(papka, "assets/index-B3x9.js"), "1");
const birinchi = qobiqniYoz(papka);
const sw = readFileSync(join(papka, "sw.js"), "utf8");
assert.equal(birinchi.soni, 3);
assert.ok(sw.includes(`const VERSIYA = "${birinchi.versiya}";`));
assert.ok(sw.includes('"assets/index-B3x9.js"'));
// Bitta fayl o'zgarsa — yangi VERSIYA (brauzer yangi kesh
// ochadi)
writeFileSync(join(papka, "sw.js"), SW);
writeFileSync(join(papka, "index.html"), "<!DOCTYPE html>!");
assert.notEqual(qobiqniYoz(papka).versiya, birinchi.versiya);
});
});t.after(...) — test tugagach (yiqilsa ham) vaqtinchalik papkani o'chiradi. Importlarga mkdirSync, mkdtempSync, rmSync, writeFileSync, tmpdir, join va qobiqniYoz qo'shiladi. Plaginning o'zini emas, uning ishini test qilamiz: plagin — yupqa qobiq, mantiq — oddiy funksiyada. Testlar 145 → 146.
git add .
git commit -m \
"build: qobiq — postbuild skripti o'rniga Vite plagini (closeBundle)"Diff: 4 fayl, +83 −20.
6. Hujumchi nigohi
Plagin — brauzerda emas, sizning kompyuteringizda, Node ichida ishlaydigan kod. Uning cheklovi yo'q: configResolved da .env fayllarni o'qiy oladi (loadEnv(mode, root, "") — VITE_ siz sirlar ham), closeBundle da istalgan faylni yozadi, fetch bilan ma'lumotni tashqariga yuboradi. Va bu npm run dev ni har bosganingizda — CI'da ham.
Hujumchi nima qiladi? Mashhur plagin nomiga o'xshash paket chiqaradi (bitta harfi farq qiladigan nom bilan) yoki haqiqiy plagin muallifining npm hisobini egallab, zararli versiya chiqaradi. Ta'minot zanjiri darsidagi hodisalar aynan shunday bo'lgan. Plagin hech qanday postinstall skriptisiz ham xavfli: zararli kod build vaqtida ishga tushadi.
Himoya — o'sha darsdagi qoidalar, plaginlarga alohida e'tibor bilan:
- Plagin qo'shishdan oldin — nomi, yuklanishlar soni, repo va muallif (Paket tanlash). 10 qatorli ishni o'zingiz yozish ko'pincha xavfsizroq:
vazifalardagi ikkala plagin o'zimizniki. - Aniq versiya (
save-exact) va lock-fayl — kutilmagan yangi versiya jim o'rnatilmaydi. - CI'da sirlar faqat kerakli qadamga beriladi: build qadamiga deploy kaliti kerak emas.
7. Ko'p uchraydigan xatolar
6.1 Plagin funksiyasini chaqirmaslik
plugins: [qobiqPlagini] — funksiyaning o'zi, obyekt emas. To'g'risi — qobiqPlagini(). Plagin "fabrika" funksiyasi parametr olishi mumkin (csv({ separator: ";" })) — shuning uchun hamma joyda shunday.
6.2 transform da filtr yo'q
Plagin har modulni (CSS, rasm, node_modules) o'zgartirib yuboradi. Birinchi qator — if (!id.endsWith(".csv")) return null;.
6.3 Natijani generateBundle da o'zgartirish va hash
Chunk kodini generateBundle hook'ida o'zgartirsangiz, fayl nomidagi hash eski qoladi — u oldinroq hisoblangan. Sinab ko'rdik: kodga izoh qo'shildi, hajm 1.02 → 1.05 kB, hash esa o'sha (index-BU9yMBgv.js). Kesh nuqtai nazaridan bu xavfli: tarkib boshqa, nom bir xil. Kodni o'zgartirish — transform yoki renderChunk da.
6.4 console.log o'rniga logger
console.log plagin ichida --logLevel silent da ham chiqadi va Vite'ning chiqishini buzadi. this.warn(), this.error() (hook ichida) yoki config.logger.
8. Mashqlar
Mashqlar kurs/mashqlar/16/31-plagin/ da — bahor-menyu nusxasi, plaginlar plaginlar/ papkasida.
1-mashq (oson): Kuzatuvchi
Darsdagi kuzatuvchi.ts ni yozing va npx vite build qiling. Keyin enforce: "pre" ni olib tashlab, yana build qiling. Ikki chiqishni solishtiring: qaysi qatorlar yo'qoldi? transform necha belgi ko'rdi?
Yechim
enforce: "pre" bilan — darsdagi ro'yxat (resolveId qatorlari, transform 379 belgi). enforce siz:
[kuzatuvchi] config
[kuzatuvchi] configResolved build
[kuzatuvchi] buildStart
[kuzatuvchi] load …/src/menyu.ts
[kuzatuvchi] transform 268 belgi
[kuzatuvchi] transformIndexHtml
[kuzatuvchi] closeBundleresolveId umuman chaqirilmadi — Vite'ning ichki yo'l topuvchisi har importni bizdan oldin topdi. transform 268 belgi ko'rdi: turlar allaqachon olib tashlangan JavaScript. load esa ikkala holatda ham bor — u qatorni faqat ko'rdi (null qaytardi).
2-mashq (o'rta): virtual:bahor-info
Darsdagi bahor-info plaginini yozing, src/virtual.d.ts qo'shing va main.ts da buildDate ni sahifa pastiga chiqaring. Sinang: (1) npx tsc --noEmit toza; (2) build ichida bugungi sana; (3) npm run dev da http://localhost:5173/__holat ochiladi.
Yechim
main.ts ga:
import { buildDate } from "virtual:bahor-info";
const footer = document.createElement("small");
footer.textContent = `Build: ${buildDate}`;
document.body.append(footer);vite.config.ts — plugins: [bahorInfo()]. Natija: tsc toza; JS ichida 2026-10-10 (sizda o'z sanangiz); curl http://localhost:5173/__holat → «Bahor» dev server ishlayapti. E'tibor bering: dev'da buildDate server ishga tushgan kun, build'da — build kuni. load har safar chaqiriladi.
3-mashq (qiyin): CSV plagini
plaginlar/csv.ts ni yozing: .csv fayl — export default [...] (birinchi qator — kalitlar). Ustunlar soni mos kelmagan qatorda build qator raqami bilan to'xtasin. TypeScript uchun src/csv.d.ts da declare module "*.csv" yozing. menyu.csv dagi taomlarni sahifaga chiqaring.
Ishora: Declaration fayllari dagi declare module "*.svg" — xuddi shunday, faqat tur Record<string, string>[].
Yechim
Plagin — darsdagi csv.ts. Turlar:
// .csv importi — plaginlar/csv.ts beradi
declare module "*.csv" {
const rows: Record<string, string>[];
export default rows;
}main.ts:
import menuRows from "./menyu.csv";
for (const row of menuRows) {
const item = document.createElement("li");
item.textContent = `${row.nom} — ${row.narx} so'm`;
list.append(item);
}Natija: build ichida [{nom:`Osh`,narx:`35000`},…]; ortiqcha ustunli qator bilan — RolldownError: 5-qator: 3 ta ustun. Qator raqami index + 2: massiv indeksi 0 dan, birinchi qator esa sarlavha. Narxni formatPrice ga berish uchun — Number(row.narx).
9. Real ishda
- Har Vite loyihasida kamida bitta plagin bor (React, Vue). Ularni o'qiy olish — konfiguratsiya muammolarini tuzatishning kaliti: "nega bu fayl o'zgarmayapti?" — ko'pincha
enforceyoki filtr. - O'z plaginlari katta jamoalarda odatiy: build ma'lumoti, tarjima fayllarini yig'ish, ichki API turlarini generatsiya qilish. Ko'pi 20–50 qator.
- Plagin API Rollup'niki — Vite, Rolldown, unplugin uni bo'lishadi. Bir marta o'rgansangiz, boshqa vositalarda ham tanish.
- Intervyu savollari: "Virtual modul nima?", "
resolveId/load/transformfarqi?", "Vite plagini va Rollup plagini farqi?" (javob: Vite qo'shimcha hook'lar —config,configureServer,transformIndexHtml— beradi).
Xulosa
- Plagin —
nameva hook'lar bor obyekt; ekotizimdagi hamma plagin shu asosda. - Har import:
resolveId→load→transform;null— "keyingisiga ber";transform— zanjir. enforce: "pre"— Vite'ning ichki plaginlaridan oldin (asl manba), sukut — keyin (JS);apply— faqat build yoki faqat dev.- Virtual modul (
\0id),configureServer(dev middleware),this.error(aniq xato) — o'z plaginlarimizda sinaldi. vazifalar: Service Worker qobig'ipostbuildo'rnigacloseBundleplaginida —npx vite buildham to'liq, VERSIYA bir xil.
Keyingi dars: Dev server proxy, HTTPS va CORS dev muhitda — server.proxy bilan API so'rovlarini dev serverdan uzatamiz, telefondan sinash va HTTPS.
Manbalar
- Vite hujjatlari: "Plugin API" (hook'lar,
enforce,apply, virtual modullar) — vite.dev/guide/api-plugin - Rolldown hujjatlari: "Plugin API" — rolldown.rs
- npm yuklanish statistikasi: api.npmjs.org/downloads/point/2026-09-29:2026-10-05/
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!