From f6d468039205bba7929394dcf27276e5226e0564 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 20 Mar 2020 12:05:30 +0200 Subject: [PATCH 1/3] Add loop metadata. --- lib/index.js | 19 ++++++++++ test/expect/loop_metadata.html | 24 +++++++++++++ test/expect/loop_nested_metadata.html | 47 +++++++++++++++++++++++++ test/fixtures/loop_metadata.html | 10 ++++++ test/fixtures/loop_nested_metadata.html | 16 +++++++++ test/test-loops.js | 12 +++++++ 6 files changed, 128 insertions(+) create mode 100644 test/expect/loop_metadata.html create mode 100644 test/expect/loop_nested_metadata.html create mode 100644 test/fixtures/loop_metadata.html create mode 100644 test/fixtures/loop_nested_metadata.html diff --git a/lib/index.js b/lib/index.js index 15c4eb7..1aac543 100644 --- a/lib/index.js +++ b/lib/index.js @@ -324,10 +324,29 @@ function walk (opts, nodes) { // run the loop, different types of loops for arrays and objects if (Array.isArray(target)) { for (let index = 0; index < target.length; index++) { + opts.locals.loop = { + index: index, + remaining: target.length - index - 1, + first: target.indexOf(target[index]) === 0, + last: index + 1 == target.length, + length: target.length + } + m.push(executeLoop(keys, target[index], index, opts.locals, treeString)) } } else { for (let key in target) { + const arr = Object.keys(target) + const index = arr.indexOf(key) + + opts.locals.loop = { + index: index, + remaining: arr.length - index - 1, + first: index === 0, + last: index + 1 == arr.length, + length: arr.length + } + m.push(executeLoop(keys, target[key], key, opts.locals, treeString)) } } diff --git a/test/expect/loop_metadata.html b/test/expect/loop_metadata.html new file mode 100644 index 0000000..7bcea1d --- /dev/null +++ b/test/expect/loop_metadata.html @@ -0,0 +1,24 @@ + diff --git a/test/expect/loop_nested_metadata.html b/test/expect/loop_nested_metadata.html new file mode 100644 index 0000000..025c2e8 --- /dev/null +++ b/test/expect/loop_nested_metadata.html @@ -0,0 +1,47 @@ + diff --git a/test/fixtures/loop_metadata.html b/test/fixtures/loop_metadata.html new file mode 100644 index 0000000..0ed8021 --- /dev/null +++ b/test/fixtures/loop_metadata.html @@ -0,0 +1,10 @@ + diff --git a/test/fixtures/loop_nested_metadata.html b/test/fixtures/loop_nested_metadata.html new file mode 100644 index 0000000..c4be33b --- /dev/null +++ b/test/fixtures/loop_nested_metadata.html @@ -0,0 +1,16 @@ + diff --git a/test/test-loops.js b/test/test-loops.js index 22c8b19..952ca0c 100644 --- a/test/test-loops.js +++ b/test/test-loops.js @@ -120,3 +120,15 @@ test('Loops - expression error', (t) => { t.is(err.message, 'Invalid or unexpected token') }) }) + +test('Loops - metadata', (t) => { + return process(t, 'loop_metadata', { + locals: { items: [1, 2, 3] } + }) +}) + +test('Loops - nested metadata', (t) => { + return process(t, 'loop_nested_metadata', { + locals: { items: { foo: [1, 2], bar: [3, 4] } } + }) +}) From 55cc66ba1058156ab9ac0dc05311e21152aaa67e Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 20 Mar 2020 18:53:01 +0200 Subject: [PATCH 2/3] Extract loop metadata object to function. --- lib/index.js | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/lib/index.js b/lib/index.js index 1aac543..ca42e15 100644 --- a/lib/index.js +++ b/lib/index.js @@ -55,6 +55,29 @@ function executeScope (scope, locals, node) { return walk({ locals: scope }, node.content) } +/** + * @description Returns an object containing loop metadata + * + * @method getLoopMeta + * + * @param {Integer|Object} index Current iteration + * @param {Object} target Object being iterated + * + * @return {Object} Object containing loop metadata + */ +function getLoopMeta (index, target) { + index = Array.isArray(target) ? index : Object.keys(target).indexOf(index) + const arr = Array.isArray(target) ? target : Object.keys(target) + + return { + index: index, + remaining: arr.length - index - 1, + first: arr.indexOf(arr[index]) === 0, + last: index + 1 == arr.length, + length: arr.length + } +} + /** * @author Jeff Escalante Denis (@jescalan), * Denis Malinochkin (mrmlnc), @@ -324,29 +347,12 @@ function walk (opts, nodes) { // run the loop, different types of loops for arrays and objects if (Array.isArray(target)) { for (let index = 0; index < target.length; index++) { - opts.locals.loop = { - index: index, - remaining: target.length - index - 1, - first: target.indexOf(target[index]) === 0, - last: index + 1 == target.length, - length: target.length - } - + opts.locals.loop = getLoopMeta(index, target) m.push(executeLoop(keys, target[index], index, opts.locals, treeString)) } } else { for (let key in target) { - const arr = Object.keys(target) - const index = arr.indexOf(key) - - opts.locals.loop = { - index: index, - remaining: arr.length - index - 1, - first: index === 0, - last: index + 1 == arr.length, - length: arr.length - } - + opts.locals.loop = getLoopMeta(key, target) m.push(executeLoop(keys, target[key], key, opts.locals, treeString)) } } From 3cff5cbfcdcd3a048061feb47ff0f0a8e0f4f3d7 Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 20 Mar 2020 19:26:17 +0200 Subject: [PATCH 3/3] Add loop metadata docs. --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index 999d940..135ba3a 100644 --- a/README.md +++ b/README.md @@ -246,6 +246,29 @@ The value of the `loop` attribute is not a pure expressions evaluation, and it d So you don't need to declare all the available variables (in this case, the index is skipped), and the expressions after `in` doesn't need to be a local variable, it can be any expressions. +#### Loop meta + +Inside a loop, you have access to a special `loop` object, which contains information about the loop currently being executed: + +- `loop.index` - the current iteration of the loop (0 indexed) +- `loop.remaining` - number of iterations until the end (0 indexed) +- `loop.first` - boolean indicating if it's the first iteration +- `loop.last` - boolean indicating if it's the last iteration +- `loop.length` - total number of items + +Example: + +```html + +
  • Item value: {{ item }}
  • +
  • Current iteration of the loop: {{ loop.index }}
  • +
  • Number of iterations until the end: {{ loop.remaining }}
  • +
  • This {{ loop.first ? 'is' : 'is not' }} the first iteration
  • +
  • This {{ loop.last ? 'is' : 'is not' }} the last iteration
  • +
  • Total number of items: {{ loop.length }}
  • +
    +``` + ### Scopes You can replace locals inside certain area wrapped in a `` tag. For example you can use it after [posthtml-include](https://github.com/posthtml/posthtml-include)