title | dateCreated | formattedDateCreated | tags | tocEnabled | excerpt | |
---|---|---|---|---|---|---|
Grunt watch those templates |
2015-01-18 |
Jan 18, 2015 |
|
true |
Setting up automatic EJS (Underscore/Lo-Dash) and Handlebars template compilation in Grunt using grunt-contrib-watch.
The aim is to be able to spawn a process which will watch our templates and compile them to JST (Javascript template) files while we're editing them. That way we can write our templates in a more readable fashion but use the actual JST files at runtime (thus avoiding the need to compile them dynamically).
cd
into an empty directory and:
$ npm init
$ npm i --save-dev grunt grunt-contrib-jst grunt-contrib-watch load-grunt-tasks grunt-contrib-handlebars
$ mkdir -p app/scripts
Start the Gruntfile.js configuration with the following, just to check that watch is working:
vim Gruntfile.js
'use strict';
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
grunt.initConfig({
watch: {
options: {
nospawn: true
},
log: {
files: [
'app/scripts/**/*.js'
],
tasks: ['tmpLog']
}
}
});
grunt.registerTask('tmpLog', function () {
grunt.log.write('watchin is workin');
});
};
Running grunt watch
and editing a JS file in 'app/scripts/**/*.js' (e.g. app/scripts/tmp.js) should give the following output:
{% asset_img watch-is-working.png [Screenshot showing that watch is set up] %}
Replace watch's log
option with the following:
jst: {
files: [
'app/scripts/templates/ejs/**/*.ejs'
],
tasks: ['jst:compile']
}
Configure JST compilation for EJS files with the following (don't include amd: true if you're not working with an AMD module loader like RequireJS):
jst: {
options: {
amd: true
},
compile: {
files: {
'app/scripts/templates/jst/ejsTemplates.js': ['app/scripts/templates/ejs/**/*.ejs']
}
}
}
Personally, I find using jst
for the key a bit confusing. The way I understand it is that both grunt-contrib-jst and grunt-contrib-handlebars, as well as any other JST compiler, compiles some kind of template file to a Javascript file in order to make the authoring and maintaing of these JST files easier (since these Javascript files tend to be heavy on string concatenation and such).
Since grunt-contrib-jst compiles EJS templates, it seems to me that it would have made more sense to call the plugin grunt-contrib-ejs and to use ejs as the key to configure this plugin in grunt.initConfig. But the key to use for the plugin is jst
, so I'm also sticking to jst
in the watch plugin configuration. I will, however, be suffixing these template files with .ejs, hence the above configuration.
Running grunt watch
and editing an EJS file in our watched path should trigger the compilation, e.g:
vim app/scripts/templates/ejs/tmp.ejs
<ul>
<% for(var i=0; i<supplies.length; i++) { %>
<li>
<a href='supplies/<%= supplies[i] %>'>
<%= supplies[i] %>
</a>
</li>
<% } %>
</ul>
Add the following option to the watch task configuration:
handlebars: {
files: [
'app/scripts/templates/hbs/**/*.hbs'
],
tasks: ['handlebars:compile']
}
Then configure the grunt-contrib-handlebars plugin itself (again, I want the generated JST file to be wrapped in an AMD define, YMMV):
handlebars: {
options: {
amd: true
},
compile: {
files: {
'app/scripts/templates/jst/hbsTemplates.js': ['app/scripts/templates/hbs/**/*.hbs']
}
}
}
Try it out:
vim app/scripts/templates/hbs/tmp.hbs
... and assuming you've restarted grunt watch
and it's running in the background, you should get hbsTemplates.js.
'use strict';
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
grunt.initConfig({
watch: {
options: {
nospawn: true
},
jst: {
files: [
'app/scripts/templates/ejs/**/*.ejs'
],
tasks: ['jst:compile']
},
handlebars: {
files: [
'app/scripts/templates/hbs/**/*.hbs'
],
tasks: ['handlebars:compile']
}
},
jst: {
options: {
amd: true
},
compile: {
files: {
'app/scripts/templates/jst/ejsTemplates.js': ['app/scripts/templates/ejs/**/*.ejs']
}
}
},
handlebars: {
options: {
amd: true
},
compile: {
files: {
'app/scripts/templates/jst/hbsTemplates.js': ['app/scripts/templates/hbs/**/*.hbs']
}
}
}
});
};