Source maps что это
Перейти к содержимому

Source maps что это

Source Maps: быстро и понятно

Механизм Source Maps используется для отображения исходных текстов программы на сгенерированные на их основе скрипты. Несмотря на то, что тема не нова и по ней уже написан ряд статей (например эта, эта и эта) некоторые аспекты все же нуждаются в прояснении. Представляемая статья представляет собой попытку упорядочить и систематизировать все, что известно по данной теме в краткой и доступной форме.

В статье Source Maps рассматриваются применительно к клиентской разработке в среде популярных браузеров (на примере, DevTools Google Chrome), хотя область их применения не привязана к какому-либо конкретному языку или среде. Главным источникам по Source Maps является, конечно, стандарт, хотя он до сих пор не принят (статус — proposal), но, тем не менее, широко поддерживается браузерами.

Работа над Source Maps была начата в конце нулевых, первая версия была создана для плагина Firebug Closure Inspector. Вторая версия вышла в 2010 и содержала изменения в части сокращения размера map-файла. Третья версия разработана в рамках сотрудничества Google и Mozilla и предложена в 2011 (последняя редакция в 2013).

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

Для работы Source Maps необходимы следующие файлы:

  • собственно сгенерированный JavaScript-файл
  • набор файлов с исходным кодом использовавшийся для его создания
  • map-файл отображающий их друг на друга
Map-файл

Вся работа Source Maps основана на map-файле, который может выглядеть, например, так:

Обычно, имя map-файла складывается из имени скрипта, к которому он относится, с добавлением расширения «.map», bundle.js — bundle.js.map. Это обычный json-файл со следующими полями:

  • «version» — версия Source Maps;
  • «file» — (опционально) имя сгенерированного файла, к которому относится текущий map-файл;
  • «sourceRoot» — (опционально) префикс для путей к файлам-исходникам;
  • «sources» — список путей к файлам-исходникам (разрешаются аналогично адресам src тега script, можно использовать file://.);
  • «names» — список имен переменных и функций, которые подверглись изменению в сгенерированном файле;
  • «mappings» — координаты отображения переменных и функций исходных файлов на сгенерированный файл в формате Base64 VLQ;
  • «sourcesContent» — (опционально) в случае self-contained map-файла список строк, каждая из которых содержит исходный текст файла из sources;
Загрузка Source Maps

Для того, чтобы браузер загрузил map-файл может быть использован один из следующих способов:

  • JavaScript-файл пришел с HTTP-заголовком: SourceMap: <url> (ранее использовался ныне устаревший X-SourceMap: <url>)
  • в сгенерированном JavaScript-файле есть особый комментарий вида:

Таким образом, загрузив map-файл браузер подтянет и исходники из поля «sources» и с помощью данных в поле «mappings» отобразит их на сгенерированный скрипт. Во вкладке Sources DevTools можно будет найти оба варианта.

Для указания пути может использоваться пседопротокол file://. Также, в <url> может быть включено все содержимое map-файла в кодировке Base64. В терминологии Webpack подобные Source Maps названы inline source maps.

Self-contained map-файлы

Код файлов-исходников можно включить непосредственно в map-файл в поле «sourcesContent», при наличии этого поля необходимость в их отдельной загрузке отпадает. В этом случае названия файлов в «sources» не отражают их реального адреса и могут быть совершенно произвольными. Именно поэтому, вы можете видеть во вкладке Sources DevTools такие странные «протоколы»: webpack://, ng:// и т.д

Mappings

Сущность механизма отображения состоит в том, что координаты (строка/столбец) имен переменных и функций в сгенерированном файле отображаются на координаты в соотвествующем файле исходного кода. Для работы механизма отображения необходима следующая информация:

(#1) номер строки в сгенерированном файле;
(#2) номер столбца в сгенерированном файле;
(#3) индекс исходника в «sources»;
(#4) номер строки исходника;
(#5) номер столбца исходника;

Все эти данные находятся в поле «mappings», значение которого — длинная строка с особой структурой и значениями закодированными в Base64 VLQ.

Строка разделена точками с запятой (;) на разделы, соответствующие строкам в сгенерированном файле (#1).

Каждый раздел разделен запятыми (,) на сегменты, каждый из которых может содержать 1,4 или 5 значений:

  • номер столбца в сгенерированном файле (#2);
  • индекс исходника в «sources» (#3);
  • номер строки исходника (#4);
  • номер столбца исходника (#5);
  • индекс имени переменной/функции из списка «names»;

Каждое значение представляет собой число в формате Base64 VLQ. VLQ (Variable-length quantity) представляет собой принцип кодирования сколь угодно большого числа с помощью произвольного числа двоичных блоков фиксированной длины.

В Source Maps используются шестибитные блоки, которые следуют в порядке от младшей части числа к старшей. Старший 6-й бит каждого блока (continuation bit) зарезервирован, если он установлен, то за текущим следует следующий блок относящийся к этому же числу, если сброшен — последовательность завершена.

Поскольку в Source Maps значение должно иметь знак, для него также зарезервирован младший 1-бит (sign bit), но только в первом блоке последовательности. Как и ожидается, установленный sign бит означает отрицательное число.

Таким образом, если число можно закодировать единственным блоком, оно не может быть по модулю больше 15 (11112), так как в первом шестибитном блоке последовательности два бита зарезервированы: continuation бит всегда будет сброшен, sign бит будет установлен в зависимости от знака числа.

Шестибитные блоки VLQ отображаются на кодировку Base64, где каждой шестибитной последовательности соответствует определенный символ ASCII.

Декодируем число mE. Инверсируем порядок, младшая часть последняя — Em. Декодируем числа из Base64: E — 000100, m — 100110. В первом отбрасываем старший continuation бит и два лидирующих нуля — 100. Во втором отбрасываем старший continuation и младший sign биты (sign бит сброшен — число положительное) — 0011. В итоге получаем 100 00112, что соответствует десятичному 67.

Можно и в обратную сторону, закодируем 41. Его двоичный код 1010012, разбиваем на два блока: старшая часть — 10, младшая часть (всегда 4-битная) — 1001. К старшей части добавляем старший continuation бит (сброшен) и три лидирующих нуля — 000010. К младшей части добавляем старший continuation бит (установлен) и младший sign бит (сброшен — число положительное) — 110010. Кодируем числа в Base64: 000010 — C, 110010 — y. Инверсируем порядок и, в итоге, получаем yC.

Создание и использование Source Maps для css и js в GULP 4

Хотелось бы в двух словах рассказать, как настроить Source Maps для GULP 4, но не получится вырвать из контекста просто пару строк кода, поэтому разобью статью на 2 части. Первая будет с базовым кодом для Source Maps, а во второй части будет куча кода из рабочего проекта.

Source Maps нужны для проектов, где происходит сборка нескольких файлов в один, например, компиляция файлов scss в один файл main.min.css или конкатенация js-файлов в один scripts.min.js . Карты источников нужны для того, чтобы быстро вносить правки, и не искать в каком файле задано определенное правило или функция, особенно когда для сборки используются десятки файлов или различных библиотек.

Так будет выглядеть в инспекторе код без Source Maps:

Так будет выглядеть код с использованием Source Maps:

И по итогу, если нам нужно увеличить отступ снизу у элемента .icon , то нам не нужно искать по всем файлам scss где задано это правило, а с помощью карты источников видим, что данное правило указано в файле _general.scss на 44 строке.

Если вам это не нужно, то дальше можно не читать.

1. Базовый пример вывода Source Maps для js-файла

Где – plugin1() и plugin2() – это какие-то образные плагины, которые занимаются минификацией и конкатенацией js-файлов, например.

Дальше будет много кода из рабочего примера, будет использоваться scss, сборка, минификация, конкатенация и т.д.

2. Кастомизированный пример подключения Source Maps в js-файлах и в css-файлах

Задача для компиляции файла main.min.css с Source Maps

Задача для конкатенации файла scripts.min.js с Source Maps

Все классно, но есть одна проблема.

sourcemaps.write() – не указаны пути для файла *.map, значит карта источников будет записана внутри файла в самый конец, тем самым увеличивая вес файлов более чем в два раза. Это не круто, но зато карты источников будут работать и в Chrome, и в Firefox.
sourcemaps.write(‘.’) – в таком формате source map запишется отдельным файлом в той же папке, что и основной файл, в формате main.min.css.map и scripts.min.js.map.

Проблема с лишним содержимым в виде карты путей решаема.

Source Maps нужны нам только во время разработки, поэтому при билде продашена просто удалим все комментарии из файлов .css и .js – а карта источников записана именно в виде комментария.

Это часть кода из моей таски ‘build’:

По итогу, на продакшене будут чистые минифицированные файлы без лишнего кода и комментариев.

Source Maps 101

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

Здесь вступают в игру source maps, указывая точное сопоставление в нашем production коде на исходный авторский код. В этом вводном учебнике мы рассмотрим простой проект и пропустим его через различные компиляторы JavaScript, чтобы поиграться с source maps в браузере.

Что такое Source Maps?

Source Maps предлагают не зависимый от языка способ сопоставления production кода с исходным кодом.

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

В этом уроке мы рассмотрим очень простой код JavaScript и SASS, запустим их через различные компиляторы, а затем просмотрим исходные файлы в браузере с помощью исходных карт. Загрузите демо-файлы, и давайте начнем!

Браузеры

Обратите внимание, что при написании этой статьи Chrome (версия 23) поддерживает карты исходников JavaScript и даже карты SASS. Firefox также должен получить поддержку в ближайшем будущем, так как в настоящее время он находится в активной стадии разработки. Итак, давайте посмотрим, как мы можем использовать карты источников в браузере!

Source maps в Chrome

Во-первых, мы должны включить поддержку в Chrome, выполнив следующие простые шаги:

  • Откройте инструменты разработчика Chrome: View -> Developer -> Инструменты для разработчиков
  • Нажмите кнопку «Настройки» в нижнем правом углу
  • Выберите «Общие» и выберите «Включить source maps»,

Настройка

Если вы хотите продвигаться вместе с этой статьей, загрузите демоверсию и откройте каталог «start». Файлы и структура каталогов довольно простые, с некоторым простым JavaScript внутри scripts/script.js . Вы должны иметь возможность открывать index.html и даже добавлять некоторые названия цветов CSS или шестнадцатеричные значения, чтобы изменить цвет фона.

Просмотрите простые файлы сценариев в простых JavaScript, TypeScript или CoffeeScript. Используя различные компиляторы JavaScript, мы создадим готовую для production версию, а также сгенерируем соответствующие карты.

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

Вариант A : компилятор Closure

Closure Compiler от Google — это инструмент для оптимизации JavaScript. Он делает это, анализируя ваш код, удаляя ненужные биты, а затем минимизируя остальные. Кроме того, он также может генерировать карты.

Для создания оптимизированной версии script.js , используя компилятор Closure, сделаем следующие шаги:

Когда мы открываем index.html в браузере и перейдем к Source панели в средствах разработчика, то будет ссылка только на оптимизированную версию script.closure.js ; у нас нет способа вернуть соотношение к нашему оригинальному, исходному файлу. Давайте создадим файл карты, выполнив следующую команду в каталоге scripts :

Обратите внимание, что Closure Compiler использует два варианта: —create_source_map и —source_map_format для создания файла карты script.closure.js.map с версией 3. Затем добавьте URL-адрес исходного URL-адреса в конец скомпилированного файла сценария, script.closure.js , так что оптимизированный файл содержит информацию о местоположении карты:

Теперь, когда мы просматриваем проект в браузере, в директории «scripts» под панелью Source инструментов разработчика будет показан как исходный файл, так и оптимизированная версия script.closure.js . Хотя браузер, конечно же, использует оптимизированный файл, на который мы первоначально ссылались в index.html , карты позволяют нам создать связь с исходным файлом.

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

Вариант B : задача GruntJS для JSMin

Если вы уже используете Grunt.js для процессов сборки, вам понадобится плагин Grunt для карт JSMin. Он не только оптимизирует ваш код, но также создаст карту!

Следующие шаги продемонстрируют, как создать оптимизированную версию script.js с плагином Grunt JSMin:

  1. установите Grunt.js и инициируйте gruntfile, grunt.js , в корне каталога:
  2. Установите Grunt-плагин grunt-jsmin-sourcemap; когда вы это сделаете, будет создан каталог с именем node_modules/grunt-jsmin-sourcemap :
  3. Отредактируйте вновь созданный файл grunt.js , чтобы он содержал только задачу jsmin-sourcemap — чтобы все было как можно проще.
  4. Вернитесь в командную строку и запустите grunt ; это выполнит задачу jsmin-sourcemap, поскольку задание по умолчанию указано как таковое в файле grunt.js:
  5. В файле созданной карты, script.grunt-jsmin.js.map , убедитесь, что источником является «sources»:[«script.js»] .
  6. Раскомментируйте вариант B для ссылки на вновь созданный файл, script.grunt-jsmin.js , в index.htm l, и откройте его в браузере.

С Grunt и плагином, jsmin-sourcemap, процесс сборки создал два файла: оптимизированный файл сценария с URL-адресом исходного источника, а также карту. Для просмотра всех их в браузере вам потребуются они оба.

Вариант C : UglifyJSё

UglifyJS2 — это еще один парсер JavaScript, minfier и компрессор. Подобно двум альтернативам выше, UglifyJS2 создаст оптимизированный файл сценария, добавленный с URL-адресом исходного источника, а также файл карты, который будет содержать сопоставление с исходным файлом. Чтобы использовать UglifyJS, выполните следующую команду в командной строке каталога «start»:

  1. Установите модуль NPM, uglify-js , локально; будет создан каталог node_module/uglify-js .
  2. Внутри каталога «scripts» мы выполним команду для создания оптимизированной версии, а также исходного файла с параметрами —source-map и —output , чтобы указать имя выходного файла.
  3. Наконец, убедитесь, что index.html правильно связан с скриптом script.uglify.js

Вариант D : CoffeeScript Redux

Для предыдущих трех вариантов нам потребовалась только одношаговая оптимизация — от исходного кода до оптимизированного JavaScript. Однако для таких языков, как CoffeeScript, нам нужен двухэтапный процесс: CoffeeScript > JavaScript > оптимизированный JavaScript. В этом разделе мы рассмотрим, как создавать многоуровневые карты с помощью CoffeeScript и компилятора CoffeeScript Redux.

Шаг 1: CoffeeScript для простого JavaScript

Перейдите в каталог «start» в командной строке. В следующих шагах мы сопоставим оптимизированный файл сценария с CoffeeScript:

  1. Установите CoffeeScript в качестве глобального пакета npm
  2. Скомпилируйте файл CoffeeScript, script.coffee.coffee , чтобы создать простую версию JavaScript, используя следующую команду:
  3. Установите CoffeeScript Redux:
  4. Затем мы создадим файл карты, script.coffee.js.map, который будет хранить информацию о преобразовании из сгенерированного JavaScript обратно в файл CoffeeScript:
  5. Убедитесь, что сгенерированный файл JavaScript, script.coffee.js , имеет URL-адрес исходного кода в конце со следующей строкой:
  6. Убедитесь, что файл карты, script.coffee.js.map , имеет правильный файл ссылки как «file»:»script.coffee.coffee» и исходный файл как «sources»:[«script.coffee.coffee»]

Шаг 2: Обычный JavaScript для минификации-JavaScript

  1. Наконец, мы снова будем использовать UglifyJS, чтобы минимизировать сгенерированный JavaScript, а также создать карту. На этот раз это займет карту, чтобы мы могли вернуться к исходному файлу CoffeeScript. Выполните следующую команду в каталоге «scripts»:
  2. Наконец, убедитесь, что файл карты, script.coffee.min.js.map , имеет правильный ссылочный файл как «file»:»script.coffee.min.js» и правильные источники как «sources»:[«script.coffee.coffee»] .

Вариант E : TypeScript

TypeScript, как и CoffeeScript, также требует двухэтапный процесс: TypeScript > Обычный JavaScript > Мини-код JavaScript. Поскольку скрипт использует плагин jQuery, нам нужны два файла TypeScript, которые уже предоставлены: script.typescript.ts и jquery.d.ts .

Шаг 1: TypeScript для простого JavaScript

Перейдите в каталог «scripts» из командной строки и выполните следующую команду:

Вышеприведенная команда создаст новый файл JavaScript, с именем script.typescript.js , с url внизу: //@sourceMappingURL=script.typescript.js.map . С помощью этой единственной команды он также создаст файл карты, script.typescript.js.map .

Шаг 2: Обычный JavaScript для минифицированного JavaScript

Как и в примере с CoffeeScript, следующим шагом будет использование UglifyJS.

Наконец, убедитесь, что index.htm l ссылается на правильный файл сценария, scripts/script.typescript.min.js и откройте его в браузере!

Карты для SASS

Помимо JavaScript, в настоящее время Chrome также поддерживает карты SASS или SCSS. Для сопоставления исходника SASS исправьте несколько настроек в Chrome, а затем скомпилируем SASS в CSS с параметрами отладки:

    Прежде чем изменять какие-либо настройки, обратите внимание, что при проверке элемента из инструментов разработчика он будет показывать только ссылку на файл CSS. Это не слишком полезно.

Помимо просто просмотра файла SASS, если вы используете LiveReload в фоновом режиме и вносите изменения в файл SASS, страница также будет обновляться, чтобы отразить изменения. Например, давайте откроем проект в Firefox и проверим страницу, используя расширение Firebug.

Информация на карте

Если мы просмотрим любой из файлов *.map , он будет содержать информацию о сопоставлении из исходного файла в оптимизированный файл. Структура карты обычно находится в формате JSON, используя спецификации версии 3. Обычно он содержит следующие пять свойств:

  1. version: номер версии карты — обычно «3.»
  2. file: имя оптимизированного файла.
  3. sources: Имена исходных файлов.
  4. names: Символы, используемые для сопоставления.
  5. mappings: отображение данных.

Дополнительные ресурсы

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

  • Введение в карты JavaScript Райана Седдона, HTML5 Rocks
  • Эпизод 3 точки останова: карты JavaScript от команды разработчиков Google
  • Эпизод 2 точки останова: карты SASS от команды разработчиков Google
  • Source Maps wiki о языках, инструментах и статьях об картах.
  • Многоуровневые карты с использованием CoffeeScript и TypeScript Райана Седдона

Заключение

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *