Skip to content

Commit

Permalink
Merge pull request #333 from mde/mde/promise_support
Browse files Browse the repository at this point in the history
Added Promise support with capabilities test to renderFile
  • Loading branch information
mde committed Jan 6, 2018
2 parents 8c575c9 + 7c77ceb commit 334c4e1
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 19 deletions.
70 changes: 52 additions & 18 deletions lib/ejs.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ exports.fileLoader = fs.readFileSync;

exports.localsName = _DEFAULT_LOCALS_NAME;

/**
* Promise implementation -- defaults to the native implementation if available
* This is mostly just for testability
*
* @type {Function}
* @public
*/

exports.promiseImpl = (new Function('return this;'))().Promise;

/**
* Get the path to the included file from the parent file path and the
* specified path.
Expand Down Expand Up @@ -220,13 +230,32 @@ function handleCache(options, template) {

function tryHandleCache(options, data, cb) {
var result;
try {
result = handleCache(options)(data);
if (!cb) {
if (typeof exports.promiseImpl == 'function') {
return new exports.promiseImpl(function (resolve, reject) {
try {
result = handleCache(options)(data);
resolve(result);
}
catch (err) {
reject(err);
}
});
}
else {
throw new Error('Please provide a callback function');
}
}
catch (err) {
return cb(err);
else {
try {
result = handleCache(options)(data);
}
catch (err) {
return cb(err);
}

cb(null, result);
}
return cb(null, result);
}

/**
Expand Down Expand Up @@ -399,17 +428,27 @@ exports.render = function (template, d, o) {
*/

exports.renderFile = function () {
var filename = arguments[0];
var cb = arguments[arguments.length - 1];
var args = Array.prototype.slice.call(arguments);
var filename = args.shift();
var cb;
var opts = {filename: filename};
var data;

if (arguments.length > 2) {
data = arguments[1];

// No options object -- if there are optiony names
// in the data, copy them to options
if (arguments.length === 3) {
// Do we have a callback?
if (typeof arguments[arguments.length - 1] == 'function') {
cb = args.pop();
}
// Do we have data/opts?
if (args.length) {
// Should always have data obj
data = args.shift();
// Normal passed opts (data obj + opts obj)
if (args.length) {
// Use shallowCopy so we don't pollute passed in opts obj with new vals
utils.shallowCopy(opts, args.pop());
}
// Special casing for Express (opts-in-data)
else {
// Express 4
if (data.settings) {
if (data.settings.views) {
Expand All @@ -424,11 +463,6 @@ exports.renderFile = function () {
utils.shallowCopyFromList(opts, data, _OPTS_EXPRESS);
}
}
else {
// Use shallowCopy so we don't pollute passed in opts obj with new vals
utils.shallowCopy(opts, arguments[2]);
}

opts.filename = filename;
}
else {
Expand Down
47 changes: 46 additions & 1 deletion test/ejs.js
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ suite('ejs.render(str, data, opts)', function () {

});

suite('ejs.renderFile(path, [data], [options], fn)', function () {
suite('ejs.renderFile(path, [data], [options], [fn])', function () {
test('render a file', function(done) {
ejs.renderFile('test/fixtures/para.ejs', function(err, html) {
if (err) {
Expand All @@ -303,6 +303,51 @@ suite('ejs.renderFile(path, [data], [options], fn)', function () {
});
});

test('Promise support', function(done) {
var AsyncCtor;
var func;
function checkResult(html) {
assert.equal(html, '<p>hey</p>\n');
}
// Environments without Promise support -- should throw
// when no callback provided
function checkNoPromise() {
delete ejs.promiseImpl;
assert.throws(function () {
ejs.renderFile('test/fixtures/para.ejs');
});
ejs.promiseImpl = global.Promise;
done();
}

// Check for async/await support -- have to use eval for check because
// envs without async will break at parsing step
try {
eval('AsyncCtor = (async function () {}).constructor;');
}
catch (e) {
// No-op
}

// Both async and Promise -- in both cases, also check the call
// correctly throws in non-Promise envs if no callback provided
// -------------------
// Async support -- have to use eval for constructing async func for
// same reasons as above
if (AsyncCtor) {
func = new AsyncCtor('ejs', 'checkResult', 'checkNoPromise',
"let res = await ejs.renderFile('test/fixtures/para.ejs'); checkResult(res); checkNoPromise();");
func(ejs, checkResult, checkNoPromise);
}
// Ordinary Promise support
else {
ejs.renderFile('test/fixtures/para.ejs').then(function (res) {
checkResult(res);
checkNoPromise();
});
}
});

test('accept locals', function(done) {
var data = {name: 'fonebone'};
var options = {delimiter: '$'};
Expand Down

0 comments on commit 334c4e1

Please sign in to comment.