Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,64 @@ var Handlebars = require('handlebars');
var templates = require('./templates')(Handlebars);
```


#### es2015
Type: `Boolean` or `Array`
Default: `false`

Formats the output file as an ES2015 module, exports the result both as a named export as well as a default export.

For this option to work the `namespace` property needs to be defined.
If not defined or explicitly set to `false`, the task's `namespace` property will instead default to `JST`.


If `Array` then each array entry is expected to have both a variable and path properties:

```js
es2015: [
{variable: '$', path: 'jquery'},
{variable: 'Handlebars', path: 'handlebars.runtime.js'},
{variable: '{NamedModule}', path: 'my_module.js'}
]
```

Each of these will, in turn, be imported at the top of the compilation file:


```js
import $ from 'jquery';
import Handlebars from 'handlebars.runtime.js';
import {NamedModule} from 'my_module.js';
```

If the es2015 property is a string, a function or `true` these imports will default to:

```js
import Handlebars from 'handlebars.runtime.js';
```

As you might know, you can't use `this` inside an es2015 module to reference the global scope. It's undefined as per specification.
Instead of `this`, you can pass a `root` option to be used in its place.

```js
options: {
root: 'templates'
}
```

If said option isn't declared, the task's `root` property will default to `templates`

So, for example, if you use the default namespace `JST`, the compiled file will end with:

```js
var JST = templates["JST"];
export {JST};
export default JST;
```

The `root` option (albeit is not needed except for es2015 format) can be safely used with all output formats.


#### processContent
Type: `Function`

Expand Down
152 changes: 92 additions & 60 deletions tasks/handlebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module.exports = function(grunt) {
// filename conversion for partials
var defaultProcessPartialName = function(filepath) {
var pieces = _.last(filepath.split('/')).split('.');
var name = _(pieces).without(_.last(pieces)).join('.'); // strips file extension
var name = _(pieces).without(_.last(pieces)).join('.'); // strips file extension
if (name.charAt(0) === '_') {
name = name.substr(1, name.length); // strips leading _ character
}
Expand Down Expand Up @@ -62,9 +62,20 @@ module.exports = function(grunt) {
amd: false,
commonjs: false,
knownHelpers: [],
knownHelpersOnly: false
knownHelpersOnly: false,
es2015: false
});

if (options.es2015) {
// ES2015 doesn't allow to use "this" as root element
options.root = options.root || 'templates';
}
if (options.es2015 || options.node) {
// when using es2015 or node output format, the namespace option must be defined
// Otherwise, it defaults to 'JST'
options.namespace = options.namespace || 'JST';
}

// assign regex for partials directory detection
var partialsPathRegex = options.partialsPathRegex || /./;

Expand Down Expand Up @@ -98,6 +109,10 @@ module.exports = function(grunt) {
// nsdeclare options when fetching namespace info
var nsDeclareOptions = {response: 'details', declared: nsDeclarations};

if (options.root) {
nsDeclareOptions.root = options.root;
}

// Just get the namespace info for a given template
var getNamespaceInfo = _.memoize(function(filepath) {
if (!useNamespace) {
Expand All @@ -111,78 +126,80 @@ module.exports = function(grunt) {

// iterate files, processing partials and templates separately
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;
}
return true;
})
.forEach(function(filepath) {
var src = processContent(grunt.file.read(filepath), filepath);

var Handlebars = require('handlebars');
try {
// parse the handlebars template into it's AST
ast = processAST(Handlebars.parse(src));
compiled = Handlebars.precompile(ast, compilerOptions);

// if configured to, wrap template in Handlebars.template call
if (options.wrapped === true) {
compiled = 'Handlebars.template(' + compiled + ')';
// 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;
}
} catch (e) {
grunt.log.error(e);
grunt.fail.warn('Handlebars failed to compile ' + filepath + '.');
}

// register partial or add template to namespace
if (partialsPathRegex.test(filepath) && isPartialRegex.test(_.last(filepath.split('/')))) {
filename = processPartialName(filepath);
if (options.partialsUseNamespace === true) {
nsInfo = getNamespaceInfo(filepath);
if (nsInfo.declaration) {
declarations.push(nsInfo.declaration);
return true;
})
.forEach(function(filepath) {
var src = processContent(grunt.file.read(filepath), filepath);

var Handlebars = require('handlebars');
try {
// parse the handlebars template into it's AST
ast = processAST(Handlebars.parse(src));
compiled = Handlebars.precompile(ast, compilerOptions);

// if configured to, wrap template in Handlebars.template call
if (options.wrapped === true) {
compiled = 'Handlebars.template(' + compiled + ')';
}
partials.push('Handlebars.registerPartial(' + JSON.stringify(filename) + ', ' + nsInfo.namespace +
'[' + JSON.stringify(filename) + '] = ' + compiled + ');');
} else {
partials.push('Handlebars.registerPartial(' + JSON.stringify(filename) + ', ' + compiled + ');');
} catch (e) {
grunt.log.error(e);
grunt.fail.warn('Handlebars failed to compile ' + filepath + '.');
}
} else {
if ((options.amd || options.commonjs) && !useNamespace) {
compiled = 'return ' + compiled;
}
filename = processName(filepath);
if (useNamespace) {
nsInfo = getNamespaceInfo(filepath);
if (nsInfo.declaration) {
declarations.push(nsInfo.declaration);

// register partial or add template to namespace
if (partialsPathRegex.test(filepath) && isPartialRegex.test(_.last(filepath.split('/')))) {
filename = processPartialName(filepath);
if (options.partialsUseNamespace === true) {
nsInfo = getNamespaceInfo(filepath);
if (nsInfo.declaration) {
declarations.push(nsInfo.declaration);
}
partials.push('Handlebars.registerPartial(' + JSON.stringify(filename) + ', ' + nsInfo.namespace +
'[' + JSON.stringify(filename) + '] = ' + compiled + ');');
} else {
partials.push('Handlebars.registerPartial(' + JSON.stringify(filename) + ', ' + compiled + ');');
}
templates.push(nsInfo.namespace + '[' + JSON.stringify(filename) + '] = ' + compiled + ';');
} else if (options.commonjs === true) {
templates.push(compiled + ';');
} else {
templates.push(compiled);
if ((options.amd || options.commonjs) && !useNamespace) {
compiled = 'return ' + compiled;
}
filename = processName(filepath);
if (useNamespace) {
nsInfo = getNamespaceInfo(filepath);
if (nsInfo.declaration) {
declarations.push(nsInfo.declaration);
}
templates.push(nsInfo.namespace + '[' + JSON.stringify(filename) + '] = ' + compiled + ';');
} else if (options.commonjs === true) {
templates.push(compiled + ';');
} else {
templates.push(compiled);
}
}
}
});
});

var output = declarations.concat(partials, templates);
if (output.length < 1) {
grunt.log.warn('Destination not written because compiled files were empty.');
} else {
if (useNamespace) {
if (options.node) {
output.unshift('Handlebars = glob.Handlebars || require(\'handlebars\');');
output.unshift('var glob = (\'undefined\' === typeof window) ? global : window,');

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

output.push(nodeExport);
}
if (options.node) {
output.unshift('Handlebars = glob.Handlebars || require(\'handlebars\');');
output.unshift('var glob = (\'undefined\' === typeof window) ? global : window,');

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

output.push(nodeExport);
}

if (options.amd) {
Expand Down Expand Up @@ -225,6 +242,21 @@ module.exports = function(grunt) {
output.push('};');
}

if (options.es2015) {

if (Array.isArray(options.es2015)) {
options.es2015.forEach(function(dependency) {
output.unshift('import ' + dependency.variable + ' from \'' + dependency.path + '\';');
});
} else {
output.unshift('import Handlebars from \'handlebars\';');
}

output.push('var ' + options.namespace + ' = ' + nsInfo.namespace + ';');
output.push('export {' + options.namespace + '};');
output.push('export default ' + options.namespace + ';');
}

filesCount++;
grunt.file.write(f.dest, output.join(grunt.util.normalizelf(options.separator)));
grunt.verbose.writeln('File ' + chalk.cyan(f.dest) + ' created.');
Expand Down