Repository: twigjs/twig.js (js)

Issue:
Ternary expressions without true fork work in PHP Twig, fail in twig.js
`php -r "require_once './vendor/autoload.php'; \$loader = new \Twig\Loader\ArrayLoader(['index' => '{{myvar ? : 0}}']); \$twig = new \Twig\Environment(\$loader); echo \$twig->render('index', ['myvar' => FALSE]);"`

This outputs "0".

`node -e 'const Twig = require("twig"); console.log(Twig.twig({data: "{{ myvar ? : 0 }}"}).render({myvar: false}))'`

This outputs 

Error compiling twig template undefined: 
TwigException: Twig.expression.type.operator.binary cannot follow a Twig.expression.type.operator.binary at template:6 near ':...'

I'm not sure that ternary expressions of the form `myvar ? : "falsy output"` are a good idea, but real Twig supports them, so this library really ought to as well.


Pull request description:
Allow whitespace in extended ternary operator for false conditions.
Fixes #903 

Patch:
diff --git a/src/twig.expression.js b/src/twig.expression.js
index 60040bd..50f85d6 100644
--- a/src/twig.expression.js
+++ b/src/twig.expression.js
@@ -214,7 +214,7 @@ module.exports = function (Twig) {
             type: Twig.expression.type.operator.binary,
             // Match any of ??, ?:, +, *, /, -, %, ~, <=>, <, <=, >, >=, !=, ==, **, ?, :, and, b-and, or, b-or, b-xor, in, not in
             // and, or, in, not in, matches, starts with, ends with can be followed by a space or parenthesis
-            regex: /(^\?\?|^\?:|^(b-and)|^(b-or)|^(b-xor)|^[+\-~%?]|^(<=>)|^[:](?!\d\])|^[!=]==?|^[!<>]=?|^\*\*?|^\/\/?|^(and)[(|\s+]|^(or)[(|\s+]|^(in)[(|\s+]|^(not in)[(|\s+]|^(matches)|^(starts with)|^(ends with)|^\.\.)/,
+            regex: /(^\?\?|^\?\s*:|^(b-and)|^(b-or)|^(b-xor)|^[+\-~%?]|^(<=>)|^[:](?!\d\])|^[!=]==?|^[!<>]=?|^\*\*?|^\/\/?|^(and)[(|\s+]|^(or)[(|\s+]|^(in)[(|\s+]|^(not in)[(|\s+]|^(matches)|^(starts with)|^(ends with)|^\.\.)/,
             next: Twig.expression.set.expressions,
             transform(match, tokens) {
                 switch (match[0]) {
@@ -232,6 +232,10 @@ module.exports = function (Twig) {
             compile(token, stack, output) {
                 delete token.match;
 
+                if (token.value.match(/^\?\s*:/)) {
+                    token.value = '?:';
+                }
+
                 token.value = token.value.trim();
                 const {value} = token;
                 const operator = Twig.expression.operator.lookup(value, token);


Test patch:
diff --git a/test/test.expressions.operators.js b/test/test.expressions.operators.js
index 08fddab..0840e3f 100644
--- a/test/test.expressions.operators.js
+++ b/test/test.expressions.operators.js
@@ -53,6 +53,15 @@ describe('Twig.js Expression Operators ->', function () {
             outputT.should.equal('one');
             outputF.should.equal('two');
         });
+
+        it('should support the extended ternary operator for false conditions (with whitespace in between operators)', function () {
+            const testTemplate = twig({data: '{{ a ?   : b }}'});
+            const outputT = testTemplate.render({a: 'one', b: 'two'});
+            const outputF = testTemplate.render({a: false, b: 'two'});
+
+            outputT.should.equal('one');
+            outputF.should.equal('two');
+        });
     });
 
     describe('?? ->', function () {