Skip to content

Commit

Permalink
Merge pull request #452 from EB-Forks/feat/destructured-locals
Browse files Browse the repository at this point in the history
feat: Add support for destructuring locals
  • Loading branch information
mde committed Sep 12, 2019
2 parents 09c0f51 + edc6025 commit e10447d
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -73,6 +73,8 @@ Therefore, we do not recommend using this shortcut.
- `strict` When set to `true`, generated function is in strict mode
- `_with` Whether or not to use `with() {}` constructs. If `false`
then the locals will be stored in the `locals` object. Set to `false` in strict mode.
- `destructuredLocals` An array of local variables that are always destructured from
the locals object, available even in strict mode.
- `localsName` Name to use for the object storing local variables when not using
`with` Defaults to `locals`
- `rmWhitespace` Remove all safe-to-remove whitespace, including leading
Expand Down
8 changes: 8 additions & 0 deletions docs/jsdoc/options.jsdoc
Expand Up @@ -17,6 +17,14 @@
* whose name is specified by {@link module:ejs.localsName} (default to
* `locals`).
*
* @property {Boolean} [strict=false]
* Whether to run in strict mode or not.
* Enforces `_with=false`.
*
* @property {String[]} [destructuredLocals=[]]
* An array of local variables that are always destructured from {@link module:ejs.localsName},
* available even in strict mode.
*
* @property {Boolean} [rmWhitespace=false]
* Remove all safe-to-remove whitespace, including leading and trailing
* whitespace. It also enables a safer version of `-%>` line slurping for all
Expand Down
12 changes: 12 additions & 0 deletions lib/ejs.js
Expand Up @@ -526,6 +526,7 @@ function Template(text, opts) {
options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME;
options.views = opts.views;
options.async = opts.async;
options.destructuredLocals = opts.destructuredLocals;
options.legacyInclude = typeof opts.legacyInclude != 'undefined' ? !!opts.legacyInclude : true;

if (options.strict) {
Expand Down Expand Up @@ -575,6 +576,17 @@ Template.prototype = {
if (opts.outputFunctionName) {
prepended += ' var ' + opts.outputFunctionName + ' = __append;' + '\n';
}
if (opts.destructuredLocals && opts.destructuredLocals.length) {
var destructuring = ' var __locals = (' + opts.localsName + ' || {}),\n';
for (var i = 0; i < opts.destructuredLocals.length; i++) {
var name = opts.destructuredLocals[i];
if (i > 0) {
destructuring += ',\n ';
}
destructuring += name + ' = __locals.' + name;
}
prepended += destructuring + ';\n';
}
if (opts._with !== false) {
prepended += ' with (' + opts.localsName + ' || {}) {' + '\n';
appended += ' }' + '\n';
Expand Down
36 changes: 36 additions & 0 deletions test/ejs.js
Expand Up @@ -124,6 +124,42 @@ suite('ejs.compile(str, options)', function () {
assert.equal(ejs.render(fixture('strict.ejs'), {}, {strict: true}), 'true');
});

test('destructuring works in strict mode as an alternative to `with`', function () {
var locals = Object.create(null);
locals.foo = 'bar';
assert.equal(ejs.render(fixture('strict-destructuring.ejs'), locals, {
strict: true,
destructuredLocals: Object.keys(locals),
_with: true
}), locals.foo);
});

test('destructuring works in strict and async mode', function (done) {
try {
eval('(async function() {})');
} catch (e) {
if (e instanceof SyntaxError) {
done();
return;
} else {
throw e;
}
}

var locals = Object.create(null);
locals.foo = 'bar';
ejs.render(fixture('strict-destructuring.ejs'), locals, {
strict: true,
async: true,
destructuredLocals: Object.keys(locals),
}).then(function (value) {
assert.equal(value, locals.foo);
}).then(
() => done(),
e => done(e)
);
});

test('can compile to an async function', function (done) {
try {
eval('(async function() {})');
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/strict-destructuring.ejs
@@ -0,0 +1,5 @@
<%
// Unspecified execution context should be `undefined` in strict mode
var isReallyStrict = !((function () { return this; })());
-%>
<%= isReallyStrict && foo -%>

0 comments on commit e10447d

Please sign in to comment.