Stylelint
Как и зачем линтить CSS
Андрей Ситник,
Злые марсиане


Наш опенсорс

История человечества
10К
40К
250К

Неолитическая революция


История линтеров

Исходный код
Парсер
Анализ
Список ошибок
Линтер
1978 — первый линтер Lint

1995 — JavaScript
// true
1 == "1"
// забыт var
count = 1

Линтеры
CoffeeScript
или


CoffeeScript :-(
Эволюция линтеров



JSLint
JSHint
ESLint
→
→
ESLint: белый список
module.exports = {
'rules': {
'space-before-function-paren': [2],
'no-shadow-restricted-names': [2],
'computed-property-spacing': [2],
'no-empty-character-class': [2],
'no-irregular-whitespace': [2],
'no-unexpected-multiline': [2],
'no-multiple-empty-lines': [2],
'no-constant-condition': [2],
…
ESLint: модульность
Исходный код
Парсер
Плагин 1
Список ошибок
Плагин 2
ESLint: автоисправление
if(foo) {
bar()}
if (foo) {
bar();
}
eslint --fix *.js
Самые популярные зависимости
- mocha
- chai
- lodash
- grunt
- gulp
- eslint
- babel-preset-es2015
- request
- async
- should
Линтеры CSS

Поддержка синтаксисов
SCSS
CSSLint
SCSS Lint
CSSComb
CSS
Less
Функции
Белый список
CSSLint
SCSS Lint
CSSComb
Модульность
Исправление
Количество правил
CSSLint
SCSS Lint
CSSComb
38
26
62

Stylelint

Исходный код
Парсер
Плагин 1
Список ошибок
Плагин 2
На что похожа модульность?

Единый фреймворк
Полифилы
Минификация
Линтинг
Примеси
Изоляция
Сменные парсеры
CSS
SCSS
Less
SugarSS
Битый CSS
Правило Stylelint — плагин PostCSS
const ruleName = "comment-no-empty"
(root, result) => {
root.walkComments(comment => {
if ( comment.text && comment.text.length === 0 ) {
report({ … })
}
}
}
Белый список правил
module.exports = {
'rules': {
'at-rule-name-case': 'lower',
'at-rule-semicolon-newline-after': 'always',
'block-closing-brace-newline-after': 'always',
'color-hex-case': 'lower',
'color-hex-length': 'short',
'color-hex-length': 'short',
'color-no-invalid-hex': true,
'indentation': 2,
…
Исправление
.foo {
color:black}
.foo {
color:black;
}
stylefmt *.css
postcss-browser-reporter

SCSS
CSSLint
SCSS Lint
CSSComb
CSS
Less
Stylelint
Белый список
CSSLint
SCSS Lint
CSSComb
Модульность
Исправление
Stylelint
CSSLint
SCSS Lint
CSSComb
38
26
62
Stylelint
168
Легко писать новые правила
Пользователи Stylelint

Популярность

Зачем линтить


Cеньор
Юниор
Код-ревью


Cеньор
Юниор
1. Меньше времени на код-ревью
Stylelint

«Я понимаю, что ты думаешь …, но …»

2. Критика роботов приятнее
3. Обмен практиками
module.exports = {
'extend': 'stylelint-config-wordpress'
}
4. Поиск ошибок
'color-no-invalid-hex',
'function-linear-gradient-no-nonstandard-direction',
'time-no-imperceptible',
'property-no-unknown',
'property-no-vendor-prefix',
'declaration-block-no-duplicate-properties',
'declaration-block-no-ignored-properties',
'declaration-block-no-shorthand-property-overrides',
'selector-class-pattern',
'selector-max-compound-selectors',
'selector-pseudo-element-no-unknown',
'selector-type-no-unknown',
'media-feature-no-missing-punctuation',
'no-unsupported-browser-features',
'no-unknown-animations',
'no-indistinguishable-colors',
'no-descending-specificity',
'no-browser-hacks'
#container .foo {
top: 10px;
}
…
.foo {
/* Expected selector .foo come before #container .foo */
top: 0;
}
.foo {
opacity: 1;
/* Features like this, which is unsupported in IE 8 */
}
.foo {
display: inline;
width: 100px;
/* Unexpected width with display: inline */
}
.foo {
margin-top: 10px;
margin: 0 auto;
/* Unexpected shorthand margin after margin-top */
}
.foo {
color: black;
}
…
.bar {
background: #010101;
/* Unexpected indistinguishable colors #010101 and black */
}
Как линтить

1. Линтер в текстовом редакторе

2. lint-staged — перед коммитом
"scripts": {
"lint-staged": "lint-staged",
},
"lint-staged": {
"*.css": "stylelint"
},
"pre-commit": ["lint-staged"]
3. Линтер в CI

4. Начните с популярного конфига
module.exports = {
'extend': 'stylelint-config-standard'
}

5. Добавляйте плагины
module.exports = {
'plugins': [
'stylelint-scss'
],
'rules': {
'scss/selector-no-redundant-nesting-selector': true
}
}
dustinspecker/awesome-eslint

6. Линтер — стайл-гайд
«В стайл-гайде не должно быть правил, которые нельзя описать в алгоритме»
7. Делайте исключения
.foo.is-started {
/* stylelint-disable no-unknown-animations */
/* Animation will be generated in JS*/
animation-name: js-generated-path;
}
.foo.is-started {
/* stylelint-disable no-unknown-animations */
animation-name: js-generated-path;
/* Expected comment reason after `stylelint-disable` comment */
}
8. Пишите свои правила
import { utils } from "stylelint"
export const ruleName = namespace("ИМЯ")
export const messages = utils.ruleMessages(ruleName, {
expected: "ТЕКСТ ОШИБКИ",
})
export default function () {
return (root, result) => {
/* ЛОГИКА */
}
}
Цвета повторяются — в переменные
colors = { }
return (root, result) => {
root.walkDecls(decl => {
decl.value.match(/#[0-9a-f]{3,6}/, color => {
if ( colors[color] ) {
utils.report({ … })
} else {
colors[color] = true
}
})
})
}
Правила Фейсбука
-
slow-css-properties
-
filters-with-svg-files
-
use-variables
-
mobile-flexbox
9. Два раза ошиблись — в правило

10. Линтите всё
gulp.task('default', ['lint',
'test',
'all-translated',
'spell-check',
'security-audit',
'file-size'])
postcss-sorting
div {
-moz-box-sizing: border-box;
width: 100%;
box-sizing: border-box;
position: absolute;
-webkit-box-sizing: border-box;
}
div {
position: absolute;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
}
yaspeller
$ yaspeller /home/ai/Dev/postcss/README.md
✗ /home/ai/Dev/postcss/README.md 453 ms
-----
Typos: 1
1. transorming (suggest: transforming, transporting)
-----
Node Security Platform
> nsp check
(+) 1 vulnerabilities found
┌───────────────┬───────────────────────────────────────────────────────┐
│ │ ReDoS via long string of semicolons │
├───────────────┼───────────────────────────────────────────────────────┤
│ Name │ tough-cookie │
├───────────────┼───────────────────────────────────────────────────────┤
│ Installed │ 2.2.2 │
├───────────────┼───────────────────────────────────────────────────────┤
│ Vulnerable │ >=0.9.7 <=2.2.2 │
├───────────────┼───────────────────────────────────────────────────────┤
│ Patched │ >=2.3.0 │
├───────────────┼───────────────────────────────────────────────────────┤
│ Path │ my-test-project@undefined > honeybadger@1.1.2 > requ… │
├───────────────┼───────────────────────────────────────────────────────┤
│ More Info │ https://nodesecurity.io/advisories/130 │
└───────────────┴───────────────────────────────────────────────────────┘
Ссылки
Stylelint — как и зачем линтить CSS
By Andrey Sitnik
Stylelint — как и зачем линтить CSS
- 9,875