@zinserjan/eslint-config-base

0.2.0 • Public • Published

eslint-config-base

A set of opinionated ESLint rules tailored for JS development

Installation

npm install --save-dev @zinserjan/eslint-config-base

.eslintrc

{
  "extends": [
     "@zinserjan/eslint-config-base"
  ]
}

Table of Contents

  1. Basic Rules
  2. Types
  3. Variables
  4. Strings
  5. Comparison Operators & Equality
  6. Blocks
  7. Comments
  8. Whitespace
  9. Commas
  10. Semicolons
  11. Type Casting & Coercion
  12. Naming Conventions
  13. Functions
  14. Arrow Functions
  15. Hoisting
  16. Objects
  17. Arrays
  18. Destructuring
  19. Properties
  20. Classes & Constructors
  21. Modules
  22. Iterators and Generators

Basic Rules

  • 2 spaces – for indentation
  • Double quotes for strings – except to avoid escaping
  • No unused variables
  • Semicolons
  • Space after keywords if (condition) { ... }
  • Always use === instead of == – but obj == null is allowed to check null || undefined.

back to top

Types

  • Primitives: When you access a primitive type you work directly on its value.

    • string
    • number
    • boolean
    • null
    • undefined
    const foo = 1;
    let bar = foo;
    
    bar = 9;
    
    console.log(foo, bar); // => 1, 9
  • Complex: When you access a complex type you work on a reference to its value.

    • object
    • array
    • function
    const foo = [1, 2];
    const bar = foo;
    
    bar[0] = 9;
    
    console.log(foo[0], bar[0]); // => 9, 9

Variables

  • Use const and let for all declarations of variables; avoid using var. eslint: no-var

    Why? var is function-scoped and works therefore not as everyone expects that. const and let are block scoped. See Hoisting for more details.

  • Use const for all variables. eslint: prefer-const, no-const-assign

    Why? This ensures that you can't reassign your references, which can lead to bugs and difficult to comprehend code.

    // bad
    var a = 1;
    var b = 2;
    
    // good
    const a = 1;
    const b = 2;
  • If you must reassign a variable, use let instead of const. eslint: no-var

    // bad
    var count = 1;
    if (true) {
      count += 1;
    }
    
    // good, use the let.
    let count = 1;
    if (true) {
      count += 1;
    }
  • Declare only a single variable per line. eslint: one-var

    Why? It's easier to add new variable declarations this way, and you never have to worry about swapping out a ; for a , or introducing punctuation-only diffs. You can also step through each declaration with the debugger, instead of jumping through all of them at once.

    // bad
    const items = [],
      goSportsTeam = true,
      dragonball = "z";
    
    // good
    const items = [];
    const goSportsTeam = true;
    const dragonball = "z";
  • Group all your consts and then group all your lets.

    Why? This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables.

    // bad
    let i, len, dragonball,
        items = getItems(),
        goSportsTeam = true;
    
    // bad
    let i;
    const items = getItems();
    let dragonball;
    const goSportsTeam = true;
    let len;
    
    // good
    const goSportsTeam = true;
    const items = getItems();
    let dragonball;
    let i;
    let length;
  • Assign variables where you need them, but place them in a reasonable place.

    Why? let and const are block scoped and not function scoped.

    // bad - unnecessary function call
    function checkName(hasName) {
      const name = getName();
    
      if (hasName === "test") {
        return false;
      }
    
      if (name === "test") {
        this.setName("");
        return false;
      }
    
      return name;
    }
    
    // good
    function checkName(hasName) {
      if (hasName === "test") {
        return false;
      }
    
      const name = getName();
    
      if (name === "test") {
        this.setName("");
        return false;
      }
    
      return name;
    }
  • Avoid undeclared variables. eslint: no-undef

    Why? This rule can help you locate potential ReferenceErrors resulting from misspellings of variable and parameter names, or accidental implicit globals.

    // bad
    b = 10; // would creat a global variable
    
    // good
    const b = 10;
  • Code should not contain unused variables. eslint: no-unused-vars

    Why? Variables that are declared and not used anywhere in the code are most likely an error due to incomplete refactoring. Such variables take up space in the code and can lead to confusion by readers.

    // bad
    let x;
    
    // Write-only variables are not considered as used.
    let y = 10;
    y = 5;
    
    // A read for a modification of itself is not considered as used.
    let z = 0;
    z = z + 1;
    
    // By default, unused arguments cause warnings.
    (function(foo) {
        return 5;
    })();
    
    // Unused recursive functions also cause warnings.
    function fact(n) {
        if (n < 2) return 1;
        return n * fact(n - 1);
    }
    
    // good
    const x = 10;
    alert(x);
    
    // foo is considered used here
    myFunc(function foo() {
        // ...
    }.bind(this));
    
    (function(foo) {
        return foo;
    })();
    
    let myFunc;
    myFunc = setTimeout(function() {
        // myFunc is considered used
        myFunc();
    }, 50);
  • Never assign native objects or read-only variables. eslint: no-global-assign

    Why? JavaScript environments contain a number of built-in global variables, such as window in browsers and process in Node.js. In almost all cases, you don’t want to assign a value to these global variables as doing so could result in losing access to important functionality.

    // bad
    window = {};
    
    // ok, but avoid this too
    window.test = {};
  • Don't chain variable assignments. eslint: no-undef

