eslint-config-javascript

Shareable ESLint config for the JavaScript Styleguide

Usage no npm install needed!

<script type="module">
  import eslintConfigJavascript from 'https://cdn.skypack.dev/eslint-config-javascript';
</script>

README

Banner

JavaScript Styleguide

This is a normative guide on how to format JavaScript. The main aim is to get the best possible readability. It is especially optimized for ECMAScript 2015, but should work equally good with other versions.

It's assumed that the code will be optimized by code minifiers and/or package tools for usage in a production client-side environment.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Table of Contents

Installation

The easiest way to adhere to this guide is to use the included eslint-config-javascript module to check your files. In order to set it up, fist install ESLint and the module:

npm install --save-dev eslint eslint-config-javascript

Next add following entry to your package file:

"eslintConfig": {
  "extends": "eslint-config-javascript"
}

This will load the configuration of this styleguide. Finally add a lint script to the package scripts:

"scripts": {
  "lint": "eslint --max-warnings=0 --ignore-path=.gitignore .",
  …
}

Now you can check your files for conformity simply by running

npm run lint

Guide

Naming Conventions

  • Names SHOULD be descriptive
  • camelCase MUST be used for naming objects, functions, and instances
  • PascalCase MUST be used for classes and constructor names
  • _this MUST be used as a reference to this

Whitespace

  • 2 spaces MUST be used for indentation
  • There MUST be no trailing whitespace characters
  • An empty newline must be placed at the end of a file
  • Invisibles SHOULD be displayed during coding to reduce the likelihood of whitespace mistakes
  • Tabs and spaces MUST NOT be mixed

Reasoning:

Originally this guide required the use of tabs for indentation. The problem, however, is that a tab character can have any length (normally 2 or 4 characters wide). This undermines the purpose of monospaced fonts, where every character is supposed to have the same length to ensures alignment and predictability of the text layout.

Multi-line Statements

  • Lines MUST not be longer than 80 characters
  • When a statement is too long to fit on a line, line breaks MUST occur after an operator

Semicolons

