It's time to replace CoffeeScript with Babel and CoffeeLint with eslint.
CoffeeScript was useful a few years ago. It provided many features that
JavaScript was lacking. It gave us the fat arrow (lexical this
functions),
default parameters, destructuring assignments, splats (spread operator), a
class
keyword, block strings, and more. Everything in the list above is now
part of the JavaScript standard. JavaScript is moving forward and gaining
adoption and better tools where CoffeeScript isn't.
But why isn't CoffeeScript getting better tools? Let's take a look at what Jeremy Ashkenas had to say in CODE GENIUS - Rise of the Transpiler at 11:56.
We like to cheat and make things easier for ourselves ... this is not kosher, it is introducing context sensitivity into the lexer process. If you took the actual class you would get an F doing this kind of thing. But it's just programming, it's just code. You can cheat if you want to cheat
In the end that kind of cheat meant that I can't build a tool that tracks variables, so I can't reliably build lint rules around unused or undefined variables. Typical compilers produce an Abstract Syntax Tree, or AST. The AST is a data structure that has all the info needed to produce target code. But, CoffeeScript's "AST" isn't a data structure to be read, instead it's a mix of data and executable code that modifies its own structure as it runs to fill in things like implicit returns.
Surely CoffeeScript Redux will save us! The description at the top of the project is "rewrite of the CoffeeScript compiler with proper compiler design principles and a focus on robustness and extensibility". The last change in that project that actually touched code was over a year ago. That project appears to be dead.
While CoffeeScript gained the ability to produce generators, they can't
implement syntax to use
them because for...of
already has an incompatible meaning in CoffeeScript.
Surely there must be some benefits of using CoffeeScript today. If you look through coffeesript.org's home page, these are the features, or sometimes mis-features that CoffeeScript provides.
pop quiz! Given this line of CoffeeScript foo bar and hello world
which of
these is the JavaScript it produces
foo(bar) && hello(world)
- `foo(bar && hello(world))``
I got that example from Goodbye CoffeeScript, Hello TypeScript
The compiler is so loose that it only cares that blocks are indented different from the parent.
if zeroSpaces
if oneSpace
if threeSpaces
if sevenSpaces
undefined # 11 spaces
When you chain calls together CoffeeScript silently consumes whitespace.
$('body')
.addClass('k')
.removeClass 'k'
.animate()
.hide()
You might think those calls are indented, but technically they aren't. CoffeeScript simply consumes that indentation forcing CoffeeLint to need special hacks to lint that code.
"Safety" is a hilarious claim. Your variables are actually far less safe
because they are implicitly declared. how many times have you had a path = require('path')
in your code, and then somewhere you have a loop that operates
on a file path, so you called the variable path
? I'd have a rule catch this
for you, but as explained above, you can't build one properly out of the AST.
There is a 3rd party rule that attempts this, but it's incomplete.
scope1 = ->
# bar is created here in scope 1 thanks to variable hoisting
scope2 = ->
# foo is created here in scope 2
foo = "foo"
scope3 = ->
console.log(bar)
bar = foo # outermost foo reference. Doesn't create the variable
# outermost bar assignment does create the variable
Everything being an expression did seem nice when I used it. Implicit returns
for any multi-line function is just a bad idea though. You can never really tell
if a function needs to return something or not, because it will either way. I
have had times where I had to go track down all the callers of a function to
figure out if any of them were using the return value or the return value was
accidental because I didn't opt out of it with an explicit return undefined
It's an interesting idea, but I never found it useful. I tried to use it a few times, but it didn't really make sense in any project I ever worked on.
These seem nice, but are easily reproducible with Array.prototype.map and Array.prototype.filter.
These are nice.
You get to write and
instead of &&
! I'm not really impressed. This is kind
of nice sometimes, but it's not significant.
I want this in JavaScript.
healthy = 200 > cholesterol > 60
This would be nice, but since we don't have it you just repeat the inner variable.
const healthy = 200 > cholesterol && cholesterol > 60
Thank you for writing this!