
Show HN: Decaffeinate converts CoffeeScript projects to modern JS - alangpierce
https://github.com/decaffeinate/decaffeinate
======
blitmap
I don't understand the trend to move from Coffeescript to ES6. I like both,
but I write Coffeescript for its concision. I have no issue compiling
Coffeescript to ES6 as a separate build step, but I'm not going to write ES6
myself. That said, I'm sure a project like Decaffeinate has brought a lot of
beloved Coffeescript projects into the hands of ES6 developers who wanted to
contribute.

~~~
yojo
ES6 has eliminated most (though not all) of the pain Coffeescript solved. I
work on a large code base that went from Coffeescript -> Typescript (mostly
manual, some decadfeinate), and typesafety has been a huge boon for our
developers. For smaller teams/codebases, you might be happy sticking with
Coffeescript.

~~~
xiaoma
I see ES6 or especially Typescript as essentially a recreation of ES4/Flash
AS3, with some renaissance-era Javascript footguns attached. You've got
classes, various class based OO features, optional typing major industry
support, probably improved performance coming soon, etc...

Coffeescript was a bit different. It meant _fewer_ lines of code, not more. It
had significant whitespace and some other Python-inspired idioms like array
comprehensions and object comprehensions. Most importantly, Coffeescript was a
new language and didn't have to re-implement deleterious Javascript features
like the == vs === distinction, undeclared vars leaking to global scope,
baroque keywords like "with", etc...

That said as others have pointed out, ES6 did borrow _many_ good features from
CS and CS is slowly dying. Fortunately other languages like Elm and, to a
lesser degree, Reason have offered even more compelling "footgun free"
alternatives.

~~~
masklinn
> It had significant whitespace and some other Python-inspired idioms like
> array comprehensions and object comprehensions.

Modern JS has standard HoFs (on arrays) and an iteration protocol.

> undeclared vars leaking to global scope

Strict mode (which syntactic ES6 features enable by default e.g. class
contents are in strict mode always).

> baroque keywords like "with"

Which can trivially be linted away.

> ES6 did borrow many good features from CS

Not really from CS, rather from similar (much older) sources CS got them from.

~~~
xiaoma
HoFs are not array comprehensions and array comprehensions aren't supported in
ES6.

[https://gist.github.com/sleepyfox/1879845](https://gist.github.com/sleepyfox/1879845)

[https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Operators/Array_comprehensions)

~~~
masklinn
> HoFs are not array comprehensions and array comprehensions aren't supported
> in ES6.

Neither of these is a claim I made so I'm not sure why you feel the need to
point these out.

However native HoF obviate much of the need for comprehensions, especially
combined with terser "arrow functions". Languages like Smalltalk or Ruby do
just fine without comprehensions for instance.

~~~
maxscam
If you quote the fact that coffeescript has significant whitespace, list
comprehension and then in response say that javascript has HoF and iteration
protocol, it does sound like youre trying to counter the point, so it seems
like for someone to bring up the fact that they are not identical is fair
game.

------
matthewmacleod
Mixed feelings about CoffeeScript, as ever.

ES6 obviously deals with some of the issues that CoffeeScript was intended do.
That's awesome. Unfortunately, it's still difficult to go back to what seems
like the relatively baroque syntax of Javascript afterwards, as someone who
has a bit of a vendetta against over-syntax. SO MANY BRACKETS.

In particular, there was CJSX - JSX with CoffeeScript. Writing React
components using it was an absolute joy in comparison, and helped eliminate a
lot of noise – making it much more obvious what a component was doing.

But yeah, it's been hard to convince others that it's worth the investment,
when honestly it's really just a personal preference at this stage.

~~~
s_kilk
> SO MANY BRACKETS.
    
    
                                    })
                                  })
                                })
                              })
                            })
                          })
                        },
                      })
                    })
                  })
                })
              })
            })
          },
    

Whatever do you mean?

~~~
ulucs
Promises returning promises (or async/await which is just sugar-coating for
promises) solves this very cleanly in my experience

~~~
horsawlarway
I agree. Promises have solved most our team's nesting issues.

It's incredibly clear, allows clean and concise error handling, and is easy to
use.

------
andrew_wc_brown
I love CS and I see myself using it for another 5 years. The aversion
developers have had to CS never were legit reasons. If you put all your time
in ES6 and CS, I think you'll be more productive in CS.

