Giter Club home page Giter Club logo

coffeescript-style-guide's Introduction

CoffeeScript Style Guide

This guide presents a collection of best-practices and coding conventions for the CoffeeScript programming language.

This guide is intended to be community-driven, and contributions are highly encouraged.

Please note that this is a work-in-progress: there is much more that can be specified, and some of the guidelines that have been specified may not be deemed to be idiomatic by the community (in which case, these offending guidelines will be modified or removed, as appropriate).

Inspiration

The details in this guide have been very heavily inspired by several existing style guides and other resources. In particular:

Table of Contents

## Code layout ### Tabs or Spaces?

Use spaces only, with 2 spaces per indentation level. Never mix tabs and spaces.

### Maximum Line Length

Limit all lines to a maximum of 79 characters.

### Blank Lines

Separate top-level function and class definitions with a single blank line.

Separate method definitions inside of a class with a single blank line.

Use a single blank line within the bodies of methods or functions in cases where this improves readability (e.g., for the purpose of delineating logical sections).

### Trailing Whitespace

Do not include trailing whitespace on any lines.

### Optional Commas

Avoid the use of commas before newlines when properties or elements of an Object or Array are listed on separate lines.

# Yes
foo = [
  'some'
  'string'
  'values'
]
bar:
  label: 'test'
  value: 87

# No
foo = [
  'some',
  'string',
  'values'
]
bar:
  label: 'test',
  value: 87
### Encoding

UTF-8 is the preferred source file encoding.

## Module Imports

If using a module system (CommonJS Modules, AMD, etc.), require statements should be placed on separate lines.

require 'lib/setup'
Backbone = require 'backbone'

These statements should be grouped in the following order:

  1. Standard library imports (if a standard library exists)
  2. Third party library imports
  3. Local imports (imports specific to this application or library)
## Whitespace in Expressions and Statements

Avoid extraneous whitespace in the following situations:

  • Immediately inside parentheses, brackets or braces

       ($ 'body') # Yes
       ( $ 'body' ) # No
  • Immediately before a comma

       console.log x, y # Yes
       console.log x , y # No

Additional recommendations:

  • Always surround these binary operators with a single space on either side

    • assignment: =

      • Note that this also applies when indicating default parameter value(s) in a function declaration

        test: (param = null) -> # Yes
        test: (param=null) -> # No
    • augmented assignment: +=, -=, etc.

    • comparisons: ==, <, >, <=, >=, unless, etc.

    • arithmetic operators: +, -, *, /, etc.

    • (Do not use more than one space around these operators)

         # Yes
         x = 1
         y = 1
         fooBar = 3
      
         # No
         x      = 1
         y      = 1
         fooBar = 3
## Comments

If modifying code that is described by an existing comment, update the comment such that it accurately reflects the new code. (Ideally, improve the code to obviate the need for the comment, and delete the comment entirely.)

The first word of the comment should be capitalized, unless the first word is an identifier that begins with a lower-case letter.

If a comment is short, the period at the end can be omitted.

### Block Comments

Block comments apply to the block of code that follows them.

Each line of a block comment starts with a # and a single space, and should be indented at the same level of the code that it describes.

Paragraphs inside of block comments are separated by a line containing a single #.

  # This is a block comment. Note that if this were a real block
  # comment, we would actually be describing the proceeding code.
  #
  # This is the second paragraph of the same block comment. Note
  # that this paragraph was separated from the previous paragraph
  # by a line containing a single comment character.

  init()
  start()
  stop()
### Inline Comments

Inline comments are placed on the line immediately above the statement that they are describing. If the inline comment is sufficiently short, it can be placed on the same line as the statement (separated by a single space from the end of the statement).

All inline comments should start with a # and a single space.

The use of inline comments should be limited, because their existence is typically a sign of a code smell.

Do not use inline comments when they state the obvious:

  # No
  x = x + 1 # Increment x

However, inline comments can be useful in certain scenarios:

  # Yes
  x = x + 1 # Compensate for border
## Naming Conventions

Use camelCase (with a leading lowercase character) to name all variables, methods, and object properties.

Use CamelCase (with a leading uppercase character) to name all classes. (This style is also commonly referred to as PascalCase, CamelCaps, or CapWords, among other alternatives.)

(The official CoffeeScript convention is camelcase, because this simplifies interoperability with JavaScript. For more on this decision, see here.)

For constants, use all uppercase with underscores:

CONSTANT_LIKE_THIS

Methods and variables that are intended to be "private" should begin with a leading underscore:

_privateMethod: ->
## Functions

(These guidelines also apply to the methods of a class.)

