Skip to content

axept/javascript

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

59 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Axept JavaScript Style Guide

Follow the original Airbnb JavaScript Style Guide.

This document describes only those rules which we decided to extend, to override or to add.

Other Style Guides:

TODO:

Contents

  1. Comparison Operators & Equality
  2. Comments
  3. Semicolons
  4. Lines
  5. Naming Conventions
  6. Imports
  7. Packages
  8. Type Casting & Coercion
  9. Template Strings
  10. Objects
  11. Classes & Constructors
  12. Unsorted Yet

Comparison Operators & Equality

Extends: Section 15

  • 1.1 Ternaries should not be as a part of expression, only whole expression.

    TODO: find or make ESLint rule for this

    Why? The following example demonstrate how it could be misunderstood.

    // bad
    const query = { 'forward_url': '/' }
    const targetURL = 'https://qwe.com/auth?' + (typeof query === 'string') ? query : JSON.stringify(query)
    
    // Uncaught TypeError: split is not a function
    // because type of targetURL is Object
    const partsOfURL = targetURL.split('?')
    
    
    // may be
    const query = { 'forward_url': '/' }
    const queryString = typeof query === 'string' ? query : JSON.stringify(query)
    const targetURL = 'https://qwe.com/auth?' + queryString
    
    
    // good
    const query = { 'forward_url': '/' }
    const queryString = (typeof query === 'string') ? query : JSON.stringify(query)
    const targetURL = 'https://qwe.com/auth?' + queryString

⬆ back to top

Comments

Extends: Section 18

  • 2.1 Avoid using obvious comments.

    Why? It makes code harder to read because you can think that here is something else than you see.

    // bad - it duplicates the function name
    
    /**
     * Update hosts
     */
    function updateHosts(list) {
      hosts = list
    }
    
    
    // good - nothing bad is better than something bad
    function updateHosts(list) {
      hosts = list
    }
    
    
    // bad - it duplicates the module path
    import Drawer from 'react-native-drawer'
    
    // Components
    import DrawerMenu from './components/drawer-menu'
    
    
    // good - see the previous "good" explaination
    import Drawer from 'react-native-drawer'
    import DrawerMenu from './components/drawer-menu'
    
    
    // good
    if (!root.isAbstract()) {
      // No need to wrap child nodes of a known concrete type.
      return nextChildren;
    }

⬆ back to top

Semicolons

Extends: Section 21

  • 3.1 Don't use semicolons, in the presence of excellent automation tools.

    ESLint rule: semi

    Why? Since we always have linting tools we can detect dangerous situations automatically. But this style makes us really faster according to a few years experience.

⬆ back to top

