From 0f9eb88232f789483a9583dfa0c3adab40812a82 Mon Sep 17 00:00:00 2001 From: Denis Malinochkin Date: Thu, 1 Dec 2016 20:12:06 +0300 Subject: [PATCH 1/6] docs(index): mrmlnc is Denis Malinochkin --- lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index 7039ecc..8123247 100644 --- a/lib/index.js +++ b/lib/index.js @@ -57,7 +57,7 @@ function executeScope (scope, locals, node) { /** * @author Jeff Escalante Denis (@jescalan), - * Malinochkin (mrmlnc), + * Denis Malinochkin (mrmlnc), * Michael Ciniawsky (@michael-ciniawsky) * @description Expressions Plugin for PostHTML * @license MIT From c86596953e21d8ef6dc59c4d81774eb737341919 Mon Sep 17 00:00:00 2001 From: Denis Malinochkin Date: Thu, 1 Dec 2016 20:12:40 +0300 Subject: [PATCH 2/6] fix(index): copy/paste for loops --- lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index 8123247..5af56c7 100644 --- a/lib/index.js +++ b/lib/index.js @@ -229,7 +229,7 @@ function walk (opts, nodes) { if (node.tag === loops[0]) { // handle syntax error if (!(node.attrs && node.attrs.loop)) { - throw new Error(`the "${conditionals[1]}" tag must have a "loop" attribute`) + throw new Error(`the "${loops[0]}" tag must have a "loop" attribute`) } // parse the "loop" param From 88058d1c64ced99c94b802cd076d6c812c7ed4ef Mon Sep 17 00:00:00 2001 From: Denis Malinochkin Date: Tue, 6 Dec 2016 12:26:22 +0300 Subject: [PATCH 3/6] feat(index): add switch statement --- lib/index.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index 5af56c7..a010997 100644 --- a/lib/index.js +++ b/lib/index.js @@ -10,7 +10,7 @@ const revertBackupedLocals = require('./backup').revert const placeholders = require('./placeholders') let delimitersSettings = [] -let conditionals, loops, scopes +let conditionals, switches, loops, scopes /** * @description Creates a set of local variables within the loop, and evaluates all nodes within the loop, returning their contents @@ -84,6 +84,7 @@ module.exports = function postHTMLExpressions (options) { delimiters: ['{{', '}}'], unescapeDelimiters: ['{{{', '}}}'], conditionalTags: ['if', 'elseif', 'else'], + switchTags: ['switch', 'case', 'default'], loopTags: ['each'], scopeTags: ['scope'] }, options) @@ -92,6 +93,7 @@ module.exports = function postHTMLExpressions (options) { loops = options.loopTags scopes = options.scopeTags conditionals = options.conditionalTags + switches = options.switchTags // make a RegExp's to search for placeholders let before = escapeRegexpString(options.delimiters[0]) @@ -169,7 +171,6 @@ function walk (opts, nodes) { // сalculate the first path of condition expression let expressionIndex = 1 - let expression = `if (${node.attrs.condition}) { 0 } ` const branches = [node.content] @@ -225,6 +226,50 @@ function walk (opts, nodes) { return m } + // switch tag + if (node.tag === switches[0]) { + // throw an error if it's missing the "condition" attribute + if (!(node.attrs && node.attrs.expression)) { + throw new Error(`the "${switches[0]}" tag must have a "expression" attribute`) + } + + // сalculate the first path of condition expression + let expressionIndex = 0 + let expression = `switch(${node.attrs.expression}) {` + + const branches = [] + + for (let i = 0; i < node.content.length; i++) { + const currentNode = node.content[i] + if (typeof currentNode === 'string') { + continue + } + + if (currentNode.tag === switches[1]) { + // throw an error if it's missing the "n" attribute + if (!(currentNode.attrs && currentNode.attrs.n)) { + throw new Error(`the "${switches[1]}" tag must have a "n" attribute`) + } + expression += `case ${currentNode.attrs.n}: {${expressionIndex++}}; break; ` + } else if (currentNode.tag === switches[2]) { + expression += `default: {${expressionIndex++}}` + } else { + throw new Error(`the "${switches[0]}" tag can contain only "${switches[1]}" tags and one "${switches[2]}" tag`) + } + branches.push(currentNode) + } + + expression += '}' + + // evaluate the expression, get the winning switch branch + const branch = branches[vm.runInContext(expression, ctx)] + + // recursive evaluate of branch + Array.prototype.push.apply(m, walk(opts, branch.content)) + + return m + } + // parse loops if (node.tag === loops[0]) { // handle syntax error From 53c64c4c5bf610f190146c9690a5f6547d6c505e Mon Sep 17 00:00:00 2001 From: Denis Malinochkin Date: Tue, 6 Dec 2016 12:27:48 +0300 Subject: [PATCH 4/6] test(*): add tests for switch statement --- test/expect/switch.html | 2 + test/expect/switch_customtag.html | 2 + test/expect/switch_default.html | 2 + test/expect/switch_nested.html | 2 + test/expect/switch_number.html | 2 + test/fixtures/switch.html | 14 +++++++ test/fixtures/switch_bad_flow.html | 9 +++++ test/fixtures/switch_customtag.html | 14 +++++++ test/fixtures/switch_default.html | 14 +++++++ test/fixtures/switch_nested.html | 27 +++++++++++++ test/fixtures/switch_no_attr.html | 8 ++++ test/fixtures/switch_no_case_attr.html | 8 ++++ test/fixtures/switch_number.html | 11 ++++++ test/index.js | 54 ++++++++++++++++++++++++++ 14 files changed, 169 insertions(+) create mode 100644 test/expect/switch.html create mode 100644 test/expect/switch_customtag.html create mode 100644 test/expect/switch_default.html create mode 100644 test/expect/switch_nested.html create mode 100644 test/expect/switch_number.html create mode 100644 test/fixtures/switch.html create mode 100644 test/fixtures/switch_bad_flow.html create mode 100644 test/fixtures/switch_customtag.html create mode 100644 test/fixtures/switch_default.html create mode 100644 test/fixtures/switch_nested.html create mode 100644 test/fixtures/switch_no_attr.html create mode 100644 test/fixtures/switch_no_case_attr.html create mode 100644 test/fixtures/switch_number.html diff --git a/test/expect/switch.html b/test/expect/switch.html new file mode 100644 index 0000000..03167ab --- /dev/null +++ b/test/expect/switch.html @@ -0,0 +1,2 @@ + +

Hello, from Germany!

diff --git a/test/expect/switch_customtag.html b/test/expect/switch_customtag.html new file mode 100644 index 0000000..f76db89 --- /dev/null +++ b/test/expect/switch_customtag.html @@ -0,0 +1,2 @@ + +

Hello, from United States!

diff --git a/test/expect/switch_default.html b/test/expect/switch_default.html new file mode 100644 index 0000000..50adb3b --- /dev/null +++ b/test/expect/switch_default.html @@ -0,0 +1,2 @@ + +

Hello, from Earth!

diff --git a/test/expect/switch_nested.html b/test/expect/switch_nested.html new file mode 100644 index 0000000..b4c53c3 --- /dev/null +++ b/test/expect/switch_nested.html @@ -0,0 +1,2 @@ + +

Hello, from Russia!

diff --git a/test/expect/switch_number.html b/test/expect/switch_number.html new file mode 100644 index 0000000..2d7deef --- /dev/null +++ b/test/expect/switch_number.html @@ -0,0 +1,2 @@ + +

3

diff --git a/test/fixtures/switch.html b/test/fixtures/switch.html new file mode 100644 index 0000000..b97d2d7 --- /dev/null +++ b/test/fixtures/switch.html @@ -0,0 +1,14 @@ + + +

Hello, from Russia!

+
+ +

Hello, from Germany!

+
+ +

Hello, from United States!

+
+ +

Hello, from Earth!

+
+
diff --git a/test/fixtures/switch_bad_flow.html b/test/fixtures/switch_bad_flow.html new file mode 100644 index 0000000..b82d830 --- /dev/null +++ b/test/fixtures/switch_bad_flow.html @@ -0,0 +1,9 @@ + + +

Hello, from Russia!

+
+

What?

+ +

Hello, from Earth!

+
+
diff --git a/test/fixtures/switch_customtag.html b/test/fixtures/switch_customtag.html new file mode 100644 index 0000000..18844f7 --- /dev/null +++ b/test/fixtures/switch_customtag.html @@ -0,0 +1,14 @@ + + +

Hello, from Russia!

+
+ +

Hello, from Germany!

+
+ +

Hello, from United States!

+
+ +

Hello, from Earth!

+
+
diff --git a/test/fixtures/switch_default.html b/test/fixtures/switch_default.html new file mode 100644 index 0000000..b97d2d7 --- /dev/null +++ b/test/fixtures/switch_default.html @@ -0,0 +1,14 @@ + + +

Hello, from Russia!

+
+ +

Hello, from Germany!

+
+ +

Hello, from United States!

+
+ +

Hello, from Earth!

+
+
diff --git a/test/fixtures/switch_nested.html b/test/fixtures/switch_nested.html new file mode 100644 index 0000000..f2210cd --- /dev/null +++ b/test/fixtures/switch_nested.html @@ -0,0 +1,27 @@ + + +

Hello, from Russia!

+
+ +

Hello, from Germany!

+
+ +

Hello, from United States!

+
+ + + +

Hello, from Russia!

+
+ +

Hello, from Germany!

+
+ +

Hello, from United States!

+
+ +

Hello, from Earth!

+
+
+
+
diff --git a/test/fixtures/switch_no_attr.html b/test/fixtures/switch_no_attr.html new file mode 100644 index 0000000..6542c23 --- /dev/null +++ b/test/fixtures/switch_no_attr.html @@ -0,0 +1,8 @@ + + +

Hello, from Russia!

+
+ +

Hello, from Earth!

+
+
diff --git a/test/fixtures/switch_no_case_attr.html b/test/fixtures/switch_no_case_attr.html new file mode 100644 index 0000000..370ac79 --- /dev/null +++ b/test/fixtures/switch_no_case_attr.html @@ -0,0 +1,8 @@ + + +

Hello, from Russia!

+
+ +

Hello, from Earth!

+
+
diff --git a/test/fixtures/switch_number.html b/test/fixtures/switch_number.html new file mode 100644 index 0000000..e1ab546 --- /dev/null +++ b/test/fixtures/switch_number.html @@ -0,0 +1,11 @@ + + +

2

+
+ +

3

+
+ +

default

+
+
diff --git a/test/index.js b/test/index.js index 5f1bb14..37aa044 100644 --- a/test/index.js +++ b/test/index.js @@ -120,6 +120,60 @@ test('Conditionals - expression in else/elseif', (t) => { }) }) +test('Switch', (t) => { + return Promise.all([ + process(t, 'switch', { locals: { country: 'germany' } }) + ]) +}) + +test('Switch - default branch', (t) => { + return Promise.all([ + process(t, 'switch_default', { locals: { country: 'venezuela' } }) + ]) +}) + +test('Switch - nested', (t) => { + return Promise.all([ + process(t, 'switch_nested', { + locals: { + country_one: 'venezuela', + country_two: 'russia' + } + }) + ]) +}) + +test('Switch - custom tag', (t) => { + return process(t, 'switch_customtag', { + switchTags: ['s', 'c', 'd'], + locals: { country: 'us' } + }) +}) + +test('Switch - dynamic expression', (t) => { + return Promise.all([ + process(t, 'switch_number', { locals: { items: [1, 2, 3] } }) + ]) +}) + +test('Switch - no switch attribute', (t) => { + return error('switch_no_attr', (err) => { + t.truthy(err.toString() === 'Error: the "switch" tag must have a "expression" attribute') + }) +}) + +test('Switch - no case attribute', (t) => { + return error('switch_no_case_attr', (err) => { + t.truthy(err.toString() === 'Error: the "case" tag must have a "n" attribute') + }) +}) + +test('Switch - bad flow', (t) => { + return error('switch_bad_flow', (err) => { + t.truthy(err.toString() === 'the "switch" tag can contain only "case" tags and one "default" tag') + }) +}) + test('Loops', (t) => { return process(t, 'loop', { locals: { items: [1, 2, 3] } }) }) From 3a5d5dce1ab22d4b405819f61104fda3a9cfa029 Mon Sep 17 00:00:00 2001 From: Denis Malinochkin Date: Tue, 6 Dec 2016 12:38:59 +0300 Subject: [PATCH 5/6] docs(README): add docs for switch statement --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index 93a4751..ab89e2c 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ You have full control over the delimiters used for injecting locals, as well as | **unescapeDelimiters** | `['{{{', '}}}']` | Array containing beginning and ending delimiters for unescaped locals | | **locals** | `{}` | Object containing any local variables you want to be available inside your expressions | | **conditionalTags** | `['if', 'elseif', 'else']` | Array containing names for tags used for `if/else if/else` statements | +| **switchTags** | `['switch', 'case', 'default']` | Array containing names for tags used for `switch/case/default` statements | | **loopTags** | `['each']` | Array containing names for `for` loops | | **scopeTags** | `['scope']` | Array containing names for scopes | @@ -155,6 +156,34 @@ else p Foo is probably just foo in the end. ``` +### Switch statement + +Switch statements act like streamline conditionals. They are useful for when you want to compare a single variable against a series of constants. + +```js +locals: { foo: 'foo' } +``` + +```html + + +

Foo really is bar! Revolutionary!

+
+ +

Foo is wow, oh man.

+
+ +

Foo is probably just foo in the end.

+
+
+``` + +```html +

Foo is probably just foo in the end.

+``` + +Anything in the `expression` attribute is evaluated directly as an expressions. + ### Loops You can use the `each` tag to build loops. It works with both arrays and objects. For example: From 553b0fa330dd7819b5c8324e4bdf367de9b996bc Mon Sep 17 00:00:00 2001 From: Denis Malinochkin Date: Tue, 6 Dec 2016 12:43:08 +0300 Subject: [PATCH 6/6] fix(index): comments --- lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index a010997..b472357 100644 --- a/lib/index.js +++ b/lib/index.js @@ -228,7 +228,7 @@ function walk (opts, nodes) { // switch tag if (node.tag === switches[0]) { - // throw an error if it's missing the "condition" attribute + // throw an error if it's missing the "expression" attribute if (!(node.attrs && node.attrs.expression)) { throw new Error(`the "${switches[0]}" tag must have a "expression" attribute`) }