Semicolons MUST NOT be used to terminate statements except the next line starts with an (, [ or `

Commas

Inline Commas

An inline comma MUST be followed by one space character

const colors = ["green", "yellow", "red"]

instead of

const colors = ["green","yellow","red"]

Trailing Commas

Trailing commas MUST be used in Arrays

const fruits = [
  apple,
  banana,
  melon,
]

instead of

const fruits = [
  apple,
  banana,
  melon
]

… and Objects

const person = {
  firstName: "John",
  lastName: "Smith",
}

instead of

const person = {
  firstName: "John",
  lastName: "Smith"
}

Reasoning:

To add an element to the end of an Array or Object, 2 lines must be modified. This adds unnecessary visual clutter to diffs. Furthermore, elements can be more easily rearranged when they all have the same structure.

Leading Commas

No leading commas MUST be used

const fruits = [
  apple,
  banana,
  peach,
  melon,
]

instead of

const fruits = [ apple
             , banana
             , peach
             , melon
             ]

and

const person = {
  firstName: "John",
  lastName: "Smith",
  age: 30,
}

instead of

const person = { firstName: "John"
             , lastName: "Smith"
             , age: 30
             }

Variables / Constants

Read Only

Read only references to a value MUST be declared with const

const answerToEverything = 42

instead of

let answerToEverything = 42

Reassignable

Reassignable variables must be declared with let

let currentPage = 138

function turnPageOver () {
  currentPage += 1
}

instead of

const currentPage = 138

function turnPageOver () {
  currentPage += 1
}

var keyword

Variables must never be declared with var

Declaration Group

Each variable MUST be declared with exactly one const or let keyword

const name = "John"
const age = 34
const instrument = "guitar"

instead of

const name = "John",
      age = 34,
      instrument = "guitar"

Global Variables

If a variable shall be exposed globally it MUST be explicitly declared as a property of the global scope (window/global object)

window.brandName = "Stark Industries"
// or
global.brandName = "Stark Industries"

instead of

brandName = "Stark Industries"

Unassigned variables

Unassigned let variables MUST be declared last in a group of declarations

const foo = 7
let bar = 4
let baz

Operators

Position

Line breaks must occur after operators:

const sentence = "This is a " +
  "short sentence."

Except for the ternary operator:

const status = isTesting
  ? "We are currently testing"
  : "Everything operational"

Placing the ? and the : at the beginning of the line makes it easier to skim the code.

Relational Operators

  • === MUST be used instead of ==
  • !== MUST be used instead of != except in value != null where it has the same effect as value !== null && value !== undefined

Shortcuts MAY be used where appropriate:

if (name) {
  …
}

instead of

if (name !== "") {
  …
}

and

if (collection.length) {
  …
}

instead of

if (collection.length > 0) {
  …
}

Blocks

Blocks MAY be used to create an encapsulated scope

{
  const age = 34
}

{
  const age = 27
}

Conditionals

Spaces Around Condition

Before and after the condition must be a single space character.

if (testValue) {
  doSomething()
}

instead of

if(testValue){
  doSomething()
}

Blocks for Multiline Statements

Blocks MAY be omitted in single line statements but must be used for multiline statements.

if (testValue) doSomething()

// or

if (testValue) {
  doSomething()
}
if (testValue) {
  doSomething()
  doThisToo()
}

Placement of else

else MUST be placed on a newline after the if-block.

if (testValue) {
  doSomething()
}
else if (otherTestValue) {
  doSomethingElse()
}
else {
  doDefault()
}

instead of

if (testValue) {
  doSomething()
} else if (otherTestValue) {
  doSomethingElse()
} else {
  doDefault()
}

Reasoning:

  • Increases readability through space between if and else block
  • As else is at the beginning of the line it's easier to skim down a list of ifs and elses.

Comments

  • API documentation MUST NOT be written in comments. It lives in its own directory/repository

  • Obscure code SHOULD be commented but not obvious things So do not write comments like this:

    // If person is older than 34
    if (person.age > 34) {
      …
    }
    

Single Line

  • // MUST be used for single line comments
  • There MUST be a space between // and the first character of the comment
  • Single line comments SHOULD be placed on a newline above the subject of the comment
  • One empty line MUST be put before a single line comment which starts at the beginning of a line.
  • // FIXME: MAY be used to annotate problems
  • // TODO: MAY be used to capture issues which need to get solved

Multiline

  • /* … */ MUST be used for multiline comments
  • Two empty line MUST be put before a multi line comment
  • The start and end tag MUST be on a separate line
/*
This is a
multiline comment
*/

Strings

Double quotes MUST be used for strings.

Since most languages use double quotes for strings, this reduces the friction in multi-language projects.

Objects

Arrays

Functions

Anonymous

Anonymous functions MUST start with a !

!function () {
  …
}

Reasoning:

(function () {}) can lead to errors when relying on ASI (Automatic Semicolon Insertion) or when concatenating several JavaScript files

Properties

Methods

Chaining

Indentation SHOULD be used for long method chains. At the most one method should be called per line.

$("#items")
  .find(".selected")
  .highlight()

instead of

$("#items").find(".selected").highlight()

Methods SHOULD return this to enable method chaining.

Setters

Properties SHOULD be settable via setters and via a set* method to enable chaining.

person.status = single

person
  .setName("John")
  .marryTo("Linda")
  .setStatus("married")

This enables easy traversing of structures involving several classes:

database
  .load("John")
  .setAge("34")
  .sister
  .husband
  .setName("Tom")

Getters

Retrieving of properties MUST at least be implemented via a getter.

console.log(person.age)

instead of

console.log(person.age())

Type Casting

  • To string: String(123)

  • To number: Number("123")

  • To boolean: Boolean(1)

  • To array:

    Array.from("test")
    // => ["t", "e", "s", "t"]
    
    function doSomething () {
      Array.from(arguments)
    }
    doSomething(1, 2, 3)
    // => [1, 2, 3]
    

// TODO: to date, …

General

You MAY (when it's absolutely necessary) differ from any rules of this guide to increase performance. You MUST, however, explain the reasons for not sticking to a rule in a comment.

Framework specific

jQuery

Object variables MUST be prefixed with a $

const $form = $("#myForm")

instead of

const form = $("#myForm")

Lookups MUST be cached

const $form = $("#form")

$form.css({
  "background-color": "pink"
})

// …

$form.hide()

instead of

$("#form").css({
  "background-color": "pink"
})

// …

$("#form").hide()

Best Practices

Imports

Imports and requires should be sorted by type.

  1. Native
  2. External
  3. Internal

To improve readability keep an empty newline between each section and 2 empty newlines below all imports / requires.

For example ES2015 style imports:

import path from "path"
import fs from "fs"

import lodash from "lodash"

import app from "./source/app"


const port = 1234

The same applies to CommonJS requires:

const path = require("path")
const fs = require("fs")

const lodash = require("lodash")

const app = require("./source/app")


const port = 1234

Functions

Every non-primitive function should look like this:

function doSomething (options = {}) {
    const {
        hasThis = true, // Some clear description why this is here
        isThat = false,  // More clear descriptions
        importantNumber = 1234, // The clearest description in the universe
        content = "Some thoughtful text", // Crystal clear
    } = options

    // Optional type checks
    if (typeof importantNumber !== "number") {
        throw new TypeError(
            `importantNumber must be a number and not "${importantNumber}"`
        )
    }
}

This ensures:

  • Arguments have appropriate default values
  • Other developers can easily see which arguments are expected
  • No incorrect values can be passed
  • Function is extendable without a need to change the function signature

Related