Skip to content
Permalink
bem-info-data
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time

borschik

borschik — это простой, но мощный сборщик файлов текстовых форматов.

Основная задача – сборка статических файлов веб-проектов (CSS, JS и т.д.).

Что он умеет делать с файлами:

  • склеивать
  • преобразовывать
  • минимизировать

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

borschik оперирует двумя понятиями — include и link.

  • include: вы указываете ссылку на файл, после сборки ссылка заменяется на содержимое файла.
  • link: вы указываете ссылку на файл, после сборки ссылка преобразовывается.

Фактически каждая технология определяет как найти link или include и как их обработать.

Склейка файлов

CSS

Из коробки borschik умеет раскрывать правила @import и заменять их на содержимое указанного файла.

Допустим, у нас есть два CSS-файла

  • b-header/b-header.css
.b-header
{
    border: 1px solid #000;
}
  • b-footer/b-footer.css
.b-footer
{
    border-top: 1px solid #000;
    background: url("bg.png");
}

И мы хотим собрать их в один файл page.css, который будет лежать в корне проекта

@import url("b-header/b-header.css");
@import url("b-footer/b-footer.css");

С помощью команды

$ borschik --input=page.css --minimize=no

Получаем следующий результат

/* b-header/b-header.css begin */
.b-header
{
    border: 1px solid #000;
}

/* b-header/b-header.css end */


.b-title
{
    font-size: 120%
}

/* b-footer/b-footer.css begin */
.b-footer
{
    border-top: 1px solid #000;
    background: url("b-footer/bg.png");
}

/* b-footer/b-footer.css end */

Добавление комментариев при раскрытии можно отлючить с помощью опции --comments=no.

Примечание: несмотря на то, что картинка bg.png лежит в папке b-footer, путь до нее преобразовался и стал относительным файла page.css. Точно также можно указывать вложенные правила @import: пути до них будут правильно раскрыты.

###JS По аналогии с CSS можно склеивать и JS-файлы. Так как в JS нет стандартных методов для импорта файлов, в borschik придуман специльный синтаксис borschik:include:path/to/fie.js. Такие конструкции нужно указывать либо в блочных комментариях /*borschik:include:file.js*/, либо как строку "borschik:include:file.js".

Если include указан как комментарий, то он заменится на содержимое файла без какой-либо обработки. page.js

var prj = {};
/* borschik:include:components/cookie.js */

Собираем:

$ borschik --input=page.js --minimize=no

Получаем:

var prj = {};
/* components/cookie.js begin */
prj.cookie = {
    set: function(){},
    get: function(){}
};

/* components/cookie.js end */

Если include указан как строка, то содержимое файла будет обработано с помощью JSON.stringify. page.js

prj.STATIC_HOST = "borschik:include:components/host.txt";

Преобразуется в:

prj.STATIC_HOST = "//yandex.st";

Минимизация CSS и JS

По умолчанию borschik минимизирует CSS посредством CSSO , а JS с помощью UglifyJS v1.2.

Чтобы отключить минимизацию, используйте опцию --minimize=no. По умолчанию, она включена.

Относительные ссылки на ресурсы

Упоминание картинок в CSS файлах можно изменить с относительных путей на абсолютные (или другие относительные) с помощью конфига (файл .borschik в любой директории). Конфиг считается относящимся к той директории, в которой он находится. Конфиги в директории более верхнего уровня (ближе к корню проекта) важнее конфигов в директории более нижнего.

Пример синтаксиса:

    {
        "paths" : {
            "./": "/",
            "css/": "//yandex.st/my-prj/css/"
        }
    }

Находяcь в корне проекта, этот конфиг приводит все ссылки к абсолютному в пределах домена пути, а файлы в директории css/ – к //yandex.st/my-prj/css/.

Например, если в проекте будет файл css/my.css, в котором написано background-image: url(../a/b.gif); (относительная ссылка на картинку), то из-за мапинга в .borschik про "./": "/" в результате будет background-image: url("/a/b.gif");.

Раскрытие символических ссылок

В конфиге можно использовать ключ follow_symlinks

Например,

    "follow_symlinks" : {
        "./a/b/c.css" : true,
        "./a" : true
    }

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

«Заморозка» статических ресурсов (freeze)

Зачем нужна «заморозка» статики