> Why? Chaining variable assignments creates implicit global variables.

```javascript
// bad
(function example() {
  // JavaScript interprets this as
  // let a = ( b = ( c = 1 ) );
  // The let keyword only applies to variable a; variables b and c become
  // global variables.
  let a = b = c = 1;
}());

console.log(a); // undefined
console.log(b); // 1
console.log(c); // 1

// good
(function example() {
  let a = 1;
  let b = a;
  let c = a;
}());

console.log(a); // undefined
console.log(b); // undefined
console.log(c); // undefined

// the same applies for `const`
```
  • Avoid using unary increments and decrements (++, --). eslint no-plusplus

    Why? Per the eslint documentation, unary increment and decrement statements are subject to automatic semicolon insertion and can cause silent errors with incrementing or decrementing values within an application. It is also more expressive to mutate your values with statements like num += 1 instead of num++ or num ++. Disallowing unary increment and decrement statements also prevents you from pre-incrementing/pre-decrementing values unintentionally which can also cause unexpected behavior in your programs.

      // bad
      let array = [1, 2, 3];
      let num = 1;
      num++;
      --num;
    
      let sum = 0;
      let truthyCount = 0;
      for(let i = 0; i < array.length; i++){
        let value = array[i];
        sum += value;
        if (value) {
          truthyCount++;
        }
      }
    
      // good
      let array = [1, 2, 3];
      let num = 1;
      num += 1;
      num -= 1;
    
      const sum = array.reduce((a, b) => a + b, 0);
      const truthyCount = array.filter(Boolean).length;

back to top

Strings

  • Use double quotes "" for strings. eslint: quotes

    Why? We prefer double quotes over single quotes cause they are easier distinguishable from template strings and makes the code more consistent as we are already using double quotes for attributes in JSX.

    // bad
    const name = 'Capt. Janeway';
    
    // bad - template literals should contain interpolation or newlines
    const name = `Capt. Janeway`;
    
    // good
    const name = "Capt. Janeway";
  • When programmatically building up strings, use template strings instead of concatenation. eslint: prefer-template template-curly-spacing

    Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features.

    // bad
    function sayHi(name) {
      return "How are you, " + name + "?";
    }
    
    // bad
    function sayHi(name) {
      return ["How are you, ", name, "?"].join("");
    }
    
    // bad
    function sayHi(name) {
      return `How are you, ${ name }?`;
    }
    
    // good
    function sayHi(name) {
      return `How are you, ${name}?`;
    }
  • Never use eval() on a string, it opens too many vulnerabilities. eslint: no-eval

    const obj = { x: "foo" };
    const key = "x";
    
    // bad
    const value = eval(`obj.${ key }`);
    
    // good
    const value = obj[key];
  • Do not unnecessarily escape characters in strings. eslint: no-useless-escape

    Why? Backslashes harm readability, thus they should only be present when necessary.

    // bad
    const foo = "\'this\' \i\s \"quoted\"";
    
    // good
    const foo = "'this' is \"quoted\"";

back to top

Comparison Operators & Equality

TODO

back to top

Blocks

TODO

back to top

Comments

TODO

back to top

Whitespace

TODO

back to top

Commas

TODO

back to top

Semicolons

TODO

back to top

Type Casting & Coercion

TODO

back to top

Naming Conventions

TODO

back to top

Functions

TODO

back to top

Arrow Functions

TODO

back to top

Hoisting

TODO

back to top

Objects

TODO

back to top

Arrays

TODO

back to top

Destructuring

TODO

back to top

Properties

TODO

back to top

Classes & Constructors

TODO

back to top

