From 40786f500ead6f3d68b12fe184812d44c5ad47ea Mon Sep 17 00:00:00 2001 From: Patrick Steele-Idem Date: Thu, 11 Aug 2016 17:32:00 -0600 Subject: [PATCH] Fixes #355 - status-var/separator not handled when looping over properties --- compiler/ast/ForEachProp.js | 41 ++++++++++++++++--- runtime/forEachPropStatusVar.js | 32 +++++++++++++++ taglibs/core/util/parseFor.js | 4 +- .../render/for-props-separator/expected.html | 1 + .../render/for-props-separator/template.marko | 1 + .../render/for-props-separator/test.js | 1 + .../expected.html | 1 + .../template.marko | 1 + .../for-props-status-var-separator/test.js | 1 + .../render/for-props-status-var/expected.html | 1 + .../for-props-status-var/template.marko | 3 ++ .../render/for-props-status-var/test.js | 1 + 12 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 runtime/forEachPropStatusVar.js create mode 100644 test/autotests/render/for-props-separator/expected.html create mode 100644 test/autotests/render/for-props-separator/template.marko create mode 100644 test/autotests/render/for-props-separator/test.js create mode 100644 test/autotests/render/for-props-status-var-separator/expected.html create mode 100644 test/autotests/render/for-props-status-var-separator/template.marko create mode 100644 test/autotests/render/for-props-status-var-separator/test.js create mode 100644 test/autotests/render/for-props-status-var/expected.html create mode 100644 test/autotests/render/for-props-status-var/template.marko create mode 100644 test/autotests/render/for-props-status-var/test.js diff --git a/compiler/ast/ForEachProp.js b/compiler/ast/ForEachProp.js index 7ae1874eb..b380875e2 100644 --- a/compiler/ast/ForEachProp.js +++ b/compiler/ast/ForEachProp.js @@ -8,6 +8,8 @@ class ForEachProp extends Node { this.nameVarName = def.nameVarName; this.valueVarName = def.valueVarName; this.in = def.in; + this.separator = def.separator; + this.statusVarName = def.statusVarName; this.body = this.makeContainer(def.body); ok(this.nameVarName, '"nameVarName" is required'); @@ -20,16 +22,45 @@ class ForEachProp extends Node { var valueVarName = this.valueVarName; var inExpression = this.in; var body = this.body; + var separator = this.separator; + var statusVarName = this.statusVarName; + + if (separator && !statusVarName) { + statusVarName = '__loop'; + } var builder = codegen.builder; - let forEachVarName = codegen.addStaticVar('forEachProp', '__helpers.fp'); + if (statusVarName) { + let helperVar = builder.require(builder.literal('marko/runtime/forEachPropStatusVar')); + let forEachVarName = codegen.addStaticVar('forEacPropStatusVar', helperVar); + let body = this.body; - return builder.functionCall(forEachVarName, [ - inExpression, - builder.functionDeclaration(null, [nameVarName, valueVarName], body) - ]); + if (separator) { + let isNotLastTest = builder.functionCall( + builder.memberExpression(statusVarName, builder.identifier('isLast')), + []); + isNotLastTest = builder.negate(isNotLastTest); + + body = body.items.concat([ + builder.ifStatement(isNotLastTest, [ + builder.text(separator) + ]) + ]); + } + + return builder.functionCall(forEachVarName, [ + inExpression, + builder.functionDeclaration(null, [nameVarName, valueVarName, statusVarName], body) + ]); + } else { + let forEachVarName = codegen.addStaticVar('forEachProp', '__helpers.fp'); + return builder.functionCall(forEachVarName, [ + inExpression, + builder.functionDeclaration(null, [nameVarName, valueVarName], body) + ]); + } } walk(walker) { diff --git a/runtime/forEachPropStatusVar.js b/runtime/forEachPropStatusVar.js new file mode 100644 index 000000000..39aca95be --- /dev/null +++ b/runtime/forEachPropStatusVar.js @@ -0,0 +1,32 @@ +function LoopStatus(getLength, isLast, isFirst, getIndex) { + this.getLength = getLength; + this.isLast = isLast; + this.isFirst = isFirst; + this.getIndex = getIndex; +} + +module.exports = function forEachPropStatusVar(object, callback) { + var keys = Object.keys(object); + + var i = 0; + var len = keys.length; + var loopStatus = new LoopStatus( + function getLength() { + return len; + }, + function isLast() { + return i === len - 1; + }, + function isFirst() { + return i === 0; + }, + function getIndex() { + return i; + }); + + for (; i < len; i++) { + var key = keys[i]; + var value = object[key]; + callback(key, value, loopStatus); + } +}; \ No newline at end of file diff --git a/taglibs/core/util/parseFor.js b/taglibs/core/util/parseFor.js index cfc8c5ad5..d04dc20ab 100644 --- a/taglibs/core/util/parseFor.js +++ b/taglibs/core/util/parseFor.js @@ -333,7 +333,9 @@ module.exports = function(str) { 'loopType': loopType, 'nameVarName': nameVarName, 'valueVarName': valueVarName, - 'in': inExpression + 'in': inExpression, + 'separator': separatorExpression, + 'statusVarName': statusVarName }; } else if (loopType === 'ForRange') { return { diff --git a/test/autotests/render/for-props-separator/expected.html b/test/autotests/render/for-props-separator/expected.html new file mode 100644 index 000000000..6de235e41 --- /dev/null +++ b/test/autotests/render/for-props-separator/expected.html @@ -0,0 +1 @@ +[foo=low],[bar=high] \ No newline at end of file diff --git a/test/autotests/render/for-props-separator/template.marko b/test/autotests/render/for-props-separator/template.marko new file mode 100644 index 000000000..1cf5ad6a5 --- /dev/null +++ b/test/autotests/render/for-props-separator/template.marko @@ -0,0 +1 @@ +[${name}=${value}] \ No newline at end of file diff --git a/test/autotests/render/for-props-separator/test.js b/test/autotests/render/for-props-separator/test.js new file mode 100644 index 000000000..c4013b344 --- /dev/null +++ b/test/autotests/render/for-props-separator/test.js @@ -0,0 +1 @@ +exports.templateData = {}; diff --git a/test/autotests/render/for-props-status-var-separator/expected.html b/test/autotests/render/for-props-status-var-separator/expected.html new file mode 100644 index 000000000..3d043d356 --- /dev/null +++ b/test/autotests/render/for-props-status-var-separator/expected.html @@ -0,0 +1 @@ +0) [foo=low],1) [bar=high] \ No newline at end of file diff --git a/test/autotests/render/for-props-status-var-separator/template.marko b/test/autotests/render/for-props-status-var-separator/template.marko new file mode 100644 index 000000000..22d3a1f4e --- /dev/null +++ b/test/autotests/render/for-props-status-var-separator/template.marko @@ -0,0 +1 @@ +${loop.getIndex()}) [${name}=${value}] \ No newline at end of file diff --git a/test/autotests/render/for-props-status-var-separator/test.js b/test/autotests/render/for-props-status-var-separator/test.js new file mode 100644 index 000000000..c4013b344 --- /dev/null +++ b/test/autotests/render/for-props-status-var-separator/test.js @@ -0,0 +1 @@ +exports.templateData = {}; diff --git a/test/autotests/render/for-props-status-var/expected.html b/test/autotests/render/for-props-status-var/expected.html new file mode 100644 index 000000000..8dd13e24a --- /dev/null +++ b/test/autotests/render/for-props-status-var/expected.html @@ -0,0 +1 @@ +0) [foo=low]1) [bar=high] \ No newline at end of file diff --git a/test/autotests/render/for-props-status-var/template.marko b/test/autotests/render/for-props-status-var/template.marko new file mode 100644 index 000000000..5c1db9ca6 --- /dev/null +++ b/test/autotests/render/for-props-status-var/template.marko @@ -0,0 +1,3 @@ + + ${loop.getIndex()}) [${name}=${value}] + \ No newline at end of file diff --git a/test/autotests/render/for-props-status-var/test.js b/test/autotests/render/for-props-status-var/test.js new file mode 100644 index 000000000..c4013b344 --- /dev/null +++ b/test/autotests/render/for-props-status-var/test.js @@ -0,0 +1 @@ +exports.templateData = {};