Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move to utils, handle older runtimes, fix tests #603

Merged
merged 1 commit into from Jun 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 8 additions & 19 deletions lib/ejs.js
Expand Up @@ -67,17 +67,6 @@ var _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat('cache');
var _BOM = /^\uFEFF/;
var _JS_IDENTIFIER = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;

var createObj = function() {
if (typeof Object.create !== 'function') {
return function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
return Object.create;
}();

/**
* EJS template function cache. This can be a LRU object from lru-cache NPM
* module. By default, it is {@link module:utils.cache}, a simple in-process
Expand Down Expand Up @@ -318,7 +307,7 @@ function fileLoader(filePath){
*/

function includeFile(path, options) {
var opts = utils.shallowCopy(createObj(null), options);
var opts = utils.shallowCopy(utils.createNullProtoObjWherePossible(), options);
opts.filename = getIncludePath(path, opts);
if (typeof options.includer === 'function') {
var includerResult = options.includer(path, opts.filename);
Expand Down Expand Up @@ -424,8 +413,8 @@ exports.compile = function compile(template, opts) {
*/

exports.render = function (template, d, o) {
var data = d || createObj(null);
var opts = o || createObj(null);
var data = d || utils.createNullProtoObjWherePossible();
var opts = o || utils.createNullProtoObjWherePossible();

// No options object -- if there are optiony names
// in the data, copy them to options
Expand Down Expand Up @@ -496,7 +485,7 @@ exports.renderFile = function () {
opts.filename = filename;
}
else {
data = createObj(null);
data = utils.createNullProtoObjWherePossible();
}

return tryHandleCache(opts, data, cb);
Expand All @@ -518,8 +507,8 @@ exports.clearCache = function () {
};

function Template(text, opts) {
opts = opts || createObj(null);
var options = createObj(null);
opts = opts || utils.createNullProtoObjWherePossible();
var options = utils.createNullProtoObjWherePossible();
this.templateText = text;
/** @type {string | null} */
this.mode = null;
Expand Down Expand Up @@ -705,14 +694,14 @@ Template.prototype = {
// Adds a local `include` function which allows full recursive include
var returnedFn = opts.client ? fn : function anonymous(data) {
var include = function (path, includeData) {
var d = utils.shallowCopy(createObj(null), data);
var d = utils.shallowCopy(utils.createNullProtoObjWherePossible(), data);
if (includeData) {
d = utils.shallowCopy(d, includeData);
}
return includeFile(path, opts)(d);
};
return fn.apply(opts.context,
[data || createObj(null), escapeFn, include, rethrow]);
[data || utils.createNullProtoObjWherePossible(), escapeFn, include, rethrow]);
};
if (opts.filename && typeof Object.defineProperty === 'function') {
var filename = opts.filename;
Expand Down
26 changes: 26 additions & 0 deletions lib/utils.js
Expand Up @@ -183,3 +183,29 @@ exports.cache = {
exports.hyphenToCamel = function (str) {
return str.replace(/-[a-z]/g, function (match) { return match[1].toUpperCase(); });
};

/**
* Returns a null-prototype object in runtimes that support it
*
* @return {Object} Object, prototype will be set to null where possible
* @static
* @private
*/
exports.createNullProtoObjWherePossible = (function () {
if (typeof Object.create == 'function') {
mde marked this conversation as resolved.
Show resolved Hide resolved
return function () {
return Object.create(null);
};
}
if (!({__proto__: null} instanceof Object)) {
return function () {
return {__proto__: null};
};
}
// Not possible, just pass through
return function () {
return {};
};
})();


10 changes: 6 additions & 4 deletions test/ejs.js
Expand Up @@ -1183,24 +1183,26 @@ suite('identifier validation', function () {
test('invalid outputFunctionName', function() {
assert.throws(function() {
ejs.compile('<p>yay</p>', {outputFunctionName: 'x;console.log(1);x'});
}, /outputFunctionName is not a valid JS identifier/)
}, /outputFunctionName is not a valid JS identifier/);
});

test('invalid localsName', function() {
var locals = Object.create(null);
void(locals); // For linting;
assert.throws(function() {
ejs.compile('<p>yay</p>', {
localsName: 'function(){console.log(1);return locals;}()'});
}, /localsName is not a valid JS identifier/)
}, /localsName is not a valid JS identifier/);
});

test('invalid destructuredLocals', function() {
var locals = {};
void(locals); // For linting;
assert.throws(function() {
ejs.compile('<p>yay</p>', {
destructuredLocals: [
'console.log(1); //'
]});
}, /destructuredLocals\[0\] is not a valid JS identifier/)
}, /destructuredLocals\[0\] is not a valid JS identifier/);
});
});
});