I'm building an Ember.js application using Express.js as the backend. Right now, I load all my *.js files individually and store my Handlebars templates in my HTML file. I like to replace with a full-fledged "asset pipline" similar to the one in Rails. In a perfect world, this would support:
- Convert CoffeeScript to JavaScript.
- Pre-compile Handlebars templates using the Ember.js extensions.
- Concatenate and minify JavaScript and CSS (production only).
I've looked briefly at Require.js, connect-assets and convoy. The first two don't seem to offer any easy way to precompile the Handlebars templates, and the Ember convoy integration is based on an out-of-date version of Ember.
ember-runner hasn't been updated for a while. grunt-ember-templates looks like a reasonable way to compile Ember templates to a single *.js file, so that might be a building block of a larger solution.
Is there any Node.js asset compilation system which Just Works with Ember.js? I'd love to have a Node.js equivalent of ember-rails.
It's possible to build a very convenient system using nothing but connect-assets, grunt-ember-templates and a Gruntfile. First, we need to add the following configuration to Gruntfile.coffee:
grunt.initConfig
watch:
ember_templates:
files: 'assets/templates/**/*.hbr'
tasks: 'ember_templates'
ember_templates:
compile:
options:
templateName: (sourceFile) ->
sourceFile.replace(/assets\/templates\//, '').replace(/\.hbr$/, '')
files:
'assets/js/templates.js': 'assets/templates/**/*.hbr'
# Plugins.
grunt.loadNpmTasks('grunt-contrib-watch')
grunt.loadNpmTasks('grunt-ember-templates')
# Default task.
grunt.registerTask('default', ['ember_templates'])
Then, in our Express.js server configuration:
app.use require("connect-assets")()
In our index.html file, we need to add two lines in the appropriate places. These need be to processed through the Node.js template engine of our choice:
<%- css("application") %>
<%- js("application") %>
We then need to create assets/css/application.styl (which can use @import directives) and assets/js/application.coffee (which can use "#= require foo" comments).
To use this system, first run:
grunt
To keep the template.js file permanently up to date, run:
grunt watch
For everything else, see the connect-assets documentation. Note that I had more luck with Stylus than with Less, which appears to have issues with connect-assets at the time of writing.
Other tools which are maturing rapidly
Since I wrote this answer, I've come across several other good options that handle asset compilation in various ways:
- ember-tools doesn't support CoffeeScript or stylesheets (as far as I can tell), but it handles other asset compilation, and it seems quite popular.
- brunch.io handles a wide range of asset compilation tasks, including CoffeeScript and various CSS preprocesors. The most promising plugin appears to be brunch-with-ember-reloaded at the moment.
A good starting point example project using Grunt:
https://github.com/trek/ember-todos-with-build-tools-tests-and-other-modern-conveniences
I started working on a setup for using an Assetfile with an ember project, this is based on the peepcode tutorial and added the build tools, see: https://github.com/pixelhandler/peepcode-ordr
As for compiling coffee script this is an example doing that... https://github.com/OC-Emberjs/peepcode-ordr-test/blob/assetmanager/Assetfile
# Assetfile
require 'rake-pipeline-web-filters'
output "public"
input "js/tests" do
match "**/*.coffee" do
coffee_script
concat "tests.js"
end
end
# vim:ft=ruby
And precompiling the Handlebars templates like so...
# Assetfile
# See Getting Started readme
# - https://github.com/livingsocial/rake-pipeline/blob/master/GETTING_STARTED.md
# See Assetfile examples:
# - https://gist.github.com/dudleyf/1557811
# - https://github.com/ryanto/ryanto.github.com/blob/master/Assetfile
require "rake-pipeline-web-filters"
output "public"
class HandlebarsFilter < Rake::Pipeline::Filter
def generate_output(inputs, output)
inputs.each do |input|
# for sub-templates we can't really use '/' in a filename so using '__' instead, then replacing
name = File.basename(input.fullpath, ".handlebars").sub(/__/, "/")
output.write "return Ember.TEMPLATES['#{name}'] = Ember.Handlebars.compile(#{input.read.to_json})"
end
end
end
input "app/templates" do
match "**/*.handlebars" do
filter HandlebarsFilter
name = proc { |input| File.basename(input.fullpath, ".handlebars").sub(/__/, "/") + "_template" }
minispade :module_id_generator => name
concat "js/templates.js"
end
end
# vim:ft=ruby
Here is an example file I used to start from: https://github.com/hjr3/dasfd/blob/master/Assetfile