The only issue I foresee is tools being built around the use of ES6 that
compel you to abandon it.

I've learned that less is better. I don't use JQuery, I don't use Lodash. I
don't use React, I don't use ImmutableJs, I don't use Webpack or CommonJS. All
of these tools are more a burden that a blessing and you just end up stacking
on 100 dependieces asking "Is this really better?"

I learned this lesson with Cucumber/RSpec/Caybara and etc.. I started asking
why I had to use these over plain old TDD and so I used TDD for a month and I
found out everything was totally fine.

I don't even really use Arel in ActiveRecord. I just write raw SQL that serves
JSON directly back. I made it easier for me to organize SQL in partials just
like views and to injects variables and conditions into my SQL.

I went to great lengths to evaluate all these tools because as a work-form
home contractor I can afford the time, and the "what if" really bothers me.

I still working in Rails with Sprockets and CS and I write all my SQL by hand.

Less is Better.

All these kids want to do a bunch of busy work and for no good reason. It
makes the feel productive.

I have tried dropping CS but the difference in productive was too great to
drop. Its not that I'm married to CS its that it does what it says. It makes
your JS concise so you can be more productive.

I felt AngularJS dying and so I spent 3 months researching React and building
client apps in React. I just didn't get all the work extra work and settled on
Mithril.

The hardest thing was giving up writing HTML-like templates like I did in
AngularJS but I remember that was my first aversion to AngularJS where I
swallowed the medicine. I had to swallow more medicine to unlearn that.
Mithril paird with CoffeScript makes writing markup in CS a joy. If I had to
do that in regular JS I could see why people would be compelled to use ugly
JSX.

~~~
naiyt
> All these kids want to do a bunch of busy work and for no good reason. It
> makes the feel productive.

This is pretty patronizing. Not everybody who uses these tools are "kids who
just want to do busy work". In fact, I'd argue that "kids" (or more
accurately, "new developers") are the least likely to want to do busy work, as
they'd prefer to be able to build stuff fast. (And for the record, I feel like
I suddenly started doing a whole lot LESS busy work once I got decent with
React.)

I generally agree that "less is better", until you have to start re-inventing
solved problems.

> I learned this lesson with Cucumber/RSpec/Caybara and etc.. I started asking
> why I had to use these over plain old TDD and so I used TDD for a month and
> I found out everything was totally fine.

RSpec is just a testing framework though? It's not something you chose instead
of TDD...

~~~
cookiecaper
>I generally agree that "less is better", until you have to start re-inventing
solved problems.

I think the takeaway is that _before_ you assume you have an "unsolved
problem" due to all the buzz/hype around $HOT_LIB, study a little more, try it
with the upstream, make sure it's really necessary before you get a tortured
contrivance of a stack in place. Consider the cost of adding more dependencies
and whether the functionality you're getting from the third party lib is
really worth it.

Let's be honest, the core problems you're likely to run into on any given day
are probably "solved" pretty reasonably by any language that's at the
appropriate level for the problem space and has seen significant use over the
last 10 years. You're more likely to be reinventing the wheel by pulling in
$COOL_GITHUB_PROJECT than you are by taking a few more lines to implement with
the stdlib.

