Mundarija (21)
- Bu darsda
- 1. Nega bu kerak?
- 2. Xato — xaritasiz va xarita bilan
- 2.1 Kichik kassa
- 2.2 Xaritasiz
- 2.3 Xarita bilan
- 3. .map fayli ichida
- 3.1 Tuzilma
- 3.2 mappings: siqilgan jadval
- 4. build.sourcemap: to'rt qiymat
- 5. Xavfsizlik: xaritada nima bor
- 6. Production'dagi xatolar: Sentry
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): Qaysi qiymat?
- 2-mashq (o'rta): Ikki stack trace
- 3-mashq (qiyin): Xaritadan joyni topish
- 4-mashq: Vazifalar qadami — source map'lar
- 9. Real ishda
- Xulosa
- Manbalar
Source map'lar: minifikatsiya qilingan koddagi xatoni asl qatorga qaytarish
Qisqacha: Source map (manba xaritasi) —
.mapfayli: u siqilgan koddagi har joy asl fayldagi qaysi qator va ustunga mos kelishini yozib qo'yadi. Build fayli oxiridagi//# sourceMappingURL=…izohi xaritani ko'rsatadi, DevTools (vanode --enable-source-maps) uni o'qib,kassa.js:1:31o'rniganarx.ts:8:15ni ko'rsatadi. Vite'dabuild.sourcemap:true(fayl + havola),"hidden"(fayl bor, havola yo'q — xato kuzatish xizmatiga yuklash uchun),"inline"(xarita JS ichida),false. Xaritada asl kod ham bor — yopiq loyihada uni ochiq qoldirmang.
Bu darsda
- Source map nima ekanini va
.mapfaylining tuzilishini o'qiysiz. - Bir xil xatoni source map'siz va bilan solishtirasiz (Node'da, haqiqiy stack trace).
build.sourcemapning to'rt qiymatini va har birining hajmini bilasiz.- Production'da source map xavfini va Sentry kabi xizmatlarga "yashirin" yuklashni tushunasiz.
vazifalarga source map qo'shasiz va Service Worker uni keshlamasligini ta'minlaysiz.
Oldin bilishingiz kerak: Stack trace va JavaScript xato turlari, Tree shaking va minifikatsiya, Bundler nima.
1. Nega bu kerak?
Oldingi darsda minifikatsiyani ko'rdik: applyDiscount(orderTotal, discountPercent) → e(e,t), butun fayl — bitta qator. Hajm ikki baravar kamaydi. Lekin bitta muammo tug'ildi.
Tasavvur qiling: «Bahor» saytida mijozda xato chiqdi. Sardor xato xabarini ko'rdi: RangeError … at e (kassa.js:1:31). "1-qator, 31-ustun" — butun fayl bitta qator, e esa nimadir funksiya. Qaysi .ts fayl? Qaysi funksiya? Bilib bo'lmaydi.
Stack trace darsida bunday holat uchun bir va'da bor edi: siqilgan fayl oxirida xaritaga havola turadi, DevTools uni topsa — asl faylni ko'rsatadi. Bugun shu xaritani ochamiz.
Hayotiy o'xshatish: kitobning qisqartirilgan nashri. Unda sahifa raqamlari boshqa. Kimdir "qisqa nashrning 12-sahifasi" desa, to'liq nashrda qayerligini topish uchun moslik jadvali kerak: "qisqa 12 → to'liq 47–49". Source map — aynan shu jadval.
Maslahat: Chiqishlar 2026-10-10 da Node 24.21.0 va Vite 8.3.3 bilan. Stack trace'lardagi to'liq yo'llarni
…bilan qisqartirdik.
2. Xato — xaritasiz va xarita bilan
2.1 Kichik kassa
Ikki fayl. narx.ts:
// narx.ts — buyurtma summasi
export function orderTotal(
prices: number[],
discount: number,
): number {
if (discount > 50) {
// Chegirma 50% dan oshmasin — kassir xatosi
throw new RangeError(`Chegirma juda katta: ${discount}%`);
}
const sum = prices.reduce((a, b) => a + b, 0);
return sum - (sum * discount) / 100;
}
console.log(orderTotal([35000, 28000], 10));
console.log(orderTotal([35000], 70));Konsolda:
56700
RangeError: Chegirma juda katta: 70%Loyihada oxirgi ikki qator alohida main.ts faylida (import { orderTotal } from "./narx.ts"). Vite bilan build.sourcemap: true qilib yig'dik (Node'da ishga tushirish uchun bitta kassa.js chiqadigan qilib sozladik):
dist/kassa.js 0.20 kB │ gzip: 0.18 kB │ map: 0.94 kBdist/kassa.js ning to'liq matni — ikki qator:
function e(e,t){if(t>50)throw RangeError(`Chegirma juda katta: ${t}%`);let n=e.reduce((e,t)=>e+t,0);return n-n*t/100}console.log(e([35e3,28e3],10)),console.log(e([35e3],70));
//# sourceMappingURL=kassa.js.mapOxirgi qator — sourceMappingURL izohi: "bu faylning xaritasi yonidagi kassa.js.map da". JavaScript uchun bu oddiy izoh, bajarilmaydi. Uni faqat xaritani tushunadigan vositalar o'qiydi.
2.2 Xaritasiz
node dist/kassa.js56700
file:///…/dist/kassa.js:1
function e(e,t){if(t>50)throw RangeError(`Chegirma juda katta: ${t}%`);let n=e.reduce((e,t)=>e+t,0);return n-n*t/100}console.log(e([35e3,28e3],10)),console.log(e([35e3],70));
^
RangeError: Chegirma juda katta: 70%
at e (file:///…/dist/kassa.js:1:31)
at file:///…/dist/kassa.js:1:161at e (kassa.js:1:31) — funksiya e, 1-qator, 31-ustun. Xabar matni saqlangan (satrlar minifikatsiyada o'zgarmaydi), lekin joy — foydasiz.
2.3 Xarita bilan
Node xaritani sukut bo'yicha o'qimaydi — --enable-source-maps bayrog'i kerak:
node --enable-source-maps dist/kassa.js56700
…\src\narx.ts:8
throw new RangeError(`Chegirma juda katta: ${discount}%`);
^
RangeError: Chegirma juda katta: 70%
at orderTotal (…\src\narx.ts:8:15)
at <anonymous> (…\src\main.ts:5:13)Nima bo'ldi? Bir xil fayl, bir xil xato — lekin endi: narx.ts, 8-qator, asl matn (discount, new RangeError) va asl funksiya nomi orderTotal. Ikkinchi qator — chaqirgan joy: main.ts:5:13. Brauzerning DevTools'i ham xuddi shunday qiladi — Console'dagi xato havolasi va Sources paneli asl .ts faylni ko'rsatadi.
Tekshirib ko'ring:
dist/kassa.jsdagiRangeErroroldidanewyo'q (throw RangeError(…)), asl kodda esa bor. Bu xato emasmi?
Javob
Yo'q. RangeError(…) va new RangeError(…) bir xil natija beradi — o'rnatilgan xato klasslari new siz chaqirilsa ham obyekt yaratadi. Minifikator buni biladi va 4 belgini tejaydi. Source map esa asl matnni ko'rsatadi — new bilan. Xarita "o'girilgan kod qayerdan kelgan"ni aytadi, kodni qayta tiklamaydi.
3. .map fayli ichida
3.1 Tuzilma
kassa.js.map — JSON. Boshi (qisqartirilgan):
{"version":3,"file":"kassa.js","names":["orderTotal","discount","sum","prices","a","b"],"sources":["../src/narx.ts","../src/main.ts"],"sourcesContent":["// narx.ts — buyurtma summasi\nexport function orderTotal(
prices: number[], …","…"],"mappings":"AACA,SAAgBA,EACd,EACA,EACQ,CACR,GAAIC,…"}| Kalit | Nima |
|---|---|
version |
format versiyasi — doim 3 |
file |
qaysi faylning xaritasi |
sources |
asl fayllar (xaritaga nisbatan yo'l) |
sourcesContent |
asl fayllarning to'liq matni |
names |
asl nomlar: orderTotal, discount… |
mappings |
moslik jadvali, siqilgan ko'rinishda |
3.2 mappings: siqilgan jadval
AACA,SAAgBA,EACd,… — tasodifiy harflar emas. Har vergul orasidagi bo'lak — bitta moslik: "siqilgan fayldagi N-ustun → sources dagi K-fayl, L-qator, M-ustun, names dagi J-nom". Raqamlar Base64 VLQ kodlashda (raqamni harflar bilan qisqa yozish usuli) va oldingisiga nisbatan farq sifatida saqlanadi — shuning uchun xarita kichik. ; — siqilgan fayldagi yangi qator.
Uni qo'lda o'qish shart emas. Node'da tayyor SourceMap klassi bor (node:module):
import { SourceMap } from "node:module";
const map = new SourceMap(mapJson);
// SourceMap 0 dan sanaydi, stack trace esa 1 dan
const entry = map.findEntry(0, 30); // 1-qator, 31-ustun
console.log(entry.originalSource, entry.originalLine + 1);Natija: ../src/narx.ts 8. 3-mashqda shu klass bilan stack trace qatorini asl joyga qaytaradigan kichik vosita yozasiz.
flowchart LR
E["xato: kassa.js:1:31"] --> U["sourceMappingURL<br/>→ kassa.js.map"]
U --> M["mappings:<br/>1:31 → 0-fayl, 8:15"]
M --> S["sources[0]<br/>../src/narx.ts"]
S --> C["sourcesContent:<br/>asl matn"]4. build.sourcemap: to'rt qiymat
Kassa loyihasini har qiymat bilan yig'dik:
| Qiymat | kassa.js |
.map fayl |
sourceMappingURL |
|---|---|---|---|
false (sukut) |
0.17 kB | yo'q | yo'q |
true |
0.20 kB | 0.94 kB | bor |
"hidden" |
0.17 kB | 0.94 kB | yo'q |
"inline" |
1.49 kB | yo'q (JS ichida) | data: manzil |
true— xarita alohida faylda, havola JS oxirida. Brauzer.mapni faqat DevTools ochiq bo'lganda yuklaydi — oddiy foydalanuvchi uni yuklab olmaydi, sahifa sekinlashmaydi."hidden"— xarita yaratiladi, lekin JS unga ishora qilmaydi. DevTools uni topmaydi. Xarita sizning xato kuzatish xizmatingizga yuklanadi va u yerda ishlatiladi (pastda)."inline"— xarita//# sourceMappingURL=data:application/json;base64,…ko'rinishida JS'ning ichida. JS 7 baravar kattalashdi — har foydalanuvchi uni yuklaydi. Faqat dev uchun. Vite'ning dev server'i o'zi o'zgartirgan modullarga aynan shundaydata:xarita qo'shadi — Bundler nima darsida ko'rgan/src/main.jsjavobining oxiriga qarasangiz, shu qatorni topasiz.false— xarita yo'q. Xato kelsa, faqate (kassa.js:1:31).
Dev server (npm run dev) xaritani har doim beradi — sozlash shart emas. build.sourcemap faqat production build uchun.
5. Xavfsizlik: xaritada nima bor
sourcesContent ga qayta qarang: unda asl fayllar to'liq — izohlar ham (// Chegirma 50% dan oshmasin — kassir xatosi), o'zgaruvchi nomlari ham, tur nomlari ham. .map ochiq bo'lsa, istalgan odam DevTools'da sizning manba kodingizni o'qiydi.
Bu har doim ham muammo emas:
- Ochiq repo (GitHub'da hamma ko'radi) — xaritada yangi narsa yo'q.
vazifalar— shunday. - Yopiq loyiha — biznes mantig'i, ichki API manzillari, izohlardagi "TODO: bu yerda tekshiruv yo'q" kabi yozuvlar raqobatchi yoki hujumchiga ochiladi.
Ikkinchi holatda: "hidden" yoki false. Va bir qoida hamma loyihaga: frontend kodida sir bo'lmaydi. Xaritasiz ham siqilgan JS'ni o'qish mumkin — API kalitlari, parollar u yerda bo'lmasligi kerak (Vite III: env darsida VITE_ o'zgaruvchilari bilan buni yana ko'ramiz).
Diqqat:
.mapfayllarni o'chirib qo'yish kodni "yashirmaydi". Minifikatsiya qilingan JS baribir brauzerda. Source map faqat o'qishni osonlashtiradi — u himoya emas, uni o'chirish ham himoya emas.
6. Production'dagi xatolar: Sentry
Foydalanuvchilarda chiqqan xatolarni kim ko'radi? Buning uchun xato kuzatish (error tracking) xizmatlari bor — eng mashhuri Sentry. Saytga kichik kod ulanadi, u har xatoni stack trace bilan serverga yuboradi. Server stack'ni source map orqali asl qatorlarga qaytaradi va sizga ko'rsatadi.
Ish tartibi:
build.sourcemap: "hidden"— xarita bor, havola yo'q.- Build'dan keyin
.mapfayllar Sentry'ga yuklanadi (Vite plagini@sentry/vite-plugin, 2026-10 holatiga 5.4.1). .mapfayllardist/dan o'chiriladi — saytga chiqmaydi.
Sentry hujjatidagi sozlama namunasi (biz uni ishga tushirmadik — Sentry hisobi va maxfiy token kerak):
// vite.config.ts — Sentry hujjatidan namuna
import { sentryVitePlugin } from "@sentry/vite-plugin";
import { defineConfig } from "vite";
export default defineConfig({
build: { sourcemap: "hidden" },
plugins: [
sentryVitePlugin({
org: "bahor",
project: "menyu",
// token — muhit o'zgaruvchisidan, kodda EMAS
authToken: process.env.SENTRY_AUTH_TOKEN,
sourcemaps: { filesToDeleteAfterUpload: ["./dist/**/*.map"] },
}),
],
});Natija: foydalanuvchi hech qachon xaritani ko'rmaydi, siz esa Sentry'da narx.ts:8 ni ko'rasiz. Monitoringni keyingi qismlarda (production) chuqur o'rganamiz.
Tekshirib ko'ring: Nega Sentry'ga yuklangan
.mapfayllarnidist/dan o'chirish kerak, agar"hidden"rejimda JS ularga baribir ishora qilmasa?
Javob
Ishora yo'q — lekin fayl serverda turibdi. Kimdir kassa.js.map manzilini taxmin qilib (nom — JS nomi + .map) to'g'ridan-to'g'ri so'rashi mumkin va asl kodni yuklab oladi. "hidden" faqat DevTools'ning avtomatik topishini o'chiradi; faylning o'zini o'chirmasangiz, u ochiq.
7. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| DevTools'da asl fayl yo'q | build.sourcemap yo'q yoki "hidden" |
true (ochiq loyiha) |
| xarita bor, qatorlar noto'g'ri | build'dan keyin JS'ni boshqa vosita o'zgartirgan, xarita eskirgan | o'zgartirishni build ichida qiling |
| Node'da minifikatsiya qilingan stack | --enable-source-maps yo'q |
bayroqni qo'shing |
"inline" production'da |
har foydalanuvchiga ortiqcha yuk | true yoki "hidden" |
.map Service Worker keshida |
oflayn uchun keraksiz megabaytlar | qobiq ro'yxatidan chiqaring |
Oxirgi qator — bugungi vazifalar qadamining bir qismi.
8. Mashqlar
Mashqlar kurs/mashqlar/16/24-source-map/ da.
1-mashq (oson): Qaysi qiymat?
- Ochiq repo, DevTools'da asl kod ko'rinsin:
- Yopiq loyiha, xatolar Sentry'da:
- Dev server'dagidek, xarita JS ichida:
- Xarita umuman kerak emas:
Yechim
true — xarita + havola (DevTools o'zi topadi). "hidden" — xarita bor, havola yo'q (xizmatga yuklanadi, keyin dist/ dan o'chiriladi). "inline" — JS ichida, faqat dev'da. false — sukut. Biror loyihada tanlovni sabab bilan yozing — masalan, vite.config.ts dagi izohda.
2-mashq (o'rta): Ikki stack trace
Darsdagi kassa loyihasini yarating (narx.ts, main.ts, build.sourcemap: true, chiqish fayli kassa.js). So'ng:
node dist/kassa.jsvanode --enable-source-maps dist/kassa.js— ikkala stack'ning birinchiatqatorini yozing.sourcemap: "hidden"qilib,--enable-source-mapsbilan qayta ishga tushiring. Nima o'zgardi?
Yechim
- Xaritasiz:
at e (…/dist/kassa.js:1:31). Xarita bilan:at orderTotal (…\src\narx.ts:8:15). "hidden"dakassa.jsoxiridasourceMappingURLyo'q — Node xaritani topa olmaydi va yanaat e (…/dist/kassa.js:1:31)ko'rsatadi, garchikassa.js.mapfayli yonida turgan bo'lsa ham. Hidden xarita faqat uni aniq so'raydigan vosita (Sentry, 3-mashqdagi skript) uchun.
3-mashq (qiyin): Xaritadan joyni topish
qaytar.mjs yozing: node qaytar.mjs dist/kassa.js:1:31 — fayl oxiridagi sourceMappingURL dan xaritani topib, asl joyni chiqarsin: { source, line, column, name }.
Ishoralar: import { SourceMap } from "node:module"; new SourceMap(json).findEntry(line, column) — 0 dan sanaydi, stack trace esa 1 dan. Natijada originalSource, originalLine, originalColumn, name. sourceMappingURL ni regex bilan oling: /\/\/# sourceMappingURL=(\S+)\s*$/. Argumentni "dist/kassa.js:1:31" ni bo'lishda ehtiyot bo'ling: Windows yo'lida ham : bor (C:\…) — faqat raqamdan oldingi : bo'yicha bo'ling (split(/:(?=\d)/), RegExp lookahead).
Yechim
// qaytar.mjs — minifikatsiya qilingan joyni (fayl:qator:ustun)
// source map orqali asl fayl va qatorga qaytaradi
import { readFileSync } from "node:fs";
import { SourceMap } from "node:module";
import { dirname, join } from "node:path";
export function originalPosition(mapJson, line, column) {
const map = new SourceMap(mapJson);
// SourceMap 0 dan sanaydi, stack trace esa 1 dan
const entry = map.findEntry(line - 1, column - 1);
if (!entry.originalSource) return null;
return {
source: entry.originalSource,
line: entry.originalLine + 1,
column: entry.originalColumn + 1,
name: entry.name ?? null,
};
}
// node qaytar.mjs dist/kassa.js:1:31
if (import.meta.main) {
const [file, line, column] = process.argv[2].split(/:(?=\d)/);
const code = readFileSync(file, "utf8");
const url = code.match(/\/\/# sourceMappingURL=(\S+)\s*$/)?.[1];
if (!url) throw new Error(`${file} da sourceMappingURL yo'q`);
const mapJson = JSON.parse(readFileSync(join(dirname(file), url)));
const pos = originalPosition(mapJson, Number(line), Number(column));
console.log(pos);
}node qaytar.mjs dist/kassa.js:1:31
node qaytar.mjs dist/kassa.js:1:161{ source: '../src/narx.ts', line: 8, column: 15, name: null }
{ source: '../src/main.ts', line: 5, column: 13, name: 'orderTotal' }Ikkalasi --enable-source-maps ko'rsatgan joylar bilan bir xil. Ikkinchida name: 'orderTotal' — names massividan: siqilgan koddagi e aslida orderTotal edi. Testlar (qaytar.test.mjs, saqlangan .map fayl bilan) — ℹ tests 2, ℹ pass 2. Sentry'ning serveri ham aynan shu ishni qiladi — faqat millionlab xato uchun.
4-mashq: Vazifalar qadami — source map'lar
- Branch:
build/source-map - Commit:
build: source map'lar; qobiq .map fayllarni keshlamaydi
vite.config.ts—build.sourcemap: true(izoh bilan: negatrue).skriptlar/qobiq.js—qobiqRoyxati.mapfayllarni Service Worker qobig'iga qo'shmasin.tekshiruv/qobiq.test.js— test ro'yxatiga.mapfayl qo'shing va u natijada yo'qligini tekshiring.
Yechim
vite.config.ts (build ichida):
// Source map (16/#24): DevTools minifikatsiya qilingan koddagi
// xatoni asl .ts fayl va qatorga qaytaradi. Repo ochiq
// (Pages) — xaritada sir yo'q; .map faqat DevTools ochilganda
// yuklanadi
sourcemap: true,skriptlar/qobiq.js:
export function qobiqRoyxati(fayllar) {
const qolgani = fayllar
.filter((yol) => yol !== "sw.js" && !yol.endsWith(".map"))
.toSorted();
return ["./", ...qolgani];
}tekshiruv/qobiq.test.js — test nomi "sahifa birinchi, sw.js va .map yo'q, qolgani tartiblangan", kirish ro'yxatiga "assets/index-B3x9.js.map" qo'shildi, kutilgan natija o'zgarmadi.
Natija (build chiqishidan):
dist/assets/js/tema.js 0.16 kB │ gzip: 0.15 kB │ map: 0.94 kB
dist/assets/olchov-G4bloHwN.js 8.92 kB │ gzip: 3.42 kB │ map: 22.87 kB
dist/assets/index-Dvsf2Sj6.js 25.37 kB │ gzip: 9.51 kB │ map: 115.37 kBindex chunk 25.33 → 25.37 kB: farq faqat oxirgi qatorda — //# sourceMappingURL=index-Dvsf2Sj6.js.map. Fayl nomi (hash) o'zgarmadi: hash kodning o'zidan hisoblanadi. Xaritada sources — 16 ta .ts fayl, sourcesContent — 16 (asl matn ichida). .map jami ~139 kB — lekin u faqat DevTools ochilganda yuklanadi. Service Worker qobig'i — o'sha 12 fayl (.map siz).
Nega true: vazifalar reposi ochiq (GitHub Pages), xaritada yangi narsa yo'q. Yopiq loyihada — "hidden" va xato kuzatish xizmati yoki false.
Testlar: 145/145. Brauzer: 159/159. Diff: 3 fayl, +11 −3.
git switch -c build/source-map
npm run build && npm test
git add vite.config.ts skriptlar/qobiq.js tekshiruv/qobiq.test.js
git commit -m \
"build: source map'lar; qobiq .map fayllarni keshlamaydi"
git push -u origin build/source-map
gh pr create --fill
gh pr merge --mergeCommit sarlavhasi — 55 belgi.
9. Real ishda
- Har production loyihada savol bor: "source map ochiqmi?". Ko'p kompaniyalarda javob:
"hidden"+ Sentry (yoki Datadog, Bugsnag) +.mapni saytdan o'chirish. - Node serverlarda (24-qismdan) TypeScript build'i bilan
--enable-source-maps— xato loglari to'g'ridan-to'g'ri.tsqatorlarini ko'rsatadi. - Intervyu savollari: "Source map nima?", "
hiddensource map nima uchun?", "Production'da source map xavflimi?", "Brauzer.mapni qachon yuklaydi?".
Xulosa
- Source map — siqilgan joy → asl fayl, qator, ustun, nom jadvali (
version,sources,sourcesContent,names,mappings). //# sourceMappingURL=izohi xaritani ko'rsatadi; DevTools vanode --enable-source-mapsuni o'qiydi.build.sourcemap:true(havola bilan),"hidden"(havolasiz, xizmatga yuklash uchun),"inline"(JS ichida, faqat dev),false.sourcesContentda asl kod to'liq — yopiq loyihada.mapochiq qolmasin; lekin xaritani yashirish himoya emas: frontend'da sir bo'lmaydi.vazifalar:sourcemap: true(ochiq repo), Service Worker.mapni keshlamaydi;indexhash o'zgarmadi.
Keyingi dars: Code splitting, chunk nomlari va hash bilan keshlash — olchov chunk'i nega alohida, fayl nomidagi hash keshni qanday xavfsiz qiladi va "deploydan keyin eski chunk topilmadi" xatosi qayerdan keladi.
Manbalar
- Source Map spetsifikatsiyasi (ECMA-426) — tc39.es/ecma426
- Vite: "Build Options → build.sourcemap" — vite.dev/config/build-options
- Node.js: "Command-line → --enable-source-maps", "Modules → Source map v3 support (
SourceMap)" — nodejs.org/api - Chrome DevTools: "Map preprocessed code to source code" — developer.chrome.com/docs/devtools/javascript/source-maps
- Sentry: "Vite → Source Maps" (
@sentry/vite-plugin) — docs.sentry.io/platforms/javascript/sourcemaps/uploading/vite
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!