79309659e1
closes #1391
uglify-js is an optional dependency and should be treated as such.
This commit gracefully handles MODULE_NOT_FOUND errors while loading
uglify.
- Check for existing uglify-js (and load uglify-js) only if minification
was activated
- Use "require.resolve" to check if uglify exists. Otherwise, a missing
dependency of uglify-js would cause the same behavior as missing
uglify-js. (Only a warning, no error)
- The code to load and run uglify is put into a single for readability
purposes
- Tests use a mockup Module._resolveFilename to simulate the missing module.
This function is used by both "require" and "require.resolve", so both
are mocked equally.
(cherry picked from commit d5caa56)
315 lines
11 KiB
JavaScript
315 lines
11 KiB
JavaScript
/* eslint-disable no-console */
|
|
describe('precompiler', function() {
|
|
// NOP Under non-node environments
|
|
if (typeof process === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
var Handlebars = require('../lib'),
|
|
Precompiler = require('../dist/cjs/precompiler'),
|
|
fs = require('fs'),
|
|
uglify = require('uglify-js');
|
|
|
|
var log,
|
|
logFunction,
|
|
errorLog,
|
|
errorLogFunction,
|
|
|
|
precompile,
|
|
minify,
|
|
|
|
emptyTemplate = {
|
|
path: __dirname + '/artifacts/empty.handlebars',
|
|
name: 'empty',
|
|
source: ''
|
|
},
|
|
|
|
file,
|
|
content,
|
|
writeFileSync;
|
|
|
|
/**
|
|
* Mock the Module.prototype.require-function such that an error is thrown, when "uglify-js" is loaded.
|
|
*
|
|
* The function cleans up its mess when "callback" is finished
|
|
*
|
|
* @param {Error} loadError the error that should be thrown if uglify is loaded
|
|
* @param {function} callback a callback-function to run when the mock is active.
|
|
*/
|
|
function mockRequireUglify(loadError, callback) {
|
|
var Module = require('module');
|
|
var _resolveFilename = Module._resolveFilename;
|
|
delete require.cache[require.resolve('uglify-js')];
|
|
delete require.cache[require.resolve('../dist/cjs/precompiler')];
|
|
Module._resolveFilename = function(request, mod) {
|
|
if (request === 'uglify-js') {
|
|
throw loadError;
|
|
}
|
|
return _resolveFilename.call(this, request, mod);
|
|
};
|
|
try {
|
|
callback();
|
|
} finally {
|
|
Module._resolveFilename = _resolveFilename;
|
|
delete require.cache[require.resolve('uglify-js')];
|
|
delete require.cache[require.resolve('../dist/cjs/precompiler')];
|
|
}
|
|
}
|
|
|
|
beforeEach(function() {
|
|
precompile = Handlebars.precompile;
|
|
minify = uglify.minify;
|
|
writeFileSync = fs.writeFileSync;
|
|
|
|
// Mock stdout and stderr
|
|
logFunction = console.log;
|
|
log = '';
|
|
console.log = function() {
|
|
log += Array.prototype.join.call(arguments, '');
|
|
};
|
|
errorLogFunction = console.error;
|
|
errorLog = '';
|
|
console.error = function() {
|
|
errorLog += Array.prototype.join.call(arguments, '');
|
|
};
|
|
|
|
fs.writeFileSync = function(_file, _content) {
|
|
file = _file;
|
|
content = _content;
|
|
};
|
|
});
|
|
afterEach(function() {
|
|
Handlebars.precompile = precompile;
|
|
uglify.minify = minify;
|
|
fs.writeFileSync = writeFileSync;
|
|
console.log = logFunction;
|
|
console.error = errorLogFunction;
|
|
});
|
|
|
|
it('should output version', function() {
|
|
Precompiler.cli({templates: [], version: true});
|
|
equals(log, Handlebars.VERSION);
|
|
});
|
|
it('should throw if lacking templates', function() {
|
|
shouldThrow(function() {
|
|
Precompiler.cli({templates: []});
|
|
}, Handlebars.Exception, 'Must define at least one template or directory.');
|
|
});
|
|
it('should handle empty/filtered directories', function() {
|
|
Precompiler.cli({hasDirectory: true, templates: []});
|
|
// Success is not throwing
|
|
});
|
|
it('should throw when combining simple and minimized', function() {
|
|
shouldThrow(function() {
|
|
Precompiler.cli({templates: [__dirname], simple: true, min: true});
|
|
}, Handlebars.Exception, 'Unable to minimize simple output');
|
|
});
|
|
it('should throw when combining simple and multiple templates', function() {
|
|
shouldThrow(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});
|
|
}, Handlebars.Exception, 'Unable to output multiple templates in simple mode');
|
|
});
|
|
|
|
it('should output simple templates', function() {
|
|
Handlebars.precompile = function() { return 'simple'; };
|
|
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});
|
|
equal(/template\(amd\)/.test(log), true);
|
|
});
|
|
it('should output multiple amd', function() {
|
|
Handlebars.precompile = function() { return 'amd'; };
|
|
Precompiler.cli({templates: [emptyTemplate, emptyTemplate], amd: true, namespace: 'foo'});
|
|
equal(/templates = foo = foo \|\|/.test(log), true);
|
|
equal(/return templates/.test(log), true);
|
|
equal(/template\(amd\)/.test(log), true);
|
|
});
|
|
it('should output amd partials', function() {
|
|
Handlebars.precompile = function() { return 'amd'; };
|
|
Precompiler.cli({templates: [emptyTemplate], amd: true, partial: true});
|
|
equal(/return Handlebars\.partials\['empty'\]/.test(log), true);
|
|
equal(/template\(amd\)/.test(log), true);
|
|
});
|
|
it('should output multiple amd partials', function() {
|
|
Handlebars.precompile = function() { return 'amd'; };
|
|
Precompiler.cli({templates: [emptyTemplate, emptyTemplate], amd: true, partial: true});
|
|
equal(/return Handlebars\.partials\[/.test(log), false);
|
|
equal(/template\(amd\)/.test(log), true);
|
|
});
|
|
it('should output commonjs templates', function() {
|
|
Handlebars.precompile = function() { return 'commonjs'; };
|
|
Precompiler.cli({templates: [emptyTemplate], commonjs: true});
|
|
equal(/template\(commonjs\)/.test(log), true);
|
|
});
|
|
|
|
it('should set data flag', function() {
|
|
Handlebars.precompile = function(data, options) { equal(options.data, true); return 'simple'; };
|
|
Precompiler.cli({templates: [emptyTemplate], simple: true, data: true});
|
|
equal(log, 'simple\n');
|
|
});
|
|
|
|
it('should set known helpers', function() {
|
|
Handlebars.precompile = function(data, options) { equal(options.knownHelpers.foo, true); return 'simple'; };
|
|
Precompiler.cli({templates: [emptyTemplate], simple: true, known: 'foo'});
|
|
equal(log, 'simple\n');
|
|
});
|
|
it('should output to file system', function() {
|
|
Handlebars.precompile = function() { return 'simple'; };
|
|
Precompiler.cli({templates: [emptyTemplate], simple: true, output: 'file!'});
|
|
equal(file, 'file!');
|
|
equal(content, 'simple\n');
|
|
equal(log, '');
|
|
});
|
|
|
|
it('should output minimized templates', function() {
|
|
Handlebars.precompile = function() { return 'amd'; };
|
|
uglify.minify = function() { return {code: 'min'}; };
|
|
Precompiler.cli({templates: [emptyTemplate], min: true});
|
|
equal(log, 'min');
|
|
});
|
|
|
|
it('should omit minimization gracefully, if uglify-js is missing', function() {
|
|
var error = new Error("Cannot find module 'uglify-js'");
|
|
error.code = 'MODULE_NOT_FOUND';
|
|
mockRequireUglify(error, function() {
|
|
var Precompiler = require('../dist/cjs/precompiler');
|
|
Handlebars.precompile = function() { return 'amd'; };
|
|
Precompiler.cli({templates: [emptyTemplate], min: true});
|
|
equal(/template\(amd\)/.test(log), true);
|
|
equal(/\n/.test(log), true);
|
|
equal(/Code minimization is disabled/.test(errorLog), true);
|
|
});
|
|
});
|
|
|
|
it('should fail on errors (other than missing module) while loading uglify-js', function() {
|
|
mockRequireUglify(new Error('Mock Error'), function() {
|
|
shouldThrow(function() {
|
|
var Precompiler = require('../dist/cjs/precompiler');
|
|
Handlebars.precompile = function() { return 'amd'; };
|
|
Precompiler.cli({templates: [emptyTemplate], min: true});
|
|
}, Error, 'Mock Error');
|
|
});
|
|
});
|
|
|
|
it('should output map', function() {
|
|
Precompiler.cli({templates: [emptyTemplate], map: 'foo.js.map'});
|
|
|
|
equal(file, 'foo.js.map');
|
|
equal(log.match(/sourceMappingURL=/g).length, 1);
|
|
});
|
|
|
|
it('should output map', function() {
|
|
Precompiler.cli({templates: [emptyTemplate], min: true, map: 'foo.js.map'});
|
|
|
|
equal(file, 'foo.js.map');
|
|
equal(log.match(/sourceMappingURL=/g).length, 1);
|
|
});
|
|
|
|
describe('#loadTemplates', function() {
|
|
it('should throw on missing template', function(done) {
|
|
Precompiler.loadTemplates({files: ['foo']}, function(err) {
|
|
equal(err.message, 'Unable to open template file "foo"');
|
|
done();
|
|
});
|
|
});
|
|
it('should enumerate directories by extension', function(done) {
|
|
Precompiler.loadTemplates({files: [__dirname + '/artifacts'], extension: 'hbs'}, function(err, opts) {
|
|
equal(opts.templates.length, 1);
|
|
equal(opts.templates[0].name, 'example_2');
|
|
done(err);
|
|
});
|
|
});
|
|
it('should enumerate all templates by extension', function(done) {
|
|
Precompiler.loadTemplates({files: [__dirname + '/artifacts'], extension: 'handlebars'}, function(err, opts) {
|
|
equal(opts.templates.length, 3);
|
|
equal(opts.templates[0].name, 'bom');
|
|
equal(opts.templates[1].name, 'empty');
|
|
equal(opts.templates[2].name, 'example_1');
|
|
done(err);
|
|
});
|
|
});
|
|
it('should handle regular expression characters in extensions', function(done) {
|
|
Precompiler.loadTemplates({files: [__dirname + '/artifacts'], extension: 'hb(s'}, function(err) {
|
|
// Success is not throwing
|
|
done(err);
|
|
});
|
|
});
|
|
it('should handle BOM', function(done) {
|
|
var opts = {files: [__dirname + '/artifacts/bom.handlebars'], extension: 'handlebars', bom: true};
|
|
Precompiler.loadTemplates(opts, function(err, opts) {
|
|
equal(opts.templates[0].source, 'a');
|
|
done(err);
|
|
});
|
|
});
|
|
|
|
it('should handle different root', function(done) {
|
|
var opts = {files: [__dirname + '/artifacts/empty.handlebars'], simple: true, root: 'foo/'};
|
|
Precompiler.loadTemplates(opts, function(err, opts) {
|
|
equal(opts.templates[0].name, __dirname + '/artifacts/empty');
|
|
done(err);
|
|
});
|
|
});
|
|
|
|
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);
|
|
done(err);
|
|
});
|
|
});
|
|
});
|
|
});
|