Skip to content

Instantly share code, notes, and snippets.

@cookrn
Created June 27, 2012 04:19
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save cookrn/3001401 to your computer and use it in GitHub Desktop.
Save cookrn/3001401 to your computer and use it in GitHub Desktop.
Compile *.ejs Templates

EJS Compiler

This is used to generate a templates.js file where the keys are template identifiers and the values are the raw, escaped EJS templates.

Templates are semi-compiled server side. Results of compilation are memoized on the client side using Underscore.

This is useful for maintaining multiple template files server-side and compiling them into a client-side script.

This requires the use of the ejs client side library. Requires ejs to be available on window. See: https://github.com/visionmedia/ejs#client-side-support

Resources

module.exports = function(grunt) {
var config = grunt.config
, file = grunt.file
, log = grunt.log;
grunt.initConfig( {
'ejs': {
'app/assets/js/templates.js': 'app/views/templates/**/*.ejs'
}
, 'watch': {
'files': 'app/views/templates/**/*.ejs'
, 'tasks': 'ejs'
}
} );
grunt.registerMultiTask( 'ejs' , 'Compile ejs templates to JST file' , function(){
// If namespace is specified use that, otherwise fallback
var namespace = config( 'meta.ejs.namespace' ) || 'JST';
console.log(namespace);
// Create JST file.
var files = file.expand( this.data );
file.write(
this.target
, grunt.helper( 'ejs' , files , namespace )
);
// Fail task if errors were logged.
if ( grunt.errors ) { return false; }
// Otherwise, print a success message.
log.writeln( 'File "' + this.target + '" created.' );
});
grunt.registerTask( 'default' , 'ejs' );
grunt.registerHelper( 'ejs' , function( files , namespace ){
namespace = "window['" + namespace + "']";
var contents = namespace + ' = ' + namespace + " || {};\n"
, raw_namespace = namespace + "['raw']";
contents = contents + raw_namespace + ' = ' + raw_namespace + " || {};\n\n";
// Compile the template and get the function source
contents += files ? files.map( function( filepath ){
console.log( 'compiling file:' + filepath );
var key = filepath.replace( /app\/views\/templates\// , '' ).replace( /\.ejs/ , '' )
, template = JSON.stringify( file.read( filepath ) );
var compile_fn = "function( locals ){ return window.ejs.compile( " + raw_namespace + "['" + key + "'] )( locals ); }"
, hash_fn = "function( locals ){ return _.chain( locals ).values().reduce( function( m , v ){ return m + v.toString() } , '' ).value(); }"
, template_data = '';
template_data = template_data + raw_namespace + "['" + key + "'] = " + template + ";\n";
template_data = template_data + namespace + "['" + key + "'] = _.memoize( " + compile_fn + " , " + hash_fn + " );\n";
return template_data;
} ).join( "\n\n" ) : "";
return contents;
});
};
<!-- based on the example output, this file would be at app/views/templates/activities/index.ejs -->
<% _.each( activities , function( activity , id ){ %>
<%
var id = activity.ID
, note = activity.Note;
%>
<tr>
<td><%= id %></td>
<td><%= note %></td>
<td class="code-cell">
<pre><%= directive %></pre>
</td>
<td class="code-cell">
<pre><%= response %></pre>
</td>
<td><%= on_tap %></td>
<td>
<button
class="btn btn-large btn-warning edit-activity-btn"
data-activity-id="<%= id %>"
data-activity-note="<%= note %>"
>*</button>
</td>
</tr>
<% } ); %>
// EXAMPLE OUTPUT
window['JST'] = window['JST'] || {};
window['JST']['raw'] = window['JST']['raw'] || {};
window['JST']['raw']['activities/index'] = "<% _.each( activities , function( activity , id ){ %>\n <%\n var id = activity.ID\n , note = activity.Note;\n %>\n <tr>\n <td><%= id %></td>\n <td><%= note %></td>\n <td class=\"code-cell\">\n <pre><%= directive %></pre>\n </td>\n <td class=\"code-cell\">\n <pre><%= response %></pre>\n </td>\n <td><%= on_tap %></td>\n <td>\n <button\n class=\"btn btn-large btn-warning edit-activity-btn\"\n data-activity-id=\"<%= id %>\"\n data-activity-note=\"<%= note %>\"\n >*</button>\n </td>\n </tr>\n<% } ); %>\n";
window['JST']['activities/index'] = _.memoize( function( locals ){ return window.ejs.compile( window['JST']['raw']['activities/index'] )( locals ); } , function( locals ){ return _.chain( locals ).values().reduce( function( m , v ){ return m + v.toString() } , '' ).value(); } );
@sylvinus
Copy link

sylvinus commented Jan 3, 2013

This is great. Why don't you use ejs.compile() though?

@cookrn
Copy link
Author

cookrn commented Jan 28, 2013

@sylvinus actually using it on the client side. at the time I wrote this, i was not able to get ejs.compile() working on the server side. the compiled templates were generated but not executable in the browser.

@cookrn
Copy link
Author

cookrn commented Jan 28, 2013

There's definitely some room for improvement of this code. If you'd like to see that work done, please respond here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment