Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Allow compilation of templates to Javascript functions, wrapped for J…
…ST or AMD.

Closes gh-14.
  • Loading branch information
mereskin authored and Tyler Kellen committed Mar 7, 2013
1 parent b5bec81 commit 732d57e
Show file tree
Hide file tree
Showing 15 changed files with 409 additions and 23 deletions.
37 changes: 37 additions & 0 deletions Gruntfile.js
Expand Up @@ -43,6 +43,43 @@ module.exports = function(grunt) {
year: '<%= grunt.template.today("yyyy") %>'
}
}
},

compile_amd: {
files: {
'tmp/amd/jade.js': ['test/fixtures/jade.jade'],
'tmp/amd/jade2.js': ['test/fixtures/jade2.jade'],
'tmp/amd/jadeInclude.js': ['test/fixtures/jadeInclude.jade'],
'tmp/amd/jadeTemplate.js': ['test/fixtures/jadeTemplate.jade']
},
options: {
client: true,
amd: true,
namespace: false,
compileDebug: false,
data: {
test: true,
year: '<%= grunt.template.today("yyyy") %>'
}
}
},

compile_jst: {
files: {
'tmp/jst/jade.js': ['test/fixtures/jade.jade'],
'tmp/jst/jade2.js': ['test/fixtures/jade2.jade'],
'tmp/jst/jadeInclude.js': ['test/fixtures/jadeInclude.jade'],
'tmp/jst/jadeTemplate.js': ['test/fixtures/jadeTemplate.jade']
},
options: {
client: true,
compileDebug: false,
processName: function(str) { return str.match(/^test\/fixtures\/(.*)\.jade$/)[1]; },
data: {
test: true,
year: '<%= grunt.template.today("yyyy") %>'
}
}
}
},

Expand Down
45 changes: 44 additions & 1 deletion README.md
Expand Up @@ -40,6 +40,49 @@ Type: `Object`

Sets the data passed to `jade` during template compilation. Any data can be passed to the template (including `grunt` templates).

#### comileDebug
Type: `Boolean`

Set `compileDebug: fase` to remove `jade` debug instructions in javascript templates.

#### namespace
Type: `String` `false`
Default: 'JST'

The namespace in which the precompiled templates will be assigned. *Use dot notation (e.g. App.Templates) for nested namespaces or false for no namespace wrapping.* When false with `amd` option set `true`, templates will be returned directly from the AMD wrapper.

Example:
```js
options: {
namespace: 'MyApp.Templates'
}
```

#### amd
Type: `Boolean`
default: `false`

Wraps the output file with an AMD define function and returns the compiled template namespace unless namespace has been explicitly set to false in which case the template function will be returned directly.

```js
define(function() {
//...//
returns this['[template namespace]'];
});
```

#### processName
Type: `function`

This option accepts a function which takes one argument (the template filepath) and returns a string which will be used as the key for the precompiled template object. The example below stores all templates on the default JST namespace in capital letters.

```js
options: {
processName: function(filename) {
return filename.toUpperCase();
}
}
```
### Usage Examples

```js
Expand Down Expand Up @@ -134,4 +177,4 @@ jade: {

Task submitted by [Eric Woroshow](http://ericw.ca/)

*This file was generated on Wed Feb 20 2013 12:35:52.*
*This file was generated on Wed Mar 06 2013 17:45:50.*
44 changes: 44 additions & 0 deletions docs/jade-options.md
Expand Up @@ -10,3 +10,47 @@ Output indented HTML.
Type: `Object`

Sets the data passed to `jade` during template compilation. Any data can be passed to the template (including `grunt` templates).

## comileDebug
Type: `Boolean`

Set `compileDebug: fase` to remove `jade` debug instructions in javascript templates.

## namespace
Type: `String` `false`
Default: 'JST'

The namespace in which the precompiled templates will be assigned. *Use dot notation (e.g. App.Templates) for nested namespaces or false for no namespace wrapping.* When false with `amd` option set `true`, templates will be returned directly from the AMD wrapper.

Example:
```js
options: {
namespace: 'MyApp.Templates'
}
```

## amd
Type: `Boolean`
default: `false`

Wraps the output file with an AMD define function and returns the compiled template namespace unless namespace has been explicitly set to false in which case the template function will be returned directly.

```js
define(function() {
//...//
returns this['[template namespace]'];
});
```

## processName
Type: `function`

This option accepts a function which takes one argument (the template filepath) and returns a string which will be used as the key for the precompiled template object. The example below stores all templates on the default JST namespace in capital letters.

```js
options: {
processName: function(filename) {
return filename.toUpperCase();
}
}
```
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -28,7 +28,8 @@
"test": "grunt test"
},
"dependencies": {
"jade": "~0.27.2"
"jade": "~0.27.2",
"grunt-lib-contrib": "~0.5.1"
},
"devDependencies": {
"grunt-contrib-jshint": "~0.2.0",
Expand Down
117 changes: 96 additions & 21 deletions tasks/jade.js
Expand Up @@ -9,39 +9,114 @@
'use strict';

module.exports = function(grunt) {
var _ = grunt.util._;
var helpers = require('grunt-lib-contrib').init(grunt);

grunt.registerMultiTask('jade', 'Compile Jade templates into HTML.', function() {
// content conversion for templates
var defaultProcessContent = function(content) { return content; };

// filename conversion for templates
var defaultProcessName = function(name) { return name.replace('.jade', ''); };

grunt.registerMultiTask('jade', 'Compile jade templates.', function() {
var options = this.options({
data: {}
namespace: 'JST',
separator: grunt.util.linefeed + grunt.util.linefeed,
amd: false
});

grunt.verbose.writeflags(options, 'Options');

var data = options.data;
delete options.data;

var nsInfo;

if(options.namespace !== false){
nsInfo = helpers.getNamespaceDeclaration(options.namespace);
}

// assign transformation functions
var processContent = options.processContent || defaultProcessContent;
var processName = options.processName || defaultProcessName;

this.files.forEach(function(f) {
var output = f.src.map(function(file) {
return compileJade(file, options, options.data);
}).join(grunt.util.normalizelf(grunt.util.linefeed));
var templates = [];

f.src.filter(function(filepath) {
// Warn on and remove invalid source files (if nonull was set).
if (!grunt.file.exists(filepath)) {
grunt.log.warn('Source file "' + filepath + '" not found.');
return false;
} else {
return true;
}
})
.forEach(function(filepath) {
var src = processContent(grunt.file.read(filepath));
var compiled, filename;
filename = processName(filepath);

options = grunt.util._.extend(options, { filename: filepath });

try {
compiled = require('jade').compile(src, options);
// if in client mode, return function source
if (options.client) {
compiled = compiled.toString();
} else {
compiled = compiled(data);
}

// if configured for amd and the namespace has been explicitly set
// to false, the jade template will be directly returned
if (options.client && options.amd && options.namespace === false) {
compiled = 'return ' + compiled;
}
} catch (e) {
grunt.log.error(e);
grunt.fail.warn('Jade failed to compile '+filepath+'.');
}

if (options.client && options.namespace !== false) {
templates.push(nsInfo.namespace+'['+JSON.stringify(filename)+'] = '+compiled+';');
} else {
templates.push(compiled);
}
});

var output = templates;
if (output.length < 1) {
grunt.log.warn('Destination not written because compiled files were empty.');
} else {
grunt.file.write(f.dest, output);
grunt.log.writeln('File ' + f.dest.cyan + ' created.');
if (options.client && options.namespace !== false) {
output.unshift(nsInfo.declaration);

if (options.node) {
output.unshift('var jade = jade || require(\'jade\').runtime;');

var nodeExport = 'if (typeof exports === \'object\' && exports) {';
nodeExport += 'module.exports = ' + nsInfo.namespace + ';}';

output.push(nodeExport);
}
}

if (options.amd) {
// Wrap the file in an AMD define fn.
output.unshift("define(['jade'], function(jade) { if(jade && jade['runtime'] !== undefined) { jade = jade.runtime; }");
if (options.namespace !== false) {
// Namespace has not been explicitly set to false; the AMD
// wrapper will return the object containing the template.
output.push("return "+nsInfo.namespace+";");
}
output.push("});");
}

grunt.file.write(f.dest, output.join(grunt.util.normalizelf(options.separator)));
grunt.log.writeln('File "' + f.dest + '" created.');
}
});
});

var compileJade = function(srcFile, options, data) {
options = grunt.util._.extend({filename: srcFile}, options);
delete options.data;

var srcCode = grunt.file.read(srcFile);
});

try {
return require('jade').compile(srcCode, options)(data);
} catch (e) {
grunt.log.error(e);
grunt.fail.warn('Jade failed to compile.');
}
};
};
18 changes: 18 additions & 0 deletions test/expected/amd/jade.js
@@ -0,0 +1,18 @@
define(['jade'], function(jade) { if(jade && jade['runtime'] !== undefined) { jade = jade.runtime; }

return function anonymous(locals, attrs, escape, rethrow, merge) {
attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;
var buf = [];
with (locals || {}) {
var interp;
buf.push('<div id="test" class="test"><span id="data">data</span>');
if ( test)
{
buf.push('<div>testing</div>');
}
buf.push('</div>');
}
return buf.join("");
}

});
18 changes: 18 additions & 0 deletions test/expected/amd/jade2.js
@@ -0,0 +1,18 @@
define(['jade'], function(jade) { if(jade && jade['runtime'] !== undefined) { jade = jade.runtime; }

return function anonymous(locals, attrs, escape, rethrow, merge) {
attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;
var buf = [];
with (locals || {}) {
var interp;
buf.push('<div id="test" class="test"><span id="data">data</span>');
if ( test)
{
buf.push('<div>testing 2</div>');
}
buf.push('</div>');
}
return buf.join("");
}

});
18 changes: 18 additions & 0 deletions test/expected/amd/jadeInclude.js
@@ -0,0 +1,18 @@
define(['jade'], function(jade) { if(jade && jade['runtime'] !== undefined) { jade = jade.runtime; }

return function anonymous(locals, attrs, escape, rethrow, merge) {
attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;
var buf = [];
with (locals || {}) {
var interp;
buf.push('<html><head><title>TEST</title></head><body></body></html>');
var a = 'hello jade test'
buf.push('<p>');
var __val__ = a
buf.push(escape(null == __val__ ? "" : __val__));
buf.push('</p>');
}
return buf.join("");
}

});
16 changes: 16 additions & 0 deletions test/expected/amd/jadeTemplate.js
@@ -0,0 +1,16 @@
define(['jade'], function(jade) { if(jade && jade['runtime'] !== undefined) { jade = jade.runtime; }

return function anonymous(locals, attrs, escape, rethrow, merge) {
attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;
var buf = [];
with (locals || {}) {
var interp;
buf.push('<div>');
var __val__ = year
buf.push(escape(null == __val__ ? "" : __val__));
buf.push('</div>');
}
return buf.join("");
}

});
16 changes: 16 additions & 0 deletions test/expected/jst/jade.js
@@ -0,0 +1,16 @@
this["JST"] = this["JST"] || {};

this["JST"]["jade"] = function anonymous(locals, attrs, escape, rethrow, merge) {
attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;
var buf = [];
with (locals || {}) {
var interp;
buf.push('<div id="test" class="test"><span id="data">data</span>');
if ( test)
{
buf.push('<div>testing</div>');
}
buf.push('</div>');
}
return buf.join("");
};

0 comments on commit 732d57e

Please sign in to comment.