Use spacing liberally throughout your code. “When in doubt, space it out.”
These rules encourage liberal spacing for improved developer readability. The minification process creates a file that is optimized for browsers to read and process.
- Indentation with tabs.
- No whitespace at the end of line or on blank lines.
- Lines should usually be no longer than 80 characters, and should not exceed 100 (counting tabs as 4 spaces). This is a “soft” rule, but long lines generally indicate unreadable or disorganized code.
- if/else/for/while/try blocks should always use braces, and always go on multiple lines.
- Unary special-character operators (e.g., ++, --) must not have space next to their operand.
- Any
,
and;
must not have preceding space. - Any
;
used as a statement terminator must be at the end of the line. - Any
:
after a property name in an object definition must not have preceding space. - The
?
and:
in a ternary conditional must have space on both sides. - No filler spaces in empty constructs (e.g.,
{}
,[]
,fn()
). - There should be a new line at the end of each file.
- Any
!
negation operator should have a following space - All function bodies are indented by one tab, even if the entire file is wrapped in a closure
- Spaces may align code within documentation blocks or within a line, but only tabs should be used at the start of a line
Don't forget to remove trailing whitespace.
Object declarations can be made on a single line if they are short (remember the line length guidelines). When an object declaration is too long to fit on one line, there must be one property per line. Property names only need to be quoted if they are reserved words or contain special characters:
// Preferred
var map = {
ready: 9,
when: 4,
'you are': 15
};
// Acceptable for small objects
var map = { ready: 9, when: 4, 'you are': 15 };
// Bad
var map = { ready: 9,
when: 4, 'you are': 15 };
Always include extra spaces around elements and arguments:
array = [ a, b ];
foo( arg );
foo( 'string', object );
foo( options, object[ property ] );
foo( node, 'property', 2 );
// Unlike the WordPress core standards, we always add a single space
// around object literals and callbacks.
foo( {
a: 'alpha',
b: 'beta'
} );
foo( data, function() {
// Do stuff
} );
foo( function() {
// Do stuff
}.bind( this ) );
foo( function() {
// Do stuff
}, options );
var i;
if ( condition ) {
doSomething( 'with a string' );
} else if ( otherCondition ) {
otherThing( {
key: value,
otherKey: otherValue
} );
} else {
somethingElse( true );
}
// Unlike jQuery, WordPress prefers a space after the ! negation operator.
// This is also done to conform to our PHP standards.
while ( ! condition ) {
iterating++;
}
for ( i = 0; i < 100; i++ ) {
object[ array[ i ] ] = someFn( i );
$( '.container' ).val( array[ i ] );
}
try {
// Expressions
} catch ( e ) {
// Expressions
}
Use them. Never rely on Automatic Semicolon Insertion (ASI).
Indentation and line breaks add readability to complex statements.
Tabs should be used for indentation.
if, else, for, while, and try blocks should always use braces, and always go on multiple lines. The opening brace should be on the same line as the function definition, the conditional, or the loop. The closing brace should be on the line directly following the last statement of the block.
var a, b, c;
if ( myFunction() ) {
// Expressions
} else if ( ( a && b ) || c ) {
// Expressions
} else {
// Expressions
}
When a statement is too long to fit on one line, line breaks must occur after an operator.
// Bad
var html = '<p>The sum of ' + a + ' and ' + b + ' plus ' + c
+ ' is ' + ( a + b + c );
// Good
var html = '<p>The sum of ' + a + ' and ' + b + ' plus ' + c +
' is ' + ( a + b + c );
Lines should be broken into logical groups if it improves readability, such as splitting each expression of a ternary operator onto its own line, even if both will fit on a single line.
// Acceptable
var baz = ( true === conditionalStatement() ) ? 'thing 1' : 'thing 2';
// Better
var baz = firstCondition( foo ) && secondCondition( bar )
? qux( foo, bar )
: foo;
When a conditional is too long to fit on one line, successive lines should be indented one extra level to distinguish them from the body.
if ( firstCondition() && secondCondition() &&
thirdCondition() ) {
doStuff();
}
When possible, variables should be declared using a const
declaration. Use
let
only when you anticipate that the variable value will be reassigned
during runtime. var
should not be used in any new code.
Note that const
does not protect against mutations to an object, so do not
use it as an indicator of immutability.
const foo = {};
foo.bar = true;
let counter = 0;
counter++;
Globals should almost never be used. If they are used or you need to reference a pre-existing global do so via window
.
var userId = window.currentUser.ID;
Variable and function names should be full words, using camel case with a lowercase first letter. This also applies to abbreviations and acronyms.
// Good
var userIdToDelete, siteUrl;
// Bad
var userIDToDelete, siteURL;
Names should be descriptive, but not excessively so. Exceptions are allowed for iterators, such as the use of i
to represent the index in a loop.
Constructors intended for use with new should have a capital first letter (UpperCamelCase).
Variables intended to be used as a constant can be defined with the SCREAMING_SNAKE_CASE naming convention. Note that while any variable declared using const
could be considered a constant, in the context of our application this usage should usually be limited to top-level or exported module values.
const DUMMY_VALUE = 10;
function getIncrementedDummyValue() {
const incrementedValue = DUMMY_VALUE + 1;
return incrementedValue;
}
Comments come before the code to which they refer, and should always be preceded by a blank line unless inserted as the first line in a block statement. Capitalize the first letter of the comment, and include a period at the end when writing full sentences. There must be a single space between the comment token (//) and the comment text.
Single line comments:
someStatement();
// Explanation of something complex on the next line
$( 'p' ).doSomething();
When adding documentation, use the jsdoc format.
/**
* Represents a book.
* @constructor
* @param {string} title - The title of the book.
* @param {string} author - The author of the book.
*/
function Book(title, author) {
}
Multi-line comments that are not a jsdoc comment should use //
:
//
// This is a comment that is long enough to warrant being stretched
// over the span of multiple lines.
Inline comments are allowed as an exception when used to annotate special arguments in formal parameter lists:
function foo( types, selector, data, fn, /* INTERNAL */ one ) {
// Do stuff
}
Strict equality checks (===) must be used in favor of abstract equality checks (==). The only exception is when checking for both undefined and null by way of null.
// Check for both undefined and null values, for some important reason.
if ( undefOrNull == null ) {
// Expressions
}
These are the preferred ways of checking the type of an object:
- String:
typeof object === 'string'
- Number:
typeof object === 'number'
- Boolean:
typeof object === 'boolean'
- Object:
typeof object === 'object'
- null:
object === null
- undefined:
object === undefined
or for globalstypeof window.someGlobal === 'undefined'
However, you don't generally have to know the type of an object. Prefer testing the object's existence and shape over its type.
Prefer using the power
of "truthy" in JavaScript boolean expressions to validate the existence and shape
of an object to using typeof
.
The following are all false in boolean expressions:
null
undefined
''
the empty string0
the number
But be careful, because these are all true:
'0'
the string[]
the empty array{}
the empty object-1
the number
To test the existence of an object (including arrays):
if ( object ) { ... }
To test if a property exists on an object, regardless of value, including undefined
:
if ( 'desired' in object ) { ... }
To test if a property is present and has a truthy value:
if ( object.desired ) { ... }
To test if an object exists and has a property:
if ( object && 'desired' in object ) { ... }
if ( object && object.desired ) { ... }
Use single-quotes for string literals:
var myStr = 'strings should be contained in single quotes';
When a string contains single quotes, they need to be escaped with a backslash (\):
Double quotes can be used in cases where there is a single quote in the string or in JSX attributes.
var myStr = "You're amazing just the way you are.";
var component = <div className="post"></div>;
Switch statements can be useful when there are a large number of cases – especially when multiple cases can be handled by the same block (using fall-through), or the default case can be leveraged.
When using switch statements:
- Note intentional cases of fall-through explicitly, as it is a common error to omit a break by accident.
- Indent case statements one tab within the switch.
switch ( event.keyCode ) {
// ENTER and SPACE both trigger x()
case constants.keyCode.ENTER:
case constants.keyCode.SPACE:
x();
break;
case constants.keyCode.ESCAPE:
y();
break;
default:
z();
}
The first word of a variable name should be a noun or adjective (not a verb) to avoid confusion with functions.
You can prefix a variable with verb only for boolean values when it makes code easier to read.
// bad
const play = false;
// good
const name = 'John';
const blueCar = new Car( 'blue' );
const shouldFlop = true;
The first word of a function name should be a verb (not a noun) to avoid confusion with variables.
// bad
function name() {
return 'John';
}
// good
function getName() {
return 'John';
}
// good
function isValid() {
return true;
}
Creating arrays in JavaScript should be done using the shorthand []
constructor rather than the new Array()
notation.
var myArray = [];
You can initialize an array during construction:
var myArray = [ 1, 'WordPress', 2, 'Blog' ];
In JavaScript, associative arrays are defined as objects.
There are many ways to create objects in JavaScript. Object literal notation, {}
, is both the most performant, and also the easiest to read.
var myObj = {};
Object literal notation should be used unless the object requires a specific prototype, in which case the object should be created by calling a constructor function with new.
var myObj = new ConstructorMethod();
Object properties should be accessed via dot notation, unless the key is a variable, a reserved word, or a string that would not be a valid identifier:
prop = object.propertyName;
prop = object[ variableKey ];
prop = object['default'];
prop = object['key-with-hyphens'];
Since we require strict equality checks, we are not going to enforce "Yoda" conditionals. You're welcome to use them, but the most important consideration should be readability of the conditional.
The functional programming inspired methods that were added in ECMA5 improve readability. Use them throughout.
var posts = postList.map( function( post ) {
return <Post post={ post } key={ post.global_ID } />;
} );
When iterating over a large collection using a for loop, it is recommended to store the loop’s max value as a variable rather than re-computing the maximum every time:
// Good & Efficient
var i, max;
// getItemCount() gets called once
for ( i = 0, max = getItemCount(); i < max; i++ ) {
// Do stuff
}
// Bad & Potentially Inefficient:
// getItemCount() gets called every time
for ( i = 0; i < getItemCount(); i++ ) {
// Do stuff
}
- Methods that return JSX partial code should start with
render
. - Methods that navigate to a different page should start with
go
. - Methods that are bound to event handlers should have descriptive names. Don't name methods after event handlers like
onClick
,onSubmit
, etc. You can use fat arrow functions if it makes handling the event cleaner. - Avoid prefixing method names with
_
.
// good
const CartComponent = React.createClass( {
...
renderActionButton() {
if ( this.isLoading() ) {
return null;
}
if ( this.canGoToCheckout() ) {
return <Button onClick={ this.goToCheckout }>{ this.translate( 'Go to checkout' ) }</Button>;
} else {
return <Button onClick={ this.confirmCart }>{ this.translate( 'Confirm cart' ) }</Button>;
}
},
confirmCart() {
actions.confirmCart( () => {
this.enableCheckoutStep();
} );
},
goToCheckout() {
page( this.getCheckoutPath() );
}
} );
JSX is pretty easy to read as standard HTML. That's why separate meta renderSomething
methods should only be used if there is meaningful functionality happening within the code, not just to break up content.
If you find that you have many separate rendering functions, or rendering functions that include more than a basic amount of logic, it might be a sign that there’s room for splitting off separate new components.
We support and encourage ES6 features thanks to Babel transpilation and the accompanying polyfill. There are still a couple of minor caveats to be aware of regarding Classes.
You can use const
for all of your references.
Why? This ensures that you can't reassign your references (mutation), which can lead to bugs and difficult to comprehend code.
// good
var a = 1,
b = 2;
// better
const a = 1,
b = 2;
If you must mutate references, you can use let
. Otherwise const
is preferred.
Why?
let
is block-scoped rather than function-scoped likevar
.
// good
var count = 1;
if ( true ) {
count += 1;
}
// better
let count = 1;
if ( true ) {
count += 1;
}
Note that both let
and const
are block-scoped.
// const and let only exist in the blocks they are defined in.
{
let a = 1;
const b = 1;
}
console.log( a ); // ReferenceError
console.log( b ); // ReferenceError
More about:
When you must use function expressions (as when passing an anonymous function), you can use arrow function notation.
Why? It creates a version of the function that executes in the context of
this
, which is usually what you want, and is a more concise syntax.
Why not? If you have a fairly complicated function, you might move that logic out into its own function declaration.
// good
function Person() {
this.age = 0;
setInterval( function() {
this.age++;
}.bind( this ), 1000 );
}
let p = new Person();
// better
function Person() {
this.age = 0;
setInterval( () => {
this.age++;
}, 1000 );
}
let p = new Person();
If the function body fits on one line and there is only a single argument, feel free to omit the braces and parentheses, and use the implicit return. Otherwise, add the parentheses, braces, and use a return
statement.
Why? Syntactic sugar. It reads well when multiple functions are chained together.
Why not? If you plan on returning an object.
// bad
// it's too magic, it works only with parentheses, otherwise it returns collection of undefine values
[ 1, 2, 3 ].map( x => ( { value: x * x } ) );
// good
[ 1, 2, 3 ].map( x => x * x );
// good
[ 1, 2, 3 ].reduce( ( total, n ) => {
return total + n;
}, 0 );
More about:
- Arrow Functions at MDN
You can use property value shorthand.
Why? It is shorter to write and descriptive.
const lukeSkywalker = 'Luke Skywalker';
// good
const obj = {
lukeSkywalker: lukeSkywalker,
};
// better
const obj = {
lukeSkywalker,
};
Group your shorthand properties at the beginning of your object declaration.
Why? It's easier to tell which properties are using the shorthand.
const anakinSkywalker = 'Anakin Skywalker',
lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
episodeOne: 1,
twoJediWalkIntoACantina: 2,
lukeSkywalker,
episodeThree: 3,
mayTheFourth: 4,
anakinSkywalker,
};
// good
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeOne: 1,
twoJediWalkIntoACantina: 2,
episodeThree: 3,
mayTheFourth: 4,
};
More about:
- Object Property Shorthand at MDN
You can use object method shorthand.
// good
const atom = {
value: 1,
addValue: function ( value ) {
return atom.value + value;
},
};
// better
const atom = {
value: 1,
addValue( value ) {
return atom.value + value;
},
};
More about:
- Object Method Shorthand at MDN
You can use computed property names when creating objects with dynamic property names.
Why? They allow you to define all the properties of an object in one place.
function getKey( k ) {
return `a key named ${k}`;
}
// good
const obj = {
id: 5,
name: 'San Francisco',
};
obj[ getKey( 'enabled' ) ] = true;
// better
const obj = {
id: 5,
name: 'San Francisco',
[ getKey( 'enabled' ) ]: true,
};
More about:
- Object Computed Properties at MDN
When programmatically building up strings, you can use template strings instead of concatenation.
Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features.
// good
function sayHi( name ) {
return 'How are you, ' + name + '?';
}
// better
function sayHi( name ) {
return `How are you, ${ name }?`;
}
More about:
- Template Strings at MDN
You can use object destructuring when accessing and using multiple properties of an object.
Why? Destructuring saves you from creating temporary references for those properties.
// good
function getFullName( user ) {
const firstName = user.firstName,
lastName = user.lastName;
return `${ firstName } ${ lastName }`;
}
// better
function getFullName( user ) {
const { firstName, lastName } = user;
return `${ firstName } ${ lastName }`;
}
// best
function getFullName( { firstName, lastName } ) {
return `${ firstName } ${ lastName }`;
}
You can use array destructuring.
const arr = [ 1, 2, 3, 4 ];
// good
const first = arr[0],
second = arr[1];
// better
const [ first, second ] = arr;
Use object destructuring for multiple return values, not array destructuring.
Why? You can add new properties over time or change the order of things without breaking call sites.
// bad
// the caller needs to think about the order of return data
function processInput( input ) {
let left, right, top, bottom;
// some assignment happens
return [ left, right, top, bottom ];
}
const [ left, , top ] = processInput( input );
// good
// the caller selects only the data they need
function processInput( input ) {
let left, right, top, bottom;
// some assignment happens
return { left, right, top, bottom };
}
const { left, top } = processInput( input );
More about:
- Destructuring at MDN
You can use default parameter syntax rather than mutating function arguments.
// really bad
function handleThings( opts ) {
// No! We shouldn't mutate function arguments.
// Double bad: if opts is falsy it'll be set to an object which may
// be what you want but it can introduce subtle bugs.
opts = opts || {};
// ...
}
// good
function handleThings( opts ) {
if ( opts === void 0 ) {
opts = {};
}
// ...
}
// better
function handleThings( opts = {} ) {
// ...
}
More about:
- Default Parameters at MDN
Never use arguments
, opt to use rest syntax ...
instead.
Why?
...
is explicit about which arguments you want pulled. Plus rest arguments are a real Array and not Array-like likearguments
.
// bad
function concatenateAll() {
const args = Array.prototype.slice.call( arguments );
return args.join( '' );
}
// good
function concatenateAll( ...args ) {
return args.join( '' );
}
More about:
- Rest at MDN
You can use array spreads ...
to copy arrays.
// good
const itemsCopy = items.slice();
// better
const itemsCopy = [ ...items ];
More about:
- Array Spreads at MDN
It's ok to use callbacks to manage your asynchronous flows, but if you need to handle more than one async call, you can use promises to avoid nested callbacks and to have a single exception management point to deal with.
// bad
function doSomething( callback ) {
// do stuff
callback( error, result );
}
doSomething( function( error, result ) {
if ( ! error ) {
doSomethingElse( function( error2, result2 ) {
if ( ! error2 ) {
// ad infinitum
}
} );
}
} );
// good
function doSomething() {
return new Promise( function( resolve, reject ) {
// do stuff
if ( stuff ) {
return resolve( stuff );
}
reject( error );
}
}
doSomething()
.then( doSomethingElse )
.then( doSomethingElseMore )
.catch( function( error ) {
// manage error
} );
Keep in mind that each function called from a .then
should return a promise, or else the next then/catch
in the chain would be always called immediately after it.
More about:
- Promises at MDN
- ECMAScript® 2015 Language Specification.
- Babel.
- Overview of ECMAScript 6 features.
- ECMAScript 6 new features overview & comparison.
- ECMAScript 6 compatibility table.
- More Resources - listed by Airbnb.
To help encourages folks to follow the coding standards, there is a ESLint configuration file .eslintrc
that configures ESLint to detect code that doesn't follow the guidelines. ESLint also catches basic syntax errors, and natively supports both ES6 and JSX. It can be extended by plugins, such as eslint-plugin-wpcalypso
, which we use in our configuration.
There are integrations for many editors that will automatically detect the configuration file and run the checks.
In cases where ESLint is not happy with awesome code, you can tell ESLint to ignore a set of lines:
/* eslint-disable */
// Code here will be ignored when linted by ESLint.
/* eslint-enable */
You can also selectively disable and re-enable warnings of specific rules:
/* eslint-disable no-console, no-alert */
console.log( 'word' );
alert( 'press' );
/* eslint-enable no-console */
You can also ignore a single line:
if ( a != b ) { // eslint-disable-line
// do stuff
}
Or a specific rule on a single line:
if ( a != b ) { // eslint-disable-line eqeqeq
// do stuff
}
If you would like to have your changes automatically run through ESLint - there is a git pre-commit hook in bin/pre-commit
that will perform the task. To install the git hook, execute make githooks-commit
from the root of your Calypso directory. This creates a symlink to the pre-commit hook that will run everytime you do a git commit
.
If ESLint encounters any issues inside any .jsx or .js files you have updated, an error will be displayed, and the commit will not proceed. Here is an example of an attempted commit with the hook installed:
To lint the entire project, from the root of your working directory run:
make eslint
These instructions assume Sublime Text 3
If you are using Sublime Text, you can use the SublimeLinter-eslint
plugin to visually highlight linting errors in your code. Dan Abramov has a great instructional blog post to get up and running with ESLint in Sublime Text.
Before following these instructions, you'll want to globally install ESLint and related dependencies by running the following command in your terminal:
npm install -g eslint eslint-plugin-wpcalypso eslint-plugin-react babel-eslint
When you install the SublimeLinter
package, it will identify mixed spaces and tabs in your code, but it won't identify lines whose leading characters are comprised of only spaces.
To make identifying spaces easier, you can install the Highlight Whitespaces package, then add the following user setting (Preferences > Package Settings > Highlight Whitespaces > Settings - User) so that it doesn't also highlight tabs:
{
"highlight_whitespaces_check_tabs": false
}