| # Gerrit JavaScript style guide | 
 |  | 
 | Gerrit frontend follows [recommended eslint rules](https://eslint.org/docs/rules/) | 
 | and [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html). | 
 | Eslint is used to automate rules checking where possible. You can find exact eslint rules | 
 | [here](https://gerrit.googlesource.com/gerrit/+/refs/heads/master/polygerrit-ui/app/.eslintrc.js). | 
 |  | 
 | Gerrit JavaScript code uses ES6 modules and doesn't use goog.module files. | 
 |  | 
 | Additionally to the rules above, Gerrit frontend uses the following rules (some of them have automated checks, | 
 | some don't): | 
 |  | 
 | - [Prefer null over undefined](#prefer-null) | 
 | - [Use destructuring imports only](#destructuring-imports-only) | 
 | - [Use classes and services for storing and manipulating global state](#services-for-global-state) | 
 | - [Pass required services in the constructor for plain classes](#pass-dependencies-in-constructor) | 
 | - [Assign required services in a HTML/Polymer element constructor](#assign-dependencies-in-html-element-constructor) | 
 |  | 
 | ## <a name="prefer-undefined"></a>Prefer `undefined` over `null` | 
 |  | 
 | It is more confusing than helpful to work with both `null` and `undefined`. We prefer to only use `undefined` in | 
 | our code base. Try to avoid `null`. | 
 |  | 
 | Some browser and library APIs are using `null`, so we cannot remove `null` completely from our code base. But even | 
 | then try to convert return values and leak as few `nulls` as possible. | 
 |  | 
 | ## <a name="destructuring-imports-only"></a>Use destructuring imports only | 
 | Always use destructuring import statement and specify all required names explicitly (e.g. `import {a,b,c} from '...'`) | 
 | where possible. | 
 |  | 
 | **Note:** Destructuring imports are not always possible with 3rd-party libraries, because a 3rd-party library | 
 | can expose a class/function/const/etc... as a default export. In this situation you can use default import, but please | 
 | keep consistent naming across the whole gerrit project. The best way to keep consistency is to search across our | 
 | codebase for the same import. If you find an exact match - always use the same name for your import. If you can't | 
 | find exact matches - find a similar import and assign appropriate/similar name for your default import. Usually the | 
 | name should include a library name and part of the file path. | 
 |  | 
 | You can read more about different type of imports | 
 | [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import). | 
 |  | 
 | **Good:** | 
 | ```Javascript | 
 | // Import from the module in the same project. | 
 | import {getDisplayName, getAccount} from './user-utils.js' | 
 |  | 
 | // The following default import is allowed only for 3rd-party libraries. | 
 | // Please ensure, that all imports have the same name accross gerrit project (downloadImage in this example) | 
 | import downloadImage from 'third-party-library/images/download.js' | 
 | ``` | 
 |  | 
 | **Bad:** | 
 | ```Javascript | 
 | import * as userUtils from './user-utils.js' | 
 | ``` | 
 |  | 
 | ## <a name="services-for-global-state"></a>Use classes and services for storing and manipulating global state | 
 |  | 
 | You must use classes and services to share global state across the gerrit frontend code. Do not put a state at the | 
 | top level of a module. | 
 |  | 
 | It is not easy to define precise what can be a shared global state and what is not. Below are some | 
 | examples of what can treated as a shared global state: | 
 |  | 
 | * Information about enabled experiments | 
 | * Information about current user | 
 | * Information about current change | 
 |  | 
 | **Note:** | 
 |  | 
 | Service name must ends with a `Service` suffix. | 
 |  | 
 | To share global state across modules in the project, do the following: | 
 | - put the state in a class | 
 | - add a new service to the | 
 | [appContext](https://gerrit.googlesource.com/gerrit/+/refs/heads/master/polygerrit-ui/app/services/app-context.js) | 
 | - add a service initialization code to the | 
 | [services/app-context-init.js](https://gerrit.googlesource.com/gerrit/+/refs/heads/master/polygerrit-ui/app/services/app-context-init.js) file. | 
 | - add a service or service-mock initialization code to the | 
 | [embed/app-context-init.js](https://gerrit.googlesource.com/gerrit/+/refs/heads/master/polygerrit-ui/app/embed/app-context-init.js) file. | 
 | - recommended: add a separate service-mock for testing. Do not use the same mock for testing and for | 
 | the shared gr-diff (i.e. in the `services/app-context-init.js`). Even if the mocks are simple and looks | 
 | identically, keep them separate. It allows to change them independently in the future. | 
 |  | 
 | Also see the example below if a service depends on another services. | 
 |  | 
 | **Note 1:** Be carefull with the shared gr-diff element. If a service is not required for the shared gr-diff, | 
 | the safest option is to provide a mock for this service in the embed/app-context-init.js file. In exceptional | 
 | cases you can keep the service uninitialized in | 
 | [embed/app-context-init.js](https://gerrit.googlesource.com/gerrit/+/refs/heads/master/polygerrit-ui/app/embed/app-context-init.js) file | 
 | , but it is recommended to write a comment why mocking is not possible. In the future we can | 
 | review/update rules regarding the shared gr-diff element. | 
 |  | 
 | **Good:** | 
 | ```Javascript | 
 | export class CounterService { | 
 |     constructor() { | 
 |         this._count = 0; | 
 |     } | 
 |     get count() { | 
 |         return this._count; | 
 |     } | 
 |     inc() { | 
 |         this._count++; | 
 |     } | 
 | } | 
 |  | 
 | // app-context.js | 
 | export const appContext = { | 
 |     //... | 
 |     mouseClickCounterService: null, | 
 |     keypressCounterService: null, | 
 | }; | 
 |  | 
 | // services/app-context-init.js | 
 | export function initAppContext() { | 
 |     //... | 
 |     // Add the following line before the Object.defineProperties(appContext, registeredServices); | 
 |     addService('mouseClickCounterService', () => new CounterService()); | 
 |     addService('keypressCounterService', () => new CounterService()); | 
 |     // If a service depends on other services, pass dependencies as shown below | 
 |     // If circular dependencies exist, app-init-context tests fail with timeout or stack overflow | 
 |     // (we are  going to improve it in the future) | 
 |     addService('analyticService', () => | 
 |         new CounterService(appContext.mouseClickCounterService, appContext.keypressCounterService)); | 
 |     //... | 
 |     // This following line must remains the last one in the initAppContext | 
 |     Object.defineProperties(appContext, registeredServices); | 
 | } | 
 | ``` | 
 |  | 
 | **Bad:** | 
 | ```Javascript | 
 | // module counter.js | 
 | // Incorrect: shared state declared at the top level of the counter.js module | 
 | let count = 0; | 
 | export function getCount() { | 
 |     return count; | 
 | } | 
 | export function incCount() { | 
 |     count++; | 
 | } | 
 | ``` | 
 |  | 
 | ## <a name="pass-dependencies-in-constructor"></a>Pass required services in the constructor for plain classes | 
 |  | 
 | If a class/service depends on some other service (or multiple services), the class must accept all dependencies | 
 | as parameters in the constructor. | 
 |  | 
 | Do not use appContext anywhere else in a class. | 
 |  | 
 | **Note:** This rule doesn't apply for HTML/Polymer elements classes. A browser creates instances of such classes | 
 | implicitly and calls the constructor without parameters. See | 
 | [Assign required services in a HTML/Polymer element constructor](#assign-dependencies-in-html-element-constructor) | 
 |  | 
 | **Good:** | 
 | ```Javascript | 
 | export class UserService { | 
 |     constructor(restApiService) { | 
 |         this._restApiService = restApiService; | 
 |     } | 
 |     getLoggedIn() { | 
 |         // Send request to server using this._restApiService | 
 |     } | 
 | } | 
 | ``` | 
 |  | 
 | **Bad:** | 
 | ```Javascript | 
 | import {appContext} from "./app-context"; | 
 |  | 
 | export class UserService { | 
 |     constructor() { | 
 |         // Incorrect: you must pass all dependencies to a constructor | 
 |         this._restApiService = appContext.restApiService; | 
 |     } | 
 | } | 
 |  | 
 | export class AdminService { | 
 |     isAdmin() { | 
 |         // Incorrect: you must pass all dependencies to a constructor | 
 |         return appContext.restApiService.sendRequest(...); | 
 |     } | 
 | } | 
 |  | 
 | ``` | 
 |  | 
 | ## <a name="assign-dependencies-in-html-element-constructor"></a>Assign required services in a HTML/Polymer element constructor | 
 | If a class is a custom HTML/Polymer element, the class must assign all required services in the constructor. | 
 | A browser creates instances of such classes implicitly, so it is impossible to pass anything as a parameter to | 
 | the element's class constructor. | 
 |  | 
 | Do not use appContext anywhere except the constructor of the class. | 
 |  | 
 | **Note for legacy elements:** If a polymer element extends a LegacyElementMixin and overrides the `created()` method, | 
 | move all code from this method to a constructor right after the call to a `super()` | 
 | ([example](#assign-dependencies-legacy-element-example)). The `created()` | 
 | method is [deprecated](https://polymer-library.polymer-project.org/2.0/docs/about_20#lifecycle-changes) and is called | 
 | when a super (i.e. base) class constructor is called. If you are unsure about moving the code from the `created` method | 
 | to the class constructor, consult with the source code: | 
 | [`LegacyElementMixin._initializeProperties`](https://github.com/Polymer/polymer/blob/v3.4.0/lib/legacy/legacy-element-mixin.js#L318) | 
 | and | 
 | [`PropertiesChanged.constructor`](https://github.com/Polymer/polymer/blob/v3.4.0/lib/mixins/properties-changed.js#L177) | 
 |  | 
 |  | 
 |  | 
 | **Good:** | 
 | ```Javascript | 
 | import {appContext} from `.../services/app-context.js`; | 
 |  | 
 | export class MyCustomElement extends ...{ | 
 |     constructor() { | 
 |         super(); //This is mandatory to call parent constructor | 
 |         this._userService = appContext.userService; | 
 |     } | 
 |     //... | 
 |     _getUserName() { | 
 |         return this._userService.activeUserName(); | 
 |     } | 
 | } | 
 | ``` | 
 |  | 
 | **Bad:** | 
 | ```Javascript | 
 | import {appContext} from `.../services/app-context.js`; | 
 |  | 
 | export class MyCustomElement extends ...{ | 
 |     created() { | 
 |         // Incorrect: assign all dependencies in the constructor | 
 |         this._userService = appContext.userService; | 
 |     } | 
 |     //... | 
 |     _getUserName() { | 
 |         // Incorrect: use appContext outside of a constructor | 
 |         return appContext.userService.activeUserName(); | 
 |     } | 
 | } | 
 | ``` | 
 |  | 
 | <a name="assign-dependencies-legacy-element-example"></a> | 
 | **Legacy element:** | 
 |  | 
 | Before: | 
 | ```Javascript | 
 | export class MyCustomElement extends ...LegacyElementMixin(...) { | 
 |     constructor() { | 
 |         super(); | 
 |         someAction(); | 
 |     } | 
 |     created() { | 
 |         super(); | 
 |         createdAction1(); | 
 |         createdAction2(); | 
 |     } | 
 | } | 
 | ``` | 
 |  | 
 | After: | 
 | ```Javascript | 
 | export class MyCustomElement extends ...LegacyElementMixin(...) { | 
 |     constructor() { | 
 |         super(); | 
 |         // Assign services here | 
 |         this._userService = appContext.userService; | 
 |         // Code from the created method - put it before existing actions in constructor | 
 |         createdAction1(); | 
 |         createdAction2(); | 
 |         // Original constructor code | 
 |         someAction(); | 
 |     } | 
 |     // created method is removed | 
 | } | 
 | ``` |