Code Monkey home page Code Monkey logo

js2coffee's Introduction

js2coffee

Compile JavaScript into CoffeeScript.

Status

This 2.0 release is a complete rewrite featuring a better parser (Esprima). See what's new in 2.0 for an overview of new features and consult the migration guide for info on updating from 0.x.


Install

Available on npm and bower.

npm install --global js2coffee
js2coffee --help

npm version

Also available via CDN (window.js2coffee):

https://cdn.rawgit.com/js2coffee/js2coffee/v2.1.0/dist/js2coffee.js


Command line

The command line utility accepts both filenames or stdin.

$ js2coffee file.js [file2.js ...]
$ cat file.js | js2coffee

JavaScript API

Available via npm (require('js2coffee')), or via CDN in the browser (as window.js2coffee):

result = js2coffee.build(source);

result.code     // code string
result.ast      // transformed AST
result.map      // source map
result.warnings // array of warnings

Errors are in this format:

catch (e) {
  e.message       // "index.js:3:1: Unexpected INDENT\n\n   3   var\n   ---^"
  e.description   // "Unexpected INDENT"
  e.start         // { line: 1, column: 4 }
  e.end           // { line: 1, column: 10 }
  e.sourcePreview // '...'
}

Warnings are in this format:

result.warnings.forEach((warn) => {
  warn.description   // "Variable 'x' defined twice
  warn.start         // { line: 1, column: 4 }
  warn.end           // { line: 1, column: 9 }
  warn.filename      // "index.js"
})

Docs

  • Migration guide - guide for migrating from 0.x.

  • Hacking guide - want to contribute? here are tips to get you started.

  • AST format - technical description of the CoffeeScript AST format.

  • Special cases - a list of edge cases that js2coffee accounts for.

  • Compatibility mode - list of tweaks that compatibility mode (--compat) addresses.

  • Goals - outline of the project's goals.

  • Specs - examples of how JavaScript compiles to CoffeeScript.


Thanks

js2coffee © 2012+, Rico Sta. Cruz. Released under the MIT License.
Authored by Rico Sta. Cruz with help from co-maintainers and contributors (list).

Maintainers:

js2coffee's People

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

js2coffee's Issues

multi-line/block comments

Since CS preserves block comments when compiling to JS, it's only fair to make you compile multi-line comments (/* comment */) to CS block comments ;).

converting ({..}) to (..)

app.use(express.session({ secret: 'keyboard cat' }));
to
app.use express.session(secret: "keyboard cat")

recognize integer/float ranges

[1, 2, 3, 4, 5, 6, 7, 8, 9]

to

[1..9]

I'd say anything that omits 2 or more values would be better compiled as a range. So, [4, 5, 6, 7] but not [4, 5, 6].

converting \x1b to \u001

Converting

app.use(express.logger('\x1b[33m:method\x1b[0m \x1b[32m:url\x1b[0m :response-time'));

to

app.use express.logger("\u001b[33m:method\u001b[0m \u001b[32m:url\u001b[0m :response-time")

compile `!!` to `!!` instead of `not not`

not not is not a common CS idiom. When !! behaviour is desired, I believe CS users (or at least myself) usually prefix !!. It's almost like it is treated as a separate Boolean-conversion operator.

Document conversion gotchas

Whenever folks asked me in the past why there wasn't a JS-to-CoffeeScript converter, my go-to answer was: "Well, what about ==? CoffeeScript doesn't have an equivalent. It only has ===. And what about scope? You can't have two variables with the same name in two different (nested, non-global) scopes, and you can't directly modify globals."

js2coffee takes the simplest approach: a == b is converted straight to a == b, and c = d is converted straight to c = d. But these are very different statements. The first might have the same behavior in practice, but the latter definitely won't, since c is window.c or global.c in one language and var c in the other.

So at the very least, I hope you'll put mention of some of these gotchas in the README, and encourage thorough testing of converted code. You could also go a step further by adding comments to the output, e.g. when you put in

a = b;

the output could be

a = b  # warning: original JS may modify a global

unless, until, loop

  • if(!X) y to unless X then y
  • while(!X) { Y } to until X then Y
  • while(<anything-constantly-truthy>) X to loop then X
  • do { X } while(Y) should make use of loop keyword instead of doing while true

Those constantly truthy values can be true, 1, ... however deep you want to follow that rabbit hole.

`not in` and `not instanceof`

The very common pattern

if(!(a instanceof A)) b;
if(!(a in A)) b;

can compile to

if a not instanceof A then b
if a not of A then b

Javasript "in" should? convert to coffescript "of"

Javascript:

if (foo in bar) {
  console.log(bar[foo])
}

