Skip to content

Commit

Permalink
Add support for string and stdin precompilation
Browse files Browse the repository at this point in the history
Fixes #1071
  • Loading branch information
kpdecker committed Aug 4, 2015
1 parent d716fd0 commit 0de8dac
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 4 deletions.
11 changes: 11 additions & 0 deletions bin/handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -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.',
Expand Down Expand Up @@ -92,6 +102,7 @@ var optimist = require('optimist')
}
})

.wrap(120)
.check(function(argv) {
if (argv.version) {
return;
Expand Down
76 changes: 72 additions & 4 deletions lib/precompiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -74,9 +126,7 @@ module.exports.loadTemplates = function(opts, callback) {
if (err) {
callback(err);
} else {
opts.templates = ret;

callback(undefined, opts);
callback(undefined, ret);
}
});
}
Expand All @@ -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)) {
Expand Down Expand Up @@ -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 ');
}
Expand Down Expand Up @@ -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;
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
46 changes: 46 additions & 0 deletions spec/precompiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -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});
Expand All @@ -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});
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 0de8dac

Please sign in to comment.