Lines

  • 4.1 80 characters is good. But feel free to use up to 100 characters in a line.

    ESLint rule: max-len

    Why? Almost all developers used to 80 characters. It's a very old practice. Now we have a wide screens and we used to seat a little bit more far from monitor. In this case we can use a bit more characters and code will be still readable.

    Read this and this for more details.

    // bad
    if ((this.props.suggestions.length === 0) && (newProps.suggestions.length > 0) && (newProps.autosuggestValue.length > 0) {
    
      return
    }
    
    // good
    const hasSuggestions = (this.props.suggestions.length === 0)
    const willHaveSuggestions = (newProps.suggestions.length > 0)
    if (hasSuggestions && willHaveSuggestions && (newProps.autosuggestValue.length > 0)) {
      return
    }
    
    // good
    const hasSuggestions = (this.props.suggestions.length === 0)
    const willHaveSuggestions = (newProps.suggestions.length > 0)
    const willHaveAutosuggest = (newProps.autosuggestValue.length > 0)
    if (hasSuggestions && willHaveSuggestions && willHaveAutosuggest) {
      return
    }

  • 4.2 Don't use excess empty line

    ESLint rule: no-unexpected-multiline, padded-blocks

    // bad
    const config = loadConfig()
    
    if (config) {
      applyConfig(config)
    }
    
    
    // good
    const config = loadConfig()
    if (config) {
      applyConfig(config)
    }

  • 4.3 One empty line before and after function or class declaration.

    ESLint rule: no-unexpected-multiline

    Why? No empty line needed after function or class declaration - if the following line is javascript brackets.

    // bad
    
    const x = 0
    function a() {
      return 1
    }
    const b = () => 2
    
    
    // bad
    
    const x = 0
    class A {
      a() {
        return 1
      }
      b() {
        return 2
      }
    }
    
    
    // good
    
    const x = 0
    
    function a() {
      return 1
    }
    
    const b = () => 2
    
    
    // good
    
    const x = 0
    
    class A {
    
      a() {
        return 1
      }
    
      b() {
        return 2
      }
    }

⬆ back to top

Naming Conventions

Extends: Section 22

  • 5.1 Avoid using reduced names.

    Why? It makes code harder to understand. Names are for readability, not to appease a computer algorithm.

    // bad - what does it mean "ref"? referals? references? single reference?
    const ref1 = fetch('/api/books')
    
    
    // good
    const reference1 = fetch('/api/books')

  • 5.2 Do not override globals.

    // bad 
    import fetch from './fetch-with-credentials'
    
    // good
    import fetchWithCredentials from './fetch-with-credentials'
    
    // bad
    function alert(message) {
      console.error(message)
    }
    
    // good
    function alertToConsole(message) {
      console.error(message)
    }

  • 5.3 Acronyms and initialisms should always be all capitalized.

    Why? Names are for readability, not to appease a computer algorithm.

    // bad
    import SmsContainer from './containers/SmsContainer'
    
    // bad
    const HttpRequests = [
      // ...
    ]
    
    // good
    import SMSContainer from './containers/SMSContainer'
    
    // good
    const HTTPRequests = [
      // ...
    ]
    
    // best
    import TextMessageContainer from './containers/TextMessageContainer'
    
    // best
    const requests = [
      // ...
    ]

⬆ back to top

Imports

  • 6.1 Use the following modules import order:

    • React
    • Relay
    • Redux
    • Tools from node_modules
    • Tools from current repo
    • Other components
    • Any kind of static assets
    • Styles
    • Constants

⬆ back to top

Packages

  • 7.1 Try to avoid using Lodash and Ramda.

    Why? Underscore and Lodash has been made when ES6 has not been so popular. Ramda do not have performance benefits, but being opinionated custom approach increase unnecessary requirements for Developers skills. Besides of that Ramda did not stay "standard de-facto".

⬆ back to top

Type Casting & Coercion

Extends: Section 22

  • 8.1 Do not use JavaScript "magic".

    ESLint rule: no-implicit-coercion, no-bitwise

    It makes source code more portable to other languages and available for reading by other developers.

    // bad
    const b = !!a
    
    // bad
    const b = (~a.indexOf('k'))
    
    // bad
    const c = !!d.length
    
    // good
    const b = Boolean(a)
    
    // good
    const b = (a.indexOf('k') >= 0)
    
    // good
    const c = (d.length > 0)

⬆ back to top

Template Strings

Extends: Section 6

  • 9.1 Don't call function inside template string.

    TODO: find or make ESLint rule for this

    // bad
    const targetURL = `${getHost()}/search/?query=${query}`
    
    
    // good
    const targetHost = getHost()
    const targetURL = `${targetHost}/search/?query=${query}`

⬆ back to top

Objects

Extends: Section 3

  • 10.1 Do not mix expression in one-line objects as assignment.

    TODO: find or make ESLint rule for this

    // bad
    this.setState({ hasErrors: true, route })
    
    
    // bad
    const state = { hasErrors: true, route }
    
    
    // good
    this.setState({ hasErrors, route })
    
    
    // good
    this.setState({ hasErrors: true })
    
    
    // good
    const state = { hasErrors, route }

⬆ back to top

Classes & Constructors

Extends: Section 9

  • 11.1 Static members should be declared in class body

    TODO: find or make ESLint rule for this

    // bad
    
    Profile.propTypes = {
      classes: PropTypes.object.isRequired,
      handleClick: PropTypes.func,
    }
    
    
    // good
    
    class Profile extends Component {
    
      static propTypes = {
        classes: PropTypes.object.isRequired,
        handleClick: PropTypes.func,
      }
    
      // ...
    }

⬆ back to top

Unsorted Yet

  • 12.1 Use brackets for expressions.

    TODO: find or make ESLint rule for this

    // bad
    const useSrcKey = _srcKey !== undefined || this._srcKey !== undefined
    const componentAsync = typeof src === 'function' ? src() : import(src)
    const isProduction = process.env['NODE_ENV'] === 'production'
    
    // good
    const useSrcKey = (_srcKey !== undefined) || (this._srcKey !== undefined)
    const componentAsync = (typeof src === 'function') ? src() : import(src)
    const isProduction = (process.env['NODE_ENV'] === 'production')

  • 12.2 Use dot syntax only for internal objects.

    TODO: find or make ESLint rule for this

    Seperate importable/exportable JSON data objects and internal runtime objects.

    Also it means - do not mix accessing to values by dot and by string.

    Google Closure Advanced Compilation: Whenever possible, use dot-syntax property names rather than quoted strings. Use quoted string property names only when you don't want Closure Compiler to rename a property at all. For example, to export a property you must use a quoted string. However, for properties used only within your compiled code, use dot syntax.

    // bad
    fetch('/api/users')
      .then(response => response.json())
      .then(json => {
        if (!json.errors) {
          json.data.users.forEach(user => {
            const { id, name } = user
            if (users[id].name !== name) {
              setUserName(id, name)
            }
          })
        }
      })
    
    // bad
    const a = { b: 1 }
    console.log(a['b'])
    
    // bad
    const a = { 'b': 1 }
    console.log(a.b)
    
    // good
    fetch('/api/users')
      .then(response => response.json())
      .then(json => {
        if (!json['errors']) {
          json['data']['users'].forEach(user => {
            const id = user['id']
            const name = user['name']
            if (users[id].name !== name) {
              setUserName(id, name)
            }
          })
        }
      })
      
    // good
    const a = { b: 1 }
    console.log(a.b)
    
    // good
    const a = { 'b': 1 }
    console.log(a['b'])

    It also means you can't rename stringifed keys because they are not in your code base only - in external API, data files, etc.

  • 12.3 Don't mix JS and JSX in one line.

    TODO: find or make ESLint rule for this

    // bad
    return <ReferenceLink referenceKey={key} id={ref.id} title={ref.title}/>
    
    
    // bad
    const body = items.map(it => <li>{it.title}</li>)
    
    
    // good
    return (
      <ReferenceLink referenceKey={key} id={ref.id} title={ref.title}/>
    );
    
    
    // good
    const body = items.map(it => (
      <li>{it.title}</li>
    ))
    
    
    // good
    return (
      <References>
        {items.map((it, index) => (
          <ReferenceLink referenceKey={index} id={it['id']} title={it['title']}/>
        ))}
      </References>
    )

  • 12.4 Don't mix JSX and Text node in one line.

    TODO: find or make ESLint rule for this

    // bad
    return (
      <Text style={style.openInSafariLink}>Open in Safari</Text>
    )
    
    
    // good
    return (
      <Text style={style.openInSafariLink}>
        Open in Safari
      </Text>
    )
    
    
    // good - if you need a Text node for styling
    return (
      <Text>&nbsp;</Text>
    )

⬆ back to top