Converts through Js2coffee on the interactive web client on Chrome Unbuntu to:

if foo in bar
  console.log bar[foo]

if you convert that coffeescript back into javascript via the coffescript compiler you get:

var __indexOf = Array.prototype.indexOf || function(item) {
  for (var i = 0, l = this.length; i < l; i++) {
    if (this[i] === item) return i;
  }
  return -1;
};
if (__indexOf.call(bar, foo) >= 0) {
  console.log(bar[foo]);
}

This does not always behave appropriately.

On a bigger note, considering how frequently people in regular javascript use the "in" operator, whey did coffescript change the semantic meaning of it!

try/catch simplification

You don't need to compile empty catch blocks.

try{ X } catch(Y){ /* nothing */ }
try X

for any Y, as long as the block is empty.

Handling standalone @

It would be ideal, I think, if standalone references to this remained as this, instead of becoming @ ... to avoid generating code like this:

unbind: (ev, callback) ->

  ... [more code goes here] ...        

      while i < l
        if callback is list[i]
          list.splice i, 1
          break
        i++
  @

chained comparisons

20 < X && X < 30 to 20 < X < 30
20 < X && Y == Z && X < 30 to 20 < X < 30 and Y is Z

You can extrapolate from there.

Rethink the the parser/compiler architecture

Currently, the procedures that translate a Node into a string (for (x;y;z) { a } => x; while y; a; z) is tied too much into the procedures that transform a Node into another Node (if typeof x != 'undefined' => unless x?). I think they should be decoupled to make more optimizations easier and more maintainable.

Currently, js2coffee works like this:

  1. Ask Narcissus to build a tree of the source JS
  2. Js2coffee's Builder traverses this tree and builds a CS source from it

It should be more like:

  1. Ask Narcissus to build a tree of the source JS
  2. Transform this tree by traversing it and making optimizations along the way (+++)
  3. Js2coffee's Builder traverses this new tree and builds a CS source from it

During that transformation step, that's when the Coffee'isms like the following should be made:

  • ifs should be made unless when the condition can be negated simply
  • typeof x == 'undefined' should be made into existential checks
  • switch nodes should be ensured that there aren't unnecessary break statements
  • when A: when B: should be consolidated to a compound when A, B:
  • etc...

Doesn't support JSON files

Passing the following for instance returns an error in the web app:

{
    "name": "buildr",
    "version": "0.2.9",
    "description": "The JavaScript Project Buildr. Built with Node.js and used by Aloha Editor and History.js",
    "homepage": "https://github.com/balupton/buildr.npm",
    "keywords": [
        "javascript",
        "buildr",
        "package",
        "compile",
        "compress",
        "minify",
        "bundle"
    ],
    "author": {
        "name": "Benjamin Lupton",
        "email": "[email protected]",
        "web": "http://balupton.com"
    },
    "maintainers": [
        {
            "name": "Benjamin Lupton",
            "email": "[email protected]",
            "web": "http://balupton.com"
        }
    ],
    "contributors": [
        {
            "name": "Benjamin Lupton",
            "email": "[email protected]",
            "web": "http://balupton.com"
        }
    ],
    "bugs": {
        "web": "https://github.com/balupton/buildr.npm/issues"
    },
    "licenses": [
        {
            "type": "MIT",
            "url": "http://creativecommons.org/licenses/MIT/"
        }
    ],
    "repository" : {
        "type" : "git",
        "url" : "http://github.com/balupton/buildr.npm.git"
    },
    "dependencies": {
       "uglify-js": ">=0.0.1",
       "less": ">=0.0.1",
       "jshint": ">=0.0.1"
    },
    "engines" : {
        "node": ">=0.4.0"
    },
    "directories": {
        "lib": "./lib/buildr"
    },
    "bin": {
        "buildr": "./bin/buildr.js"
    },
    "main": "./lib/buildr"
}

This is useful for https://github.com/balupton/cson.npm

Praise!

Fantastic work dude! Really really awesome stuff.

tab size control

Is there a way to change tab size from 2 to, say, 4 using cmd-line paramter?

Escaping #{}'s

"#{a}" in JS compiles into the same thing in CS, which of course is erroneous. It should be "\#{a}".

destructuring in parameter lists

Looks like you almost support destructuring, since (function([a]){}) compiles, but it's not the ([a]) -> that I would expect.

Converting %d to NaN

I am trying to convert some Node.js side script to coffeescript.
console.log("Listening on port %d",3000);

converting to
console.log "Listening on port NaN", 3000

Is it expected behavior?

compile existence checks to existential operator

Any comparison to null can be compiled to a unary postfix existential operator. It should also accept the case when this comparison is Boolean OR'd with a typeof X == "undefined" check.

