ESLint is a JavaScript linter much more capable than JSHint in terms of customizability. This post will give you detailed instructions on how to integrate it in a multi-module project environment independent of its size. The approach presented is itself modular and creates a ESLint environment which is adaptable.
ESLint is to be preferred if programmatic enforcement with JSCS is out of the question. It strength stems from the usage of Espree an JavaScript parser that produces an Abstract Syntax Tree. The exposition of the AST allows for creating own rules. Furthermore it provides a great documentation, built-in features and testing framework.
You want to create a plugin which governs your rules, therefore you will need to create a node module that will be added in each dependent's package.json
. Please find a ready-to-use skeleton implementation here.
Note that the name of the folder and plugin itself must start with
eslint-plugin-
Let us analyze the minimal structure of the plugin module:
config/
mixins/
eslintrc.base
rules/
test/
index.js
package.json
The main configuration - with your rules initialized to a sensible default - is located in config/eslintrc.base
. This file is also the "root": true
of our environment. The most important adaption is:
"plugins": {
"skeleton"
}
which allows ESlint to recognize the folder node_modules/eslint-plugin-skeleton/
as plugin once installed with npm.
ESLint configuration files are YAML, a superset of JSON, which allow comments
It is common to have different environments in your application, e.g. in Angular you want different linting processes for your source folder and test folder. Probably you'll even want to distinguish between your tests based on whether they are unit, API or E2E tests. However, all of them can share a common ground and specify their individual needs.
ESLint uses a cascading hierarchy, the proximate
.eslintrc
will have priority
This is the case for config/mixins
. The base configuration gets extended by a mixin for a particular environment:
config/mixin/browser
:{
"env": {
"node": false,
"browser": true
},
"rules": {
"strict": [2, "function"],
"no-console": 2
}
}
config/mixin/test
:{
"env": {
"mocha": true
},
"rules": {
"skeleton/leftover-only": 2
}
}
Note the prefix of the custom rule is the same as the plugin name.
leftover-only
is an example rule which detects .only
config/mixin/unit
:{
"extends": ["./browser", "./test"],
"globals": {
"inject": true,
"module": true,
"sinon": true
}
}
Custom rules go to rules
and their respective tests to test
. They have to be exported inside the index.js
:
'use strict'
module.exports = {
rules: {
'leftover-only': require('./rules/leftover-only'),
},
}
The dependent modules or applications will need to add following dependencies to their package.json
:
"eslint": "^1.1.0",
"eslint-plugin-skeleton": "^1.0.0"
You'll need a private registry or you have to use git urls
Let's construct a typical Angular application structure enriched by ESLint:
app/
bower_components/
.eslintrc
node_modules/
test/
e2e/
.eslintrc
api/
.eslintrc
unit/
.eslintrc
.eslintignore
.eslintrc
The .eslintignore
is similar to a .gitignore
and should ignore your app/bower_components
and your dist
.
The base .eslintrc
will extend the base configuration from the plugin and function as root:
{
"extends": "./node_modules/eslint-plugin-skeleton/config/eslintrc.base"
}
The specific configurations extend their mixin. Examples:
app/.eslintrc
:
{
"extends": "../node_modules/eslint-plugin-skeleton/config/mixins/browser"
}
app/test/unit/.eslintrc
:
{
"extends": "../node_modules/eslint-plugin-skeleton/config/mixins/unit"
}
and so on.
If you then run npm i
it will install the plugin and it will be available. Changes to your linting can be made at a central location, your build tool isn't a special case and it works flawless with editor plugins.
Global ESLint doesn't recognize local plugins. Always install ESLint and the plugin locally. Also you can't link your Gulp module with the lint task.