Александр Меняйло

Александр Меняйло

Разработчик. Учёный

©2021

Разработал минификаторы HTML и JavaScript для Jekyll

Мой сайт, который вы сейчас читаете, построен при помощи генератора статических сайтов Jekyll. Очень много о статических сайтах и Jekyll в частности можно прочитать в Интернете. Пользуясь этой технологией, я увидел, что Jekyll не умеет из коробки минифицировать HTML разметку и JavaScript код. В разметке или коде, который пишет человек много лишних символов: отступы, ненужные пробелы, комментарии. Всё это не влияет на отображение страниц, однако занимает место и всегда передаётся клиентскому браузеру, растрачивая трафик пользователя и его время для загрузки страницы. Сегодня редко встретишь сайты, где не используется минификация кода. Под катом вы найдёте как я для себя, пусть не окончательно, но решил эту проблему при использовании Jekyll, при этом сохраняя поддержку с GitHub Pages.

Немного теории

Под статическем сайтом понимается то, что он состоит из заранее подготовленных неизменяемых файлов. Задача web-сервера, который его обслуживает сводится к простой отдачи этих файлов браузерам пользователей. То есть не подразумевается использования какой-нибудь базы данных, или генерации HTML на лету. Все HTML страницы, стили и скриптовые файлы уже заранее сгенерированны Jekyll’ом и web-сервер их просто отдаёт по запросу клиента. Такой способ создания сайтов имеет свои преимущества и недостатки. Основной недостаток очевиден: нет возможности подключить базу данных. Но достоинства тоже большие - прежде всего малые требования к web-серверу, безопасность (здесь нечего взламывать). Ещё одним неоспоримым достоинством, является то, что для статических сайтов существуют бесплатные хостинги. В итоге, для того, чтобы создать свой сайт, вам не нужно иметь собственный или арендованный сервер, не нужно настраивать его и платить за него.

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

Зачем вообще генерировать файлы сайта, нельзя ли их просто создать руками? Ну конечно можно, и даже можно опубликовать их при помощи GitHub Pages без всякого Jekyll. Просто это неудобно. Первое, что приходит в голову о назначении генератора сайта - это повторное использование уже написанных кусков разметки и кода. Например, разные страницы могут содержать много общих элементов, каких-то заголовков, скриптов и прочее. Это всё можно написать один раз и Jekyll подставит эти данные в нужные места автоматически. Вся магия Jekyll - это возможности делать автоматизацию при помощи специальной разметки Liquid. По сути - это программирование того, как будет устроен итоговый файл, который web-сервер должен отдать клиенту. Эта разметка поддерживает циклы, условия, работу со строками, датами и т.д. Здесь я не буду подробно описывать работу с Jekyll и Liquid. На официальном сайте есть отличная документация.

Кроме того, Jekyll позволяет подключать дополнительные плагины, написанные на Ruby. И среди этих плагинов есть и такие, которые минифицируют HTML и JavaScript. Проблема в том, что GitHub Pages поддерживает только ограниченное число плагинов. Это, вероятно, сделано по соображениям безопасности. Поэтому нет штатных способов минифицировать сгенерированные Jelyll файлы.
Но GitHub Pages естественным образом работает с Liquid, а это хоть и не очень удобный для наших целей, но всё же язык программирования. Можно попробовать написать минификаторы при помощи него. Такие попытки уже были, но мне результат не очень понравился. Например, минифицированный HTML портил встроенный JavaScript. Поэтому я решил написать собственный минификатор HTML, а заодно и JavaScript.

И кстати, я ничего не говорю про минификацию css. Удивительно, но этот функционал доступен в Jekyll из коробки и прекрасно работает с GitHub Pages. Для миниификации css достаточно в _config.yml написать:

sass:
  style: :compressed

Минификация на Liquid

Под минификацией понимается:

  • Удаление комментариев.
  • Удаление ненужных пробелов, табуляций и переносов строк.
  • Если нельзя удалять переносы строк (строки в JS или теги pre, script, textarea в HTML), то оставить эти места нетронутыми.