Стандартные способы оптимизации загрузки статики выглядят так:

  • Выставить «правильные», навечно кеширующие, заголовки на сервере
Cache-Control:max-age=315360000
Expires:Thu, 31 Dec 2037 23:55:55 GMT
  • Унести статику на отдельный домен
  • Добавить версию в урл, чтобы инвалидировать ее при выпуске новой версии

К примеру, вот так вставляем CSS

<link rel="stylesheet" href="//yandex.st/my-prj/1.0.0/css/page.css"/>

Допустим, в нем есть ссылка на картинку:

.b-page
{
    background-image: url('../i/bg.png')
}

Получается, что картинка загружается по следующему URL

//yandex.st/my-prj/1.0.0/i/bg.png

Проблема: если CSS поменяется, необходимо будет изменить версию проекта, что изменит урл

<link rel="stylesheet" href="//yandex.st/my-prj/1.0.1/css/page.css"/>

Урл до картинки также поменяется

//yandex.st/my-prj/1.0.1/i/bg.png

Значит, из-за изменившегося урла пользователю придется заново загрузить не только новый CSS, но и все картинки в нем.

Подобную аналогию можно провести с JS и HTML.

Как решить проблему?

  1. Можно вручную добавлять к каждому ресурсу его версию. Это просто. Но неудобно, сложно покрыть все файлы и есть большой риск ошибиться.
  2. Можно автоматически добавлять к каждому ресурсу ревизию из VCS или timestamp последнего изменения. Сложнее в реализации, избавляет от ручной работы, но не от сложности покрытия всех файлов.

borschik предлагает простой, но комплексный вариант решения – загрузка ресурсов по урлу, не зависящему от версии проекта, а зависящему от содержимого файла, например SHA1-суммы.

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

С помощью borschik вы сможете это автоматизировать.

.borschik config

Для начала нам нужен конфиг. Он записывается в файл .borschik.

Файл .borschik влияет на директорию, где он находится, и на все вложенные директории.

Если есть несколько файлов в разных директориях, то конфиги в этих файлах переопределяют друг друга в порядке "кто выше, тот главнее".

{
    "freeze_paths": {
        "i/bg": "../../_",
        "i/ico": "../../_"
    }
}

freeze_paths — это path-mapping, который определяет, какие файлы и в какую папку замораживать.

Например, когда borschik будет обрабатывать CSS-файл и найдет ссылки на ресурсы в папках i/bg или i/ico, он их заморозит.

Ключ — это папка, файлы в которой будут заморожены.

Значение ключа указывает папку, в которую нужно cложить замороженые файлы. Эта папка считается относительно оригинальной папки.

Например,

{
    "freeze_paths": {
        "i/bg": "_"
    }
}

заморозит файлы из папки i/bg в папку i/bg/_

Важно:

  • Замораживаются не все файлы в папке, а только те, на которые есть ссылки в обрабатываемых файлах.
  • В папке фриза создаются копии оригинальных файлов, но с другим именем. Имя – хеш-сумма от содержимого файла.

###CSS freeze Например, есть такой CSS css/main.css

.b-page
{
    background-image: url('../i/bg/main.png')
}

Мы его замораживаем

$ borschik --input=css/main.css --freeze=yes

И получаем такой файл

.b-page
{
    background-image: url('//yandex.st/my-prj/_/wFPs-e1B3wMRud8TzGw7YHjS08I.png')
}

Теперь достаточно просто положить замороженные картинки и CSS на сервер. После заморозки в урле больше нет версии, и все файлы грузятся по хешу. Все ресурсы всех версий могут спокойно выкладываться в одну и ту же папку. Даже если у них будут одинаковые картинки, это не вызовет никаких проблем. Если при обновлении версии картинки не поменяются, урл к ним не изменится и пользователь не будет их заново загружать.

JS

Так как CSS является декларативным языком, то найти ссылки на картинки или шрифты не проблема. С JS все сложнее: ссылку на картинку можно указать бесконечно разными способами, а ссылки могут быть динамическими.

Статический URL

Вот так обычно выглядит ссылка на картинку

new Image().src = 'i/bg/main.png'

Чтобы помочь borschik найти и заморозить картинку, надо ее разметить с помощью borschik.link()

new Image().src = borschik.link('i/bg/main.png')

Запускаем

borschik --tech=js --freeze=yes --input=1.js

Получаем

