This repository contains the defacto-standard eslint configuration used on all Netsells projects, both internally and client.
Add the config to your project dependencies:
yarn add @netsells/eslint-config eslint@^4.10.0
Extend the config in your project. For example, a .eslintrc
file in your project root:
{
"extends": "@netsells"
}
Add the eslint-loader to your project:
yarn add eslint-loader
Add the loader into your workflow. The following example will force the linter to be ran before other loaders such as babel compilation. This means we lint the raw ES6 code rather than the transpiled ES5 output:
{
module: {
rules: [
{
test: /.(vue|js)$/,
loader: 'eslint-loader',
enforce: 'pre',
exclude: /node_modules/,
},
],
},
},
Requires trailing commas when the last element or property is in a different line than the closing ]
or }
and disallows trailing commas when the last element or property is on the same line as the closing ]
or }
. This makes git diffs a lot cleaner with single line changes rather than two.
let object = { a: 'b', c: 'd', };
let object = {
a: 'b',
c: 'd'
};
let array = ['a', 'b', 'c',];
let array = [
'a',
'b',
'c'
];
let object = { a: 'b', c: 'd' };
let object = {
a: 'b',
c: 'd',
};
let array = ['a', 'b', 'c'];
let array = [
'a',
'b',
'c',
];
Requires the dot to be located before the property rather than after the object
const item = object.
property;
const item = object
.property;
const item = object.property;
disallow empty block statements
if (foo) {
}
while (foo) {
}
switch(foo) {
}
try {
doSomething();
} catch(ex) {
} finally {
}
if (foo) {
// empty
}
while (foo) {
/* empty */
}
try {
doSomething();
} catch (ex) {
// continue regardless of error
}
try {
doSomething();
} finally {
/* continue regardless of error */
}
Disallow empty functions
function foo () {}
let foo = function () {};
let foo = () => {};
let obj = {
foo: function () {},
foo () {},
};
class A {
constructor() {}
foo() {}
}
function foo () {
// do nothing.
}
let foo = function () {
// any clear comments.
};
let foo = () => {
bar();
};
let obj = {
foo: function () {
// do nothing.
},
foo () {
// do nothing.
},
};
class A {
constructor () {
// do nothing.
}
foo () {
// do nothing.
}
}
Require a space before function parenthesis
function foo () {
// ...
}
let bar = function () {
// ...
};
class Foo {
constructor () {
// ...
}
}
let foo = {
bar () {
// ...
}
};
var foo = async() => 1
function foo() {
// ...
}
let bar = function() {
// ...
};
class Foo {
constructor() {
// ...
}
}
let foo = {
bar() {
// ...
}
};
var foo = async() => 1
Disallow mixed spaces and tabs for indentation
function add(x, y) {
// --->..return x + y;
return x + y;
}
function main() {
// --->var x = 5,
// --->....y = 7;
var x = 5,
y = 7;
}
function add(x, y) {
// --->return x + y;
return x + y;
}
Discourage code typed like yoda would speak
if ('red' === color) {
// ...
}
if (true == flag) {
// ...
}
if (5 > count) {
// ...
}
if (-1 < str.indexOf(substr)) {
// ...
}
if (5 & value) {
// ...
}
if (value === 'red') {
// ...
}
if (x < -1 || 1 < x) {
// ...
Disallow eval() function
let obj = { x: 'foo' },
key = "x",
value = eval("obj." + key);
(0, eval)("var a = 0");
let foo = eval;
foo("var a = 0");
// This `this` is the global object.
this.eval("var a = 0");
window.eval("var a = 0");
global.eval("var a = 0");
let obj = { x: 'foo' },
key = "x",
value = obj[key];
class A {
foo() {
// This is a user-defined method.
this.eval("var a = 0");
}
eval() {
}
}
Requires JSDoc definitions for all functions and classes.
methods: {
updateUser (id, data) {
return fetch(`/users/${id}`, {
method: 'POST',
body: JSON.stringify(opts),
});
},
}
methods: {
/**
* Update the user with the given id via the API
*
* @param {Number} id - id of user
* @param {Object} data - userdata object
*
* @returns {Promise}
*/
updateUser (id, data) {
return fetch(`/users/${id}`, {
method: 'POST',
body: JSON.stringify(opts),
});
},
}
Discourages using var
for creating variables and requires using let
or const
instead
var count = posts.length;
const count = posts.length;
or, if the value can be changed
let count = posts.length;
if (additionalPosts.length) {
count += additionalPosts.length;
}
Disallows using alert() function in production. Will throw a warning if the node env is not set to production (allows an alert-driven development).
if (error) {
alert(error);
}
Disallows using the console in production. Will throw a warning if the node env is not set to production.
if (error) {
console.log(error);
}
Encourages stopping mixing different types of variables for the sake of cleaner and more readable code.
// Boolean
const b = !!foo;
const b = ~foo.indexOf('.');
// Number
const n = +foo;
const n = 1 * foo;
// Strings
const s = '' + foo;
const s = `` + foo;
foo += '';
foo += ``;
// Boolean
const b = Boolean(foo);
const b = foo.includes('.');
// Number
const n = Number(foo);
const n = parseFloat(foo);
const n = parseInt(foo, 10);
// Strings
const s = String(foo);
foo = String(foo);
arrows on arrow functions should have a space before and after.
(a)=>{};
()=> {};
() =>{};
(a)=> {};
(a) =>{};
(a) => {}
Throw a warning when a regular string contains a text which looks like an ES6 template literal placeholder
const greeting = "Hello, ${name}";
const greeting = `Hello, ${name}`;
Encourage using template literals instead of '+' operator on strings
const greeting = 'Hello, ' + this.name;
const greeting = `Hello, ${this.name}`;
Forces using dot notation exclusively for getting object properties.
const a = foo['bar'];
const a = foo.bar;
const b = 'Hello';
const c = foo[b];
Disallow duplicate imports.
import { merge } from 'module';
import something from 'another-module';
import { find } from 'module';
import { merge, find } from 'module';
import something from 'another-module';
Disallows importing lodash - people should import only the lodash sub-modules they need.
import _ from 'lodash';
import flatten from 'lodash/flatten';
@throws Error
Disallow referencing this
within a template.
<template>
<div>{{ this.foo }}</div>
</template>
<template>
<div>{{ foo }}</div>
</template>
@throws Error
Enforce a rational order of Vue component data.
export default {
// Options / Misc
'name',
'delimiters',
'functional',
'model',
// Options / Assets
'components',
'directives',
'filters',
// Options / Composition
'parent',
'mixins',
'extends',
'provide',
'inject',
// Context
'el',
'template',
'props',
'propsData',
'data',
'computed',
'watch',
'LIFECYCLE_HOOKS',
'methods',
'render',
'renderError',
};
@throws Error
Enforce a consistent continuous indent of 4 spaces for both tags and tag attributes.
<template>
<div>
<my-component
:foo="bar"
:abc="xyz"
</my-component>
</div>
</template>
<template>
<div>
<my-component
:foo="bar"
:abc="xyz"
</my-component>
</div>
</template>
@throws Error
Force attributes to be hyphenated rather than camelCase.
<my-component :customAttribute="true"></my-component>
<my-component :custom-attribute="true"></my-component>
@throws Error
Error on duplicate keys to avoid conflicting and overwriting of values.
export default {
props: {
foo: String,
},
computed: {
foo: {
get () {}
},
},
data: {
foo: null,
},
methods: {
foo () {},
},
}
@throws Error
Force the shorthand syntax for event binding.
<my-component v-on:change="updateValue"></my-component>
<my-component @change="updateValue"></my-component>
@throws Error
Force the shorthand syntax for the v-bind directive.
<my-component v-bind:foo="bar"></my-component>
<my-component :foo="bar"></my-component>
@throws Error
Remove multiple spaces in a row between attributes which are not used for indentation.
<div class="foo"
:style="bar" />
<div
class="foo"
:style="bar"
/>
@throws Error
Allow only kebab-case (hyphenated) component names.
export default {
name: 'MyComponent',
}
export default {
name: 'my-component',
}
@throws Error
Enforce a single space around values in mustache echo statements.
<div>{{foo}}</div>
<div>{{ foo }}</div>
<div>{{ foo }}</div>
@throws Error
Enforce a double quotes on tag attributes.
<div class='foo'></div>
<div class=foo></div>
<div class="foo"></div>
@throws Error
Limit the max number of attributes per line. Single line tags can have a maximum of 3 attributes per line. After which each attribute should be broken down onto individual lines.
<my-component foo="bar" baz="qux" abc="123" xyz="321"></my-component>
<my-component
foo="bar" baz="qux"
><my-component>
<my-component foo="bar" baz="qux" abc="123"></my-component>
<my-component
foo="bar"
baz="qux"
abc="123"
xyz="321"
></my-component>
@throws Warning
Encourage providing default values for props.
props: {
a: Number,
b: [Number, String],
c: {
type: Number,
},
d: {
type: Number,
required: false,
},
}
props: {
a: {
type: Number,
required: true,
},
b: {
type: Number,
default: 0,
},
c: {
type: Number,
default: 0,
required: false,
},
}
@throws Warning
Encourage long-form prop definitions with at minimum a declared data type.
props: ['status'],
props: {
status: String,
}
props: {
status: {
type: String,
},
}
@throws Warning
It is considered a very bad practice to introduce side effects inside computed properties. It makes the code unpredictable and hard to understand. Discourage computed properties from mutating state.
computed: {
fullName () {
this.firstName = 'lorem'; // <- side effect
return `${this.firstName} ${this.lastName}`;
},
reversedArray () {
return this.array.reverse(); // <- side effect
},
}
computed: {
fullName () {
return `${this.firstName} ${this.lastName}`;
},
reversedArray () {
return this.array.slice(0).reverse();
},
}
@throws Error
When duplicate attributes exist, only the last one is used. Disallow duplicates for attributes other than special bindings such as class and style.
<my-component
:foo="bar"
foo="xyz"
></my-component>
<my-component
class="bar"
:class="{ foo: true }"
abc="xyz"
></my-component>
@throws Error
Make sure computed properties return a value.
computed: {
foo () {
},
}
computed: {
foo () {
return 'bar';
},
}
@throws Error
Make sure scope variables are used.
<template>
<ol v-for="i in 5"><!-- "i" is defined but never used. -->
<li>item</li>
</ol>
</template>
<template>
<ol v-for="i in 5">
<li>{{ i }}</li><!-- "i" is defined and used. -->
</ol>
</template>
@throws Error
Component data must be returned as a new instance via a function rather than a plain object.
export default {
data: {
foo: 'bar',
},
}
export default {
data () {
return {
foo: 'bar',
}
},
}
--
@throws Warning
All imports and vars that are included within code must be used.
let foo = 'bar';
function fooBar() {
//code
}
//End of file
let foo = 'bar';
function fooBar() {
return `${foo}bar`;
//code
}
//End of file
@throws Warning
Equality operators must now be type-safe - as is considered best practice in coding.
if (x == y) {
// code
}
if ("" == text) {
//code
}
if (obj.stuff != undefined) {
// code
}
if (x === y) {
// code
}
if ("" === text) {
// code
}
if (obj.stuff !== undefined) {
// code
}
@throws Warning
Prevents a return statement being called before an else. But also, in this instance, as we have allowElseIf set to false, else statements will also not be allowed in code.
function foo() {
if (x) {
return a;
} else if (y) {
return b;
} else {
return c;
}
}
function foo() {
if (x) {
return a;
}
if (y) {
return b;
}
return c;
}
@throws Warning
Prevents using floating decimals
const num = .5;
const ber = 2.;
const wang = -.7;
const num = 0.5;
const ber = 2.0;
const wang = -0.7;
@throws Warning
Curly brace conventions must follow a strict formatted pattern.
if (foo) return;
while (bar)
baz();
if (foo) {
baz();
} else qux();
if (foo) {
return;
}
while (bar) {
baz();
}
if (foo) {
baz();
} else {
qux();
}
--
@throws Warning
Discourages the assignment of variables in conditional statements
Allows assignment within params by default
const x;
if (x = 0) {
const b = 1;
}
// Practical example that is similar to an error
function setHeight(someNode) {
"use strict";
do {
someNode.height = "100px";
} while (someNode = someNode.parentNode);
}
const x;
if (x === 0) {
const b = 1;
}
// Practical example that wraps the assignment in parentheses
function setHeight(someNode) {
"use strict";
do {
someNode.height = "100px";
} while ((someNode = someNode.parentNode));
}
// Practical example that wraps the assignment and tests for 'null'
function setHeight(someNode) {
"use strict";
do {
someNode.height = "100px";
} while ((someNode = someNode.parentNode) !== null);
}
@throws Error
Forces user to use ES6 arrow function expressions
foo(function(a) {
return a;
});
foo(function() {
return this.a;
}.bind(this));
foo((a) => {
return a;
});
foo(() => {
return this.a;
});
@throws Warning
If an if statement is the only statement in the else block, it is clearer to use an else if.
if (foo) {
// ...
} else {
if (bar) {
// ...
}
}
if (condition) {
// ...
} else {
if (anotherCondition) {
// ...
}
}
if (condition) {
// ...
} else if (anotherCondition) {
// ...
} else {
// ...
}
@throws Warning
If a variable is set using 'let' and then never updated a warning will be issued as 'const' is preferred in this instance.
let a = 3;
console.log(a);
let a;
a = 1;
return a;
for (let i in [1, 2, 3]) {
console.log(i);
}
const a = 3;
console.log(a);
for (const i in [1, 2, 3]) {
console.log(i);
}
let a;
a = 1;
a = 2;
return a;
let a;
if (true) {
a = 1;
}
@throws Warning
Enforces a space after the colon in object literals.
const object = {
key:'value',
key :'value',
key : 'value',
};
const object = {
key: 'value',
};
@throws Warning
Limits the use of string quotes within JavaScript to 'single' quotation marks.
const double = "double";
const escaped = "a string with escaped 'single' quotes"
const single = 'single';
const backtick = `back${x}tick`;
@throws Warning
Prevents multiple empty lines existing within code. With rules set to a max of 1 in general and both at the beginning and the end of the file.
const foo = bar;
const bar = foo;
const foo = bar;
const bar = foo;
@throws Warning
Reports the use of redundant return statements
function foo() { return; }
function foo() {
doSomething();
return;
}
function foo() { return 5; }
function foo() {
return doSomething();
}
@throws Error
Prevents use of an identifier that has not yet been declared
alert(a);
const a = 10;
f();
function f() {};
function g() {
return b;
}
const b = 1;
const a = 10;
alert(a);
function f() {};
f();
const b = 1;
function g() {
return b;
}
@throws Warning
Disallows the use of ternary operators when simpler alternatives exist
const a = x === 2 ? true : false;
const b = x ? true : false;
const a = x === 2 ? 'yes' : 'No';
const a = x !== false;
const a = x ? 'Yes' : 'No';
@throws Warning
Requires a semi-colon at the end of every line
const foo = 'bar'
const foo = 'bar';
@throws Error
Forces a new line at the end of files.
module.exports = FooBar;
module.exports = FooBar;\n
@throws Warning
Prevents the use of mustaches within textarea form fields.
<textarea>{{ message }}</textarea>
<textarea v-model="message" />
@throws Warning
Enforces the use of spacing within template strings.
`hello, ${people.name}`;
`hello, ${ people.name}`;
`hello, ${people.name }`;
`hello, ${ people.name }`;
`hello, ${
people.name
}`;
@throws Warning
Disallows array literals which contain empty slots.
const array = [ , , ];
const array = [ 'red', , 'green'];
const array = [];
const array = [ 'red', 'green' ];
const array = new Array(23);
@throws Warning
Disallows Debugger statements
function isTruthy(x) {
debugger;
return Boolean(x);
}
function isTruthy(x) {
return Boolean(x); // set a breakpoint at this line
}
If you disagree with any rules in this linter, or feel additional rules should be added, please open an issue on this project to initiate an open dialogue with all team members. Please bear in mind this is a public repository.
Sam Turrell 💻 |
BeccaAnderton18 💻 |
rowancollins89 💻 |
Neveena 💻 |
martin91s 💻 |
Wiktor Zawierucha 💻 |
jdtjenkins 💻 |
---|---|---|---|---|---|---|
Sam Boylett 💻 |
This project follows the all-contributors specification. Contributions of any kind welcome!