Skip to content

Commit 00f7442

Browse files
committed
Convert precompiler template loading to async
1 parent a62cbad commit 00f7442

File tree

4 files changed

+113
-75
lines changed

4 files changed

+113
-75
lines changed

bin/handlebars

+11-7
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,18 @@ var optimist = require('optimist')
100100

101101

102102
var argv = optimist.argv;
103-
argv.templates = argv._;
103+
argv.files = argv._;
104104
delete argv._;
105105

106106
var Precompiler = require('../dist/cjs/precompiler');
107-
Precompiler.loadTemplates(argv);
107+
Precompiler.loadTemplates(argv, function(err, opts) {
108+
if (err) {
109+
throw err;
110+
}
108111

109-
if (argv.help || (!argv.templates.length && !argv.version)) {
110-
optimist.showHelp();
111-
} else {
112-
Precompiler.cli(argv);
113-
}
112+
if (opts.help || (!opts.templates.length && !opts.version)) {
113+
optimist.showHelp();
114+
} else {
115+
Precompiler.cli(opts);
116+
}
117+
});

lib/precompiler.js

+64-41
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,85 @@
11
/*eslint-disable no-console */
2+
import Async from 'async';
23
import fs from 'fs';
34
import * as Handlebars from './handlebars';
45
import {basename} from 'path';
56
import {SourceMapConsumer, SourceNode} from 'source-map';
67
import uglify from 'uglify-js';
78

