8.2 KiB
Style guides and linting
See the relevant style guides for our guidelines and for information on linting:
JavaScript
We defer to Airbnb on most style-related conventions and enforce them with eslint.
See our current .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
// 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
// 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.
// 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.// bad /* globals Flash, Cookies, jQuery */ // good /* global Flash */ /* global Cookies */ /* global jQuery */
-
Use up to 3 parameters for a function or class. If you need more accept an Object instead.
// bad fn(p1, p2, p3, p4) {} // good fn(options) {}
Modules, Imports, and Exports
-
Use ES module syntax to import modules
// 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:
// bad import Foo from '~/foo' // good import Foo from '../foo';
In spec/javascripts:
// 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.
// bad window.MyClass = class { /* ... */ }; // good export default class MyClass { /* ... */ }
-
Side effects are forbidden in any script which contains exports
// 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.
// 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
-
Prefer
.map
,.reduce
or.filter
over.forEach
A forEach will cause side effects, it will be mutating the array being iterated. Prefer using.map
,.reduce
or.filter
const users = [ { name: 'Foo' }, { name: 'Bar' } ]; // bad users.forEach((user, index) => { user.id = index; }); // good const usersWithId = users.map((user, index) => { return Object.assign({}, user, { id: index }); });
Parse Strings into Numbers
-
parseInt()
is preferable overNumber()
or+
// bad +'10' // 10 // good Number('10') // 10 // better parseInt('10', 10);
CSS classes used for JavaScript
- If the class is being used in Javascript it needs to be prepend with
js-
// bad <button class="add-user"> Add User </button> // good <button class="js-add-user"> Add User </button>
Vue.js
Basic Rules
-
Only include one Vue.js component per file.
-
Export components as plain objects:
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:
// 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.
// 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" />
<button class="btn">Click me</button>
// good
<component
v-if="bar"
param="baz"
/>
<button class="btn">
Click me
</button>
// 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.// bad template: ` <button :class='style'>Button</button> ` // good template: ` <button :class="style">Button</button> `
Props
-
Props should be declared as an object
// bad props: ['foo'] // good props: { foo: { type: String, required: false, default: 'bar' } }
-
Required key should always be provided when declaring a prop
// 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:
// 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// bad data: { foo: 'foo' } // good data() { return { foo: 'foo' }; }
Directives
-
Shorthand
@
is preferable overv-on
// bad <component v-on:click="eventHandler"/> // good <component @click="eventHandler"/>
-
Shorthand
:
is preferable overv-bind
// bad <component v-bind:class="btn"/> // good <component :class="btn"/>
Closing tags
-
Prefer self closing component tags
// bad <component></component> // good <component />
Ordering
- Order for a Vue Component:
name
props
data
components
computedProps
methods
- lifecycle methods
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
activated
deactivated
beforeDestroy
destroyed
template