Пошук уроків, статей та іншого контенту
Налаштуєте sourceMap, map-файли та відлагодження TypeScript-коду в браузері й Node.js.
TypeScript перед виконанням потрібно перетворити на JavaScript. Браузер і Node.js виконують уже згенерований JavaScript, тому без додаткової інформації відлагоджувач показує саме цей код.
Source map — це файл із розширенням .map, який описує відповідність між:
кодом у згенерованому JavaScript;
початковим TypeScript-кодом;
номерами рядків і позиціями символів у цих файлах.
Завдяки source map можна:
ставити breakpoint безпосередньо у .ts-файлах;
бачити TypeScript у панелі розробника браузера;
отримувати stack trace з іменами TypeScript-файлів і правильними номерами рядків;
покроково виконувати саме початковий код, а не результат компіляції.
Source map не змінює виконання програми. Він використовується інструментами розробника для навігації між початковим і згенерованим кодом.
Розглянемо невеликий TypeScript-проєкт:
project/
├── src/
│ └── main.ts
└── tsconfig.jsonФайл src/main.ts:
function calculateTotal(price: number, quantity: number): number {
return price * quantity;
}
const total = calculateTotal(250, 3);
console.log(`Сума: ${total}`);Файл tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"rootDir": "src",
"outDir": "dist",
"strict": true,
"sourceMap": true
},
"include": ["src/**/*.ts"]
}Компіляція:
npx tscПісля компіляції з’являться файли:
dist/
├── main.js
└── main.js.mapУ кінці dist/main.js TypeScript додасть спеціальний коментар:
//# sourceMappingURL=main.js.mapЦей коментар повідомляє інструментам, де шукати source map для поточного JavaScript-файлу.
Файл main.js.map містить JSON-дані. Його не потрібно редагувати вручну. Він генерується компілятором TypeScript.
Запустити результат можна так:
node dist/main.jsРезультат:
Сума: 750sourceMap{
"compilerOptions": {
"sourceMap": true
}
}Створює окремий файл .map для кожного згенерованого JavaScript-файлу.
Наприклад:
src/app.ts → dist/app.js + dist/app.js.map
src/utils.ts → dist/utils.js + dist/utils.js.mapЦе найпоширеніший варіант для розробки.
inlineSourceMap{
"compilerOptions": {
"inlineSourceMap": true
}
}У цьому випадку окремий .map-файл не створюється. Source map вбудовується безпосередньо в кінець JavaScript-файлу у вигляді Base64-даних.
Результат має такий вигляд:
//# sourceMappingURL=data:application/json;base64,...sourceMap і inlineSourceMap призначені для різних способів зберігання source map, тому їх не слід вмикати одночасно.
inlineSources{
"compilerOptions": {
"sourceMap": true,
"inlineSources": true
}
}Додає початковий TypeScript-код безпосередньо всередину source map.
Без inlineSources source map може містити лише шляхи до .ts-файлів. Інструмент розробника спробує завантажити ці файли окремо.
З inlineSources source map вже містить текст початкових файлів. Це зручно для:
відлагодження зібраних застосунків;
CI-збірок;
середовищ, де початкові .ts-файли недоступні окремо.
Приклад конфігурації:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"outDir": "dist",
"sourceMap": true,
"inlineSources": true
}
}sourceRootsourceRoot задає базовий шлях для початкових файлів, зазначених у source map:
{
"compilerOptions": {
"sourceMap": true,
"sourceRoot": "../src"
}
}Ця опція потрібна лише тоді, коли структура шляхів у середовищі збірки відрізняється від структури шляхів на машині розробника або на сервері.
У більшості простих проєктів достатньо sourceMap: true.
Створимо простий браузерний приклад.
src/main.ts:
function formatUser(name: string, age: number): string {
if (age < 0) {
throw new Error("Вік не може бути від'ємним");
}
return `${name}, ${age} років`;
}
const message = formatUser("Олена", 28);
document.body.textContent = message;
console.log(message);tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"rootDir": "src",
"outDir": "dist",
"strict": true,
"sourceMap": true,
"inlineSources": true
},
"include": ["src/**/*.ts"]
}Після виконання:
npx tscствориться файл dist/main.js і відповідний dist/main.js.map.
Створимо dist/index.html:
<!doctype html>
<html lang="uk">
<head>
<meta charset="UTF-8">
<title>Source Maps</title>
</head>
<body>
<script src="./main.js"></script>
</body>
</html>Відкрийте index.html через локальний HTTP-сервер, а не лише як файл із файлової системи. Сервер має віддавати поруч:
/index.html
/main.js
/main.js.mapБраузер прочитає коментар sourceMappingURL у main.js, завантажить main.js.map і покаже початковий TypeScript-код у DevTools.
У DevTools:
Відкрийте панель Sources.
Знайдіть файл main.ts у списку початкових файлів.
Клацніть номер рядка, щоб поставити breakpoint.
Оновіть сторінку.
Виконання зупиниться в TypeScript-файлі.
Після зупинки можна:
переглядати значення змінних;
виконувати код покроково;
переходити в наступний виклик функції;
переглядати call stack;
перевіряти області видимості.
Браузер фактично виконує main.js, але source map дозволяє відображати цей процес через main.ts.
Змінимо виклик:
const message = formatUser("Олена", -1);Після перезавантаження сторінки браузер покаже помилку та посилання на рядок у main.ts, якщо source map завантажився правильно.
Без source map посилання зазвичай вказувало б на рядок у main.js.
Node.js також може використовувати source maps для stack trace.
Використаємо такий файл src/main.ts:
function parsePort(value: string): number {
const port = Number(value);
if (!Number.isInteger(port) || port <= 0) {
throw new Error(`Некоректний порт: ${value}`);
}
return port;
}
function startServer(): void {
parsePort("not-a-port");
}
startServer();Файл tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"rootDir": "src",
"outDir": "dist",
"strict": true,
"sourceMap": true,
"inlineSources": true
},
"include": ["src/**/*.ts"]
}Компіляція:
npx tscЗапуск із підтримкою source maps:
node --enable-source-maps dist/main.jsБез --enable-source-maps stack trace може посилатися на згенерований JavaScript:
at parsePort (.../dist/main.js:...)Із увімкненою підтримкою source maps Node.js намагатиметься показати початковий файл:
at parsePort (.../src/main.ts:5:11)Номер рядка залежить від фактичного вмісту файлу, але посилання буде на TypeScript-код.
--enable-source-maps потрібно додати саме під час запуску Node.js:
node --enable-source-maps dist/main.jsЦе не опція TypeScript-компілятора.
Source map повинен бути доступним середовищу, у якому виконується код.
Для браузера зазвичай потрібні:
dist/
├── index.html
├── main.js
└── main.js.mapЯкщо main.js завантажується, але main.js.map відсутній або недоступний, код працюватиме, проте DevTools не зможе показати початкові TypeScript-файли.
Перевіряйте такі умови:
файл .map дійсно створений;
назва файлу в sourceMappingURL правильна;
.map лежить за очікуваним шляхом;
сервер не блокує його завантаження;
після зміни конфігурації проєкт було перекомпільовано.
Під час розробки зручно автоматично перекомпілювати TypeScript:
npx tsc --watchУ цьому режимі TypeScript стежить за змінами у файлах і повторно створює JavaScript та .map-файли.
Якщо браузер не показує останній TypeScript-код:
Переконайтеся, що компілятор завершив повторну збірку.
Оновіть сторінку.
За потреби очистьте кеш у DevTools.
Перевірте, що браузер завантажив актуальні main.js і main.js.map.
Source map може містити початковий код завдяки inlineSources:
{
"compilerOptions": {
"sourceMap": true,
"inlineSources": true
}
}Це спрощує відлагодження, але збільшує розмір .map-файлів.
Також варто пам’ятати: source map може розкрити початковий код клієнтського застосунку. Тому для production-збірок потрібно свідомо вирішити:
чи потрібні source maps кінцевим користувачам;
чи будуть вони доступні публічно;
чи потрібно зберігати їх лише окремо для команди розробки.
Це не впливає на безпеку серверної логіки, оскільки клієнтський код у будь-якому разі надходить у браузер. Проте source maps роблять початковий код значно зручнішим для перегляду.
Для локальної розробки зазвичай достатньо такої конфігурації:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"rootDir": "src",
"outDir": "dist",
"strict": true,
"sourceMap": true,
"inlineSources": true
},
"include": ["src/**/*.ts"]
}Потім:
npx tsc
node --enable-source-maps dist/main.jsДля браузера:
TypeScript створює .js і .js.map.
HTML завантажує .js.
Браузер знаходить sourceMappingURL.
DevTools завантажує .map.
Breakpoint-и й stack trace відображаються у .ts-файлах.
Перевірте, що у tsconfig.json є:
{
"compilerOptions": {
"sourceMap": true
}
}Також переконайтеся, що запускаєте саме той конфігураційний файл, який редагували.
Найчастіше причина — недоступний .map-файл.
Перевірте:
чи існує main.js.map;
чи лежить він поруч із main.js;
чи правильний коментар //# sourceMappingURL=...;
чи немає помилки завантаження .map у вкладці Network.
distДля запуску потрібно додати прапорець:
node --enable-source-maps dist/main.jsСамого sourceMap: true достатньо для створення карти, але Node.js має бути запущений із підтримкою її використання.
Після зміни TypeScript-файлів повторно виконайте:
npx tscЯкщо браузер використовує кеш, перезавантажте сторінку з очищенням кешу.
Не використовуйте одночасно:
{
"compilerOptions": {
"sourceMap": true,
"inlineSourceMap": true
}
}Оберіть один формат:
sourceMap — окремий .map-файл;
inlineSourceMap — карта всередині .js.
Source map пов’язує згенерований JavaScript із початковим TypeScript-кодом.
Опція sourceMap: true створює окремі .map-файли.
Коментар sourceMappingURL вказує, де знайти відповідну карту.
inlineSourceMap вбудовує карту безпосередньо в JavaScript.
inlineSources додає початковий TypeScript-код усередину source map.
Браузерні DevTools можуть ставити breakpoint-и та показувати помилки у .ts-файлах.
Для Node.js потрібно запускати програму з --enable-source-maps.
Якщо .map недоступний або застарілий, відлагоджувач показуватиме згенерований JavaScript.