8-
module.exports.loadTemplates = function(opts) {
9+
module.exports.loadTemplates = function(opts, callback) {
910
// Build file extension pattern
1011
let extension = (opts.extension || 'handlebars').replace(/[\\^$*+?.():=!|{}\-\[\]]/g, function(arg) { return '\\' + arg; });
1112
extension = new RegExp('\\.' + extension + '$');
1213

13-
let ret = [];
14-
function processTemplate(template, root) {
15-
let path = template,
16-
stat;
17-
try {
18-
stat = fs.statSync(template);
19-
} catch (err) {
20-
throw new Handlebars.Exception(`Unable to open template file "${template}"`);
21-
}
22-
23-
if (stat.isDirectory()) {
24-
opts.hasDirectory = true;
25-
26-
fs.readdirSync(template).map(function(file) {
27-
let childPath = template + '/' + file;
28-
29-
if (extension.test(childPath) || fs.statSync(childPath).isDirectory()) {
30-
processTemplate(childPath, root || template);
31-
}
32-
});
33-
} else {
34-
let data = fs.readFileSync(path, 'utf8');
14+
let ret = [],
15+
queue = opts.files.map((template) => ({template, root: opts.root}));
16+
Async.whilst(() => queue.length, function(callback) {
17+
let {template: path, root} = queue.shift();
3518

36-
if (opts.bom && data.indexOf('\uFEFF') === 0) {
37-
data = data.substring(1);
19+
fs.stat(path, function(err, stat) {
20+
if (err) {
21+
return callback(new Handlebars.Exception(`Unable to open template file "${path}"`));
3822
}
3923

40-
// Clean the template name
41-
if (!root) {
42-
template = basename(template);
43-
} else if (template.indexOf(root) === 0) {
44-
template = template.substring(root.length + 1);
24+
if (stat.isDirectory()) {
25+
opts.hasDirectory = true;
26+
27+
fs.readdir(path, function(err, children) {
28+
/* istanbul ignore next : Race condition that being too lazy to test */
29+
if (err) {
30+
return callback(err);
31+
}
32+
children.forEach(function(file) {
33+
let childPath = path + '/' + file;
34+
35+
if (extension.test(childPath) || fs.statSync(childPath).isDirectory()) {
36+
queue.push({template: childPath, root: root || path});
37+
}
38+
});
39+
40+
callback();
41+
});
42+
} else {
43+
fs.readFile(path, 'utf8', function(err, data) {
44+
/* istanbul ignore next : Race condition that being too lazy to test */
45+
if (err) {
46+
return callback(err);
47+
}
48+
49+
if (opts.bom && data.indexOf('\uFEFF') === 0) {
50+
data = data.substring(1);
51+
}
52+
53+
// Clean the template name
54+
let name = path;
55+
if (!root) {
56+
name = basename(name);
57+
} else if (name.indexOf(root) === 0) {
58+
name = name.substring(root.length + 1);
59+
}
60+
name = name.replace(extension, '');
61+
62+
ret.push({
63+
path: path,
64+
name: name,
65+
source: data
66+
});
67+
68+
callback();
69+
});
4570
}
46-
template = template.replace(extension, '');
71+
});
72+
},
73+
function(err) {
74+
if (err) {
75+
callback(err);
76+
} else {
77+
opts.templates = ret;
4778

48-
ret.push({
49-
path: path,
50-
name: template,
51-
source: data
52-
});
79+
callback(undefined, opts);
5380
}
54-
}
55-
opts.templates.forEach(function(template) {
56-
processTemplate(template, opts.root);
5781
});
58-
opts.templates = ret;
59-
};
82+
}
6083

6184
module.exports.cli = function(opts) {
6285
if (opts.version) {

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"node": ">=0.4.7"
2222
},
2323
"dependencies": {
24+
"async": "^1.4.0",
2425
"optimist": "^0.6.1",
2526
"source-map": "^0.1.40"
2627
},

spec/precompiler.js

+37-27
Original file line numberDiff line numberDiff line change
@@ -153,38 +153,48 @@ describe('precompiler', function() {
153153
});
154154

155155
describe('#loadTemplates', function() {
156-
it('should throw on missing template', function() {
157-
shouldThrow(function() {
158-
Precompiler.loadTemplates({templates: ['foo']});
159-
}, Handlebars.Exception, 'Unable to open template file "foo"');
156+
it('should throw on missing template', function(done) {
157+
Precompiler.loadTemplates({files: ['foo']}, function(err) {
158+
equal(err.message, 'Unable to open template file "foo"');
159+
done();
160+
});
160161
});
161-
it('should enumerate directories by extension', function() {
162-
var opts = {templates: [__dirname + '/artifacts'], extension: 'hbs'};
163-
Precompiler.loadTemplates(opts);
164-
equal(opts.templates.length, 1);
165-
equal(opts.templates[0].name, 'example_2');
166-
167-
opts = {templates: [__dirname + '/artifacts'], extension: 'handlebars'};
168-
Precompiler.loadTemplates(opts);
169-
equal(opts.templates.length, 3);
170-
equal(opts.templates[0].name, 'bom');
171-
equal(opts.templates[1].name, 'empty');
172-
equal(opts.templates[2].name, 'example_1');
162+
it('should enumerate directories by extension', function(done) {
163+
Precompiler.loadTemplates({files: [__dirname + '/artifacts'], extension: 'hbs'}, function(err, opts) {
164+
equal(opts.templates.length, 1);
165+
equal(opts.templates[0].name, 'example_2');
166+
done(err);
167+
});
173168
});
174-
it('should handle regular expression characters in extensions', function() {
175-
Precompiler.loadTemplates({templates: [__dirname + '/artifacts'], extension: 'hb(s'});
176-
// Success is not throwing
169+
it('should enumerate all templates by extension', function(done) {
170+
Precompiler.loadTemplates({files: [__dirname + '/artifacts'], extension: 'handlebars'}, function(err, opts) {
171+
equal(opts.templates.length, 3);
172+
equal(opts.templates[0].name, 'bom');
173+
equal(opts.templates[1].name, 'empty');
174+
equal(opts.templates[2].name, 'example_1');
175+
done(err);
176+
});
177177
});
178-
it('should handle BOM', function() {
179-
var opts = {templates: [__dirname + '/artifacts/bom.handlebars'], extension: 'handlebars', bom: true};
180-
Precompiler.loadTemplates(opts);
181-
equal(opts.templates[0].source, 'a');
178+
it('should handle regular expression characters in extensions', function(done) {
179+
Precompiler.loadTemplates({files: [__dirname + '/artifacts'], extension: 'hb(s'}, function(err) {
180+
// Success is not throwing
181+
done(err);
182+
});
183+
});
184+
it('should handle BOM', function(done) {
185+
var opts = {files: [__dirname + '/artifacts/bom.handlebars'], extension: 'handlebars', bom: true};
186+
Precompiler.loadTemplates(opts, function(err, opts) {
187+
equal(opts.templates[0].source, 'a');
188+
done(err);
189+
});
182190
});
183191

184-
it('should handle different root', function() {
185-
var opts = {templates: [__dirname + '/artifacts/empty.handlebars'], simple: true, root: 'foo/'};
186-
Precompiler.loadTemplates(opts);
187-
equal(opts.templates[0].name, __dirname + '/artifacts/empty');
192+
it('should handle different root', function(done) {
193+
var opts = {files: [__dirname + '/artifacts/empty.handlebars'], simple: true, root: 'foo/'};
194+
Precompiler.loadTemplates(opts, function(err, opts) {
195+
equal(opts.templates[0].name, __dirname + '/artifacts/empty');
196+
done(err);
197+
});
188198
});
189199
});
190200
});

0 commit comments

Comments
 (0)