Skip to content
This repository has been archived by the owner on Sep 5, 2020. It is now read-only.
Nate Cavanaugh edited this page Mar 4, 2019 · 20 revisions

Liferay has both a rigorous code review process, and strict rules about how code should be formatted, which was why this tool was created in the first place. This document seeks to communicate the main principles behind the conventions and explain the reasoning for the different source code conventions and why you may have gotten a particular error.

Principles

There are a few core principles that lie underneath all of the conventions, as arbitrary as they may seem at times. When developers don't understand the reasoning for certain rules, they are less likely to absorb why those rules are important and less likely to actually apply them, or look for areas where things could be improved to fit the principles. As a company, the Liferay culture values these things in it's code:

Consistency

Consistency is the foundation of trust. Code that is different only to be different causes confusion, and wastes time by forcing other developers to think through which pattern they should apply. If things are consistent, even if they're not ideal, they're easier to fix and improve. As styles fall in and out of favor, and coding fads come and go, there is a temptation for different developers to code in a certain way. How do you judge if it's worth changing a pattern? If it's worth changing, it's worth changing everywhere. This keeps us from introducing superficial changes that break convention, and helps ensure the changes we do make are worthwhile.

Clarity

Code is read far more often than it's written. Our code should be easy enough to understand without comments. Naming should be taken with care, thinking through how other developers will understand the meaning of the code. Logic should be explicit but concise, and "magic" and "clever" code should be avoided. Whitespace is your friend, use it smartly to group your code and make it easy to visually scan. Where possible, sort things alphabetically.

Context

There are times to break specific conventions, and it's all about the context. Languages are different and what is ideal in one is not the same as another. Code makes a good servant but a terrible master, and ultimately it is here to serve a higher purpose. So in certain situations, such as extremely performance critical contexts or avoiding negative side effects to the user, we should do what is best to accomplish the larger goal.


Conventions & Rules

Common

Has extra newlines

There should be no more than 1 empty line between two lines of code.

Invalid whitespace

This error indicates that there are invisible whitespace characters in your code. These characters never have any useful purpose and often are inserted by some editors.

Mixed spaces and tabs

In Liferay, we use tabs for indentation. This error means you have a space in your indentation.

CSS

Sort Properties

All CSS properties and @includes should be alphabetized.

Hex should be all upppercase

The format for the letters in hex colors should always be uppercase. So #FFF is correct, but not #0cc.

Hex can be shortened

This error means that your hex code can be shortened to 3 characters instead of 6. This is because in CSS, hex colors are actually 3 units of 2 characters, such as #FF 00 CC, and if the 2 characters in the group are identical, they can be shortened to #F0C.

Missing integer

Numbers that have decimal places should have a leading number. For instance, margin: .1em should instead be margin: 0.1em. The reason is to make it obvious that it contains a decimal place, which could be easy to miss when visually scanning the code.

Missing newlines

Selector blocks should have a single newline between them, and nested selectors should have a single newline between the last property and the first selector.

	.foo {
	}

	.bar {
		padding: 5px;

		.baz {
			margin: 5px;
		}
	}

Needs space between comma separated list

Comma-separated lists, such as those in rules like clip: rect(0,0,0,0) should have a single space after each comma.

Needs space between selector and bracket

