Я работаю над фронтендом огромного проекта – поисковой выдачи Яндекса. И у нас, как и у любого другого большого веб-проекта, есть огромное количество css-кода и немаленькая команда, которая с ним взаимодействует.
Когда много людей используя разные инструменты, пишет и редактирует css, со временем этот css может получиться очень запутанным, неконсистентым и в целом начинает выглядеть плохо. Например, кому-то удобнее писать вендорные префиксы в одном порядке, кому-то – в другом, кто-то ставит кавычки вокруг url, кто-то – нет, а кто-нибудь фикся срочную багу к релизу мог бы, к примеру, написать position: relative в начале блока свойств, незаметив что где-нибудь внизу между color и box-shadow, уже есть position: absolute, и долго гадать, почему у него ничего не работает.
Но несмотря на то, что все пишут код по-разному, у нас в репозитории идеальный порядок: css-код полностью консистентен, и прекрасно выглядит. Весь.
Как мы этого добились, можно прочитать под катом.
Первые шаги
Если мы хотим, чтобы весь css писался по кодстайлу, то первая очевидная мысль – это чтобы какой-нибудь самый-главный-начальник сказал: «Надо писать так-то и так-то, соблюдайте», а несоответствия отлавливать на код ревью. Звучит круто, но, к сожалению, только звучит, а на практике получается так себе:
- Все мы люди — а человеку немного сложно запомнить точный-рекомендованный-порядок всей кучи css свойств, и никогда в нем не ошибаться.
- Код ревью в таком случае превращается в набор правок по кодстайлу, а хотелось бы чтобы в нем обсуждали логику и архитектуру.
- Все ошибки в этом месте нужно исправлять руками, а все несоответствия уточнять в какой-нибудь доке.
В общем, выглядит как-то не очень, отнимает кучу времени и, что самое главное – очень расстраивает разработчиков.
Чтобы разработчики ходили довольные, пришлось искать какое то другое решение, и оно было найдено.
Немного о роботах
Был один старый робот — CSScomb, который решал самую важную часть нашей задачи:
он умел сортировать свойства в заданном порядке. Но у него было несколько недостатков:
- Кроме этого он не умел ничего, а ведь очень хотелось и кавычки где не надо убирать, и вендорные префиксы лесенкой выстраивать…
- Был написан на PHP и регэкспах, а также не предусматривал расширения.
И хотя, конечно, PHP в среде js-программистов немного известнее чем c++, это все-таки не совсем то, чего бы хотелось.
mishanga из нашей команды, посмотрел на все это дело и решил сделать инструмент, который был бы лишен этих недостатков: умел много разных штук, был бы написан на js, и мог легко расширяться. И хотя сотрудники Яндекса принимают активное участие в разработке, CSScomb.js – полноценный опенсорсный проект, давно вышедший за рамки Яндекса.
Знакомьтесь, CSScomb.js — это новая версия старого CSScomb, умеющая кучу всяких крутых вещей.
Как это выглядит ?
Рассмотрим текущий процесс разработки на примере. Допустим, исключительно в образовательных целях мы хотим закоммитить вот такой код:
.block { -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, .1); -moz-box-shadow: 0 0 0 1px rgba(0, 0, 0, .1); box-shadow: 0 0 0 1px rgba(0, 0, 0, .1); z-index: 2; position: absolute; height: 2px; width: 2px; color: #FFFFFF; }
Выглядит он не очень, и вообще не по кодстайлу, и поэтому при попытке коммита мы увидим вот что:
% git commit block.css -m"Add My awesome css" Code style errors found. ! block.css
Коммитнуть такой css-файл не удастся. Это происходит потому что в нашем репозитории в гите стоит прекоммит хук, проверяющий с помощью CSScomb все изменения в css-файлах, которые мы хотим закоммитить. Работает он в режиме линтера. Хук при этом довольно умный, поэтому проверяет только то, что мы собираемся коммитнуть, а не все подряд.
Увидев такое сообщение, мы не расстраиваемся, а просто просим CSScomb.js все поправить:
% csscomb block.css
Вот, теперь другое дело:
.block { position: absolute; z-index: 2; width: 2px; height: 2px; color: #fff; -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, .1); -moz-box-shadow: 0 0 0 1px rgba(0, 0, 0, .1); box-shadow: 0 0 0 1px rgba(0, 0, 0, .1); }
Он проделал за нас довольно много рутинных мелких правок в коде: перенес скобку на другую строку, перевел цвет в нижний регистр, выстроил вендорные префиксы лесенкой, а свойства в правильном порядке. Теперь такой код успешно пройдет автоматическую проверку и попадет на дальнейшее ревью, уже функциональных его качеств.
Таким образом, разработчику больше не нужно думать о том, как правильно написать css по кодстайлу, он просто пишет так, как ему удобно, а остальное делается автоматически. Это именно то, чего мы хотели добиться.
А внутре у ней неонка
Такая система состоит из двух частей — собственно CSScomb-а и прекоммит-хука который его использует. Про хук я расскажу в следующем разделе, а в этом подробно остановлюсь на том, как устроен CSSComb.js
В основе CSScomb.js две вещи: система плагинов и css-парсер gonzales-pe — очень крутая штука, которая умеет работать не только с чистым css, но и препроцессорами вроде Sass или LESS.
Начнем с парсера. Он натравливается на css-код и строит на его основе AST. Например, для:
.block { position: absolute }
AST будет выглядеть так:
[ "stylesheet", [ "ruleset", [ "selector", [ "simpleselector", [ "class", [ "ident", "block" ] ], [ "s", "\n" ] ] ], [ "block", [ "s", "\n " ], [ "declaration", [ "property", [ "ident", "position" ] ], [ "propertyDelim" ], [ "value", [ "s", " " ], [ "ident", "absolute" ] ] ], [ "s", "\n" ] ] ], [ "s", "\n" ] ]
Это формат очень громоздкий и неудобный для чтения человеком, зато очень удобен для автоматической обработки, которой и занимаются плагины. Всеми дальнейшими преобразованиями css занимаются именно они, причем работают плагины не непосредственно с css-кодом, а с AST, которое намного проще обрабатывать. Какие плагины использовать, а также их настройки указываются в отдельном файле с конфигурацией.
В примере выше мы выше мы не поставили точку с запятой после absolute. Плагин, который отвечает за детектирование и исправление точек с запятыми, это заметил и исправил через модификацию AST(я немного сократил дерево чтобы не дублировать большой кусок кода):
... [ "block", [ "s", "\n " ], [ "declaration", [ "property", [ "ident", "position" ] ], [ "propertyDelim" ], [ "value", [ "s", " " ], [ "ident", "absolute" ] ] ], >>>[ "declDelim" ],<<< [ "s", "\n" ] ...
После того как отработали все плагины, c помощью того же gonzales-pe AST превращается обратно в css код,
и забытая точка с запятой оказывается на своем месте:
.block { position: absolute; }
Как начать это использовать ?
Шаг первый
Нужно тем или иным способом добавить CSScomb.js в зависимости своего проекта.
В случае если на проекте используется npm, нужно добавить его в devDependencies в package.json
{ ... "devDependencies": { ... "csscomb": "2.0.4", ... } ... }
Шаг второй
Чтобы плагины приводили код к тому виду, который нам нужен, в корень проекта нужно положить файл с конфигурацией плагинов. Подробно о том, что делает каждый плагин, и какие опции у него есть, можно почитать тут.
CSScomb.js до какой-то степени может это сделать автоматически, потому что умеет генерировать конфиг, взяв за основу какую-нибудь существующую css-ку.
csscomb -d example.css > .csscomb.json
Таким образом могут быть сконфигурированы все плагины. Исключение составляет только сортировка свойств, которую по-прежнему придется писать самому, либо скопировать откуда-нибудь руками.
Шаг последний
Нужно сделать так, чтобы CSScomb.js в режиме линтера запускался перед коммитом наравне с jscs/jshint-groups или другими инструментами, которые обычно запускаются перед комитом. В случае использования git и linux/BSD он может выглядеть примерно так:
#!/usr/bin/env bash PATCH_FILE="working-tree.patch" NPM_BIN="./node_modules/.bin" function cleanup { exit_code=$? if [ -f "$PATCH_FILE" ]; then patch -p0 < "$PATCH_FILE" rm "$PATCH_FILE" fi exit $exit_code } #Прибираемся при выходе из скрипта trap cleanup EXIT SIGINT SIGHUP # Создаем патч git diff > "$PATCH_FILE" --no-prefix # Сбрасываем не застэйдженный изменения git checkout -- . # получаем список файлов в которых были изменения, которые мы хотим закоммитить git_cached_files=$(git diff --cached --name-only --diff-filter=ACMR | xargs echo) if [ "$git_cached_files" ]; then #Собственно натравливаем CSScomb.js $NPM_BIN/csscomb -v -l $git_cached_files || exit 1 fi
У этого хука есть интересная особенность. Казалось бы, почему не запускать CSSComb.js сразу в режиме исправления и затем автоматически молча коммитить? В большинстве случаев это действительно нормально работает, но проблемы возникают в ситуации, когда мы делаем несколько правок в файле, а закоммитить хотим только одну из них (git add -p). В этом случае если мы находим ошибку в той версии файла которую коммитим, начинаются неприятные ситуации:
- Мы можем запустить CSScomb.js на ту версию файла, которую собираемся закоммитить, и в некоторых ситуациях получить конфликты при наложении патча.
- Либо запустить CSScomb.js на текущем файле целиком, но в правках, которые мы пока не хотим коммитить, может быть что угодно, включая неправильный код.
Да и в целом это не очень приятно, когда файл неожиданно сам по себе меняется. Поэтому мы пришли к выводу, что CSScomb.js должен запускаться самим разработчиком.
Готово!
Всё. Теперь CSScomb.js не даст закоммитить код не по кодстайлу, и даёт возможность в одну команду привести код к нужному виду.
Вот так простая в общем-то идея и несложный инструмент помогают держать код в порядке, а также экономят время и нервы разработчиков.
Такую связку можно использовать во многих проектах, а если вам не хватает какой-либо базовой функциональности или плагинов, то всегда можно завести issue, либо законтрибьютить нужные штуки самостоятельно.
ссылка на оригинал статьи http://habrahabr.ru/company/yandex/blog/223503/

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