new Image().src = '//yandex.st/my-prj/_/wFPs-e1B3wMRud8TzGw7YHjS08I.png'

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

borschik.link = function(link) {
    return link;
}

Динамический URL

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

var icoName = 'yandex';
new Image().src = 'i/ico/' + iconName + '.png'

Такие картинки придется объявлять в отдельном JSON-файле

{
    "ico-yandex-png": "i/ico/yandex.png",
    "ico-github-png": "i/ico/github.png",
    "ico-nodejs-png": "i/ico/nodejs.png"
}

Ключ — это любое название, которое вам больше нравится, а значение — путь до файла. JS придется немного переписать: ссылаться на картинку не по урлу, а по имени из JSON

var icoName = 'yandex';
new Image().src = borschik.link('@ico-' + iconName + '-png')

@ в начале имени указывает на то, что это динамическая картинка. Ее нужно добавлять обязательно, чтобы уметь отличать имя от пути. При таком использовании вызов borschik.link() не изменится, а правильный урл будет отдаваться динамически.

Теперь берем JSON, который мы описали, и отдаем его в специальную функцию borschik.addLinks()

borschik.addLinks(/* borschik:include:_images.json */)
var icoName = 'yandex';
new Image().src = borschik.link('@ico-' + iconName + '-png')
# замораживаем JSON
$ borschik --tech=json-links --input=images.json > _images.json
# собираем JS
$ borschik --tech=js --input=1.js

Получаем

borschik.addLinks({
    "ico-yandex-png": "//yandex.st/my-prj/_/wFPs-e1B3wMRud8TzGw7YHjS08I.png",
    "ico-github-png": "//yandex.st/my-prj/_/8ge7HHM3UfpIESgvrpN3bi-Nz0.png",
    "ico-nodejs-png": "//yandex.st/my-prj/_/1z-l36qqomllvJek_InjAYnHrOE.png"
})
var icoName = 'yandex';
new Image().src = borschik.link('@ico-' + iconName + '-png')

borschik.link() и borschik.addLinks() выглядит так:

(function() {
    var borschik = window['borschik'] = {};

    var links = {};

    borschik.addLinks = function(json) {
        for (var link in json) {
            links[link] = json[link];
        }
    };

    borschik.link = function(link) {
        // link with "@" is dynamic
        if (link.charAt(0) === '@') {
            return links[link.substr(1)];
        }

        return link;
    };

})();

HTML

borschik умеет замораживать ссылки на статические ресурсы в HTML.

Например,

<html>
    <head>
        <link rel="stylesheet" href="1.css"/>
    </head>
    <body>
        <!-- <img src="1.png"> -->
        <img src="1.png">
        <script src="1.js"></script>
    </body>
</html>

Замораживаем

$ borschik --tech=html --input=index.html

Получаем

<html>
    <head>
        <link rel="stylesheet" href="//yandex.st/prj/_/n8mJAmybm5i9sdsO92s6y0.css"/>
    </head>
    <body>
        <!-- <img src="1.png"> -->
        <img src="//yandex.st/prj/_/jUK5O9GsS2gPWOhRMeBxR0GThf0.png">
        <script src="//yandex.st/prj/_/1qHhHrD9m5i9sdDbCe590URPaBw.js"></script>
    </body>
</html>

«Полная заморозка»

Как рассказано выше, borschik замораживает только те файлы, на которые есть ссылка внутри обрабатываемого файла. Естественно могут быть ситуации, когда ссылок нет. Например, при динамической загрузке JS-модулей через RequireJS. В такой ситуации будет полезно заморозить все файлы. Для этого воспользуйтесь подкомандой borschik freeze, которая согласно конфигу .borschik заморозит все файлы в указанной папке

$ borschik freeze \
  --input=path/to/dir \ # папка, в которой надо заморозить все файлы
  --output=freeze-info.json # JSON с mapping'ом оригинальный файл -> замороженный файл

Пример

$ borschik freeze --input=js > freeze-info.json
{
    "js/index.js": "//yandex.st/my-prj/_/434046cd5d1b54ae2374868a7363d7d8.js",
    "js/setup.js": "//yandex.st/my-prj/_/bcbf293578cfda2d4543d401d12e2e49.js"
}

Дальше этот JSON можно использовать в вашей системе загрузки или с небольшими преобразованиями в RequireJS.

Преимущества заморозки

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

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