------
kvz
Shameful plug: [http://invig.io](http://invig.io) is a a wrapper around
decaffinate, lebab, prettier, eslint, to to squeeze more modernness out of
legacy js/coffeescript codebases and make them standardjs.com compliant

~~~
nailer
That sounds excellent (especially if it supports await!). Slightly off-topic:

I love standardjs.com. It takes an enormous amount of chutzpah to look at
jQuery style guidelines, idiomatic.js, AirBnB and every previous JavaScript
style guide and declare the name of your own new thing is `${standard}
${languageName}`

~~~
positivecomment
"Standard" (the name is _very_ hard to tolerate) not being configurable with
questionable defaults (no semicolons!) and being JS-only (no TS or CSS
support) makes Prettier[1] a better option. It has a VS Code extension which
works great.

[1]:
[https://github.com/prettier/prettier](https://github.com/prettier/prettier)

~~~
nailer
> Prettier parses your JavaScript into an AST (Abstract Syntax Tree) and
> pretty-prints the AST, completely ignoring any of the original formatting*.
> Say hello to completely consistent syntax!

That's excellent. Thanks for the recommendation.

------
nickm12
CoffeeScript has some nice feature, especially as compared to ES5, but I
always found it a tad too clever. The fact that {} are optional in a bunch of
places makes it very difficult for me to parse mentally.

Other weird coffeescript quirk: "x isnt y" is not semantically to "x is not
y".

~~~
jononor
People overuse {}-free object literals in CoffeeScript. In function
invocations, and other in-line cases, use {}!

------
abritinthebay
Oh thank god.

CoffeeScript was an important crutch and stepping stone but we're maybe 2
years past where it has been past it's prime and this will make updating and
improving legacy projects a lot easier.

Personally was never a fan of it - it used too many Ruby idioms for my taste
and produced noisy code that was a pain to debug (at the time) - but it did
spur the development & adoption of other, better, systems (ESNext
transpilation, TypeScript, etc)

------
AriaMinaei
I did an experiment about a year ago when I re-wrote part of LiveScript's code
emitter to produce a babel-compatible AST rather than javascript code. It
would then pipe the AST to babel's generator and produce the final JavaScript.
The benefit was that I could then use any babel-compatible toolset and still
write code in LiveScript. ESLint worked with almost no tweaks necessary.
Theoretically, you could also use Flow and TypeScript with this approach.

Anyway, I didn't have enough time to finish the project, but somebody took the
idea and did most of the work [0].

[0] [https://github.com/dk00/livescript-
next](https://github.com/dk00/livescript-next)

Would love to explain more, but have to board the plain now :) These links
might help:

[https://github.com/gkz/LiveScript/issues/821](https://github.com/gkz/LiveScript/issues/821)
[https://github.com/facebook/flow/issues/1515](https://github.com/facebook/flow/issues/1515)

------
fiatjaf
This thing made me use React in 2013 (when JSX was still weird):
[http://blog.vjeux.com/2013/javascript/react-
coffeescript.htm...](http://blog.vjeux.com/2013/javascript/react-
coffeescript.html)

Now JSX is not weird anymore, it's just horrible.

------
rattray
The folks behind this have been at it a long time. I first used decaffeinate
over 18mo ago and while it was already quite helpful then, it's come a
tremendous distance since. Very tricky problem to solve well; bravo!

Somewhat surprised nobody has mentioned the upcoming release of CoffeeScript
2.0, which outputs ES6 natively:
[http://coffeescript.org/v2/](http://coffeescript.org/v2/)

It's​ in beta2 now and ready for trial.

------
dyeje
Never understood the appeal of CS, JavaScript just isn't painful enough to
warrant it. Been happy to watch it's slow exit from the mainstream.

[https://trends.google.com/trends/explore?q=coffeescript](https://trends.google.com/trends/explore?q=coffeescript)

~~~
scotty79
Meaningful indentation. I love python and I'm really not sure why other
compilers/interpreters ignore indentation since it's the de facto way of how
people express structure in any computer language.

Also when CS happened, JS didn't even have classes. Half of the ES6 features
are CS inspired.

~~~
abritinthebay
> Also when CS happened, JS didn't even have classes.

And it only has class _syntax_ now. Classes in JS are still prototypical
inheritance under the hood (and probably always will be).

> Half of the ES6 features are CS inspired.

This is rewriting history. Most of ES6 was under active development before CS
was a thing. It's more accurate to say that the same modern languages features
inspired both (with ES6 thankfully being less Ruby-like).

------
bayesian_horse
CoffeeScript is still relevant to me, mainly because of the sparser syntax.
For React, I wrap the components in functions to create syntax that is even a
little clearer than JSX, but still valid CoffeeScript.

I am still waiting for someone to finish a Python bytecode VM or transpiler
for javascript which is actually worth using...

~~~
molszanski
> waiting for someone to finish a Python bytecode VM or transpiler for
> javascript which is actually worth using...

What is that? Running JS in a python VM?

~~~
bayesian_horse
No, by that I mean a VM to run Python bytecode, running in the browser. There
is at least one project which tries to do that, but it's not really useable.

The point would be to have access to a large part of the standard library, the
full Python data model and a good integration with the javascript platform.
Only then would there be a reason to use it.

~~~
molszanski
Oh. Now I get your point.

------
patrickbolle
Oh how the tables have turned

------
tyrw
I'm assuming "modern JavaScript" means ES6. Has anyone used this, and does it
produce readable code?

~~~
alangpierce
Yeah, "modern JS" is meant to be the catch-all term for
ES6/ES7/ES2015/ES2016/ES2017. In this case, you need ES2016 to run code
produced by decaffeinate, but anything in the latest language version or at
stage 4 is fair game and might be added in the future.

Producing readable code is certainly the intent! Realistically, you need to
clean some things up by hand to make it pass lint and look nice, but I think
overall it does a pretty good job, certainly much better than the CoffeeScript
compiler. You can always play around with it by typing/pasting code into the
online repl: [http://decaffeinate-project.org/repl/](http://decaffeinate-
project.org/repl/)

~~~
uranian
You mean only ES2016 from that list?

> Producing readable code is certainly the intent!

This is hilarious! Transpiling from the most readable JS alternative to ES6?
Then you need to clean up some things, making it ready for yet another
transpiler called Babel? And then everything is OK?

Show me how you are going to make this pretty:

    
    
      if does.that?.prop?.exist then console.log 'yes, no crash!'

~~~
alangpierce
> You mean only ES2016 from that list?

No, I was intentional about my description. People tend to be informal when
talking about JS versions, and all five of those terms might be used to
describe decaffeinate's output. IMO it's best to just say that decaffeinate
produces JavaScript, and not any particular old version of JavaScript.

> Show me how you are going to make this pretty

Soak operations (anything with a question mark) are probably the hardest to
convert to JavaScript, especially when they're chained. Here's what
decaffeinate gives today:

[http://decaffeinate-
project.org/repl/#?evaluate=true&stage=f...](http://decaffeinate-
project.org/repl/#?evaluate=true&stage=full&code=if%20does.that%3F.prop%3F.exist%20then%20console.log%20'yes%2C%20no%20crash!')

And here's an open bug with thoughts on making those look better:
[https://github.com/decaffeinate/decaffeinate/issues/336](https://github.com/decaffeinate/decaffeinate/issues/336)

But many people have still found decaffeinate useful since the vast majority
of real-world CoffeeScript (at least from what I've seen) tends to convert
pretty well, and even in difficult cases, it's better to safely get it in
JavaScript first and then clean it up than to rewrite everything by hand.

~~~
uranian

      if (__guard__(does.that != null ? does.that.prop : undefined, x => x.exist)) { console.log('yes, no crash!'); }
      function __guard__(value, transform) {
        return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
      }
    

oh my..

This was 1 line of CS, how to clean this up without a chance of creating bugs?

How on earth is ESxxx an improvement on CS anyways? It's just not true, it's a
leap back.

------
treyhuffine
It's going to be very interesting to see if Atom decides to fully make the
move to JavaScript / ES6+

~~~
pier25
CS is one of the main reasons I haven't contributed more to Atom.

I guess when Atom was started it made sense to use CS, not anymore. Hopefully
Github will switch to ES6/7 or TS.

~~~
gabrielcsapo
also this

------
foxhedgehog
Wasn't JA updating CS again recently? Has anybody tried to use it after using
ES6/7?

~~~
johnfn
Hasn't the community (finally) learned that simply transposing a better syntax
onto JavaScript does not solve or help programmers in a meaningful way like
the way that static typing does? Or is me hoping that a community will
collectively reach a realization like this a pipe dream?

~~~
gmac
Patronizing? I've mostly switched to TypeScript from CoffeeScript for the time
being, because types are indeed a huge boon, but a better syntax is
meaningfully helpful and my preference any day would be for some merger into
typed CoffeeScript.

~~~
johnfn
Yeah, I guess TypedCoffeeScript would be better than just one or the other.

~~~
rpeden
For what it's worth, I've found that F# has a similar 'feel' to what I'd
expect from a typed CoffeeScript. I'm not sure exactly why; it's probably just
the look and shape of the code, along with the lack of brackets.

Writing React apps in F# using Fable is pretty nice, too.

------
leeoniya
how is this different than running the compiled CS output through lebab [1]?

[1] [https://github.com/lebab/lebab](https://github.com/lebab/lebab)

~~~
alangpierce
You lose a lot of information going from CS -> ES5 -> ES6, so decaffeinate has
an inherent advantage since it knows your original code. With decaffeinate, it
converts classes to classes, destructures to destructures, array spread to
array spread, etc. Also, the CS output destroys all inline comments and all
formatting.

------
grogenaut
In 3-5 years will we get "FreeRange" to convert typescript to "modern"
JavaScript?

------
aphextron
I love this. We've truly come full circle. Does it support a compile to
Typescript step?

~~~
alangpierce
bulk-decaffeinate ( [https://github.com/decaffeinate/bulk-
decaffeinate](https://github.com/decaffeinate/bulk-decaffeinate) ) supports an
outputFileExtension option that can make it so the resulting files end in .ts.
But really, the main thing to do when converting JavaScript to TypeScript is
adding types, which decaffeinate is much less qualified to do. But if you're
going to move from CoffeeScript to TypeScript, JavaScript is probably a pretty
important intermediate step.

------
thebouv
Remember when CoffeeScript was "modern JS"?

The whole JS stack switches every 2 years or so?

I mean, it actually switches faster than that and that's what everyone jokes
about (JavaScript fatigue and what not). But a full turnover of the stack is
about 2 years?

------
YamiOdymel
People might not know but CoffeeScript 2 is out already and supports the ES6
syntaxes. I've been using it for a large Vue.js SPA (with webpack) and some
small projects, it really improves the development and saving me a lot of the
time.

[https://github.com/coffeescript6/discuss](https://github.com/coffeescript6/discuss)

------
scotty79
Last time I tried it did not support cjsx -> jsx, as far as I can tell, so
it's useless for migrating React code bases.

~~~
alangpierce
Take a look at depercolator, it's a wrapper around cjsx-transform,
decaffeinate, and some other things:

[https://github.com/bugsnag/depercolator](https://github.com/bugsnag/depercolator)

There's an open decaffeinate bug for CJSX (
[https://github.com/decaffeinate/decaffeinate/issues/1009](https://github.com/decaffeinate/decaffeinate/issues/1009)
) but I suspect it'll be really hard to do anything better than just running
cjsx-transform and then running regular decaffeinate.

To get JSX on the JS side after decaffeinate, you can use the create-element-
to-jsx script from [https://github.com/reactjs/react-
codemod](https://github.com/reactjs/react-codemod) .

------
partycoder
This is great. I had a hard time dealing with CoffeeScript code. CoffeeScript
tools are behind the state of the art in JavaScript.

~~~
Hydraulix989
This is a cautionary tale for developers that flock to the flavor-of-the-month
tech (CoffeeScript used to be all the rage in 2013!), only to find that the
mature tech not only caught up but completely eclipsed it in 2017.

~~~
josephg
‎Jeremy Ashkenas (author of coffeescript) helped bring es6 up to par. You can
see the influence of coffeescript all over es6 in the class syntax, string
interpolation and destructuring.

It would be wrong to say that the investment we (as an industry) made in
coffeescript was a waste. Far from it.

~~~
masklinn
> ‎Jeremy Ashkenas (author of coffeescript) helped bring es6 up to par.

By spending basically the entire runup to it poo-pooing it?

> You can see the influence of coffeescript all over es6 in the class syntax,
> string interpolation and destructuring.

Yeah it couldn't be the dozens of languages which have been doing that since
the 80s and which browser developers were already mining for extensions no
sir, Coffeescript invented having a class keyword (which is why it was already
a reserved keyword in JS), invented string interpolation which it lifted
directly from Ruby (according to its own doc) and from which JS string
templates diverge both syntactically and semantically, and invented
destructuring (and will I'm sure soon invent pattern matching).

------
baby
Can you give an example of a CoffeeScript file being transcribed to modern JS
:)? I just want to see which syntax I prefer.

~~~
alangpierce
Sure, here's src/main-process/atom-protocol-handler.coffee from the atom
codebase (it's a really long URL because it includes the whole source code):

[http://bit.ly/2rIhFD4](http://bit.ly/2rIhFD4)

In this case, the __guardMethod__ usages should probably be rewritten by hand
after running decaffeinate.

In general, you can paste any code into the left side of [http://decaffeinate-
project.org/repl](http://decaffeinate-project.org/repl) to get output on the
right side.

------
traviswingo
Doesn't Coffeescript convert Coffeescript to JS? `coffee -c input.coffee`.

~~~
slig
Yes, but it's not "modern JS" and it's not meant to be edited.

This tool converts it to modern JS (ES6) that has classes, template strings,
etc.

------
jlebrech
coffeescript is best for doing a specific thing on a page. for spa it's
different.

