JavaScript coding style guide
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
busbud-lint
busbud-tslint
eslint-config-busbud
legacy
tslint-config-busbud
.gitignore
README.md

README.md

Busbud JavaScript Style Guide npm eslint version npm tslint version

Busbud sharable eslint and tslint configs.

Overview

All the goodness of semistandard for JavaScript and TypeScript with Busbud preferences.

JavaScript Usage

npm install --save-dev eslint eslint-config-busbud

Then, in your package.json:

{
  "scripts": {
    "lint": "eslint ."
  },
  "eslintConfig": {
    "extends": "busbud"
  }
}

And lint with:

npm run lint

TypeScript Usage

npm install --save-dev tslint typescript tslint-config-busbud

In your tslint.json:

{
  "extends": ["tslint-config-busbud"]
}

And lint with:

tslint --project tsconfig.json --type-check -t stylish '!(node_modules)/**/*.ts*'

Dependencies

This package depends on eslint-config-semistandard, eslint-config-standard, and eslint-plugin-standard, that are both in dependencies (for npm 3 that doesn't install peerDependencies anymore but thankfully installs everything at top-level), and peerDependencies for npm 2 because they're installed at top-level unlike dependencies which ESLint wouldn't find under eslint-config-busbud's own node_modules.

Rules not (yet) enforced by ESLint

Warning: some of these rules are not really needed anymore because ES6 (especially things related to import/export).

Basics

  • Always use // comments (with a space)
  • Use // TODO: and // FIXME:

Naming

  • snake_case for variables
  • camelCase for functions, including callbacks
  • UpperCamelCase for constructor functions
  • UPPER_SNAKE_CASE for constants

  • Only use single-letter variable names in one-line closures
  • Use a leading underscore for private variables/methods
  • Use arrow functions for syntaxical this binding
  • Preserve capitals in function names (e.g. parseWSDL)

Spacing

  • Do not pad array literal brackets or object literal braces

Variables

  • Always use const, unless you need to reassign the variable (then let)

Strings

  • Build strings using template strings.
  • Only quote keys when necessary

var foo = 'This is single-quoted';
var bar = "This isn't";

var greeting = `Hello ${name} and welcome to ${place}`;
console.log(greeting);

db.run("UPDATE table SET col = 'foo' WHERE id = 1;");

var headers = {
  Accept: 'text/plain',
  'Accept-Encoding': 'gzip, deflate'
};

Conditions

  • Place values as left-hand operands, variables as right-hand operands
  • Wrap conditions in parentheses if assigning

  • Only use the ternary operator if it neatly fits on one line
  • Wrap condition in parentheses if it contains an operator

  • Avoid multi-line if conditions
  • Factor out non-trivial conditions into descriptive variables
  • Take advantage of falsy values such as 0 and ''

var host = 'staging.busbud.com';

var is_busbud = (host.indexOf('busbud') !== -1);
var is_subdomain = (host.split('.').length > 2);

if (is_busbud && is_subdomain) {
  // ...
}

Functions

  • Return early from functions rather than nested if
  • Prefer option objects over long parameter lists (but document well)
  • Use promises over callbacks.

Errors

  • Use promises to bubble errors.
  • Use single-line error checks

  • Create errors with new
  • Prefer Error subclasses for groups of similar errors

var util = require('util');

function ToppingError(msg) {
  var err = Error.call(this, msg);
  err.name = 'ToppingError';
  return err;
}
util.inherits(ToppingError, Error);

function orderPizza(toppings, done) {
  if (toppings.length > 3) {
    return done(new ToppingError('No more than 3 toppings allowed'));
  }
  var pizza = new Pizza(toppings);
  pizza.order(done);
}

orderPizza(['cheese'], function(err, order) {
  if (err) throw err;

  console.log(order);
});

Exports

  • Only assign module.exports when exporting a constructor
  • Assign values on exports

function Pizza(toppings) {
  this.toppings = toppings;
}

module.exports = Pizza;
var Pizza = require('./pizza');

exports.cheese_pizza = new Pizza(['cheese']);
exports.all_dressed = new Pizza([
  'cheese',
  'peperoni',
  'green pepper',
  'mushroom'
]);

Requires

  • Name requires with appropriate casing (e.g. UpperCamelCase for constructors)
  • Place all requires at the top of the file
  • Group requires by category:
    1. Node standard library
    2. General-purpose libraries, sorted by ascending specificity
    3. Application code
  • Avoid directly assigning a property from a require

var util = require('util');
var fs = require('fs');

var _ = require('lodash');
var async = require('async');
var request = require('superagent');

var foo = require('./foo');
var bar = foo.bar;

Lodash

  • Always use Lodash
  • Use Lodash variants of built-in functions for consistency
  • Use multi-line chaining

var _ = require('lodash');

_.forEach(pizzas, function(pizza) {
  console.log(pizza);
});

var pizza_names = _(pizzas)
  .filter('name')
  .map('name')
  .uniq()
  .value();