diff options
author | kpdecker <kpdecker@gmail.com> | 2015-08-04 12:34:28 -0500 |
---|---|---|
committer | kpdecker <kpdecker@gmail.com> | 2015-08-04 12:34:28 -0500 |
commit | 0de8dac702f7b06161a0c5464a80bd327d708258 (patch) | |
tree | e1cb21d199f93dd0731590f1011dfc4110b14662 | |
parent | d716fd01c546593d1f4c06cd7e1a6ba81a116586 (diff) | |
download | handlebars.js-0de8dac702f7b06161a0c5464a80bd327d708258.zip handlebars.js-0de8dac702f7b06161a0c5464a80bd327d708258.tar.gz handlebars.js-0de8dac702f7b06161a0c5464a80bd327d708258.tar.bz2 |
Add support for string and stdin precompilation
Fixes #1071
-rwxr-xr-x | bin/handlebars | 11 | ||||
-rw-r--r-- | lib/precompiler.js | 76 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | spec/precompiler.js | 46 |
4 files changed, 130 insertions, 4 deletions
diff --git a/bin/handlebars b/bin/handlebars index bfe6680..7645adf 100755 --- a/bin/handlebars +++ b/bin/handlebars @@ -54,6 +54,16 @@ var optimist = require('optimist') 'description': 'Output template function only.', 'alias': 'simple' }, + 'N': { + 'type': 'string', + 'description': 'Name of passed string templates. Optional if running in a simple mode. Required when operating on multiple templates.', + 'alias': 'name' + }, + 'i': { + 'type': 'string', + 'description': 'Generates a template from the passed CLI argument.\n"-" is treated as a special value and causes stdin to be read for the template value.', + 'alias': 'string' + }, 'r': { 'type': 'string', 'description': 'Template root. Base value that will be stripped from template names.', @@ -92,6 +102,7 @@ var optimist = require('optimist') } }) + .wrap(120) .check(function(argv) { if (argv.version) { return; diff --git a/lib/precompiler.js b/lib/precompiler.js index 12a5a1f..9f23ef6 100644 --- a/lib/precompiler.js +++ b/lib/precompiler.js @@ -7,12 +7,64 @@ import {SourceMapConsumer, SourceNode} from 'source-map'; import uglify from 'uglify-js'; module.exports.loadTemplates = function(opts, callback) { + loadStrings(opts, function(err, strings) { + if (err) { + callback(err); + } else { + loadFiles(opts, function(err, files) { + if (err) { + callback(err); + } else { + opts.templates = strings.concat(files); + callback(undefined, opts); + } + }); + } + }); +}; + +function loadStrings(opts, callback) { + let strings = arrayCast(opts.string), + names = arrayCast(opts.name); + + if (names.length !== strings.length + && strings.length > 1) { + return callback(new Handlebars.Exception('Number of names did not match the number of string inputs')); + } + + Async.map(strings, function(string, callback) { + if (string !== '-') { + callback(undefined, string); + } else { + // Load from stdin + let buffer = ''; + process.stdin.setEncoding('utf8'); + + process.stdin.on('data', function(chunk) { + buffer += chunk; + }); + process.stdin.on('end', function() { + callback(undefined, buffer); + }); + } + }, + function(err, strings) { + strings = strings.map((string, index) => ({ + name: names[index], + path: names[index], + source: string + })); + callback(err, strings); + }); +} + +function loadFiles(opts, callback) { // Build file extension pattern let extension = (opts.extension || 'handlebars').replace(/[\\^$*+?.():=!|{}\-\[\]]/g, function(arg) { return '\\' + arg; }); extension = new RegExp('\\.' + extension + '$'); let ret = [], - queue = opts.files.map((template) => ({template, root: opts.root})); + queue = (opts.files || []).map((template) => ({template, root: opts.root})); Async.whilst(() => queue.length, function(callback) { let {template: path, root} = queue.shift(); @@ -74,9 +126,7 @@ module.exports.loadTemplates = function(opts, callback) { if (err) { callback(err); } else { - opts.templates = ret; - - callback(undefined, opts); + callback(undefined, ret); } }); } @@ -100,6 +150,12 @@ module.exports.cli = function(opts) { throw new Handlebars.Exception('Unable to output multiple templates in simple mode'); } + // Force simple mode if we have only one template and it's unnamed. + if (!opts.amd && !opts.commonjs && opts.templates.length === 1 + && !opts.templates[0].name) { + opts.simple = true; + } + // Convert the known list into a hash let known = {}; if (opts.known && !Array.isArray(opts.known)) { @@ -156,6 +212,10 @@ module.exports.cli = function(opts) { if (opts.simple) { output.add([precompiled, '\n']); } else { + if (!template.name) { + throw new Handlebars.Exception('Name missing for template'); + } + if (opts.amd && !multiple) { output.add('return '); } @@ -206,3 +266,11 @@ module.exports.cli = function(opts) { console.log(output); } }; + +function arrayCast(value) { + value = value != null ? value : []; + if (!Array.isArray(value)) { + value = [value]; + } + return value; +} diff --git a/package.json b/package.json index b1cafee..8548cd8 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "jison": "~0.3.0", "keen.io": "0.0.3", "mocha": "~1.20.0", + "mock-stdin": "^0.3.0", "mustache": "0.x", "semver": "^4.0.0", "underscore": "^1.5.1" diff --git a/spec/precompiler.js b/spec/precompiler.js index 25c57a8..e1ad5ad 100644 --- a/spec/precompiler.js +++ b/spec/precompiler.js @@ -71,6 +71,11 @@ describe('precompiler', function() { Precompiler.cli({templates: [__dirname + '/artifacts/empty.handlebars', __dirname + '/artifacts/empty.handlebars'], simple: true}); }, Handlebars.Exception, 'Unable to output multiple templates in simple mode'); }); + it('should throw when missing name', function() { + shouldThrow(function() { + Precompiler.cli({templates: [{source: ''}], amd: true}); + }, Handlebars.Exception, 'Name missing for template'); + }); it('should throw when combining simple and directories', function() { shouldThrow(function() { Precompiler.cli({hasDirectory: true, templates: [1], simple: true}); @@ -82,6 +87,11 @@ describe('precompiler', function() { Precompiler.cli({templates: [emptyTemplate], simple: true}); equal(log, 'simple\n'); }); + it('should default to simple templates', function() { + Handlebars.precompile = function() { return 'simple'; }; + Precompiler.cli({templates: [{source: ''}]}); + equal(log, 'simple\n'); + }); it('should output amd templates', function() { Handlebars.precompile = function() { return 'amd'; }; Precompiler.cli({templates: [emptyTemplate], amd: true}); @@ -197,6 +207,42 @@ describe('precompiler', function() { }); }); + it('should accept string inputs', function(done) { + var opts = {string: ''}; + Precompiler.loadTemplates(opts, function(err, opts) { + equal(opts.templates[0].name, undefined); + equal(opts.templates[0].source, ''); + done(err); + }); + }); + it('should accept string array inputs', function(done) { + var opts = {string: ['', 'bar'], name: ['beep', 'boop']}; + Precompiler.loadTemplates(opts, function(err, opts) { + equal(opts.templates[0].name, 'beep'); + equal(opts.templates[0].source, ''); + equal(opts.templates[1].name, 'boop'); + equal(opts.templates[1].source, 'bar'); + done(err); + }); + }); + it('should accept stdin input', function(done) { + var stdin = require('mock-stdin').stdin(); + Precompiler.loadTemplates({string: '-'}, function(err, opts) { + equal(opts.templates[0].source, 'foo'); + done(err); + }); + stdin.send('fo'); + stdin.send('o'); + stdin.end(); + }); + it('error on name missing', function(done) { + var opts = {string: ['', 'bar']}; + Precompiler.loadTemplates(opts, function(err) { + equal(err.message, 'Number of names did not match the number of string inputs'); + done(); + }); + }); + it('should complete when no args are passed', function(done) { Precompiler.loadTemplates({}, function(err, opts) { equal(opts.templates.length, 0); |