Some examples:

X == null
X === null || X === void <anything-without-side-effects>
typeof X == "undefined" || X == null
typeof X === "undefined" || X == null
typeof X == "undefined" || X === null || X === void <same>

and so on....

update: negated existence checks as well:

X != null
X !== null && X !== void <same-as-above>

and so on...

disallow reserved words as identifiers

The complete list of reserved words in CoffeeScript can be found at require("coffee-script").RESERVED.

coffee> CoffeeScript.RESERVED
[ 'case',
  'default',
  'function',
  'var',
  'void',
  'with',
  'const',
  'let',
  'enum',
  'export',
  'import',
  'native',
  '__hasProp',
  '__extends',
  '__slice',
  '__bind',
  '__indexOf',
  'true',
  'false',
  'null',
  'this',
  'new',
  'delete',
  'typeof',
  'in',
  'instanceof',
  'return',
  'throw',
  'break',
  'continue',
  'debugger',
  'if',
  'else',
  'switch',
  'for',
  'while',
  'do',
  'try',
  'catch',
  'finally',
  'class',
  'extends',
  'super',
  'undefined',
  'then',
  'unless',
  'until',
  'loop',
  'of',
  'by',
  'when',
  'and',
  'or',
  'is',
  'isnt',
  'not',
  'yes',
  'no',
  'on',
  'off' ]
coffee> 

`::` prototype operator

Expressions like A.prototype.b should compile to A::b and A.prototype should compile to A::.

Can't return object literals

I've found that

function ok() {
if (!true) {
return {a:0, b:2};
}
}

will be rendered as

ok = ->
if not true
return
a: 0
b: 2

which is not valid coffeescript.

JS line comments don't parse

The following JS yields SyntaxError: Illegal Token:

a = b; // foo

as does simply

// bar

/* ... */-style comments seem to work fine.

value == null

compiles to value? which is the opposite meaning.

value = null;

if (value == null) console.log('null');
else console.log('not null');

compiles to

value = null
if value?
console.log "null"
else
console.log "not null"

which has the opposite effect.

Invocation into the parens

With JSLint it is required to: "Move the invocation into the parens that contain the function."

(function ($) {
   $.fn.test = 1;
}(jQuery));

so this code is not converted correctly.

trace `return`s

Umm, the code example should explain this best.

function a(b, c){
  if(b) {
    return 2;
  } else {
    if(c) return 3;
    else return 4;
  }
}

should be able to compile to

a = (b, c) ->
  if b then 2 else (if c then 3 else 4)

be aware of implicit returns

In functions that don't return (implicitly return an undefined value), append a return to the coffeescript function body. Example:

This javascript:

function fn(){
  statement
}

compiles as:

fn = ->
  statement

should be:

fn = ->
  statement
  return

except in the uncommon case that statement is return, void X, or return void X where X is anything that the parser would actually allow there (so no special checks for side effects this time).

Error on variables with same name in overlapping scopes

It'd be cool if this were a syntax error:

var foo = bar;
func = function() {
  var foo = baz;
}

Something like "Cannot have variables named foo in overlapping scopes." The current compilation is, after all, going to yield very different behavior from the original.

Omit unnecessary returns

This as already being done, but could be improved.

function fn() {
  if (x) { return true; }
  else { return false; }
}

currently becomes:

fn = ->
  if x
    return true
  else
    return false

but should instead be:

fn = ->
  if x
    true
  else
    false

`do` instead of IIFEs

Immediately-invoked function expressions would look prettier if they were compiled using the prefix do operator instead. There may be some gotchas, though, I haven't fully thought it out.

Support for each + let

Not sure how this should even look like in CS. It should at least throw a meaningful error/warning.

for each (let {name: n, family: { father: f } } in people) {
  document.write ("Name: " + n + ", Father: " + f + "<br>\n");
}

failure on bare if followed by return

This little snippet causes a TypeError:

function bar() {
    if (1) return 1;
}

wrapping with curly braces or having having something other than a 'return foo' works.

Returning objects -- needs explicit braces

function asdf() {
    if (condition) {
        return {
            asdf: 1,
            asdf2: 2
        }
    }
    other_stuff()
    //function continues
}

compiles to

asdf = ->
  if condition
    return 
      asdf: 1
      asdf2: 2
  other_stuff()

which doesn't work, presumably because the "return" is processed and then CS doesn't know what to do with the dangling object.

It would work if it compiled like so:

asdf = ->
  if condition
    return {
      asdf: 1
      asdf2: 2
    }
  other_stuff()

So I suppose the rule is, if you have a return followed by an indentation, then it needs the explicit curly braces?

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.