1 /* eslint-disable no-console */
2 import Async from 'async';
4 import * as Handlebars from './handlebars';
5 import {basename} from 'path';
6 import {SourceMapConsumer, SourceNode} from 'source-map';
7 import uglify from 'uglify-js';
9 module.exports.loadTemplates = function(opts, callback) {
10 loadStrings(opts, function(err, strings) {
14 loadFiles(opts, function(err, files) {
18 opts.templates = strings.concat(files);
19 callback(undefined, opts);
26 function loadStrings(opts, callback) {
27 let strings = arrayCast(opts.string),
28 names = arrayCast(opts.name);
30 if (names.length !== strings.length
31 && strings.length > 1) {
32 return callback(new Handlebars.Exception('Number of names did not match the number of string inputs'));
35 Async.map(strings, function(string, callback) {
37 callback(undefined, string);
41 process.stdin.setEncoding('utf8');
43 process.stdin.on('data', function(chunk) {
46 process.stdin.on('end', function() {
47 callback(undefined, buffer);
51 function(err, strings) {
52 strings = strings.map((string, index) => ({
57 callback(err, strings);
61 function loadFiles(opts, callback) {
62 // Build file extension pattern
63 let extension = (opts.extension || 'handlebars').replace(/[\\^$*+?.():=!|{}\-\[\]]/g, function(arg) { return '\\' + arg; });
64 extension = new RegExp('\\.' + extension + '$');
67 queue = (opts.files || []).map((template) => ({template, root: opts.root}));
68 Async.whilst(() => queue.length, function(callback) {
69 let {template: path, root} = queue.shift();
71 fs.stat(path, function(err, stat) {
73 return callback(new Handlebars.Exception(`Unable to open template file "${path}"`));
76 if (stat.isDirectory()) {
77 opts.hasDirectory = true;
79 fs.readdir(path, function(err, children) {
80 /* istanbul ignore next : Race condition that being too lazy to test */
84 children.forEach(function(file) {
85 let childPath = path + '/' + file;
87 if (extension.test(childPath) || fs.statSync(childPath).isDirectory()) {
88 queue.push({template: childPath, root: root || path});
95 fs.readFile(path, 'utf8', function(err, data) {
96 /* istanbul ignore next : Race condition that being too lazy to test */
101 if (opts.bom && data.indexOf('\uFEFF') === 0) {
102 data = data.substring(1);
105 // Clean the template name
108 name = basename(name);
109 } else if (name.indexOf(root) === 0) {
110 name = name.substring(root.length + 1);
112 name = name.replace(extension, '');
129 callback(undefined, ret);
134 module.exports.cli = function(opts) {
136 console.log(Handlebars.VERSION);
140 if (!opts.templates.length && !opts.hasDirectory) {
141 throw new Handlebars.Exception('Must define at least one template or directory.');
144 if (opts.simple && opts.min) {
145 throw new Handlebars.Exception('Unable to minimize simple output');
148 const multiple = opts.templates.length !== 1 || opts.hasDirectory;
149 if (opts.simple && multiple) {
150 throw new Handlebars.Exception('Unable to output multiple templates in simple mode');
153 // Force simple mode if we have only one template and it's unnamed.
154 if (!opts.amd && !opts.commonjs && opts.templates.length === 1
155 && !opts.templates[0].name) {
159 // Convert the known list into a hash
161 if (opts.known && !Array.isArray(opts.known)) {
162 opts.known = [opts.known];
165 for (let i = 0, len = opts.known.length; i < len; i++) {
166 known[opts.known[i]] = true;
170 const objectName = opts.partial ? 'Handlebars.partials' : 'templates';
172 let output = new SourceNode();
175 output.add('define([\'' + opts.handlebarPath + 'handlebars.runtime\'], function(Handlebars) {\n Handlebars = Handlebars["default"];');
176 } else if (opts.commonjs) {
177 output.add('var Handlebars = require("' + opts.commonjs + '");');
179 output.add('(function() {\n');
181 output.add(' var template = Handlebars.template, templates = ');
182 if (opts.namespace) {
183 output.add(opts.namespace);
185 output.add(opts.namespace);
191 opts.templates.forEach(function(template) {
194 knownHelpersOnly: opts.o
198 options.srcName = template.path;
204 let precompiled = Handlebars.precompile(template.source, options);
206 // If we are generating a source map, we have to reconstruct the SourceNode object
208 let consumer = new SourceMapConsumer(precompiled.map);
209 precompiled = SourceNode.fromStringWithSourceMap(precompiled.code, consumer);
213 output.add([precompiled, '\n']);
215 if (!template.name) {
216 throw new Handlebars.Exception('Name missing for template');
219 if (opts.amd && !multiple) {
220 output.add('return ');
222 output.add([objectName, '[\'', template.name, '\'] = template(', precompiled, ');\n']);
226 // Output the content
230 output.add(['return ', objectName, ';\n']);
233 } else if (!opts.commonjs) {
240 output.add('\n//# sourceMappingURL=' + opts.map + '\n');
243 output = output.toStringWithSourceMap();
244 output.map = output.map + '';
247 output = uglify.minify(output.code, {
250 outSourceMap: opts.map,
251 inSourceMap: JSON.parse(output.map)
254 output.code += '\n//# sourceMappingURL=' + opts.map + '\n';
259 fs.writeFileSync(opts.map, output.map, 'utf8');
261 output = output.code;
264 fs.writeFileSync(opts.output, output, 'utf8');
270 function arrayCast(value) {
271 value = value != null ? value : [];
272 if (!Array.isArray(value)) {