\o/
Lint all the things
\o/
Reasons to lint
Standardize the code
- Makes it easier to read
- Looks like one person wrote the entire code base
Improve code quality
- Limit cyclomatic complexity
- Limit function nesting
- Limit file size
Improve code reviews
- Reviewer can focus on code instead of style
- Some things (code duplication, perf, naming*...) are not linted with common tools
Easy to use
- Does not take long to configure
- Runs quickly even on a large code base (can be cached)
Riddle me this
const person = {
age: 27,
name: 'Alex'
}
function sayHi (person) {
switch (person.name) {
case 'Alex':
const age = person.age
console.log(`Hello Alex, you are ${age} yo., yet your prez is terrible.`)
break
default:
const age = person.age
console.log(`Hi ${person.name}. ${age} years old, right?`)
break
}
}
sayHi(person)
const age = person.age;
^^^
SyntaxError: Identifier 'age' has already been declared
at Object.exports.runInThisContext (vm.js:76:16)
at Module._compile (module.js:528:28)
at Object.Module._extensions..js (module.js:565:10)
at Module.load (module.js:473:32)
at tryModuleLoad (module.js:432:12)
at Function.Module._load (module.js:424:3)
at Module.runMain (module.js:590:10)
at run (bootstrap_node.js:394:7)
at startup (bootstrap_node.js:149:9)
at bootstrap_node.js:509:3
Linters in JS
Configurable
Opinionated




ESLint
- Works well with modern JS
- .eslintrc config file linting files inside folders
- Multiples style guides available
- https://github.com/google/eslint-config-google
- https://github.com/airbnb/javascript
- https://github.com/Meetic/eslint-config-meetic
- Override specific rules from a config
- Extend multiple configs
- Change errors into warning to highlight particular files to refactor
/* eslint eqeqeq: "off", curly: "warn" */
{
"extends": ["eslint:recommended", "google"],
"rules": {
// Additional, per-project rules...
}
}
/*eslint complexity: ["error", 2]*/
function a(x) {
if (true) {
return x;
} else if (false) {
return x+1;
} else {
return 4; // 3rd path
}
}
// recommended in eslint-config-meetic : complexity: ["error", 6]
const formIsValid = () => {
if (!this.firstName) {
this.firstNameIsError = true;
return false;
}
this.firstNameIsError = false;
if (!this.lastName) {
this.lastNameIsError = true;
return false;
}
this.lastNameIsError = false;
if (!this.address) {
this.addressIsError = true;
return false;
}
this.addressIsError = false;
if (!this.city) {
this.cityIsError = true;
return false;
}
this.cityIsError = false;
if (!this.postalCode) {
this.postalCodeIsError = true;
return false;
}
this.postalCodeIsError = false;
if (!this.country) {
this.countryIsError = true;
return false;
}
this.countryIsError = false;
if (!this.phone) {
this.phoneIsError = true;
return false;
}
this.phoneIsError = false;
return true;
};
Fresh example from a MR
/*eslint id-blacklist: ["error", "data", "err", "e", "cb", "callback"] */
var data = {...};
function callback() {
// ...
}
element.callback = function() {
// ...
};
var itemSet = {
data: [...]
};
/*eslint max-nested-callbacks: ["error", 3]*/
foo1(function() {
foo2(function() {
foo3(function() {
foo4(function() {
// Do something
});
});
});
});
Standard
npm install standard --save-dev
- No semicolons
- Other rules to protect you because you don't use semicolons
- Rest of eslint config