When declaring a function that takes arguments, always use a single space after the closing parenthesis of the arguments list:

foo = (arg1, arg2) -> # Yes
foo = (arg1, arg2)-> # No

Do not use parentheses when declaring functions that take no arguments:

bar = -> # Yes
bar = () -> # No

In cases where method calls are being chained and the code does not fit on a single line, each call should be placed on a separate line and indented by one level (i.e., two spaces), with a leading ..

[1..3]
  .map((x) -> x * x)
  .concat([10..12])
  .filter((x) -> x < 11)
  .reduce((x, y) -> x + y)

When calling functions, choose to omit or include parentheses in such a way that optimizes for readability. Keeping in mind that "readability" can be subjective, the following examples demonstrate cases where parentheses have been omitted or included in a manner that the community deems to be optimal:

baz 12

brush.ellipse x: 10, y: 20 # Braces can also be omitted or included for readability

foo(4).bar(8)

obj.value(10, 20) / obj.value(20, 10)

print inspect value

new Tag(new Value(a, b), new Arg(c))

You will sometimes see parentheses used to group functions (instead of being used to group function parameters). Examples of using this style (hereafter referred to as the "function grouping style"):

($ '#selektor').addClass 'klass'

(foo 4).bar 8

This is in contrast to:

$('#selektor').addClass 'klass'

foo(4).bar 8

In cases where method calls are being chained, some adopters of this style prefer to use function grouping for the initial call only:

($ '#selektor').addClass('klass').hide() # Initial call only
(($ '#selektor').addClass 'klass').hide() # All calls

The function grouping style is not recommended. However, if the function grouping style is adopted for a particular project, be consistent with its usage.

## Strings

Use string interpolation instead of string concatenation:

"this is an #{adjective} string" # Yes
"this is an " + adjective + " string" # No

Prefer single quoted strings ('') instead of double quoted ("") strings, unless features like string interpolation are being used for the given string.

## Conditionals

Favor unless over if for negative conditions.

Instead of using unless...else, use if...else:

  # Yes
  if true
    ...
  else
    ...

  # No
  unless false
    ...
  else
    ...

Multi-line if/else clauses should use indentation:

  # Yes
  if true
    ...
  else
    ...

  # No
  if true then ...
  else ...
## Looping and Comprehensions

Take advantage of comprehensions whenever possible:

  # Yes
  result = (item.name for item in array)

  # No
  results = []
  for item in array
    results.push item.name

To filter:

result = (item for item in array when item.name is "test")

To iterate over the keys and values of objects:

object = one: 1, two: 2
alert("#{key} = #{value}") for key, value of object
## Extending Native Objects

Do not modify native objects.

For example, do not modify Array.prototype to introduce Array#forEach.

## Exceptions

Do not suppress exceptions.

## Annotations

Use annotations when necessary to describe a specific action that must be taken against the indicated block of code.

Write the annotation on the line immediately above the code that the annotation is describing.

The annotation keyword should be followed by a colon and a space, and a descriptive note.

  # FIXME: The client's current state should *not* affect payload processing.
  resetClientState()
  processPayload()

If multiple lines are required by the description, indent subsequent lines with two spaces:

  # TODO: Ensure that the value returned by this call falls within a certain
  #   range, or throw an exception.
  analyze()

Annotation types:

  • TODO: describe missing functionality that should be added at a later date
  • FIXME: describe broken code that must be fixed
  • OPTIMIZE: describe code that is inefficient and may become a bottleneck
  • HACK: describe the use of a questionable (or ingenious) coding practice
  • REVIEW: describe code that should be reviewed to confirm implementation

If a custom annotation is required, the annotation should be documented in the project's README.

## Miscellaneous

and is preferred over &&.

or is preferred over ||.

is is preferred over ==.

isnt is preferred over !=.

not is preferred over !.

or= should be used when possible:

temp or= {} # Yes
temp = temp || {} # No

Prefer shorthand notation (::) for accessing an object's prototype:

Array::slice # Yes
Array.prototype.slice # No

Prefer @property over this.property.

return @property # Yes
return this.property # No

However, avoid the use of standalone @:

return this # Yes
return @ # No

Avoid return where not required, unless the explicit return increases clarity.

Use splats (...) when working with functions that accept variable numbers of arguments:

console.log args... # Yes

(a, b, c, rest...) -> # Yes

coffeescript-style-guide's People

Contributors

am avatar dyegocosta avatar gobhi avatar michaelficarra avatar mjrusso avatar svenwin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

coffeescript-style-guide's Issues

Style Guide Corrections ;)

Instead of doing this in a fork -- I'd rather put these in a ticket to make conversation easier...

