- Rename your file extension from
foo.coffee
tofoo.js
- Open http://js2.coffee/ & paste the entire contents
foo.js
into the CoffeeScript side - Paste the entire contents of the output back into your
foo.js
- Rebuild the app, functionality should be unchanged & there should be no JS errors in the console
- If necessary, convert AMD (requirejs) style modules to Browserify (commonjs) style, as described below.
- Refactor the “CoffeeScript magic”, using examples below
- If this conversion is being done at the same time as a feature, submit a PR using the interstitial PR process
An AMD style module looks like this:
define(function(require, exports, module) {
var foo = require('foo');
var MyModel = Backbone.Model.extend(_.extend({
/* ... */
}, foo);
return MyModel;
});
To convert to commonjs style, remove the surrounding define
and assign the export to module.exports instead of returning it. It should end up looking about the same as modules in serverside projects.
Be aware that some of our AMD style modules are currently getting away with using certain dependencies (like jquery or underscore) without requiring them. This will not work once the modules are converted, so require statements may need to be added in.
var _ = require('underscore');
var foo = require('foo');
var MyModel = Backbone.Model.extend(_.extend({
/* ... */
}, foo);
module.exports = MyModel;
Once everything is commonjs style, we can remove more magic from our build process so it's closer to using vanilla browserify.
Disclaimer: This file is not a JavaScript style guide. Reference the following files for coding style & principles:
class CoreProduct extends Product
initialize: ->
super
This is what would be output from js2coffee
var CoreProduct,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
CoreProduct = (function(superClass) {
extend(CoreProduct, superClass);
function CoreProduct() {
return CoreProduct.__super__.constructor.apply(this, arguments);
}
CoreProduct.prototype.initialize = function() {
return CoreProduct.__super__.initialize.apply(this, arguments);
};
return CoreProduct;
})(Product);
// ---
// generated by coffee-script 1.9.2
We can change from CoffeeScript's classes to Backbone's classes, like so
var CoreProduct = Product.extend({
initialize: function() {
// we also change from using from `CoreProduct.__super__` to `Product.prototype`
return Product.prototype.initialize.apply(this, arguments);
}
});
_ = require('underscore')
Product = require('models/product')
TerminalMixins = require('presenters/mixins/terminal')
This is what would be output from js2coffee
var Product, TerminalMixins, _;
_ = require('underscore');
Product = require('models/product');
TerminalMixins = require('presenters/mixins/terminal');
// ---
// generated by coffee-script 1.9.2
We would move the variable declarations to the same line that they are initialised, like so
var _ = require('underscore');
var Product = require('models/product');
var TerminalMixins = require('presenters/mixins/terminal');
_hasFlightCode: ->
@get('flights')?.out?.flight?.code
This is what would be output from js2coffee
_hasFlightCode: function() {
var ref, ref1, ref2;
return (ref = this.get('flights')) != null ? (ref1 = ref.out) != null ? (ref2 = ref1.flight) != null ? ref2.code : void 0 : void 0 : void 0;
}
// ---
// generated by coffee-script 1.9.2
This is fairly unreadable, we would remove the temporary ref
variables and do the following.
_hasFlightCode: function() {
var flights = this.get('flights');
if(!flights || !flights.out || !flights.out.flight || !flights.out.flight.code)) {
return;
}
return flights.out.flight.code;
}
Note: this particular example is excessively complicated. Ideally, if we're having to check a property that's nested this deeply then the checks should be pushed further upstream, such as a flight model in this case.
parent: ->
@someMethod (err, result) =>
@foo() # Executed in context of parent
This is what would be output after the conversion:
parent: function() {
return this.someMethod((function(_this) {
return function(err, result) {
return _this.foo(); // Executed in context of parent
};
})(this));
}
parent: function() {
var self = this;
this.someMethod(function(err, result) {
self.foo(); // Executed in context of parent
});
}
func = ->
doSomething()
This is what would be output after the conversion:
var func;
func = function() {
return doSomething();
};
With coffeescript the last statement of a function is always returned even if not required.
The first thing to do here is to check how this function is called. It could be called from many places so it's best to perform a search of the code base for the method name.
If the callers expect a return value from the function please keep the return
keyword, if not please remove it to avoid confusion.
There are occasions when you will come across static methods or properties that are attached directly to the class itself.
class Foo extends Bar
@staticBaz = ->
...
This is what would be output after the conversion:
Foo = (function(superClass) {
...
Foo.staticBaz = function() {};
...
})(Bar);
// ---
// generated by coffee-script 1.9.2
};
We can use the optional classProperties
parameter in Backbone's extend function to achieve the same thing.
var Foo = Bar.extend({
...
}, {
staticBaz: function() {
...
});
Please check all comments in the source coffeescript as converters such as http://js2.coffee/ will not convert comments and they will be removed in the Javascript output.
Tripapp does not have many comments but those that are there normally indicate particular problem areas or complex logic which may reference JIRAs etc.
If the comment is still relevant after the conversion please make sure the comment is kept on the appropriate place.
https://gist.github.com/jackdcrawford/a8e5dacf6cecdb9b6bfe
The process for converting CoffeeScript to JavaScript at the same time as writing features
- Create a new “interstitial” branch, based on the project’s master branch
- Create a “code conversion” branch, based on the “interstitial” branch
- Create a “feature” branch, based on the “code conversion branch”
- Both “code conversion” and “feature” can then be merged into “interstitial”, removing the need for two pull requests to be tested