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

Add support for ES iterators #1058

Merged
merged 1 commit into from
Jan 28, 2018
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ master (unreleased)

* Support objects created with Object.create(null). fixes [#468](https://github.com/mozilla/nunjucks/issues/468)

* Support ESNext iterators, using Array.from. Merge of
[#760](https://github.com/mozilla/nunjucks/pull/760)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean #1058? 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, but I also want to mention that PR and I didn't understand what the format was. Would you mind making a PR that corrects this, please?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@maxnordlund maxnordlund Feb 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you


3.0.1 (May 24 2017)
-------------------
Expand Down
17 changes: 17 additions & 0 deletions docs/templating.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,23 @@ var food = {
The [`dictsort`](http://jinja.pocoo.org/docs/templates/#dictsort) filter is
available for sorting objects when iterating over them.

ES iterators are supported, like the new builtin Map and Set. But also
anything implementing the iterator protocal.

```js
var fruits = new Map([
["banana", "yellow"],
["apple", "red"],
["peach", "pink"]
])
```

```jinja
{% for fruit, color in fruits %}
Did you know that {{ fruit }} is {{ color }}?
{% endfor %}
```

Additionally, Nunjucks will unpack arrays into variables:

```js
Expand Down
5 changes: 3 additions & 2 deletions nunjucks/src/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ class Compiler extends Obj {
this.emitLine(';');

this.emit(`if(${arr}) {`);
this.emitLine(arr + ' = runtime.fromIterator(' + arr + ');');

// If multiple names are passed, we need to bind them
// appropriately
Expand Down Expand Up @@ -754,9 +755,9 @@ class Compiler extends Obj {

this.emitLine('frame = frame.push();');

this.emit('var ' + arr + ' = ');
this.emit('var ' + arr + ' = runtime.fromIterator(');
this._compileExpression(node.arr, frame);
this.emitLine(';');
this.emitLine(');');

if (node.name instanceof nodes.Array) {
const arrayLen = node.name.children.length;
Expand Down
18 changes: 17 additions & 1 deletion nunjucks/src/runtime.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
'use strict';

var lib = require('./lib');
var arrayFrom = Array.from;
var supportsIterators = (
typeof Symbol === 'function' && Symbol.iterator && typeof arrayFrom === 'function'
);


// Frames keep track of scoping both at compile-time and run-time so
// we know how to access variables. Block tags can introduce special
Expand Down Expand Up @@ -342,6 +347,16 @@ function asyncAll(arr, dimen, func, cb) {
}
}

function fromIterator(arr) {
if (arr == null || lib.isArray(arr)) {
return arr;
} else if (supportsIterators && Symbol.iterator in arr) {
return arrayFrom(arr);
} else {
return arr;
}
}

module.exports = {
Frame: Frame,
makeMacro: makeMacro,
Expand All @@ -360,5 +375,6 @@ module.exports = {
markSafe: markSafe,
asyncEach: asyncEach,
asyncAll: asyncAll,
inOperator: lib.inOperator
inOperator: lib.inOperator,
fromIterator: fromIterator
};
36 changes: 36 additions & 0 deletions tests/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,42 @@
},
'showing test\nshowing 1\nshowing 2\nshowing 3\n');
});
/* global Set */
it('should work with Set builtin', function() {
if (typeof Set === 'undefined') {
this.skip();
} else {
equal('{% ' + block + ' i in set %}{{ i }}{% ' + end + ' %}',
{ set: new Set([1, 2, 3, 4, 5]) },
'12345');

equal('{% ' + block + ' i in set %}{{ i }}{% else %}empty{% ' + end + ' %}',
{ set: new Set([1, 2, 3, 4, 5]) },
'12345');

equal('{% ' + block + ' i in set %}{{ i }}{% else %}empty{% ' + end + ' %}',
{ set: new Set() },
'empty');
}
});
/* global Map */
it('should work with Map builtin', function() {
if (typeof Map === 'undefined') {
this.skip();
} else {
equal('{% ' + block + ' k, v in map %}[{{ k }},{{ v }}]{% ' + end + ' %}',
{ map: new Map([[1, 2], [3, 4], [5, 6]]) },
'[1,2][3,4][5,6]');

equal('{% ' + block + ' k, v in map %}[{{ k }},{{ v }}]{% else %}empty{% ' + end + ' %}',
{ map: new Map([[1, 2], [3, 4], [5, 6]]) },
'[1,2][3,4][5,6]');

equal('{% ' + block + ' k, v in map %}[{{ k }},{{ v }}]{% else %}empty{% ' + end + ' %}',
{ map: new Map() },
'empty');
}
});
});
}

Expand Down