123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- /* eslint-disable no-console */
- import Async from 'neo-async';
- import fs from 'fs';
- import * as Handlebars from './handlebars';
- import { basename } from 'path';
- import { SourceMapConsumer, SourceNode } from 'source-map';
- 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 }));
- Async.whilst(
- () => queue.length,
- function(callback) {
- let { template: path, root } = queue.shift();
- fs.stat(path, function(err, stat) {
- if (err) {
- return callback(
- new Handlebars.Exception(`Unable to open template file "${path}"`)
- );
- }
- if (stat.isDirectory()) {
- opts.hasDirectory = true;
- fs.readdir(path, function(err, children) {
- /* istanbul ignore next : Race condition that being too lazy to test */
- if (err) {
- return callback(err);
- }
- children.forEach(function(file) {
- let childPath = path + '/' + file;
- if (
- extension.test(childPath) ||
- fs.statSync(childPath).isDirectory()
- ) {
- queue.push({ template: childPath, root: root || path });
- }
- });
- callback();
- });
- } else {
- fs.readFile(path, 'utf8', function(err, data) {
- /* istanbul ignore next : Race condition that being too lazy to test */
- if (err) {
- return callback(err);
- }
- if (opts.bom && data.indexOf('\uFEFF') === 0) {
- data = data.substring(1);
- }
- // Clean the template name
- let name = path;
- if (!root) {
- name = basename(name);
- } else if (name.indexOf(root) === 0) {
- name = name.substring(root.length + 1);
- }
- name = name.replace(extension, '');
- ret.push({
- path: path,
- name: name,
- source: data
- });
- callback();
- });
- }
- });
- },
- function(err) {
- if (err) {
- callback(err);
- } else {
- callback(undefined, ret);
- }
- }
- );
- }
- module.exports.cli = function(opts) {
- if (opts.version) {
- console.log(Handlebars.VERSION);
- return;
- }
- if (!opts.templates.length && !opts.hasDirectory) {
- throw new Handlebars.Exception(
- 'Must define at least one template or directory.'
- );
- }
- if (opts.simple && opts.min) {
- throw new Handlebars.Exception('Unable to minimize simple output');
- }
- const multiple = opts.templates.length !== 1 || opts.hasDirectory;
- if (opts.simple && multiple) {
- 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)) {
- opts.known = [opts.known];
- }
- if (opts.known) {
- for (let i = 0, len = opts.known.length; i < len; i++) {
- known[opts.known[i]] = true;
- }
- }
- const objectName = opts.partial ? 'Handlebars.partials' : 'templates';
- let output = new SourceNode();
- if (!opts.simple) {
- if (opts.amd) {
- output.add(
- "define(['" +
- opts.handlebarPath +
- 'handlebars.runtime\'], function(Handlebars) {\n Handlebars = Handlebars["default"];'
- );
- } else if (opts.commonjs) {
- output.add('var Handlebars = require("' + opts.commonjs + '");');
- } else {
- output.add('(function() {\n');
- }
- output.add(' var template = Handlebars.template, templates = ');
- if (opts.namespace) {
- output.add(opts.namespace);
- output.add(' = ');
- output.add(opts.namespace);
- output.add(' || ');
- }
- output.add('{};\n');
- }
- opts.templates.forEach(function(template) {
- let options = {
- knownHelpers: known,
- knownHelpersOnly: opts.o
- };
- if (opts.map) {
- options.srcName = template.path;
- }
- if (opts.data) {
- options.data = true;
- }
- let precompiled = Handlebars.precompile(template.source, options);
- // If we are generating a source map, we have to reconstruct the SourceNode object
- if (opts.map) {
- let consumer = new SourceMapConsumer(precompiled.map);
- precompiled = SourceNode.fromStringWithSourceMap(
- precompiled.code,
- consumer
- );
- }
- 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 ');
- }
- output.add([
- objectName,
- "['",
- template.name,
- "'] = template(",
- precompiled,
- ');\n'
- ]);
- }
- });
- // Output the content
- if (!opts.simple) {
- if (opts.amd) {
- if (multiple) {
- output.add(['return ', objectName, ';\n']);
- }
- output.add('});');
- } else if (!opts.commonjs) {
- output.add('})();');
- }
- }
- if (opts.map) {
- output.add('\n//# sourceMappingURL=' + opts.map + '\n');
- }
- output = output.toStringWithSourceMap();
- output.map = output.map + '';
- if (opts.min) {
- output = minify(output, opts.map);
- }
- if (opts.map) {
- fs.writeFileSync(opts.map, output.map, 'utf8');
- }
- output = output.code;
- if (opts.output) {
- fs.writeFileSync(opts.output, output, 'utf8');
- } else {
- console.log(output);
- }
- };
- function arrayCast(value) {
- value = value != null ? value : [];
- if (!Array.isArray(value)) {
- value = [value];
- }
- return value;
- }
- /**
- * Run uglify to minify the compiled template, if uglify exists in the dependencies.
- *
- * We are using `require` instead of `import` here, because es6-modules do not allow
- * dynamic imports and uglify-js is an optional dependency. Since we are inside NodeJS here, this
- * should not be a problem.
- *
- * @param {string} output the compiled template
- * @param {string} sourceMapFile the file to write the source map to.
- */
- function minify(output, sourceMapFile) {
- try {
- // Try to resolve uglify-js in order to see if it does exist
- require.resolve('uglify-js');
- } catch (e) {
- if (e.code !== 'MODULE_NOT_FOUND') {
- // Something else seems to be wrong
- throw e;
- }
- // it does not exist!
- console.error(
- 'Code minimization is disabled due to missing uglify-js dependency'
- );
- return output;
- }
- return require('uglify-js').minify(output.code, {
- sourceMap: {
- content: output.map,
- url: sourceMapFile
- }
- });
- }
|