diff options
Diffstat (limited to 'doc/development/fe_guide/style_guide_js.md')
-rw-r--r-- | doc/development/fe_guide/style_guide_js.md | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/doc/development/fe_guide/style_guide_js.md b/doc/development/fe_guide/style_guide_js.md new file mode 100644 index 00000000000..abd241c0bc8 --- /dev/null +++ b/doc/development/fe_guide/style_guide_js.md @@ -0,0 +1,415 @@ +# Style guides and linting +See the relevant style guides for our guidelines and for information on linting: + +## JavaScript +We defer to [Airbnb][airbnb-js-style-guide] on most style-related +conventions and enforce them with eslint. + +See [our current .eslintrc][eslintrc] for specific rules and patterns. + +### Common + +#### ESlint + +- **Never** disable eslint rules unless you have a good reason. You may see a lot of legacy files with `/* eslint-disable some-rule, some-other-rule */` at the top, but legacy files are a special case. Any time you develop a new feature or refactor an existing one, you should abide by the eslint rules. + +- **Never Ever EVER** disable eslint globally for a file + + ```javascript + // bad + /* eslint-disable */ + + // better + /* eslint-disable some-rule, some-other-rule */ + + // best + // nothing :) + ``` + +- If you do need to disable a rule for a single violation, try to do it as locally as possible + + ```javascript + // bad + /* eslint-disable no-new */ + + import Foo from 'foo'; + + new Foo(); + + // better + import Foo from 'foo'; + + // eslint-disable-next-line no-new + new Foo(); + ``` + +- When they are needed _always_ place ESlint directive comment blocks on the first line of a script, followed by any global declarations, then a blank newline prior to any imports or code. + + ```javascript + // bad + /* global Foo */ + /* eslint-disable no-new */ + import Bar from './bar'; + + // good + /* eslint-disable no-new */ + /* global Foo */ + + import Bar from './bar'; + ``` + +- **Never** disable the `no-undef` rule. Declare globals with `/* global Foo */` instead. + +- When declaring multiple globals, always use one `/* global [name] */` line per variable. + + ```javascript + // bad + /* globals Flash, Cookies, jQuery */ + + // good + /* global Flash */ + /* global Cookies */ + /* global jQuery */ + ``` + +#### Modules, Imports, and Exports +- Use ES module syntax to import modules + + ```javascript + // bad + require('foo'); + + // good + import Foo from 'foo'; + + // bad + module.exports = Foo; + + // good + export default Foo; + ``` + +- Relative paths + + Unless you are writing a test, always reference other scripts using relative paths instead of `~` + + In **app/assets/javascripts**: + ```javascript + // bad + import Foo from '~/foo' + + // good + import Foo from '../foo'; + ``` + + In **spec/javascripts**: + ```javascript + // bad + import Foo from '../../app/assets/javascripts/foo' + + // good + import Foo from '~/foo'; + ``` + +- Avoid using IIFE. Although we have a lot of examples of files which wrap their contents in IIFEs (immediately-invoked function expressions), this is no longer necessary after the transition from Sprockets to webpack. Do not use them anymore and feel free to remove them when refactoring legacy code. + +- Avoid adding to the global namespace. + + ```javascript + // bad + window.MyClass = class { /* ... */ }; + + // good + export default class MyClass { /* ... */ } + ``` + +- Side effects are forbidden in any script which contains exports + + ```javascript + // bad + export default class MyClass { /* ... */ } + + document.addEventListener("DOMContentLoaded", function(event) { + new MyClass(); + } + ``` + + +#### Data Mutation and Pure functions +- Strive to write many small pure functions, and minimize where mutations occur. + + ```javascript + // bad + const values = {foo: 1}; + + function impureFunction(items) { + const bar = 1; + + items.foo = items.a * bar + 2; + + return items.a; + } + + const c = impureFunction(values); + + // good + var values = {foo: 1}; + + function pureFunction (foo) { + var bar = 1; + + foo = foo * bar + 2; + + return foo; + } + + var c = pureFunction(values.foo); + ``` + +- Avoid constructors with side-effects + + +#### Parse Strings into Numbers +- `parseInt()` is preferable over `Number()` or `+` + + ```javascript + // bad + +'10' // 10 + + // good + Number('10') // 10 + + // better + parseInt('10', 10); + ``` + + +### Vue.js + + +#### Basic Rules +- Only include one Vue.js component per file. +- Export components as plain objects: + + ```javascript + export default { + template: `<h1>I'm a component</h1> + } + ``` + +#### Naming +- **Extensions**: Use `.vue` extension for Vue components. +- **Reference Naming**: Use PascalCase for Vue components and camelCase for their instances: + ```javascript + // bad + import cardBoard from 'cardBoard'; + + // good + import CardBoard from 'cardBoard' + + // bad + components: { + CardBoard: CardBoard + }; + + // good + components: { + cardBoard: CardBoard + }; + ``` +- **Props Naming:** +- Avoid using DOM component prop names. +- Use kebab-case instead of camelCase to provide props in templates. + + ```javascript + // bad + <component class="btn"> + + // good + <component css-class="btn"> + + // bad + <component myProp="prop" /> + + // good + <component my-prop="prop" /> +``` + +#### Alignment +- Follow these alignment styles for the template method: + + ```javascript + // bad + <component v-if="bar" + param="baz" /> + + // good + <component + v-if="bar" + param="baz" + /> + + // if props fit in one line then keep it on the same line + <component bar="bar" /> + ``` + +#### Quotes +- Always use double quotes `"` inside templates and single quotes `'` for all other JS. + + ```javascript + // bad + template: ` + <button :class='style'>Button</button> + ` + + // good + template: ` + <button :class="style">Button</button> + ` + ``` + +#### Props +- Props should be declared as an object + + ```javascript + // bad + props: ['foo'] + + // good + props: { + foo: { + type: String, + required: false, + default: 'bar' + } + } + ``` + +- Required key should always be provided when declaring a prop + + ```javascript + // bad + props: { + foo: { + type: String, + } + } + + // good + props: { + foo: { + type: String, + required: false, + default: 'bar' + } + } + ``` + +- Default key should always be provided if the prop is not required: + + ```javascript + // bad + props: { + foo: { + type: String, + required: false, + } + } + + // good + props: { + foo: { + type: String, + required: false, + default: 'bar' + } + } + + // good + props: { + foo: { + type: String, + required: true + } + } + ``` + +#### Data +- `data` method should always be a function + + ```javascript + // bad + data: { + foo: 'foo' + } + + // good + data() { + return { + foo: 'foo' + }; + } + ``` + +#### Directives + +- Shorthand `@` is preferable over `v-on` + + ```javascript + // bad + <component v-on:click="eventHandler"/> + + + // good + <component @click="eventHandler"/> + ``` + +- Shorthand `:` is preferable over `v-bind` + + ```javascript + // bad + <component v-bind:class="btn"/> + + + // good + <component :class="btn"/> + ``` + +#### Closing tags +- Prefer self closing component tags + + ```javascript + // bad + <component></component> + + // good + <component /> + ``` + +#### Ordering +- Order for a Vue Component: + 1. `name` + 2. `props` + 3. `data` + 4. `components` + 5. `computedProps` + 6. `methods` + 7. lifecycle methods + 1. `beforeCreate` + 2. `created` + 3. `beforeMount` + 4. `mounted` + 5. `beforeUpdate` + 6. `updated` + 7. `activated` + 8. `deactivated` + 9. `beforeDestroy` + 10. `destroyed` + 8. `template` + + +## SCSS +- [SCSS](style_guide_scss.md) + +[airbnb-js-style-guide]: https://github.com/airbnb/javascript +[eslintrc]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.eslintrc |