WTF?!
Riddle me this
const works = 'it works'
console.log(works)
(function () {
console.log('...or does it')
})()
/Users/alexandrebarbier/riddles/riddle-semicolons.js:4
(function () {
^
TypeError: console.log(...) is not a function
at Object.<anonymous> (/Users/alexandrebarbier/riddles/riddle-semicolons.js:4:1)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:394:7)
at startup (bootstrap_node.js:149:9)
at bootstrap_node.js:509:3
Linters in CSS
Wait, CSS is not code ?
... right ?
- 6 months after -
I can't maintain this... I'm out

Many linters
- CSSLint
- SassLint
- SCSSLint
- stylelint (PostCSS)
https://github.com/Meetic/stylelint-config-meetic
/* Bad color examples */
.foo {
color: #FFF; /* use lowercase for hex colors */
}
.bar {
color: #ffffff; ; /* use short notation for hex colors */
}
.baz {
color: #fffffz; /* invalid hex are disallowed */
}
.qux {
color: rebeccapurple; /* named color are disallowed */
}
/* Good color example (short notation, lowercase, not a named color) */
.foo {
color: #fff;
}
/* Bad number */
.foo {
line-height: .5; /* use a leading zero for fractional numbers less than 1 */
}
.bar {
top: 3.1234567px; /* number of decimal places is limited to 6 */
}
.baz {
top: 1.0px; /* no trailing zeros allowed */
}
.qux {
top: 0px; /* don't use units for zero lengths */
}
/* Good number */
.foo {
line-height: 0.5; /* use a leading zero for fractional numbers less than 1 */
}
.bar {
top: 3.123456px; /* number of decimal places is limited to 6 */
}
.baz {
top: 1px; /* no trailing zeros allowed */
}
.qux {
top: 0; /* don't use units for zero lengths */
}
Linters in HTML
What could possibly go wrong ?
HTML is simple, right?
- Let's run htmlhint in our beautiful project
<meetic-icon
icon-name="'apple'"
class="funnel-node-finish__icon">
+ </meetic-icon>
</div>
</a>
<a
@@ -48,6 +49,7 @@
<meetic-icon
icon-name="'windows'"
class="funnel-node-finish__icon">
+ </meetic-icon>
</div>
</a>
Oops
ng-class="'account-nav__interaction--' + $index"
ui-sref="dating.profile.page(::{aboid: member.get('aboid'), state: 'interactions', path: $ctrl.statePathToAboids})"
ng-style="::{backgroundImage: 'url(' + $ctrl.getMemberProfilePictureUrl(member, 'profile_list_card') + ')'}">
- <a/>
+ </a>
Really ?
<span
class="account__summary-city"
- class="account__summary-city"
ng-bind="::$ctrl.city">
</span>
You should stop cmd + shift + d
ead9d684 (n.labyt 1) <form
ead9d684 (n.labyt 2) class="inbox-thread-inputs"
ead9d684 (n.labyt 3) name="messageForm"
ead9d684 (n.labyt 4) novalidate
6c530eb5 (Matthieu Kluj 5) ng-submit="$ctrl.sendMessage()">
64a07c3b (Antoine Jackson 6) <div class="inbox-thread-inputs__input-wrapper">
64a07c3b (Antoine Jackson 7) <input
64a07c3b (Antoine Jackson 8) class="inbox-thread-inputs__input"
64a07c3b (Antoine Jackson 9) type="text"
ead9d684 (n.labyt 10) required
fcfca375 (Jenkins 11) placeholder="{{::'dating.inbox.thread.input-placeholder' | translate:{ nickname: $ctrl.nickname} }}"
d121c391 (Matthieu Kluj 12) meetic-auto-focus
ead9d684 (n.labyt 13) ng-model="$ctrl.inboxInputMessage">
64a07c3b (Antoine Jackson 14) </meetic-icon>
c4310873 (Antoine Jackson 15) </div>
64a07c3b (Antoine Jackson 16) <button
64a07c3b (Antoine Jackson 17) type="submit"
7d527283 (Oleg SKLYANCHUK 18) class="inbox-thread-inputs__send-button"
7d527283 (Oleg SKLYANCHUK 19) ng-disabled="messageForm.$invalid">
7d527283 (Oleg SKLYANCHUK 20) <meetic-icon
7d527283 (Oleg SKLYANCHUK 21) class="inbox-thread-inputs__send-icon"
7d527283 (Oleg SKLYANCHUK 22) icon-name="'send'">
7d527283 (Oleg SKLYANCHUK 23) </meetic-icon>
64a07c3b (Antoine Jackson 24) </button>
c4310873 (Antoine Jackson 25) </form>
git blame it on the alcohol
JSX
https://github.com/jkroso/eslint-plugin-jsx
Lint workflow
The sooner, the better
- Install lint extension in your code editor (atom, vim...)
- Use lint with githooks (pre-commit)
- Lint on CI only as a safety net (--no-verify)
#!/usr/bin/env bash
windows() { [[ -n "$WINDIR" ]]; }
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
ESLINT="$DIR/../../node_modules/.bin/eslint"
# Running ES lint before committing
if windows; then
JS_COUNT=$(git diff --name-only --cached | findstr -i ".js");
else
JS_COUNT=$(git diff --name-only --cached | grep -i "\.js$");
fi
if [[ "$JS_COUNT" ]]; then
echo "Linting JavaScript..."
"$ESLINT" '*.js' '{app,config,gulp}/**/*.js' --cache --cache-location 'cache/.eslintcache'
if [[ $? = 0 ]]; then
echo "✔ ES lint passed"
exit 0
else
echo "✘ ES lint failed. Please check your code."
exit 1
fi
fi
Scary githook script
Overcommit
Written in ruby, library of githooks configurable by .yml
https://github.com/brigade/overcommit
PreCommit:
EsLint:
enabled: true
command: ['eslint', 'app/scripts']
flags: ['--fix', '--format=compact']
include:
- '**/*.js'
- '.eslintrc'
ghooks
Written on JS, configure inside your package.json
https://github.com/gtramontina/ghooks
{
…
"config": {
"ghooks": {
"pre-commit": "gulp lint",
"commit-msg": "validate-commit-msg",
"pre-push": "make test",
"post-merge": "npm install",
"post-rewrite": "npm install",
…
}
}
…
}

deck
By Alexandre BARBIER
deck
- 355