Do not use more than one space around these operators:

# Yes
x = 1
y = 1
fooBar = 3

# No
x      = 1
y      = 1
fooBar = 3

... I think this particular point is still fairly undecided. There are a lot of sequences of CoffeeScript where aligning your left and right values to look more like a table is pleasing. I know a style guide has to pick one way or the other, but I think it's still an open question.


test: (param=null) -> # Yes
test: (param = null) -> # No

Nope -- quite the opposite. If a = b should always be written with spaces around the =, then the same holds true in default arguments, Python notwithstanding.


Ideally, improve the code to obviate the need for the
comment, and delete the comment entirely.

Actually, CoffeeScript tries to encourage pervasive commenting ... not so much to answer the "what" or the "how" of any individual line of code, but to answer the "why".

If a comment is short, the period at the end can be omitted.

Nope -- comments should be written as full sentences, with proper punctuation.


The bit that you describe as "block comments" aren't CoffeeScript block comments, which look like this:

###
comment
###

... but, the ones that you describe are always preferred instead of block comments -- which should only be used if you intend to pass along the comment to the underlying JS, like a license, or warning.


The use of inline comments should be limited, because their existence is typically a sign of a code smell.

Nope -- poor comments are poor, and good comments are good. There's no problem with writing good inline comments to explain an un-obvious function or tricky algorithm.


For constants, use all uppercase with underscores

I don't think that this one is settled quite yet. In JavaScript, of course, until const lands, there's no such thing as a constant. For that reason, I think that simply using local variable style for constants may actually make more sense. I'm not sure which should be prescribed.


When calling functions, omit the parentheses on the final method call in a chain.

I think that parentheses inclusion or omission doesn't have a hard and fast rule. For example, your second example doesn't benefit:

foo(4).bar(8) # ... is probably more readable than:
foo(4).bar 8

Usually, it should be obvious (or at least obvious-ish) when leaving parentheses (and braces) off makes things clearer, and when it doesn't:

brush.ellipse x: 10, y: 20

obj.value(10, 20) / obj.value(20, 10)

print inspect value

new Tag(new Value(a, b), new Arg(c))

The correct way to apply the function grouping style when chaining is to use it for the initial call only.

I personally don't use it, or recommend it, but I would think that folks that do prefer it would take issue with this rule.


Finally, you may want to add a note to always avoid use of standalone @, all by itself. A simple this is preferred. (A common question.)


Thanks for this style guide -- it's really great stuff.

How to name constants; top-level variables and naming collisions

It might be worth mentioning a strategy to avoid naming collisions between variables at the top level scope of your file and local variables within more narrow scopes.

Some of this is already implicitly covered--if you name your classes CamelCase and your constants ALL_CAP_CASE, then you're unlikely to accidentally alias local variables.

If you have functions at top level scope, then there can be gotchas if they have the same name as local variables.

Thanks for publishing the style guide!

Improve treatment of comments

There are several issues with the way that the style guide currently describes how comments should be written. In general, a literate programming style is encouraged (but certainly not mandated). However, the current guide is not compatible with said literate programming style.

The following is a summary of issues as logged by @jashkenas, moved here from ticket #2:


Ideally, improve the code to obviate the need for the
comment, and delete the comment entirely.

Actually, CoffeeScript tries to encourage pervasive commenting ... not so much to answer the "what" or the "how" of any individual line of code, but to answer the "why".

If a comment is short, the period at the end can be omitted.

Nope -- comments should be written as full sentences, with proper punctuation.


The bit that you describe as "block comments" aren't CoffeeScript block comments, which look like this:

###
comment
###

... but, the ones that you describe are always preferred instead of block comments -- which should only be used if you intend to pass along the comment to the underlying JS, like a license, or warning.


The use of inline comments should be limited, because their existence is typically a sign of a code smell.

Nope -- poor comments are poor, and good comments are good. There's no problem with writing good inline comments to explain an un-obvious function or tricky algorithm.

try without catch should be banned

This is a serious gotcha that a style guide would do well to document (and forbid):

jashkenas/coffeescript#3958 (comment)

try 1/0

suppresses the exception.

I propose amending Exceptions to say "Do not suppress exceptions. Do not use try without catch nor finally, which is an undocumented and surprising way to suppress exceptions."

How to Handle Docstrings

We're having an internal discussion on our team about the best practice for handling docstrings. In Python, we'd do something like:

"""This is a module level docstring."""

class SomeClass(object):
    """This is a class level docstring."""

   def some_method(self, arg1, arg2):
       """This is a method level docstring."""