Минификатор JavaScript не переименовывает названия переменных и функций, поэтому нельзя сказать, что он максимально сжимает исходный код. Но работа в этом направлении ведётся.
Обновление от 09 декабря 2020 года:
Теперь минификатор JavaScript умеет переименовывать названия переменных, функций, параметров функций! И этот функционал доступен по умолчанию. Но если из-за этого возникают ошибки в коде, то переименовывание можно отключить глобально в первой строке после текста лицензии в файле js_minifier.liquid (переменную replaceNames поставить в false). Так же отключить переименование можно отдельно для каждого скрипта (см. ниже).

Сами разработанные минификаторы я выложил на GitHub под MIT лицензией.

Как использовать

  1. Скачайте или клонируйте проект с GitHub.
  2. Поместите файлы html_minifier.liquid и js_minifier.liquid в папку _layouts вашего проекта. Эти файлы находятся в папке _layouts репозитория.
  3. Укажите в HTML файлах проекта шаблон html_minifier. Например, в большинстве случаев достаточно в начало _layouts/default.html поместить следующее:
    ---
    layout: html_minifier
    ---
    

    Если все ваши HTML файлы основаны на шаблоне default, то в этом случае они автоматически минифицируются.
    В JavaScript файлах, которые вы хотите минифицировать укажите:

    ---
    layout: js_minifier
    ---
    

    *Обновление от 10 декабря 2020 года:
    Можно выключить в заданном скрипте переименование имён переменных и функций. Это может быть полезно, когда переменные используются глобально, например в другом скрипте. Для этого нужно указать replace_names: false в заголовке JavaScript файла.

    ---
    layout: js_minifier
    replace_names: false
    ---
    

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

Особенности минификатора HTML

В минификаторе можно указать какие теги не нужно минифицировать. По умолчанию - это теги script, pre, svg и textarea. Т.е. на данный момент встроенный JavaScript в HTML разметку минифицироваться не будет. Также можно указать для каких тэгов нужно удалять отступы, но нельзя удалять переносы строк. В этом случае перенос строки будет заменяться пробелом. По умолчанию указан только тэг p. Если у вас в разметке важны переносы строк в div, a, td, span и т.д, то можно добавить и эти тэги, но смотрите что выйдет в итоге, т.к., пробелы вокруг этих тэгов пропадут или, например, если эти тэги будут внутри pre, то ваша разметка может поехать.
Скорректировать какие теги нужно оставлять интактными, а где нельзя удалять переносы строк можно в первых строчках скрипта.

Не работает с XHTML, т.к. убирает концевые “/” в тэгах

Когда не будет работать минификатор JavaScript

  • Если в коде есть JS строки, содержащие символы комментария “//”, “/*” и “*/”. Исключение сделано для сочетания “://”, т.к. часто в строках хранят адреса. Т.е. код
    const str = 'Hello wor//ld! ';
    

    непозволит минифицировать JS, а код

    const str = 'https://example.com';
    

    никак не повредит минификации.

  • Если в коде есть комментарии, содержащие символы комментария “//”, “/*” и “*/”. Такой код сломает минифкатор:
    //const str = 'Hello wor//ld! ';
    
  • Если в коде между ключевым словом (function, let, const, var и др.) и именем находится чётное число пробелов. Такой код сломает минифкатор:
    function  foo(){}
    

    И такой код тоже:

    let  foo = 3;
    

    Здесь после function и после let идёт два пробела.

  • Если в строках будут непарные символы “ ‘ “ или “ “ “ или “ ` “. Например, следующий код не пройдёт из-за апострофа:
    const str = "I can't write this!";
    

    Однако, непарный символ можно экранировать. Это будет всё ещё валидный JS, который будет работать и без минификации и одновременно минификация на нём не сломается:

    const str = "I can\'t write this!";
    

    Если в строке указанные символы парные, то никаких проблем с минификацией не возникнет:

    const str = 'I can write this: "Hello!".';
    
  • И самое главное, конструкции в Вашем коде должны заканчиваться “;”. Если вы применяете другой паттерн написания кода, и не пользуетесь символом “;”, то на данный момент этот минификатор для Вас работать не будет.

Также следует отметить, что внутри строковых выражений код не минифицируется, т.е. в таком коде лишние пробелы не удалятся:
Обновление от 06 декабря 2020:
Теперь в таком коде пробелы удалятся.

const xx=`x умножить на x будет ${x    *    x}`;

Если вы пользуетесь Jekyll и GitHub Pages, поробуйте минифицировать Ваш код, отпишитесь в комментариях, что получилось, что нет, что нужно исправить.