Difference between revisions of "JavaScript Coding Conventions"
(Created page with "== Introduction == This document is based on [http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml Google's JavaScript Style Guide] and Douglas Crockford's [h...") |
(No difference)
|
Latest revision as of 12:09, 16 December 2021
Introduction
This document is based on Google's JavaScript Style Guide and Douglas Crockford's Code Conventions for the JavaScript Programming Language
Linting and Formatting
To use these new tools, it is required to have nodejs/npm installed in your machine |
ESLint and Prettier are the tools used in Openbravo to format check Javascript code. For convenience, has bundled some scripts in order to use these tools, either as standalone scripts and as Mercurial hooks to check the code before commiting it.
Standalone scripts
- ESLint:
Running the jslint script without parameters passes the linter to all non-ignored .js files in the project, scanning first the files in core and all modules that has no special ignore rules (i.e. that has a .eslintignore file in the root of the module). <source lang="bash">./modules/org.openbravo.client.kernel/jslint/jslint </source>
Adding the -f flag, ESLint will try to fix all warnings/errors that can be autofixed:
<source lang="bash">./modules/org.openbravo.client.kernel/jslint/jslint -f </source>
Finally, one or more files paths can be added to run ESLint for those specific files.
<source lang="bash">./modules/org.openbravo.client.kernel/jslint/jslint modules/org.openbravo.client.application/web/org.openbravo.client.application/js/utilities/ob-utilities.js modules/org.openbravo.client.application/web/org.openbravo.client.application/js/utilities/ob-utilities-date.js </source>
- Prettier:
Running the jsformatter script without parameters passes the formatter to all non-ignored .js files in the project, scanning first the files in core and all modules that has no special ignore rules (i.e. that has a .prettierignore file in the root of the module). <source lang="bash">./modules/org.openbravo.client.kernel/jsformatter/jsformatter </source>
Adding the -w flag, instead of checking whether the files are formatted, performs the actual formatting and writes them in their corresponding files:
<source lang="bash">./modules/org.openbravo.client.kernel/jsformatter/jsformatter -w </source>
Finally, one or more files paths can be added to run Prettier for those specific files.
<source lang="bash">./modules/org.openbravo.client.kernel/jsformatter/jsformatter modules/org.openbravo.client.application/web/org.openbravo.client.application/js/utilities/ob-utilities.js modules/org.openbravo.client.application/web/org.openbravo.client.application/js/utilities/ob-utilities-date.js </source>
Running npm script directly
Note that if your IDE supports npm, the following can be used as well to check your code:
- ESLint
<source lang="bash">npm run jslint modules/org.openbravo.client.application/web/org.openbravo.client.application/js/utilities/ob-utilities.js</source>
- Prettier:
<source lang="bash">npm run jsformat -- --check modules/org.openbravo.client.application/web/org.openbravo.client.application/js/utilities/ob-utilities.js</source>
or, if you want to check a whole module:
<source lang="bash">npm run jsformat -- --check 'modules/org.openbravo.mobile.core/**/*.js'</source>
Git hooks
For using these tools as Git hooks, run this command from inside an openbravo repository: <source lang="bash">git config core.hooksPath .githooks</source>
To hook this script to another repository, copy the .gitHooks to that repo and run the same command.
If you want this hooks in external/retail modules, from inside modules/<name-of-module>, execute: <source lang="bash">git config core.hooksPath ../../.githooks</source>
Note: Those scripts use realpath bash command to check between staged and unstaged changes. If not installed(it is by default on most systems) hooks will only check files in their current state, not differentiating between staged and unstaged changes.
Set hooks globally
It is possible to set those git hooks globally for all git repositories. This means .githooks will be executed on any commit in all your git repositories locally. <source lang="bash">git config --global core.hooksPath /absolute/path/to/.githooks</source> Example (assuming myHooks dir contains .githooks): <source lang="bash">git config --global core.hooksPath /home/openbravo/myHooks/.githooks</source> If for any reason you want to remove this configuration execute the following: <source lang="bash">git config --global --unset core.hooksPath</source>
Mercurial hooks
For using these tools as Mercurial hooks, add the following at the end of .hg/hgrc:
<source lang="ini">[hooks] pretxncommit= ./modules/org.openbravo.client.kernel/jslint/jslint-hg pre-commit = ./modules/org.openbravo.client.kernel/jsformatter/jsformatter-hg </source>
Or the following if working in a module:
<source lang="ini">[hooks] pretxncommit= ../org.openbravo.client.kernel/jslint/jslint-hg pre-commit = ../org.openbravo.client.kernel/jsformatter/jsformatter-hg </source>
Note: You can hook the script to another mercurial hooks. See this link.
Using JSLint and JSBeautifier (before PR19Q3)
In older versions of Openbravo, JSLint and JSBeautifier are the tools you should use if you're developing JavaScript. Openbravo has in place a wrapper scripts to check the code before committing it. You just need to open the .hg/hgrc in your working copy and add at the end of the file: <source lang="ini"> [hooks] pretxncommit= ./modules/org.openbravo.client.kernel/jslint/jscheck-hg pre-commit = ./modules/org.openbravo.client.kernel/jsbeautify/jscheck-btfy-hg </source>
If you work in a custom module then from MP31 for JSLint and from 16Q3 for JSBeautifier, there are another scripts which you should use, add hooks like these in the hgrc file in the .hg directory in your module: <source lang="ini"> [hooks] pretxncommit = ../org.openbravo.client.kernel/jslint/jscheck-module-hg pre-commit = ../org.openbravo.client.kernel/jsbeautify/jscheck-btfy-module-hg </source>
You also can execute these scripts for js files in openbravo using these comands: <source lang="bash">//Execute this in openbravo root folder to check all js files ./modules/org.openbravo.client.kernel/jslint/jslint ./modules/org.openbravo.client.kernel/jsbeautify/jscheck-btfy
//Execute this in module root folder to check jsbeautify in all js files of your module ../org.openbravo.client.kernel/jsbeautify/jscheck-btfy-module </source>
Code Formatting
Starting from PR19Q3, Javascript code is formatted using Prettier. The following conventions still applies, but check Prettier rationale for some of the choices they made regarding formatting. |
Spaces instead of Tabs
Configure your editor to use 2 spaces instead of tabs.
Curly Braces
Because of implicit semicolon insertion, always start your curly braces on the same line as whatever they're opening. For example: <source lang="javascript"> if (something) {
// ...
} else {
// ...
} </source>
Variable Declaration
A single var statement is preferred. All the variables in a function must be declared on top of the function.
Why do I need to declare the variables on top of the function? Check JavaScript Hosting explained |
It is a good coding practice to initialize variables when you declare them. So we must to to declare and initialize in one coding line all vars of the function. Using this convention we will define all vars at the top of the function and we will "set" the type of the var adding more info to it.
<source lang="javascript"> function f() {
var a = 0, b = , c = null;
}
function f() {
var i = 0, j = 1; // Just one var // {...}
}
function x() {
var i = 0; // one var statement per variable is discouraged var j = 0; // {...}
}
// Variables on top of the function
function z() {
var i, j, k; // {...}
}
function s() {
var i; for (i = 0; i < 10; i++) { // do something } // {...} var j; // Variable declaration in the middle of a function is discouraged
} </source>
Array and Object Initializers
Single-line array and object initializers are allowed when they fit on a line: <source lang="javascript"> var arr = [1, 2, 3]; // No space after [ or before ]. var obj = {a: 1, b: 2, c: 3}; // No space after { or before }. </source>
Multiline array initializers and object initializers are indented 2 spaces, just like blocks. <source lang="javascript"> // Object initializer. var inset = {
top: 10, // Notice the space after the colon right: 20, bottom: 15, left: 12
};
// Array initializer. this.rows_ = [
'"Slartibartfast" <fjordmaster@magrathea.com>', '"Zaphod Beeblebrox" <theprez@universe.gov>', '"Ford Prefect" <ford@theguide.com>', '"Arthur Dent" <has.no.tea@gmail.com>', '"Marvin the Paranoid Android" <marv@googlemail.com>', 'the.mice@magrathea.com'
];
// Used in a method call. OB.ViewManager.openView('_140', {
command: 'DEFAULT' icon: 'Window' id: '180' tabId: '180' tabTitle: 'Product' viewId: '_140' windowId: '140'
}); </source>
Single Quotes
A string can be defined with single or double quotes. For consistency, single quotes are preferred. <source lang="javascript"> var s = 'Openbravo 3 is great!'; </source>
Function Declaration
All functions should be declared before they are used. Inner functions should follow the var statement. This helps make it clear what variables are included in its scope.
There should be no space between the name of a function and the ( (left parenthesis) of its parameter list. There should be one space between the ) (right parenthesis) and the { (left curly brace) that begins the statement body. The body itself is indented four spaces. The } (right curly brace) is aligned with the line containing the beginning of the declaration of the function. <source lang="javascript"> function outer(c, d) {
var e = c * d;
function inner(a, b) { return (e * a) + b; }
return inner(0, 1);
} </source>
If a function literal is anonymous, there should be one space between the word function and the ( (left parenthesis). If the space is omited, then it can appear that the function's name is function, which is an incorrect reading. <source lang="javascript"> someObj = {
method: function () { return this.datum; }, datum: 0
}; </source>
Function Arguments
When possible, all function arguments should be listed on the same line. If doing so would exceed the 100-column limit, the arguments must be line-wrapped in a readable way. To save space, you may wrap as close to 100 as possible, or put each argument on its own line to enhance readability. The indentation may be either four spaces, or aligned to the parenthesis. <source lang="javascript"> // Four-space, wrap at 100. Works with very long function names, survives // renaming without reindenting, low on space. OB.foo.bar.doThingThatIsVeryDifficultToExplain = function (
veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo, tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) { // ...
};
// Four-space, one argument per line. Works with long function names, // survives renaming, and emphasizes each argument. OB.foo.bar.doThingThatIsVeryDifficultToExplain = function (
veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo, tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) { // ...
};
// Parenthesis-aligned indentation, wrap at 80. Visually groups arguments, // low on space. function foo(veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo,
tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) { // ...
}
// Parenthesis-aligned, one argument per line. Visually groups and // emphasizes each individual argument. function bar(veryDescriptiveArgumentNumberOne,
veryDescriptiveArgumentTwo, tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) { // ...
} </source>
Naming files
Files name must be only in lower case and dash to separate words. Some servers are not case sensitive and spaces are a bad idea.
Example:
Wrong: LoginModel.js, cashUpWIndow.js...
Right: login-model.js, cashup-window.js...
Return object
Functions which returns an object will return a variable instead of the object. Returning a variable well named will help us to understand better what is returned (together with the function name) and will be also more readable. There is no need of creating a variable to return if we won't "work" with that object. Coding, we usually create a var to return because we will assign some values to that object but if the functions will return a new object we can do it directly, see some examples:
<source lang="javascript"> function myFunction (){
var myObject = {}; myObject.time = new Date(); myObject.total = getNet() + getTax(); if(total > 0){ myObject.isNegative = false; }else { myObject.isNegative = true; } return myObject;
}
function myFunction (){
return {name: getName(), address: getDefaultAddress() + getCountry()};
} </source>
Tips and Tricks
True and False Boolean Expressions
The following are all false in boolean expressions: <source lang="javascript">
null undefined // the empty string 0 // the number
</source>
But be careful, because these are all true: <source lang="javascript">
'0' // the string [] // the empty array {} // the empty object
</source>
This means that instead of this: <source lang="javascript"> while (x != null) { </source> you can write this shorter code (as long as you don't expect x to be 0, or the empty string, or false): <source lang="javascript"> while (x) { </source>
And if you want to check a string to see if it is null or empty, you could do this: <source lang="javascript"> if (y != null && y != ) { </source>
But this is shorter and nicer: <source lang="javascript"> if (y) { </source>
Caution: There are many unintuitive things about boolean expressions. Here are some of them: <source lang="javascript">
Boolean('0') == true '0' != true 0 != null 0 == [] 0 == false Boolean(null) == false null != true null != false Boolean(undefined) == false undefined != true undefined != false Boolean([]) == true [] != true [] == false Boolean({}) == true {} != true {} != false
</source>
Conditional (Ternary) Operator (?:)
Instead of this: <source lang="javascript"> if (val != 0) {
return foo();
} else {
return bar();
} </source> you can write this: <source lang="javascript"> return val ? foo() : bar(); </source>
&& and ||
These binary boolean operators are short-circuited, and evaluate to the last evaluated term. || has been called the 'default' operator, because instead of writing this: <source lang="javascript"> function foo(opt_win) {
var win; if (opt_win) { win = opt_win; } else { win = window; } // ...
} </source> you can write this: <source lang="javascript"> function foo(opt_win) {
var win = opt_win || window; // ...
} </source>
&& is also useful for shortening code. For instance, instead of this: <source lang="javascript"> if (node) {
if (node.kids) { if (node.kids[index]) { foo(node.kids[index]); } }
} </source> you could do this: <source lang="javascript"> if (node && node.kids && node.kids[index]) {
foo(node.kids[index]);
} </source>
or this: <source lang="javascript"> var kid = node && node.kids && node.kids[index]; if (kid) {
foo(kid);
} </source>