js2coffee
Version:
JavaScript to CoffeeScript compiler
618 lines (466 loc) • 9.93 kB
Markdown
## Comments
### Block comments
In block comments, CoffeeScript converts `#` into JavaDoc-style ` *`. Hence,
Js2coffee will transform ` *` inside block comments into the more
CoffeeScript-like `#`.
```js
// Input:
a();
/*
* hello
*/
b();
```
```coffee
# Output:
a()
###
# hello
###
b()
```
## Compatibility mode
### Assignment of reserved words
Certain keywords in CoffeeScript are not allowed. For instance, `on` is
actually an alias for `true`.
The CoffeeScript code `on = 2` will produce errors. As such, Js2coffee will
throw an error if any of the CoffeeScript reserved keywords are used.
If compatibility mode ins on (`--compat`), it will be escaped in backticks so
to prevent any side effects.
```js
// Input:
on = 2
```
```coffee
# Output:
`on = 2`
```
### Equals
The `==` operator has no equivalent in CoffeeScript. If you use `==` in
CoffeeScript, it will be compiled into `===`.
As such, Js2coffee will throw a warning when `==` is used, but it will behave
like `===`.
If compatibility mode ins on (`--compat`), it will be escaped in backticks so
to prevent any side effects.
```js
// Input:
if (a == b(c + 2)) { run(); }
```
```coffee
# Output:
if `a == b(c + 2)`
run()
```
### Named function expressions
Named function expressions are not supported in CoffeeScript.
If compatibility mode is on (`--compat`), they will be escaped into backticks.
```js
// Input:
var x = function fn() {
return fn;
}
```
```coffee
# Output:
x = (`function fn() {
return fn;
}`)
```
### Named function expressions off
Named function expressions are not supported in CoffeeScript.
If compatibility mode is off (`--compat`), they will be treated like any
other function expression, but may behave unexpectedly. In this example,
the `typeof` will return `'undefined'` in CoffeeScript instead of the
expected `'function'`.
```js
// Input:
var x = function fn() {
return fn;
};
alert(typeof x())
```
```coffee
# Output:
x = ->
fn
alert typeof x()
```
### Undefined
It's possible for `undefined` to be redefined in JavaScript, eg, `var
undefined = 2`. While this is undesirable and never recommended, Js2coffee
will that using `undefined` will use whatever `undefined` is defined as.
This is only available if compatibility mode is on (`--compat`).
```js
// Input:
undefined
```
```coffee
# Output:
`undefined`
```
## Function calls
### Call with multiple objects
Objects aren't usually braced unless necessary. This leads to ambiguous
constructions such as `one:1, two:2` where both objects are meant to be
separated.
```js
// Input:
a({ one: 1 }, { two: 2 })
```
```coffee
# Output:
a { one: 1 }, two: 2
```
## Functions
### Function reordering
Named function declarations in JavaScript can appear at any point of the
scope, and they will be available anywhere in the scope. CoffeeScript doesn't
allow named function declarations, however.
To get around this, js2coffee takes function declarations and puts the on top
of the scope.
This is an improvement over js2coffee 0.x that only looks in the same level
of the function body, not recursing into deeper blocks.
```js
// Input:
alert(name());
if (ok) {
a();
function name() {
return "John";
}
}
```
```coffee
# Output:
name = ->
'John'
alert name()
if ok
a()
```
### Return object
Having a return of an object without braces is ambiguous:
> ```
> return
> a: 1
> b: 2
> ```
CoffeeScript and CoffeeScriptRedux will both choke on this. Js2coffee will
put braces around the object to make it work.
```js
// Input:
function fn() {
if (x)
return { a: 1, b: 2 };
return true;
}
```
```coffee
# Output:
fn = ->
if x
return {
a: 1
b: 2
}
true
```
### Undefined in parameters
Some libraries shield against the JavaScript flaw of `undefined` being
possible to redefine by using a function wrapper that includes an
`undefined` parameter.
CoffeeScript will throw an error when one of the functions have `undefined`
as its parameters (eg, `(undefined) ->`). Js2coffee will simply strip it out
of the parameter list.
This is not accounted for in js2coffee 0.x.
```js
// Input:
function fn (undefined) {
return true;
}
```
```coffee
# Output:
fn = ->
true
```
## If
### Blank ifs
CoffeeScript doesn't support `if` blocks that don't have anything in it (as
of CoffeeScript v1.8.0). To work around this, Js2coffee inserts an empty
`else` block along with it.
```js
// Input:
if (condition) {}
```
```coffee
# Output:
if condition
else
```
## Loops
### Empty while
CoffeeScript doesn't allow loop constructs (while/loop/for) without any
body. To get around this, we use `continue` in place of an empty body.
```js
// Input:
while (a) {}
```
```coffee
# Output:
while a
continue
```
### For with continue
Since CoffeeScript has no `for` loops, they
have to be converted to `while` loops. If
`continue` happens inside the loop, it needs
to re-run the update expression just before
it.
```js
// Input:
for (a; b; update++) {
if (x) continue;
d()
}
```
```coffee
# Output:
a
while b
if x
update++
continue
d()
update++
```
### Simple for
CoffeeScript has no `for` loop, so they are converted into `while` loops.
```js
// Input:
for (a;b;c) {
d();
}
```
```coffee
# Output:
a
while b
d()
c
```
## Regexp
### Blank with flag
Expressions that begin with a space will be converted into `RegExp(...)` to
avoid parse errors.
```js
// Input:
/ /g
```
```coffee
# Output:
RegExp ' ', 'g'
```
### Equals
A RegExp literal starting with an equal sign is not allowed in CoffeeScript,
as it's ambiguous and clashes with the `/=` operator.
```js
// Input:
a(/=\s/)
```
```coffee
# Output:
a RegExp('=\\s')
```
## Shadowing
### Var shadowing
CoffeeScript doesn't support shadowing of outer variables (see
[coffee-script#712]). js2coffee uses a terrible hack to make this work.
Previously, this is unsupported in js2coffee 0.x.
[coffee-script#712]: https://github.com/jashkenas/coffee-script/issues/712
```js
// Input:
var val = 2;
var fn = function () {
var val = 1;
return;
}
fn();
assert(val === 2);
```
```coffee
# Output:
val = 2
fn = ->
`var val`
val = 1
return
fn()
assert val == 2
```
## Simple
### Exponents
CoffeeScript supports the `**` binary expression to calculate exponents.
```js
// Input:
Math.pow(2, 8)
```
```coffee
# Output:
2 ** 8
```
### Nested function calls
CoffeeScript allows function calls without parentheses, such as `alert
"Hello"`. With this, you can do strange constructions such as `push new
Sidebar $ "left"` (that is: `push(new Sidebar($("#left")))`).
This is unreadable, however. Ruby has the same constructions, but Ruby
styleguides often advocate *not* omitting parentheses unless the call
expression is a statement.
Js2coffee takes the same convention into consideration.
```js
// Input:
a(b(c(d())));
```
```coffee
# Output:
a b(c(d()))
```
### Prototype
CoffeeScript allow prototypes as `::`, as in `Array::join = ->`.
It also allows `a::` without anything on the right side (as is the case
of the 2nd line), but CoffeeScriptRedux doesn't.
```js
// Input:
a.prototype.b = 1
a.prototype = {}
```
```coffee
# Output:
a::b = 1
a.prototype = {}
```
### Standalone this
CoffeeScript allows `this` as `@`. In fact, js2coffee compiles `this.x` into `@x`.
Using a standalone `@` was once allowed in CoffeeScript, but was removed in
future versions. Hence, standalone JavaScript `this` expressions compile into
the same thing, `this`.
```js
// Input:
var a = this;
```
```coffee
# Output:
a = this
```
### Ternary operator nesting
This is previously broken in js2coffee 0.x since Narcissus didn't handle this
case properly.
```js
// Input:
a ? b : c ? d : e
```
```coffee
# Output:
if a then b else if c then d else e
```
### This prefix
When using `this` on the left-side of a `.`, it gets converted into
CoffeeScript-style `@` prefixes.
When `this` is used on its own (such as in the case of the 2nd `this` in the
example), it is left alone as using a standalone `@` is discouraged.
```js
// Input:
this.run(this);
```
```coffee
# Output:
@run this
```
### Void 0
CoffeeScript doesn't support the `void` operator. However, doing `void
(anything)` will always be identical to `void 0`, and CoffeeScript compiles
the `undefined` keyword into `void 0`. Hence, all `void XXX` expressions will
compile into `undefined`.
That is: `void 100 === void 0 === undefined`.
```js
// Input:
void 0
```
```coffee
# Output:
undefined
```
## Strings
### Prevent interpolation
Strings are defaulted to single quotes to prevent interpolation.
```js
// Input:
"#{a}"
```
```coffee
# Output:
'#{a}'
```
## Switch
### Case consolidation
CoffeeScript doesn't support adding `when` clauses that are empty, as you
probably would in JavaScript. Js2coffee will consolidate empty `case` clauses
together to make things more readable.
```js
// Input:
switch (a) {
case one:
case two:
b();
break;
default:
c();
}
```
```coffee
# Output:
switch a
when one, two
b()
else
c()
```
### Case consolidation with default
CoffeeScript doesn't support adding `when` clauses that are empty, as you
probably would in JavaScript. When an empty `case` is used just before
`default:`, it is effectively useless and is stripped away.
```js
// Input:
switch (a) {
case one:
b();
break;
case two:
default:
c();
}
```
```coffee
# Output:
switch a
when one
b()
else
c()
```
## Var
### Var without initializer
Doing `var a` in JavaScript will declare that the current
function scope has a variable `a` in it, preventing things like `alert(a)`
from getting the global `a`.
CoffeeScript has no such construct as `var`. However, since JavaScript
initializes all variables as `undefined`, doing an assignment to undefined
(`a = undefined`) will yield the same result.
```js
// Input:
var a;
```
```coffee
# Output:
a = undefined
```