Selectors should be have a single space beween the rule and the opening bracket, like so: .foo {

Needless quotes

In CSS, you don't need to quote values passed into url() calls.

Needless unit

Whether it's 0px, 0em, or 0%, it is still the same value: 0.

Trailing newlines

This indicates that there is an extraneous newline in the code (perhaps before the close of a rule, or more than one newline together). The most number of lines anything is separated by is 1.

Trailing comma in selector

This indicates that your rule has a trailing comma in the selector, like .foo, {.

Invalid border reset

This indicates that your rule has a border property like border: none or border: 0. We discourage this because it forces anyone who wishes to add the border back, to also have to duplicate the original color as well. For instance, doing this:

	// Default rule
	.rule {
		border: 1px solid #900;
	}
	//... some other file
	.rule {
		border: none;
	}
	// User's theme
	.rule {
		border: 1px solid #900;
	}

Which as you can see means they have to keep their color and border style in sync and duplicate whatever the default is. What's instead better is if you wish to remove a border, do border-width: 0;, which has the same effect, but allows someone to add the border back and not require them to duplicate the defaults.

Invalid property spacing

This indicates that your property is formatted as something like either color:#FFF or color: #FFF. There should be exactly one space after the property (and no spaces before).

HTML

Anonymous block containers

When someone uses <div> without any sort of identifying information, it's usually a code smell that they are trying to use the div for it's side-effects (that it's a block element, and will place it's contents onto it's own line). However, this is not good for semantic reasons, as well as the fact that it limits theme developers from being able to style it to behave differently. Add either a class or an ID to this element to both make it's purpose clear and give developers a hook into styling it. Why only block level tags? Well, technically it's not all block level items. Items like <p> paragraph tags are allowed to be anonymous, since it's they're not usually targetable (and if they are, they're targetable pretty predictably, or will be flagged with a class or ID). It's usually just div tags that are sprinkled haphazardly, but it's common to see something like:

<ul class="list">
  <li>Foo</li>
  <li>Bar</li>
</ul>

Or a semantically neutral:

<div class="generic-list">
  <span>Foo</span>
  <span>Bar</span>
</div>

But if you find that you have random text blocks dynamically placed in certain areas, you can go with either a span or div, but in either case, it's better to give it a class name, just in case.

Sort Attributes

Attributes on elements should be sorted alphabetically.

Sort Attribute Values

Values inside of many attributes should be sorted alphabetically. For instance, the names of CSS classes inside of the class attribute, or the CSS rules inside of a style attribute. We try to ignore most attributes where this doesn't make sense, but the general rule is, if there is a list of values in the attribute, it should probably be sorted.

JavaScript

Invalid argument format

The general rule here is that if any of the arguments to a function are on their own line, then all of the arguments should be on their own line. If the function has no arguments, it should be on one line, and if none of the arguments break to a new line, they should all be on the same line. If any of the arguments are an anonymous function, or an object with properties, all of the arguments should be on their own line. For example:

Invalid
	alert(1,
		2,
	3);

	alert(function(){
	},
	1, 2);

	alert(
		1, 2, 3
	);

	alert(
	);

	alert({x: 1});
Valid
	alert({}, 1);

	alert(
		function() {
		}
	);

	alert(1, 2, 3);

Invalid bracket format

Whenever you have a closing parenthesis or keyword followed by an opening bracket (like so: ) {, or try {), there should always be exactly 1 space between them. This includes if, while, for, try, etc.

Invalid function format

Anonymous functions, and function declarations should always be formatted as function() { with no spaces after the function keyword.

Don't pass variables to Liferay.Language.get()

Liferay.Language.get() will do a synchronous AJAX request to the server to retrieve the proper string value for the language key. This is to guarantee correctness, but has many downsides, mainly being that it will lock up the browser until the value has returned. However, we also pre-process .js files so that the strings are replaced inline with the proper localized string for the users language, and thereby avoiding the synchronous AJAX calls (or the call to Liferay.Language.get() at all). When a variable is passed to Liferay.Language.get(), the server has no way to resolve the JS variable (since it will be at execution time of that code block), and can't replace the value, in effect forcing a synchronous call.

Don't use Liferay.Language.get() outside of .js files

For the same reasons as the previous rule, since we don't process non JS files for Liferay.Language.get(), this will also trigger a synchronous AJAX call.

Debugging statement

This means you have either a console.log(), console.trace(), console.warn(), etc. within your code (basically, any method on the console object is assumed to be a debugging statement).

Variable declaration needs a newline after it

We distinguish between variable declaration/assignment and code which is acting on those variables. eg.

Invalid
	var foo = function(){
	};
	var bar = 0;
	setTimeout(foo, bar);
Valid
	var foo = function(){
	};
	var bar = 0;

	setTimeout(foo, bar);

Don't use Liferay.provide in an aui:script block that has a use="..." attribute

The cause of this is because the purpose of Liferay.provide is to provide a function that will be available synchronously but that will execute asynchronously. The purpose of the use="..." attribute is to load execute the body of the script tag asynchronously. By defining a function inside of an asynchronous <aui:script> means that the function could be defined at any time, including after when the function is called, which will throw an error. This also means that the error could appear sporadically, making for hard to debug issues. Here's the possible error flow:

  • Async request is made for the JS
  • Network delay...
  • User clicks on button that tries to execute foo()
  • Error thrown because foo is defined in the async callback

Trailing or leading spaces in arrays

Arrays should not contain spaces at the beginning or end of the array, e.g.

Invalid
	[ 1, 2, 3 ]
	[ 1, 2, 3]
	[1, 2, 3 ]
Valid
	[1, 2, 3]

Invalid spacing between array elements

Arrays should have exactly one space between elements

Invalid
	[1,2,3]
	[1, 2,  3]
Valid
	[1, 2, 3]

Catch statement param should be e

Catch statements are passed an error argument, and that argument name should be e.

Empty catch statement formatting

Empty catch statements should be formatted with the closing bracket exactly 1 line after the opening, e.g.:

	catch (e) {
	}

Constants should be separated by a single new line

Constants should be separated with exactly one line between them, e.g.

	var BAR = 1;

	var FOO = 2;

Format multiline variables

Variables which span over multiple lines (for instance, strings of multiline HTML), should be formatted so that the start of the last line is aligned to the start of the variable name, not the var keyword, e.g.

Invalid
	var FOO = '<p>' +
		'<span></span>'
	'</p>';

	var FOO =
	'<p>' +
		'<span></span>'
	'</p>';
Valid
	var FOO = '<p>' +
			'<span></span>'
		'</p>';

Format Liferay.provide()

Liferay.provide() has a very specific method signature, and every argument is required. This error usually occurs when someone doesn't understand the purpose of the method. It is not a way to create a general function, or a method on a property, and it would not be ideal to do it with Liferay.provide(). The purpose of Liferay.provide() is to declare a function which depends on a set of modules, and only once those modules are loaded, will it execute. The signature of the method is:

Liferay.provide(
	object (to add the method to),
	string (name of the method),
	function (to execute once the dependencies are loaded),
	array (of dependencies)
);

Each variable should have it's own var statement

Variables should not be declared with a single var statement, but each one should have it's own. The reason for this is because when assigning variables like so:

	var foo = 1,
		bar = 2,
		baz = 3;

It can become very easy to not see a missing comma, leading to the creation of a global variable. Having the var statement also helps break up visually the area for declaring variables vs. the area for working with them.

Unnecessary semicolon

You only need one semicolon to terminate a line. Anything more than that is not necessary.

Variable/property names should not start with is

The reason this is logged as an error is because the common convention for getter methods that return a boolean property is to use isFoo(), which would return the state of foo. However, if the property is named .isFoo, the by convention, the getter method would be .isIsFoo, which is odd. Instead, it's easier to just have .foo and .isFoo().

No undefined variables

Undefined variables are either references to implied global objects or going to cause an explicit error. If the variable is an implied global, add this line to the top of your JS file (assuming the name of the variable is SomeGlobalVar): /* global SomeGlobalVar */ which will remove the error.

Sort constants

Constants should be sorted alphabetically, whenever possible. If the constant is needed to be defined because it's used in another constant later, then it's okay, and we won't flag that as an error. However, all others should be sorted.

Sort methods/properties

All properties and methods on an object should be sorted alphabetically amongst their type, and the types should be ordered as:

	var obj = {
		// Public properties
		// Public methods
		// Private methods
		// Private properties
	};

Sort modules in requires array

Inside of a list of a YUI modules dependencies (which are stored in a requires array), the names of the modules should be alphabetically sorted.

Don't use variables before they're defined

ESLint Rule Documentation

Use dot notation

ESLint Rule Documentation

Treat var as block scoped

ESLint Rule Documentation

Format braces

ESLint Rule Documentation

No trailing commas

ESLint Rule Documentation

Format comma spacing

ESLint Rule Documentation

Place commas at the end

ESLint Rule Documentation

Return a consistent value

ESLint Rule Documentation

Always use instance as an alias for this

ESLint Rule Documentation

Always use braces

ESLint Rule Documentation

Use dot notation

ESLint Rule Documentation

Maintain consistent indentation

ESLint Rule Documentation

Keep consistent property spacing

ESLint Rule Documentation

Use parenthesis when using new

ESLint Rule Documentation

Use array literals to create dense arrays

ESLint Rule Documentation

Don't use arguments.callee

ESLint Rule Documentation

Don't set the catch param to an existing variable

ESLint Rule Documentation

Don't assign variables in conditions

ESLint Rule Documentation

Don't use console.*

TODO ESLint Rule Documentation

Don't use constant expressions in conditions

ESLint Rule Documentation

Don't use invisible characters in a regular expression

ESLint Rule Documentation

Don't use debugger

ESLint Rule Documentation

Don't try to delete variables

ESLint Rule Documentation

Don't create duplicate arguments

ESLint Rule Documentation

Don't create duplicate keys

ESLint Rule Documentation

Don't duplicate case labels

ESLint Rule Documentation

Don't place a return value in an else

ESLint Rule Documentation

Don't use empty character classes in regex

ESLint Rule Documentation

Don't use empty labels

ESLint Rule Documentation

Don't use eval

ESLint Rule Documentation

Don't overwrite exception param

ESLint Rule Documentation

Don't extend native objects

ESLint Rule Documentation

Don't bind functions unnecessarily

ESLint Rule Documentation

Don't cast to boolean unnecessarily

ESLint Rule Documentation

Don't use fallthrough's in switch statements

ESLint Rule Documentation

Don't overwrite a function declaration

ESLint Rule Documentation

Don't use implied eval()

ESLint Rule Documentation

Comments must come on their own lines

ESLint Rule Documentation

Don't use function declarations internally

ESLint Rule Documentation

No invalid regular expressions

ESLint Rule Documentation

No irregular whitespace

ESLint Rule Documentation

Don't use the __iterator__ property

ESLint Rule Documentation

Don't give labels existing variable names

ESLint Rule Documentation

Don't use labels

ESLint Rule Documentation

Don't nest blocks unnecessarily

ESLint Rule Documentation

Don't use an if as the only statement in an else block

ESLint Rule Documentation

Don't create functions in loops

ESLint Rule Documentation

Don't mix tabs and spaces

ESLint Rule Documentation

Don't use multiple spaces in expressions

ESLint Rule Documentation

Don't create multiline strings

ESLint Rule Documentation

Don't use extraneous newlines

ESLint Rule Documentation

Don't reassign native objects

ESLint Rule Documentation

Don't negate left operand of in operator

ESLint Rule Documentation

Don't use nested ternaries

ESLint Rule Documentation

Don't create functions with the Function constructor

ESLint Rule Documentation

Use Object literals to create objects

ESLint Rule Documentation

Don't call new on primitive wrapper objects

ESLint Rule Documentation

Don't call certain global objects as functions

ESLint Rule Documentation

Don't use octal literals

ESLint Rule Documentation

Don't use octal escapes

ESLint Rule Documentation

Don't use process.exit()

ESLint Rule Documentation

Don't use __proto__

ESLint Rule Documentation

Don't redeclare variables

ESLint Rule Documentation

Don't use spaces in regular expressions

ESLint Rule Documentation

Don't assign variable in return statement

ESLint Rule Documentation

Don't use comma operator

ESLint Rule Documentation

Don't overwrite restricted names

ESLint Rule Documentation

Don't use spaces in function calls

ESLint Rule Documentation

Don't use sparse arrays

ESLint Rule Documentation

Don't use trailing spaces at the end of lines

ESLint Rule Documentation

Don't use undefined variables

ESLint Rule Documentation

Don't initialize a variable to undefined

ESLint Rule Documentation

No unreachable code

ESLint Rule Documentation

No unused expressions

ESLint Rule Documentation

Don't use with statements

ESLint Rule Documentation

Don't use parenthesis around functions

ESLint Rule Documentation

Use the operator assignment shorthand

ESLint Rule Documentation

Don't use double quotes for string literals

ESLint Rule Documentation

You must pass a radix to parseInt

ESLint Rule Documentation

You must use semicolons

ESLint Rule Documentation

Place spaces after semicolons

TODO ESLint Rule Documentation

Sort variables

ESLint Rule Documentation

You must have a space following keywords

TODO ESLint Rule Documentation

You must use a space before blocks

TODO ESLint Rule Documentation

Don't place a space before the function parenthesis

TODO ESLint Rule Documentation

Don't use spaces inside of parentheses

ESLint Rule Documentation

You must place a space around infix operators

ESLint Rule Documentation

You must place a space following return, throw, and case

ESLint Rule Documentation

You must place a space after unary operators

TODO ESLint Rule Documentation

Format single line comments

ESLint Rule Documentation

Use isNaN()

ESLint Rule Documentation

Valid typeof

ESLint Rule Documentation

Don't use Yoda conditions

ESLint Rule Documentation

Clone this wiki locally