In coffeescript, we'd like to mimic the pattern:

### This is a module level docstring. ###

SomeObject =
    ### This is an object level docstring. ###

    someMethod: ->
      ### This is a method level docstring. ###
      ...
      # Some inline comment
      ...

I like this approach as it distinctly separates "docstrings" from inline comments, but it appears that triple ### renders a multi-line comment to the compiled javascript -- which is probably undesired in most cases.

Any guidance would be helpful - thx :)

Why 2 spaces for tabs?

This is relatively minor, but since 2 spaces is in bold, it seems to imply that you should strictly use only 2 spaces for indents.

I personally prefer 4 spaces as I find 2 spaces to degrade readability (which is one of the primary benefits to using CoffeeScript). Is there a technical reason as to why 2 spaces is better than 4? Or should the style guide be changed to say "2 or 4 spaces per indentation level"?

Optional commas for objects and arrays

We are through a debate about the optional commas when defining an array or object, see the examples in the coffeecript docs:

song = ["do", "re", "mi", "fa", "so"]

singers = {Jagger: "Rock", Elvis: "Roll"}

bitlist = [
  1, 0, 1
  0, 0, 1
  1, 1, 0
]

kids =
  brother:
    name: "Max"
    age:  11
  sister:
    name: "Ida"
    age:  9

The documentation explicitly says the commas are optional, and here resides our doubt. We would like to hear some community opinions, is there any rules, thumbs up that you apply to your code? Should this be added to the style-guide in some way?

Regarding the code we can see from coffeescript whenever there is a new line the comma is omitted.

Here we have both opinions:

  1. When each property is listed on its own line there is no need for commas as the indentation provide enough readability.
  2. The commas are an enforcement to understand the continuation, even when there is a new line. Besides commas are not optional in ruby or python, since coffeescript is heavily based on those it makes sense to maintain this as a style "rule".

We would love to know your opinion regarding this.
Thank you!

`(a)->` (no space) potential benefit

When declaring a function that takes arguments, always use a single space after the closing parenthesis of the arguments list:

foo = (arg1, arg2) -> # Yes
foo = (arg1, arg2)-> # No

I prefer the (a) -> (space) style (I think), possibly mainly because most (all?) CoffeeScript code I've seen uses it. However, I recently ran into this problem:

I wanted to do this:

asyncFunc (result) ->
  console.log result

But I somehow messed up and lost a space:

asyncFunc(result) ->
  console.log result

Woops! Still valid code, though, but now it does something entirely different. Took me some time to spot.

If I had used a (a)-> (no space) style, I could have solved it much faster, since the following is a syntax error:

asyncFunc(result)->
  console.log result

Luckily, many (async) functions take another argument first.

asyncFunc 3000, (result) ->
  console.log result

Any thoughts?

Why "Do not modify native objects"?

It's obvious that extending native objects is bad if you're doing it improperly:

String::capitalize = ->
  this[0].toUpperCase() + this[1...]

console.log char for char of 'str'
# => 0, 1, 2, capitalize

But ES5 has Object.defineProperty, which allows you to extend objects with non-enumerable properties:

Object.defineProperty String, 'capitalize' value: ->
  this[0].toUpperCase() + this[1...]

console.log char for char of 'str'
# => 0, 1, 2

Replace or= with ?=

CoffeeScript 1.3.1 made or= a syntax error. Conditional assignment should be made with the ?= operator.

Module Imports example does not represent the style guide

Hey in the section Module Imports it's mentioned to order your imports by:

  1. Standard library imports (if a standard library exists)
  2. Third party library imports
  3. Local imports (imports specific to this application or library)

in the example it's written:
require 'lib/setup'
Backbone = require 'backbone'

But isn't "require 'lib/setup'" a local import and "Backbone = require 'backbone'" a third-party library? If this is correct one should switch the order in the example.

Multi line function calls

I found your coffeescript styleguide looking for advice on multiline function calls, but I don't see this topic covered in the guide. Any advice on which of the following is more idiomatic?

aLongFunctionName firstLongArgumentName, secondLongArgument,
thirdArgumentIsAnObject: 'bar'
aLongFunctionName firstLongArgumentName, secondLongArgument,
  thirdArgumentIsAnObject: 'bar'

Also, while we're at it, when creating objects, which of these is more idiomatic?

foo:
  bar: 'baz
foo: bar: 'baz

Thanks for your work on the style guide, it's extremely useful to have a standard.

Why 2 spaces?

I have often used two spaces for coffee script because of recommendations from others, however with literate coffee script it seems I need 4 spaces for it to work properly? Is this always the case and why use two spaces in the first place?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.