Skip to content

Commit e10447d

Browse files
authored
Merge pull request #452 from EB-Forks/feat/destructured-locals
feat: Add support for destructuring locals
2 parents 09c0f51 + edc6025 commit e10447d

File tree

5 files changed

+63
-0
lines changed

5 files changed

+63
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ Therefore, we do not recommend using this shortcut.
7373
- `strict` When set to `true`, generated function is in strict mode
7474
- `_with` Whether or not to use `with() {}` constructs. If `false`
7575
then the locals will be stored in the `locals` object. Set to `false` in strict mode.
76+
- `destructuredLocals` An array of local variables that are always destructured from
77+
the locals object, available even in strict mode.
7678
- `localsName` Name to use for the object storing local variables when not using
7779
`with` Defaults to `locals`
7880
- `rmWhitespace` Remove all safe-to-remove whitespace, including leading

docs/jsdoc/options.jsdoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@
1717
* whose name is specified by {@link module:ejs.localsName} (default to
1818
* `locals`).
1919
*
20+
* @property {Boolean} [strict=false]
21+
* Whether to run in strict mode or not.
22+
* Enforces `_with=false`.
23+
*
24+
* @property {String[]} [destructuredLocals=[]]
25+
* An array of local variables that are always destructured from {@link module:ejs.localsName},
26+
* available even in strict mode.
27+
*
2028
* @property {Boolean} [rmWhitespace=false]
2129
* Remove all safe-to-remove whitespace, including leading and trailing
2230
* whitespace. It also enables a safer version of `-%>` line slurping for all

lib/ejs.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ function Template(text, opts) {
526526
options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME;
527527
options.views = opts.views;
528528
options.async = opts.async;
529+
options.destructuredLocals = opts.destructuredLocals;
529530
options.legacyInclude = typeof opts.legacyInclude != 'undefined' ? !!opts.legacyInclude : true;
530531

531532
if (options.strict) {
@@ -575,6 +576,17 @@ Template.prototype = {
575576
if (opts.outputFunctionName) {
576577
prepended += ' var ' + opts.outputFunctionName + ' = __append;' + '\n';
577578
}
579+
if (opts.destructuredLocals && opts.destructuredLocals.length) {
580+
var destructuring = ' var __locals = (' + opts.localsName + ' || {}),\n';
581+
for (var i = 0; i < opts.destructuredLocals.length; i++) {
582+
var name = opts.destructuredLocals[i];
583+
if (i > 0) {
584+
destructuring += ',\n ';
585+
}
586+
destructuring += name + ' = __locals.' + name;
587+
}
588+
prepended += destructuring + ';\n';
589+
}
578590
if (opts._with !== false) {
579591
prepended += ' with (' + opts.localsName + ' || {}) {' + '\n';
580592
appended += ' }' + '\n';

test/ejs.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,42 @@ suite('ejs.compile(str, options)', function () {
124124
assert.equal(ejs.render(fixture('strict.ejs'), {}, {strict: true}), 'true');
125125
});
126126

127+
test('destructuring works in strict mode as an alternative to `with`', function () {
128+
var locals = Object.create(null);
129+
locals.foo = 'bar';
130+
assert.equal(ejs.render(fixture('strict-destructuring.ejs'), locals, {
131+
strict: true,
132+
destructuredLocals: Object.keys(locals),
133+
_with: true
134+
}), locals.foo);
135+
});
136+
137+
test('destructuring works in strict and async mode', function (done) {
138+
try {
139+
eval('(async function() {})');
140+
} catch (e) {
141+
if (e instanceof SyntaxError) {
142+
done();
143+
return;
144+
} else {
145+
throw e;
146+
}
147+
}
148+
149+
var locals = Object.create(null);
150+
locals.foo = 'bar';
151+
ejs.render(fixture('strict-destructuring.ejs'), locals, {
152+
strict: true,
153+
async: true,
154+
destructuredLocals: Object.keys(locals),
155+
}).then(function (value) {
156+
assert.equal(value, locals.foo);
157+
}).then(
158+
() => done(),
159+
e => done(e)
160+
);
161+
});
162+
127163
test('can compile to an async function', function (done) {
128164
try {
129165
eval('(async function() {})');
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<%
2+
// Unspecified execution context should be `undefined` in strict mode
3+
var isReallyStrict = !((function () { return this; })());
4+
-%>
5+
<%= isReallyStrict && foo -%>

0 commit comments

Comments
 (0)