Skip to content
This repository has been archived by the owner on Feb 26, 2022. It is now read-only.

Coding style guide

Gozala edited this page Jun 4, 2012 · 12 revisions

This document attempts to explain the basic styles and patterns that are used in the Jetpack codebase. While existing code may not always comply to this style, new code should try to conform to these standards so that it is as easy to maintain as existing code. Of course every rule has an exception, but it's important to know the rules nonetheless!

General

2-column indentation. 80-character limit for all lines.

Use single quotation symbol ' for strings as they're easier to type. Although some files be authored in with double quotes ", stay consistent with the style use by a file when making changes to it and use ". Do not mix two styles in the same file:

// Good!
let panel = require('panel');
let widget = require('widget');

// Acceptable
let panel = require("panel");
let widget = require("widget");

// Bad
let panel = require('panel');
let widget = require("widget");

Use dots at the end of lines, not the front:

// Good
let browsers = Cc['@mozilla.org/appshell/window-mediator;1'].
  getService(Ci.nsIWindowMediator).
  getEnumerator('navigator:browser');

// Bad
let browsers = Cc['@mozilla.org/appshell/window-mediator;1']
  .getService(Ci.nsIWindowMediator)
  .getEnumerator('navigator:browser');

Declarations

Variables

Forget about var and use let instead. It ok to use var if code is going to be used on other run times.

// Good
let n = 5;

// Bad
var n = 5;

Constants

For constants use const and name them LIKE_THIS.

// Good
const FOO = Math.random();

// Bad
const foo = Math.random();
const Foo = Math.random();
var FOO = Math.random();
let FOO = Math.random();

Functions

Do not declare function within blocks.

Naming

  • Use camelCase for functions and variables.
  • Use capitalized CamelCase for classes / constructor functions.
  • Use all lowercase in order to avoid confusion on case-sensitive platforms. Filenames should end in .js and should contain no punctuation except for -.

Conditionals

A branch follows its conditional on a new line and is indented:

if (foo)
  bar();

If all branches of a conditional are one-liner single statements, no braces needed:

if (foo)
  bar();
else if (baz)
  beep();
else
  qux();

A single-statement branch that requires multiple lines also requires braces. The opening brace follows the conditional on the same line:

if (foo) {
  if (bar)
    baz();
}

if (foo) {
  Cc['@mozilla.org/appshell/window-mediator;1'].
    getService(Ci.nsIWindowMediator).
    getEnumerator('navigator:browser').
    doSomethingOnce();
}

If any branch requires braces, use them for all:

if (foo) {
  bar();
}
else {
  doThis();
  andThat();
}

Do not cuddle else:

// Good
if (foo) {
  bar();
  baz();
}
else {
  qux();
  qix();
}

// Bad
if (foo) {
  bar();
  baz();
} else {
  qux();
  qix();
}

Use triple equal === instead of double == unless there is a reason not to. If in a given case == is preferred add a comment to explain why.

// Good
if (password === secret)
  authorize()
else
  deny()

// Bad
if (password == secret)
  authorize()
else
  deny()

Do not compare to booleans unless exactly true or false is expected (add comment if that's a case):

// Good
if (x)
  doThis()
else if (!y)
  doThat()
else
  doSomethingElse()

// Bad
if (x === true)
  doThis()
else if (y != false)
  doThat()
else
  doSomethingElse()

Loops

Conditional style also applies to loop style.

for (let i = 0; i < arr.len; i++)
  arr[i] = 0;

for (let i = 0; i < arr.len; i++) {
  if (i % 2)
    arr[i] = 0;
}

Try - Catch

Do not cuddle catch:

// Good
try {
  bar();
}
catch (err) {
  baz();
}

// Bad
try {
  bar();
} catch (err) {
  baz();
}

Good practices

Reuse functions where possible, creating closures on every call has worse performance and generates more garbage to be GC-ed.

// Good
function isOdd(x) { return x % 2 }
function sum(x, y) { return x + y }
function foo(nums) {
  return nums.filter(isOdd).reduce(sum);
}

// Bad
function foo(nums) {
  return nums.filter(function(x) {
    return x % 2;
  }).reduce(function(a, b) {
    return a + b;
  });
}

Comments and Documentation

This applies to in-source docs only, not nice docs meant for end users.

All exported functions should be documented in JSDoc style. Their purpose is to help people looking at your code.

/**
 * This function registers given user.
 * @param {String} name
 *    The name of the user.
 * @param {String|Number} id
 *    Unique user ID.
 * @param {String[]} aliases
 *    Array of aliases user
 * @param {String} [accessLevel='user']
 *    Optional `accessLevel` for a user.
 */
function register(name, id, aliases, accessLevel) {
  // ...
}

/**
 * Registers user and returns associated ID.
 * @param {Object} options
 *    Information about the user.
 * @param {String} options.name
 *    The name of the user.
 * @param {String} [options.aliases]
 *    Optional array of aliases
 */
function registerUser(options) {
  // ...
}

Module internal utility functions don't need to be documented this way, but it's encouraged.

For all other comments use single line comments. If a comment is a full sentence, capitalize and punctuate it. If a comment is almost a full sentence, make it a full sentence. Full sentences are generally preferable to fragments, but fragments are sometimes more effective. Fragments should be very terse. Don't capitalize or punctuate fragments.

Quote identifiers with backticks:

// Returns string content under given `uri`. Throws
// exception if such `uri` does not exists.
function readURI(uri) {
}

Module Exports

Exported functions should be named and defined at the top level module scope. Assignment to exports should follow a definition as separate statement:

// Good
function doSomething() {
  // ....
}
exports.doSomething = doSomething;

// Bad
exports.doSomething = function() {
  // ...
}

// Bad
var doSomething = exports.doSomething = function doSomething() {
  // ...
}

Exported functions should be referenced via local name and not as an exported property. This is both faster and future proof, since in upcoming standard JS modules export will be a statement and exports will have to be referenced via local name:

// Good 
function doThis() {
  // ...
  runExportedF()
  // ...
}

// Bad
function doThis() {
  // ...
  exports.runExportedF()
  // ...
}

Same rules apply to non function exports as well:

// Good
var foo = {
  // ...
};
exports.foo = foo;

function bar() {
  // ...
  doSomething(foo);
  // ...
}

// Bad

exports.foo = {
  // ...
};
function bar() {
  // ...
  doSomething(exports.foo);
  // ...
}

Use only critical subset of Mozilla JS extensions

Projects target audience consists of JS developers. Commonly JS developers outside of Mozilla are not familiar with Mozilla specific language extensions (JS 1.7, JS 1.8, E4X). Use of them would limit target audience or require higher learning curve which does not pays off with a gained benefit. There for we limit used extensions to a critical subset:

var { foo, bar } = require('foo/bar');
// Good
if (x > y) {
  let gamma = 12.7 + y;  
  // ...
}

// Bad
if (x > y) {
  var gamma = 12.7 + y;  
  // ...
}

// Bad
let (x = 10, y = 12) {  
  console.log(x+y);
}  
  • const statement

Further Reading