Modules

  • Always use modules (import/export) over a non-standard module system. You can always transpile to your preferred module system. eslint: import/no-commonjs, import/no-amd,

    Why? Modules are the future, let's start using the future now.

    // bad
    const _ = require("lodash");
    module.exports = _.map;
    
    // ok
    import _ from "lodash";
    export default _.map;
    
    // best
    import { map } from "lodash";
    export default map;
  • Do not use wildcard/namespace imports. eslint: import/no-namespace

    Why? This makes sure you have a single default export.

    // bad
    import * as _ from "lodash";
    
    // good
    import _ from "lodash";
  • And do not export directly from an import.

    Why? Although the one-liner is concise, having one clear way to import and one clear way to export makes things consistent.

    // bad
    // filename map.js
    export { map as default } from "lodash";
    
    // good
    // filename map.js
    import { map } from "lodash";
    export default map;
  • Only import from a path in one place. eslint: no-duplicate-imports

    Why? Having multiple lines that import from the same path can make code harder to maintain.

    // bad
    import foo from "foo";
    // … some other imports … //
    import { named1, named2 } from "foo";
    
    // good
    import foo, { named1, named2 } from "foo";
    
    // good
    import foo, {
      named1,
      named2,
    } from "foo";
  • Do not export mutable bindings. eslint: import/no-mutable-exports

    Why? Mutation should be avoided in general, but in particular when exporting mutable bindings. While this technique may be needed for some special cases, in general, only constant references should be exported.

    // bad
    let foo = 3;
    export { foo }
    
    // good
    const foo = 3;
    export { foo }
  • Catch funny business with exports, like repeated exports of names or defaults. eslint: import/export

    Why? Could be the result of copy/paste, code duplication with intent to rename, etc.

    // bad
    export default class MyClass { /*...*/ } // Multiple default exports.
    
    function makeClass() { return new MyClass(...arguments) }
    
    export default makeClass // Multiple default exports.
    
    // bad
    
    export const foo = function () { /*...*/ } // Multiple exports of name 'foo'.
    
    function bar() { /*...*/ }
    export { bar as foo } // Multiple exports of name 'foo'.
    
    // good
    
    export default class MyClass { /*...*/ } // Multiple default exports.
    export function makeClass() { return new MyClass(...arguments) }
  • Put all imports above non-import statements. eslint: import/first

    Why? Since imports are hoisted, keeping them all at the top prevents surprising behavior.

    // bad
    import foo from "foo";
    foo.init();
    
    import bar from "bar";
    
    // good
    import foo from "foo";
    import bar from "bar";
    
    foo.init();
  • Put a new line after import statements. eslint: import/newline-after-import

    Why? Application code should be visually splitted from dependencies.

    // bad
    import foo from "foo";
    foo.init();
    
    // good
    import foo from "foo";
    
    foo.init();
  • Disallow importing default as a named import. eslint: import/no-named-default

    Why? Just to make the code more readable.

    // foo.js
    export default "foo";
    export const bar = "baz";
    
    // bar.js
    
    // bad
    import { default as foo } from "./foo.js";
    import { default as foo, bar } from "./foo.js";
    
    // good
    import foo from "./foo.js";
    import foo, { bar } from "./foo.js";
  • Enforce a convention in module import order. eslint: import/order

    The order is as shown in the following example:

    // 1. node "builtins"
    import fs from "fs";
    import path from "path";
    // 2. "external" modules
    import _ from "lodash";
    import chalk from "chalk";
    // 3. "internal" modules
    // (if you have configured your path or webpack to handle your internal paths differently)
    import foo from "src/foo";
    // 4. modules from a "parent" directory
    import foo from "../foo";
    import qux from "../../foo/qux";
    // 5. "sibling" modules from the same or a sibling's directory
    import bar from "./bar";
    import baz from "./bar/baz";
    // 6. "index" of the current directory
    import main from "./";
  • Forbid import of modules using absolute module paths. eslint: import/no-absolute-path

    Why? Node.js allows the import of modules using an absolute path such as /home/xyz/file.js. That is a bad practice as it ties the code using it to your computer, and therefore makes it unusable in packages distributed on npm for instance.

    // bad
    import foo from "/foo";
    
    // good
    import foo from "foo";
  • Disallow Webpack loader syntax in module import statements. eslint: import/no-webpack-loader-syntax

    Why? Using Webpack syntax in the imports couples the code to a module bundler. Prefer using the loader syntax in webpack.config.js.

    // bad
    import fooSass from "css!sass!foo.scss";
    import barCss from "style!css!bar.css";
    
    // good
    import fooSass from "foo.scss";
    import barCss from "bar.css";

Iterators and Generators

TODO

back to top

Readme

Keywords

none

Package Sidebar

Install

npm i @zinserjan/eslint-config-base

Weekly Downloads

1

Version

0.2.0

License

MIT

Last publish

Collaborators

  • zinserjan