Что такое исходные карты?

Улучшите возможности веб-отладки с помощью исходных карт.

Сегодня мы говорим о картах исходного кода — важнейшем инструменте современной веб-разработки, который значительно упрощает отладку. В этой статье мы рассмотрим основы карт исходного кода, то, как они генерируются и как они улучшают процесс отладки.

Необходимость исходных карт

В старые добрые времена мы создавали веб-приложения на чистом HTML, CSS и JavaScript и размещали те же файлы в Интернете.

Однако, поскольку в настоящее время мы создаем более сложные веб-приложения, ваш рабочий процесс разработки может включать использование различных инструментов. Например:

  • Языки шаблонов и препроцессоры HTML: Pug , Nunjucks , Markdown .
  • Препроцессоры CSS: SCSS , LESS , PostCSS .
  • Фреймворки JavaScript: Angular, React, Vue, Svelte.
  • Мета-фреймворки JavaScript: Next.js , Nuxt , Astro .
  • Языки программирования высокого уровня: TypeScript , Dart , CoffeeScript .
  • И более. Список можно продолжать и продолжать!

Краткий обзор различных инструментов.

Этим инструментам требуется процесс сборки для преобразования вашего кода в стандартные HTML, JavaScript и CSS, понятные браузерам. Кроме того, для оптимизации производительности общепринятой практикой является сжатие (например, использование Terser для минимизации и искажения JavaScript) и объединение этих файлов, уменьшая их размер и делая их более эффективными для Интернета.

Например, используя инструменты сборки, мы можем перенести и сжать следующий файл TypeScript в одну строку JavaScript. Поиграться с демо можно в моем репозитории на GitHub .

/* A TypeScript demo: example.ts */

document.querySelector('button')?.addEventListener('click', () => {
  const num: number = Math.floor(Math.random() * 101);
  const greet: string = 'Hello';
  (document.querySelector('p') as HTMLParagraphElement).innerText = `${greet}, you are no. ${num}!`;
  console.log(num);
});

Сжатая версия будет:

/* A compressed JavaScript version of the TypeScript demo: example.min.js  */

document.querySelector("button")?.addEventListener("click",(()=>{const e=Math.floor(101*Math.random());document.querySelector("p").innerText=`Hello, you are no. ${e}!`,console.log(e)}));

Однако такая оптимизация может усложнить отладку. Сжатый код, в котором все содержится в одной строке, и более короткие имена переменных могут затруднить определение источника проблемы. Вот тут-то и приходят на помощь исходные карты — они отображают ваш скомпилированный код обратно в исходный код.

Генерация исходных карт

Исходные карты — это файлы, имена которых заканчиваются на .map (например, example.min.js.map styles.css.map ). Их можно сгенерировать с помощью большинства инструментов сборки, например, Vite , webpack , Rollup , Parcel , esbuild и других.

Некоторые инструменты включают исходные карты по умолчанию, тогда как другим может потребоваться дополнительная настройка для их создания.

/* Example configuration: vite.config.js */
/* https://vitejs.dev/config/ */

export default defineConfig({
  build: {
    sourcemap: true, // enable production source maps
  },
  css: {
    devSourcemap: true // enable CSS source maps during development
  }
})

Понимание исходной карты

Эти файлы исходных карт содержат важную информацию о том, как скомпилированный код сопоставляется с исходным кодом, что позволяет разработчикам с легкостью выполнять отладку. Вот пример исходной карты.

{
  "mappings": "AAAAA,SAASC,cAAc,WAAWC, ...",
  "sources": ["src/script.ts"],
  "sourcesContent": ["document.querySelector('button')..."],
  "names": ["document","querySelector", ...],
  "version": 3,
  "file": "example.min.js.map"
}

Чтобы понять каждое из этих полей, вы можете прочитать спецификацию исходной карты или эту классическую статью об анатомии исходной карты .

Наиболее важным аспектом исходной карты является поле mappings . Он использует строку в кодировке VLQ Base 64 для сопоставления строк и местоположений в скомпилированном файле с соответствующим исходным файлом. Это сопоставление можно визуализировать с помощью визуализатора исходной карты, такого как source-map-visualization и Source Map Visualization .

Визуализация исходной карты.
На изображении показана визуализация нашего предыдущего примера кода выше, созданная визуализатором .

Сгенерированный столбец слева показывает сжатое содержимое, а исходный столбец показывает исходный источник.

Цвет визуализатора кодирует каждую строку в исходном столбце и соответствующий код в сгенерированном столбце.

В разделе сопоставлений показаны декодированные сопоставления кода. Например, запись 65-> 2:2 означает:

  • Сгенерированный код: слово const начинается с позиции 65 в сжатом содержимом.
  • Исходный код: слово const начинается со второй строки и второго столбца исходного содержимого.

Картографическая запись.

Таким образом, разработчики могут быстро определить взаимосвязь между минимизированным и исходным кодом, что упрощает процесс отладки.

Инструменты разработчика браузера применяют эти карты исходного кода, чтобы помочь вам быстрее выявлять проблемы отладки прямо в браузерах.

Инструменты разработчика применяют исходную карту.

На изображении показано, как инструменты разработчика браузера применяют исходные карты, а также сопоставления между файлами.

Расширения исходной карты

Исходные карты поддерживают расширения. Расширения — это настраиваемые поля, которые начинаются с соглашения об именовании x_ . Одним из примеров является поле расширения x_google_ignoreList предложенное Chrome DevTools. См. x_google_ignoreList , чтобы узнать больше о том, как эти расширения помогают вам сосредоточиться на своем коде.

Это не идеально

В нашем примере переменная greet была оптимизирована в процессе сборки. Значение было непосредственно встроено в окончательный вывод строки.

Приветствие Variaqble не является картой.

В этом случае при отладке кода инструменты разработчика могут не получить возможность вывести и отобразить фактическое значение. Это не только проблемы для инструментов разработчика браузера. Это также усложняет мониторинг и анализ кода.

Переменная приветствие не определена.

Это, конечно, решаемая проблема. Один из способов — включить информацию об области действия в карты исходного кода, как это делают другие языки программирования со своей отладочной информацией.

Однако это требует совместной работы всей экосистемы над улучшением спецификации и реализации исходных карт. Идет активное обсуждение улучшения возможностей отладки с помощью карт исходного кода.

Мы с нетерпением ждем возможности улучшить исходные карты и сделать отладку еще менее утомительной!