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

feat: Add support for destructuring locals #452

Merged
merged 3 commits into from Sep 12, 2019
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
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 -%>