Я занимаюсь CSSO. В ходе работы над ним мне пришлось погрузиться в процесс парсинга CSS. В результате парсер (тот, что в CSSO) был не раз переписан. Пришло время сделать его отдельным инструментом.
Новый быстрый детальный парсер CSS, его AST, области применения и кое-что ещё.
2. Руководитель
фронтенда в Avito
Основной интерес – SPA
Open source:
basis.js, CSSO,
component-inspector,
csstree и другие
За любую движуху,
кроме голодовки ;)
9. 9
Светлая сторона
Все хорошо знают и часто
используют
Знают очень опытные
или копипаста
Темная сторона
Тайное экспертное знание, может
даже (пока) нигде не работает
Новое
CSS
Уже с нами
12. 11
#31 + 2 {
content: "hello
world";
background: url(p(4).jpg)
}
Валидный ли CSS?
Да, совершенно валидный!
Э – экранирование
13. 12
#31 + 2 {
content: "hello
world";
background: url(p(4).jpg);
}
Экранирование
drafts.csswg.org/cssom/#the-css.escape()-method
В идентификаторах, именах
классов, атрибутов и т.д.
можно использовать любые
символы, если экранировать,
даже придумали метод для
этого – CSS.escape()
CSS.escape('1 + 2') === '31 + 2'
14. 13
#31 +2 {
content: "hello
world";
background: url(p(4).jpg);
}
Экранирование
www.w3.org/TR/CSS22/grammar.html#grammar
Можно экранировать
перевод строки в строках,
прям как в JavaScript ;)
15. 14
#31 +2 {
content: "hello
world";
background: url(p(4).jpg);
}
Экранирование
www.w3.org/TR/CSS22/grammar.html#grammar
Можно экранировать
специальные символы
в url()
22. 21
@supports (background: top calc(25% + 2px) !important) {
…
}
@supports
Браузер может не верно ответить на вопрос,
но синтаксис разрешает все,
что можно в декларации, даже !important
www.w3.org/TR/css3-conditional/#at-supports
24. 23
:not(a.foo) { ... }
:not(a):not(b) { ... }
:not()
Было можно указывать только простые селекторы
www.w3.org/TR/css3-selectors/#negation
25. 24
:not(a, b) { ... } /* :not(a):not(b) */
img:not(a *) { ... } /* изображение не внутри ссылки */
:not()
CSS Selectors Level 4 разрешил список любых селекторов,
уже поддерживается в Safari 9+
drafts.csswg.org/selectors-4/#negation
tinyurl.com/Webkit-Complex-Selectors
26. 25
:nth-child(1 of p img) { ... } /* первый <img> в <p> */
:nth-last-child(2n + 1 of .foo, .bar) { ... }
:nth-child()/:nth-last-child()
CSS Selectors Level 4 и тут разрешил список любых селекторов,
тоже поддерживается в Safari 9+
tinyurl.com/Webkit-Complex-Selectors
drafts.csswg.org/selectors-4/#the-nth-child-pseudo
27. 26
.attr {
width: attr(name %, var(--default-width, calc(50% + 2em)));
}
attr()
CSS Values and Units Module Level 4 разрешает attr() в любом
месте + тип или единица изменения + fallback значение
drafts.csswg.org/css-values/#attr-notation
Пока никем не поддерживается и в состоянии at risk
28. Самое «вкусное»
• Баги браузеров
• Хаки _property, value !ie, 9, …
• Легаси префиксы, filter, expression(), …
27
37. Плюсы PostCSS
• Развивается и поддерживается
• Хорошо справляется с синтаксисом CSS и даже
будущим + tolerant mode
• Сохраняет информацию о форматировании
• Удобное API для работы с AST
• Быстрый
36
39. Это вынуждает разработчиков
• Использовать костыли
• Писать свои парсеры
• Использовать дополнительные парсеры:
postcss-selector-parser
postcss-value-parser
38
40. 39
// !singlequotes|!doublequotes|!url()|pixelunit
var pxRegex = /"[^"]+"|'[^']+'|url([^)]+)|(d*.?d+)px/ig;
css.walkDecls(function (decl, i) {
if (opts.propWhiteList.indexOf(decl.prop) === -1) return;
…
decl.value = decl.value.replace(pxRegex, function (m, $1) {
if (!$1) return m;
var pixels = parseFloat($1);
if (pixels < minPixelValue) return m;
return toFixed((pixels / rootValue), unitPrecision) + 'rem';
});
});
Кто во что горазд
postcss-pxtorem
42. Основные цели
• Правильный разбор с учетом семантики CSS
• Детальное, корректное и удобное AST
• Удобное API для работы с AST
• Сформировать«стандарт» для CSS AST
на подобии ESTree
• Быстрый и экономный
41
44. Чуть меньше года назад я стал
мейнтейнером CSSO
(минификатор CSS)
43
github.com/css/csso
45. CSSO работал на основе
парсера Gonzales
44
github.com/css/gonzales
46. Проблемы
• Не развивается с 2013
• Неудобный формат AST, местами странный
• Много ошибок
• Запутанная и сложная кодовая база
• Медленный, потребляет много памяти, GC
45
54. Хотите чтобы ваш JavaScript
работал так же быстро как Си,
сделайте его похожим на Си
53
55. Немного из того, что сделано
• Максимум string -> number
• Массив объектов -> несколько TypedArray
• Отказ от RegExp, toLowerCase(), …
• array -> bi-directional lists
• immutable objects
• …
54
56. 55
CSSTree: 13 ms
Mensch: 31 ms
CSSOM: 36 ms
PostCSS: 38 ms
Rework: 81 ms
PostCSS Full: 100 ms
Gonzales: 175 ms
Stylecow: 176 ms
Gonzales PE: 214 ms
ParserLib: 414 ms
bootstrap.css v3.3.7 (146Kb)
github.com/postcss/benchmark
Не детальное AST
Детальное AST
PostCSS Full =
PostCSS
+ postcss-selector-parser
+ postcss-value-parser
57. 56
CSSTree: 130 ms
PostCSS: 234 ms
CSSOM: 250 ms
Mensch: 381 ms
Rework: 507 ms
PostCSS Full: 571 ms
Stylecow: 1156 ms
Gonzales: 1202 ms
Gonzales PE: 1646 ms
ParserLib: 5509 ms
actiagent.ru (983Kb)
github.com/postcss/benchmark
69. Год шел к имплементации…
Запилил за несколько дней :)
67
70. Еще несколько дней ушло на
исправление ошибок в синтаксисах,
актуализации и дополнении
68
github.com/csstree/csstree/blob/master/data/patch.json
Получившийся патч (181 синтаксис)
75. 73
var csstree = require('css-tree');
var syntax = csstree.syntax.defaultSyntax;
var ast = csstree.parse('… your css …');
csstree.walkDeclarations(ast, function(node) {
if (!syntax.match(node.property.name, node.value)) {
console.log(syntax.lastMatchError);
}
});
Свой валидатор в 8 строк
76. 74
var csstree = require('css-tree');
var mySyntax = csstree.syntax.сreate({
generic: true, // чтобы добавились примитивы вроде <ident>, <number> etc
types: {
name: '<ident>',
role: 'organizer | sponsor | speaker | attendee | volunteer',
person: '<name> <role>'
},
properties: {
FrontTalks: '<person>#'
}
});
mySyntax.match('fronttalks', 'Oleg organizer, Roman speaker'); // ok
mySyntax.match('fronttalks', 'Roman'); // Mismatch
Cобственный синтаксис
78. css-values vs. csstree.syntax
76
с марта 2016
postcss, ES6 и т.д.
70% (254 из 361) свойств
Mozilla Template:CSSData
с 4 сентября 2016 :)
csstree, ES5
100% свойств
Mozilla Template:CSSData
+ 80 свойств
+ исправления
+ легаси
+ …
79. Кое что еще
• csstree-validator – npm пакет + консольная команда
• stylelint-csstree-validator – плагин для stylelint
• gulp-csstree – плагин для gulp
• SublimeLinter-contrib-csstree – плагин для Sublime Text
• vscode-csstree – плагин для VS Code
More is coming…
77
(запилили вчера, не шутка ;)
81. Подробное и правильное AST
– проще код инструментов,
работающие в большинстве
случаев
79
82. Планы: парсер
• Исправить/улучшить парсинг/AST
• Возможность расширять/патчить основной синтаксис
• Возможность задавать уровень детализации
• Использовать заданный синтаксис значений при разборе
(опционально)
• Оптимизировать/ускорить еще
• …
80
84. Уже есть положительный эффект
• Найдены ошибки в Mozilla Template:CSSData
https://bugzilla.mozilla.org/show_bug.cgi?id=1300329
• Найдена ошибка в спеке
https://github.com/w3c/csswg-drafts/issues/458
• Появляются вопросы к спекам
https://github.com/w3c/csswg-drafts/issues/461
• Появляются инструменты для валидации синтаксисов,
в том числе будущих
82