From 6c61d194d55ee18fd85e8396297912dc22043981 Mon Sep 17 00:00:00 2001 From: Brendan Rice Date: Sun, 8 Nov 2015 13:09:26 -0500 Subject: [PATCH 01/25] Remove extra/unnecessary word --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3a7d742a..2272f8946 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ To run patternlab-node using grunt, do the following in the directory you downlo * Not deleting `builder/patternlab_gulp.js` may cause a harmless error when running grunt. Delete it. 3. Run `grunt` or `grunt serve` from the command line -This creates all patterns, the styleguide, and the pattern lab site. It's strongly recommended to run `grunt serve` to see have BrowserSync spin up and serve the files to you. +This creates all patterns, the styleguide, and the pattern lab site. It's strongly recommended to run `grunt serve` to have BrowserSync spin up and serve the files to you. ### Getting Started - Gulp From d5de17372425adcddd4c2043b368f4f9c6f43bf6 Mon Sep 17 00:00:00 2001 From: BRIAN MUENZENMEYER Date: Thu, 12 Nov 2015 00:54:05 -0600 Subject: [PATCH 02/25] Updated package.json devDependencies for grunt and gulp configurations. --- package.gulp.json | 20 ++++++++++---------- package.json | 26 ++++++++++++++------------ 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/package.gulp.json b/package.gulp.json index 1ee3ab48b..ad7e69c77 100644 --- a/package.gulp.json +++ b/package.gulp.json @@ -3,20 +3,20 @@ "description": "Pattern Lab is a collection of tools to help you create atomic design systems. This is the node command line interface (CLI).", "version": "0.14.0", "devDependencies": { - "browser-sync": "^2.8.2", - "del": "^1.2.1", - "diveSync": "^0.2.1", - "fs-extra": "^0.14.0", - "glob": "^4.3.5", - "gulp": "^3.8.8", - "gulp-connect": "^2.0.6", + "browser-sync": "^2.10.0", + "del": "^2.0.2", + "diveSync": "^0.3.0", + "fs-extra": "^0.26.2", + "glob": "^6.0.1", + "gulp": "^3.9.0", + "gulp-connect": "^2.2.0", "gulp-copy": "0.0.2", - "gulp-header": "^1.0.5", + "gulp-header": "^1.7.1", "gulp-load": "^0.1.1", "gulp-nodeunit": "0.0.5", "gulp-strip-banner": "0.0.2", - "html-entities": "^1.1.1", - "mustache": "^1.0.0" + "html-entities": "^1.2.0", + "mustache": "^2.2.0" }, "keywords": [ "Pattern Lab", diff --git a/package.json b/package.json index 75ab5d5e8..93d648ca8 100644 --- a/package.json +++ b/package.json @@ -3,37 +3,39 @@ "description": "Pattern Lab is a collection of tools to help you create atomic design systems. This is the node command line interface (CLI).", "version": "0.14.0", "devDependencies": { - "bs-html-injector": "^2.0.4", - "diveSync": "^0.2.1", - "fs-extra": "^0.24.0", - "glob": "^4.3.5", - "grunt": "~0.4.0", - "grunt-browser-sync": "^2.1.3", - "grunt-contrib-concat": "^0.5.0", - "grunt-contrib-copy": "^0.7.0", + "bs-html-injector": "^3.0.0", + "diveSync": "^0.3.0", + "fs-extra": "^0.26.2", + "glob": "^6.0.1", + "grunt": "~0.4.5", + "grunt-browser-sync": "^2.2.0", + "grunt-contrib-concat": "^0.5.1", + "grunt-contrib-copy": "^0.8.2", "grunt-contrib-nodeunit": "^0.4.1", "grunt-contrib-watch": "^0.6.1", - "html-entities": "^1.1.1", - "matchdep": "^0.3.0", - "mustache": "^1.0.0" + "html-entities": "^1.2.0", + "matchdep": "^1.0.0", + "mustache": "^2.2.0" }, "keywords": [ "Pattern Lab", "Atomic Web Design", "Node", "Grunt", + "Gulp", "Javascript" ], "repository": { "type": "git", "url": "git://github.com/pattern-lab/patternlab-node.git" }, + "bugs": "https://github.com/pattern-lab/patternlab-node/issues", "author": "Brian Muenzenmeyer", "license": "MIT", "scripts": { "test": "grunt travis --verbose" }, "engines": { - "node": ">=0.1" + "node": ">=0.1 <4.0" } } From 9b1d6a939bbfcea72dbff17344edd8a276c3f26d Mon Sep 17 00:00:00 2001 From: BRIAN MUENZENMEYER Date: Thu, 12 Nov 2015 00:55:43 -0600 Subject: [PATCH 03/25] Updated the changelog and readme to reflext node 4 and 5 support. --- CHANGELOG | 4 ++++ README.md | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 79c3cc66f..df3b02510 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ THIS CHANGELOG IS AN ATTEMPT TO DOCUMENT CHANGES TO THIS PROJECT. +PL-node-v0.15.0 +- CHG: Updated package.json devDependencies for Node 4.X and 5.X support. +- CHG: Updated package.gulp.json devDependencies for Node 4.X and 5.X support. + PL-node-v0.14.0 - ADD: Support for style modifiers - ADD: Support for styleGuideExcludes diff --git a/README.md b/README.md index 2272f8946..820ecf1e6 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,12 @@ This repository contains the vanilla builder logic, grunt and gulp configuration * Download the [latest release of patternlab-node](https://github.com/pattern-lab/patternlab-node/releases/latest) from Github * Via npm, run `npm install patternlab-node` (Note this will auto install the grunt version currently. see below) -* **NOTE** Node version 4.X is not officially supported yet, citing [a lot of Windows issues](https://github.com/nodejs/node-gyp/issues/629), including [mine](https://github.com/pattern-lab/patternlab-node/issues/162). Upgrade node at your own risk until otherwise stated. +* **NOTE** Node version 4.X and 5.X have tentative support, citing [a lot of Windows issues](https://github.com/nodejs/node-gyp/issues/629), including [mine](https://github.com/pattern-lab/patternlab-node/issues/162). Upgrade node at your own risk until otherwise stated. I've tried to catalog some issues and troubleshooting steps on the [wiki](https://github.com/pattern-lab/patternlab-node/wiki/Windows-Issues). ### Choose Your Adventure! Now Vanilla, Grunt & Gulp This repository ships with two `package.json` files, a `Gruntfile.js`, and a `gulpfile.js`. The default is grunt currently. The core builder is not dependent on either. -**HELP WANTED** Please help me test both of the configurations by [reporting](https://github.com/pattern-lab/patternlab-node/blob/dev/CONTRIBUTING.md) any issues encountered. - ### Getting Started - Grunt To run patternlab-node using grunt, do the following in the directory you downloaded and extracted the zipped release: From 56c0efa19ee07ac16a1cf1964f5ff74fb1bb37ed Mon Sep 17 00:00:00 2001 From: BRIAN MUENZENMEYER Date: Thu, 12 Nov 2015 00:59:32 -0600 Subject: [PATCH 04/25] bumped the version --- builder/lineage_hunter.js | 2 +- builder/list_item_hunter.js | 2 +- builder/media_hunter.js | 2 +- builder/object_factory.js | 2 +- builder/parameter_hunter.js | 2 +- builder/pattern_assembler.js | 10 ++-------- builder/pattern_exporter.js | 2 +- builder/patternlab.js | 2 +- builder/patternlab_grunt.js | 2 +- builder/patternlab_gulp.js | 2 +- builder/pseudopattern_hunter.js | 2 +- builder/style_modifier_hunter.js | 2 +- package.gulp.json | 4 +++- package.json | 4 ++-- 14 files changed, 18 insertions(+), 22 deletions(-) diff --git a/builder/lineage_hunter.js b/builder/lineage_hunter.js index f908fe90f..4bd4acc9c 100644 --- a/builder/lineage_hunter.js +++ b/builder/lineage_hunter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.14.0 - 2015 + * patternlab-node - v0.15.0 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/list_item_hunter.js b/builder/list_item_hunter.js index decef1082..970e44f98 100644 --- a/builder/list_item_hunter.js +++ b/builder/list_item_hunter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.14.0 - 2015 + * patternlab-node - v0.15.0 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/media_hunter.js b/builder/media_hunter.js index 9eddbbac4..94ea61e09 100644 --- a/builder/media_hunter.js +++ b/builder/media_hunter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.14.0 - 2015 + * patternlab-node - v0.15.0 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/object_factory.js b/builder/object_factory.js index 2c91bc006..685f95196 100644 --- a/builder/object_factory.js +++ b/builder/object_factory.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.14.0 - 2015 + * patternlab-node - v0.15.0 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/parameter_hunter.js b/builder/parameter_hunter.js index c19effb22..1d1962008 100644 --- a/builder/parameter_hunter.js +++ b/builder/parameter_hunter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.14.0 - 2015 + * patternlab-node - v0.15.0 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/pattern_assembler.js b/builder/pattern_assembler.js index 214e066fa..8fc134c9e 100644 --- a/builder/pattern_assembler.js +++ b/builder/pattern_assembler.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.14.0 - 2015 + * patternlab-node - v0.15.0 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. @@ -262,13 +262,7 @@ throw 'Could not find pattern with key ' + key; } - /** - * Recursively merge properties of two objects. - * - * @param {Object} obj1 If obj1 has properties obj2 doesn't, add to obj2. - * @param {Object} obj2 This object's properties have priority over obj1. - * @returns {Object} obj2 - */ + function mergeData(obj1, obj2){ if(typeof obj2 === 'undefined'){ obj2 = {}; diff --git a/builder/pattern_exporter.js b/builder/pattern_exporter.js index 816a1d0ab..5ac4421d1 100644 --- a/builder/pattern_exporter.js +++ b/builder/pattern_exporter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.14.0 - 2015 + * patternlab-node - v0.15.0 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/patternlab.js b/builder/patternlab.js index 662b1105f..25b2232de 100644 --- a/builder/patternlab.js +++ b/builder/patternlab.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.14.0 - 2015 + * patternlab-node - v0.15.0 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/patternlab_grunt.js b/builder/patternlab_grunt.js index 7cb6bb7d7..3e09643fd 100644 --- a/builder/patternlab_grunt.js +++ b/builder/patternlab_grunt.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.14.0 - 2015 + * patternlab-node - v0.15.0 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/patternlab_gulp.js b/builder/patternlab_gulp.js index eceabee57..2b1db5d0c 100644 --- a/builder/patternlab_gulp.js +++ b/builder/patternlab_gulp.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.14.0 - 2015 + * patternlab-node - v0.15.0 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/pseudopattern_hunter.js b/builder/pseudopattern_hunter.js index f085fe3f6..d47153cbe 100644 --- a/builder/pseudopattern_hunter.js +++ b/builder/pseudopattern_hunter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.14.0 - 2015 + * patternlab-node - v0.15.0 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/style_modifier_hunter.js b/builder/style_modifier_hunter.js index 38e0de440..139fc9b2f 100644 --- a/builder/style_modifier_hunter.js +++ b/builder/style_modifier_hunter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.14.0 - 2015 + * patternlab-node - v0.15.0 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/package.gulp.json b/package.gulp.json index ad7e69c77..288f11faa 100644 --- a/package.gulp.json +++ b/package.gulp.json @@ -1,7 +1,7 @@ { "name": "patternlab-node", "description": "Pattern Lab is a collection of tools to help you create atomic design systems. This is the node command line interface (CLI).", - "version": "0.14.0", + "version": "0.15.0", "devDependencies": { "browser-sync": "^2.10.0", "del": "^2.0.2", @@ -23,12 +23,14 @@ "Atomic Web Design", "Node", "Grunt", + "Gulp", "Javascript" ], "repository": { "type": "git", "url": "git://github.com/pattern-lab/patternlab-node.git" }, + "bugs": "https://github.com/pattern-lab/patternlab-node/issues", "author": "Brian Muenzenmeyer", "license": "MIT", "scripts": { diff --git a/package.json b/package.json index 93d648ca8..c451cb4d4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "patternlab-node", "description": "Pattern Lab is a collection of tools to help you create atomic design systems. This is the node command line interface (CLI).", - "version": "0.14.0", + "version": "0.15.0", "devDependencies": { "bs-html-injector": "^3.0.0", "diveSync": "^0.3.0", @@ -36,6 +36,6 @@ "test": "grunt travis --verbose" }, "engines": { - "node": ">=0.1 <4.0" + "node": ">=0.1" } } From 0afb234bd5e55a155c0230a11c2b05ca0e01c0cb Mon Sep 17 00:00:00 2001 From: BRIAN MUENZENMEYER Date: Thu, 12 Nov 2015 01:00:39 -0600 Subject: [PATCH 05/25] pushed the travis config with more node options. fixes #162 fixes #180 --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 25b0a05cd..611cfd7d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: node_js node_js: + - stable + - 4.0 - 0.12 - 0.11 From e5ac6be158b9c02129803f30b1c1110f8b22d450 Mon Sep 17 00:00:00 2001 From: BRIAN MUENZENMEYER Date: Mon, 16 Nov 2015 22:53:25 -0600 Subject: [PATCH 06/25] fix issue with style modifiers not working with spaces in the template closes #183 --- CHANGELOG | 4 ++++ builder/lineage_hunter.js | 2 +- builder/list_item_hunter.js | 2 +- builder/media_hunter.js | 2 +- builder/object_factory.js | 2 +- builder/parameter_hunter.js | 2 +- builder/pattern_assembler.js | 2 +- builder/pattern_exporter.js | 2 +- builder/patternlab.js | 2 +- builder/patternlab_grunt.js | 2 +- builder/patternlab_gulp.js | 2 +- builder/pseudopattern_hunter.js | 2 +- builder/style_modifier_hunter.js | 4 ++-- package.gulp.json | 2 +- package.json | 2 +- test/style_modifier_hunter_tests.js | 19 +++++++++++++++++++ 16 files changed, 38 insertions(+), 15 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index df3b02510..5f22cfeb9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ THIS CHANGELOG IS AN ATTEMPT TO DOCUMENT CHANGES TO THIS PROJECT. +PL-node-v0.15.1 +- FIX: Resolve issue with styleModifiers not being replaced when the partial has spaces in it. +- THX: Thanks @theorise for the issue report! + PL-node-v0.15.0 - CHG: Updated package.json devDependencies for Node 4.X and 5.X support. - CHG: Updated package.gulp.json devDependencies for Node 4.X and 5.X support. diff --git a/builder/lineage_hunter.js b/builder/lineage_hunter.js index 4bd4acc9c..9056cdeac 100644 --- a/builder/lineage_hunter.js +++ b/builder/lineage_hunter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.15.0 - 2015 + * patternlab-node - v0.15.1 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/list_item_hunter.js b/builder/list_item_hunter.js index 970e44f98..87574eee9 100644 --- a/builder/list_item_hunter.js +++ b/builder/list_item_hunter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.15.0 - 2015 + * patternlab-node - v0.15.1 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/media_hunter.js b/builder/media_hunter.js index 94ea61e09..d5ada91ce 100644 --- a/builder/media_hunter.js +++ b/builder/media_hunter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.15.0 - 2015 + * patternlab-node - v0.15.1 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/object_factory.js b/builder/object_factory.js index 685f95196..d1206866d 100644 --- a/builder/object_factory.js +++ b/builder/object_factory.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.15.0 - 2015 + * patternlab-node - v0.15.1 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/parameter_hunter.js b/builder/parameter_hunter.js index 1d1962008..5b21008a4 100644 --- a/builder/parameter_hunter.js +++ b/builder/parameter_hunter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.15.0 - 2015 + * patternlab-node - v0.15.1 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/pattern_assembler.js b/builder/pattern_assembler.js index 8fc134c9e..4a1588ff3 100644 --- a/builder/pattern_assembler.js +++ b/builder/pattern_assembler.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.15.0 - 2015 + * patternlab-node - v0.15.1 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/pattern_exporter.js b/builder/pattern_exporter.js index 5ac4421d1..90915ec86 100644 --- a/builder/pattern_exporter.js +++ b/builder/pattern_exporter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.15.0 - 2015 + * patternlab-node - v0.15.1 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/patternlab.js b/builder/patternlab.js index 25b2232de..9ea38d604 100644 --- a/builder/patternlab.js +++ b/builder/patternlab.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.15.0 - 2015 + * patternlab-node - v0.15.1 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/patternlab_grunt.js b/builder/patternlab_grunt.js index 3e09643fd..843dd07f1 100644 --- a/builder/patternlab_grunt.js +++ b/builder/patternlab_grunt.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.15.0 - 2015 + * patternlab-node - v0.15.1 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/patternlab_gulp.js b/builder/patternlab_gulp.js index 2b1db5d0c..d4014cb79 100644 --- a/builder/patternlab_gulp.js +++ b/builder/patternlab_gulp.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.15.0 - 2015 + * patternlab-node - v0.15.1 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/pseudopattern_hunter.js b/builder/pseudopattern_hunter.js index d47153cbe..9f698650f 100644 --- a/builder/pseudopattern_hunter.js +++ b/builder/pseudopattern_hunter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.15.0 - 2015 + * patternlab-node - v0.15.1 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. diff --git a/builder/style_modifier_hunter.js b/builder/style_modifier_hunter.js index 139fc9b2f..b0b909b4e 100644 --- a/builder/style_modifier_hunter.js +++ b/builder/style_modifier_hunter.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v0.15.0 - 2015 + * patternlab-node - v0.15.1 - 2015 * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. @@ -24,7 +24,7 @@ } //replace the stylemodifier placeholder with the class name - pattern.extendedTemplate = pattern.extendedTemplate.replace('{{styleModifier}}', styleModifier); + pattern.extendedTemplate = pattern.extendedTemplate.replace(/{{[ ]?styleModifier[ ]?}}/i, styleModifier); } } diff --git a/package.gulp.json b/package.gulp.json index 288f11faa..099c8d907 100644 --- a/package.gulp.json +++ b/package.gulp.json @@ -1,7 +1,7 @@ { "name": "patternlab-node", "description": "Pattern Lab is a collection of tools to help you create atomic design systems. This is the node command line interface (CLI).", - "version": "0.15.0", + "version": "0.15.1", "devDependencies": { "browser-sync": "^2.10.0", "del": "^2.0.2", diff --git a/package.json b/package.json index c451cb4d4..8d3953d22 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "patternlab-node", "description": "Pattern Lab is a collection of tools to help you create atomic design systems. This is the node command line interface (CLI).", - "version": "0.15.0", + "version": "0.15.1", "devDependencies": { "bs-html-injector": "^3.0.0", "diveSync": "^0.3.0", diff --git a/test/style_modifier_hunter_tests.js b/test/style_modifier_hunter_tests.js index 0245c7349..2753c60f1 100644 --- a/test/style_modifier_hunter_tests.js +++ b/test/style_modifier_hunter_tests.js @@ -23,6 +23,25 @@ test.equals(pattern.extendedTemplate, '
'); test.done(); }, + 'replaces style modifiers with spaces in the syntax' : function(test){ + //arrange + var pl = {}; + pl.config = {}; + pl.config.debug = false; + + var pattern = { + extendedTemplate: '
' + }; + + var style_modifier_hunter = new smh(); + + //act + style_modifier_hunter.consume_style_modifier(pattern, '{{> partial:bar}}', pl); + + //assert + test.equals(pattern.extendedTemplate, '
'); + test.done(); + }, 'does not alter pattern extendedTemplate if styleModifier not found in partial' : function(test){ //arrange var pl = {}; From 3c3270ad013b24cf8394c3424ae8280add10cd51 Mon Sep 17 00:00:00 2001 From: BRIAN MUENZENMEYER Date: Mon, 16 Nov 2015 23:44:18 -0600 Subject: [PATCH 07/25] added a note to CONTRIBUTING.md that mentions up for grabs issues --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dcaef2d3d..87af6849d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,7 @@ #Contributing to Patternlab - Node If you'd like to contribute to patternlab - node, please do so! There is always a lot of ground to cover, with patternlab - php being so feature-rich. -No pull request is too small. +No pull request is too small. Check out any [up for grabs issues](https://github.com/pattern-lab/patternlab-node/labels/up%20for%20grabs) as a good way to get your feet wet. ##Guidelines 1. Please keep your pull requests concise and limited to **ONE** substantive change at a time. From a818d284b2d96caa0840323d13de88788bdea363 Mon Sep 17 00:00:00 2001 From: BRIAN MUENZENMEYER Date: Tue, 17 Nov 2015 00:14:12 -0600 Subject: [PATCH 08/25] support for multiple classes on stylemodifier closes #186 --- CHANGELOG | 3 ++- README.md | 5 +++++ builder/pattern_assembler.js | 22 +++++++++++----------- builder/style_modifier_hunter.js | 17 ++++++++++------- test/style_modifier_hunter_tests.js | 19 +++++++++++++++++++ 5 files changed, 47 insertions(+), 19 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 5f22cfeb9..1a65bc614 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,7 +2,8 @@ THIS CHANGELOG IS AN ATTEMPT TO DOCUMENT CHANGES TO THIS PROJECT. PL-node-v0.15.1 - FIX: Resolve issue with styleModifiers not being replaced when the partial has spaces in it. -- THX: Thanks @theorise for the issue report! +- ADD: Support multiple styleModifier classes using the | pipe syntax +- THX: Thanks @theorise for the issue reports! PL-node-v0.15.0 - CHG: Updated package.json devDependencies for Node 4.X and 5.X support. diff --git a/README.md b/README.md index 820ecf1e6..56421f9f5 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,11 @@ Once rendered, it looks like this: ``` +You may also specify multiple classes using a pipe character (|). + +``` +{{> atoms-message:error|is-on }} +``` diff --git a/builder/pattern_assembler.js b/builder/pattern_assembler.js index 4a1588ff3..1fcdc2719 100644 --- a/builder/pattern_assembler.js +++ b/builder/pattern_assembler.js @@ -1,10 +1,10 @@ -/* - * patternlab-node - v0.15.1 - 2015 - * +/* + * patternlab-node - v0.15.1 - 2015 + * * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. + * Licensed under the MIT license. + * + * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. * */ @@ -24,19 +24,19 @@ // returns any patterns that match {{> value:mod }} or {{> value:mod(foo:"bar") }} within the pattern function findPartialsWithStyleModifiers(pattern){ - var matches = pattern.template.match(/{{>([ ])?([\w\-\.\/~]+)(?!\()(\:[A-Za-z0-9-_]+)+(?:(| )\(.*)?([ ])?}}/g); + var matches = pattern.template.match(/{{>([ ])?([\w\-\.\/~]+)(?!\()(\:[A-Za-z0-9-_|]+)+(?:(| )\(.*)?([ ])?}}/g); return matches; } // returns any patterns that match {{> value(foo:"bar") }} or {{> value:mod(foo:"bar") }} within the pattern function findPartialsWithPatternParameters(pattern){ - var matches = pattern.template.match(/{{>([ ])?([\w\-\.\/~]+)(?:\:[A-Za-z0-9-_]+)?(?:(| )\(.*)+([ ])?}}/g); + var matches = pattern.template.match(/{{>([ ])?([\w\-\.\/~]+)(?:\:[A-Za-z0-9-_|]+)?(?:(| )\(.*)+([ ])?}}/g); return matches; } //find and return any {{> template-name* }} within pattern function findPartials(pattern){ - var matches = pattern.template.match(/{{>([ ])?([\w\-\.\/~]+)(?:\:[A-Za-z0-9-_]+)?(?:(| )\(.*)?([ ])?}}/g); + var matches = pattern.template.match(/{{>([ ])?([\w\-\.\/~]+)(?:\:[A-Za-z0-9-_|]+)?(?:(| )\(.*)?([ ])?}}/g); return matches; } @@ -207,7 +207,7 @@ //do something with the regular old partials for(i = 0; i < foundPatternPartials.length; i++){ - var partialKey = foundPatternPartials[i].replace(/{{>([ ])?([\w\-\.\/~]+)(:[A-z-_]+)?(?:\:[A-Za-z0-9-]+)?(?:(| )\(.*)?([ ])?}}/g, '$2'); + var partialKey = foundPatternPartials[i].replace(/{{>([ ])?([\w\-\.\/~]+)(:[A-z-_|]+)?(?:\:[A-Za-z0-9-]+)?(?:(| )\(.*)?([ ])?}}/g, '$2'); var partialPath; @@ -262,7 +262,7 @@ throw 'Could not find pattern with key ' + key; } - + function mergeData(obj1, obj2){ if(typeof obj2 === 'undefined'){ obj2 = {}; diff --git a/builder/style_modifier_hunter.js b/builder/style_modifier_hunter.js index b0b909b4e..9baa6f0aa 100644 --- a/builder/style_modifier_hunter.js +++ b/builder/style_modifier_hunter.js @@ -1,10 +1,10 @@ -/* - * patternlab-node - v0.15.1 - 2015 - * +/* + * patternlab-node - v0.15.1 - 2015 + * * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. + * Licensed under the MIT license. + * + * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. * */ @@ -16,9 +16,12 @@ function consumestylemodifier(pattern, partial, patternlab){ //extract the classname from the stylemodifier which comes in the format of :className - var styleModifier = partial.match(/:([\w\-_])+/g) ? partial.match(/:([\w\-_])+/g)[0].slice(1) : null; + var styleModifier = partial.match(/:([\w\-_|])+/g) ? partial.match(/:([\w\-_|])+/g)[0].slice(1) : null; if(styleModifier){ + //replace the special character pipe | used to separate multiple classes with a space + styleModifier = styleModifier.replace(/\|/g, ' '); + if(patternlab.config.debug){ console.log('found partial styleModifier within pattern ' + pattern.key); } diff --git a/test/style_modifier_hunter_tests.js b/test/style_modifier_hunter_tests.js index 2753c60f1..4951dde18 100644 --- a/test/style_modifier_hunter_tests.js +++ b/test/style_modifier_hunter_tests.js @@ -42,6 +42,25 @@ test.equals(pattern.extendedTemplate, '
'); test.done(); }, + 'replaces multiple style modifiers' : function(test){ + //arrange + var pl = {}; + pl.config = {}; + pl.config.debug = false; + + var pattern = { + extendedTemplate: '
' + }; + + var style_modifier_hunter = new smh(); + + //act + style_modifier_hunter.consume_style_modifier(pattern, '{{> partial:bar|baz|dum}}', pl); + + //assert + test.equals(pattern.extendedTemplate, '
'); + test.done(); + }, 'does not alter pattern extendedTemplate if styleModifier not found in partial' : function(test){ //arrange var pl = {}; From 157a06e2c644ddd0968e187cf5b7df04ebcaed7e Mon Sep 17 00:00:00 2001 From: BRIAN MUENZENMEYER Date: Tue, 17 Nov 2015 00:32:20 -0600 Subject: [PATCH 09/25] updated the instructions to point to the wiki for now --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 56421f9f5..c628bf065 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ It's not expected to toggle between the two build systems, but for those migrati ### Upgrading -You can find some simple upgrade documenation in it's current home here (unreleased but confirmed to work): [https://github.com/pattern-lab/website/blob/dev/patternlabsite/docs/node/upgrading.md](https://github.com/pattern-lab/website/blob/dev/patternlabsite/docs/node/upgrading.md) +You can find instructions on how to upgrade from version to version of Pattern Lab Node here: [https://github.com/pattern-lab/patternlab-node/wiki/Upgrading](https://github.com/pattern-lab/patternlab-node/wiki/Upgrading) ### Command Line Interface From 8fb7eea79774268f37fb06f352d825f1f9b3aa05 Mon Sep 17 00:00:00 2001 From: BRIAN MUENZENMEYER Date: Tue, 24 Nov 2015 23:38:02 -0600 Subject: [PATCH 10/25] Unit test to cover correct application of multiple partials with differing style modifiers --- builder/pattern_assembler.js | 2 +- .../_patterns/00-test/03-styled-atom.mustache | 3 ++ .../files/_patterns/00-test/04-group.mustache | 6 ++++ .../_patterns/00-test/05-group2.mustache | 6 ++++ test/pattern_assembler_tests.js | 33 +++++++++++++++++++ 5 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 test/files/_patterns/00-test/03-styled-atom.mustache create mode 100644 test/files/_patterns/00-test/04-group.mustache create mode 100644 test/files/_patterns/00-test/05-group2.mustache diff --git a/builder/pattern_assembler.js b/builder/pattern_assembler.js index 1fcdc2719..5363acbde 100644 --- a/builder/pattern_assembler.js +++ b/builder/pattern_assembler.js @@ -207,7 +207,7 @@ //do something with the regular old partials for(i = 0; i < foundPatternPartials.length; i++){ - var partialKey = foundPatternPartials[i].replace(/{{>([ ])?([\w\-\.\/~]+)(:[A-z-_|]+)?(?:\:[A-Za-z0-9-]+)?(?:(| )\(.*)?([ ])?}}/g, '$2'); + var partialKey = foundPatternPartials[i].replace(/{{>([ ])?([\w\-\.\/~]+)(:[A-z-_|]+)?(?:\:[A-Za-z0-9-_]+)?(?:(| )\(.*)?([ ])?}}/g, '$2'); var partialPath; diff --git a/test/files/_patterns/00-test/03-styled-atom.mustache b/test/files/_patterns/00-test/03-styled-atom.mustache new file mode 100644 index 000000000..b736c06aa --- /dev/null +++ b/test/files/_patterns/00-test/03-styled-atom.mustache @@ -0,0 +1,3 @@ + + {{message}} + diff --git a/test/files/_patterns/00-test/04-group.mustache b/test/files/_patterns/00-test/04-group.mustache new file mode 100644 index 000000000..24ee58979 --- /dev/null +++ b/test/files/_patterns/00-test/04-group.mustache @@ -0,0 +1,6 @@ +
+ {{> test-styled-atom:test_1 }} + {{> test-styled-atom:test_2 }} + {{> test-styled-atom:test_3 }} + {{> test-styled-atom:test_4 }} +
diff --git a/test/files/_patterns/00-test/05-group2.mustache b/test/files/_patterns/00-test/05-group2.mustache new file mode 100644 index 000000000..a3986a1b5 --- /dev/null +++ b/test/files/_patterns/00-test/05-group2.mustache @@ -0,0 +1,6 @@ +
+ {{> test-styled-atom:test_1(message: "1" ) }} + {{> test-styled-atom:test_2(message: "2" ) }} + {{> test-styled-atom:test_3(message: "3" ) }} + {{> test-styled-atom:test_4(message: "4" ) }} +
diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js index cc4e55333..da024251f 100644 --- a/test/pattern_assembler_tests.js +++ b/test/pattern_assembler_tests.js @@ -2,6 +2,7 @@ "use strict"; var pa = require('../builder/pattern_assembler'); + var object_factory = require('../builder/object_factory'); exports['pattern_assembler'] = { 'find_pattern_partials finds partials' : function(test){ @@ -283,6 +284,38 @@ //test that 00-foo.mustache included partial 01-bar.mustache test.equals(fooExtended, 'bar'); + test.done(); + }, + 'processPatternRecursive - correctly replaces all stylemodifiers when multiple duplicate patterns with different stylemodifiers found' : function(test){ + //arrange + var fs = require('fs-extra'); + var pattern_assembler = new pa(); + + var pl = {}; + pl.config = {}; + pl.data = {}; + pl.data.link = {}; + pl.config.debug = false; + pl.patterns = []; + var patterns_dir = './test/files/_patterns'; + + var atomPattern = new object_factory.oPattern('test/files/_patterns/00-test/03-styled-atom.mustache', '00-test', '03-styled-atom.mustache'); + atomPattern.template = fs.readFileSync(patterns_dir + '/00-test/03-styled-atom.mustache', 'utf8'); + atomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(atomPattern); + + + var groupPattern = new object_factory.oPattern('test/files/_patterns/00-test/04-group.mustache', '00-test', '04-group.mustache'); + groupPattern.template = fs.readFileSync(patterns_dir + '/00-test/04-group.mustache', 'utf8'); + groupPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(groupPattern); + + pl.patterns.push(atomPattern); + pl.patterns.push(groupPattern); + + //act + pattern_assembler.process_pattern_recursive('test/files/_patterns/00-test/04-group.mustache', pl, {}); + //assert + var expectedValue = '
{{message}} {{message}} {{message}} {{message}}
'; + test.equals(groupPattern.extendedTemplate.replace(/\s\s+/g, ' ').trim(), expectedValue.trim()); test.done(); } }; From ffc80cb4eeb8ff739563b9eb9606835d62c390ee Mon Sep 17 00:00:00 2001 From: BRIAN MUENZENMEYER Date: Tue, 24 Nov 2015 23:53:24 -0600 Subject: [PATCH 11/25] a unit test to cover half of #190 --- .../files/_patterns/00-test/06-mixed.mustache | 6 ++++ test/pattern_assembler_tests.js | 34 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 test/files/_patterns/00-test/06-mixed.mustache diff --git a/test/files/_patterns/00-test/06-mixed.mustache b/test/files/_patterns/00-test/06-mixed.mustache new file mode 100644 index 000000000..b0c6d610f --- /dev/null +++ b/test/files/_patterns/00-test/06-mixed.mustache @@ -0,0 +1,6 @@ +
+ {{> test-styled-atom }} + {{> test-styled-atom:test_2 }} + {{> test-styled-atom:test_3 }} + {{> test-styled-atom:test_4 }} +
diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js index da024251f..af082fda5 100644 --- a/test/pattern_assembler_tests.js +++ b/test/pattern_assembler_tests.js @@ -303,7 +303,6 @@ atomPattern.template = fs.readFileSync(patterns_dir + '/00-test/03-styled-atom.mustache', 'utf8'); atomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(atomPattern); - var groupPattern = new object_factory.oPattern('test/files/_patterns/00-test/04-group.mustache', '00-test', '04-group.mustache'); groupPattern.template = fs.readFileSync(patterns_dir + '/00-test/04-group.mustache', 'utf8'); groupPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(groupPattern); @@ -313,10 +312,43 @@ //act pattern_assembler.process_pattern_recursive('test/files/_patterns/00-test/04-group.mustache', pl, {}); + //assert var expectedValue = '
{{message}} {{message}} {{message}} {{message}}
'; test.equals(groupPattern.extendedTemplate.replace(/\s\s+/g, ' ').trim(), expectedValue.trim()); test.done(); + }, + 'processPatternRecursive - correctly ignores a partial without a style modifier when the same partial later has a style modifier' : function(test){ + //arrange + var fs = require('fs-extra'); + var pattern_assembler = new pa(); + + var pl = {}; + pl.config = {}; + pl.data = {}; + pl.data.link = {}; + pl.config.debug = false; + pl.patterns = []; + var patterns_dir = './test/files/_patterns'; + + var atomPattern = new object_factory.oPattern('test/files/_patterns/00-test/03-styled-atom.mustache', '00-test', '03-styled-atom.mustache'); + atomPattern.template = fs.readFileSync(patterns_dir + '/00-test/03-styled-atom.mustache', 'utf8'); + atomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(atomPattern); + + var mixedPattern = new object_factory.oPattern('test/files/_patterns/00-test/06-mixed.mustache', '00-test', '06-mixed.mustache'); + mixedPattern.template = fs.readFileSync(patterns_dir + '/00-test/06-mixed.mustache', 'utf8'); + mixedPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(mixedPattern); + + pl.patterns.push(atomPattern); + pl.patterns.push(mixedPattern); + + //act + pattern_assembler.process_pattern_recursive('test/files/_patterns/00-test/06-mixed.mustache', pl, {}); + + //assert. here we expect {{styleModifier}} to be in the first group, since it was not replaced by anything. rendering with data will then remove this (correctly) + var expectedValue = '
{{message}} {{message}} {{message}} {{message}}
'; + test.equals(mixedPattern.extendedTemplate.replace(/\s\s+/g, ' ').trim(), expectedValue.trim()); + test.done(); } }; }()); From 9cd187b607f9a474f022d6ddeb5d330e004682f8 Mon Sep 17 00:00:00 2001 From: BRIAN MUENZENMEYER Date: Wed, 25 Nov 2015 00:40:35 -0600 Subject: [PATCH 12/25] additional tests to cover styleModifier / pattern Parameter relationship fixes #190 --- CHANGELOG | 1 + builder/parameter_hunter.js | 14 +-- .../00-test/07-mixed-params.mustache | 6 ++ .../00-test/08-bookend-params.mustache | 6 ++ .../_patterns/00-test/09-bookend.mustache | 6 ++ test/pattern_assembler_tests.js | 100 ++++++++++++++++++ 6 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 test/files/_patterns/00-test/07-mixed-params.mustache create mode 100644 test/files/_patterns/00-test/08-bookend-params.mustache create mode 100644 test/files/_patterns/00-test/09-bookend.mustache diff --git a/CHANGELOG b/CHANGELOG index 1a65bc614..6e29f56f0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ THIS CHANGELOG IS AN ATTEMPT TO DOCUMENT CHANGES TO THIS PROJECT. PL-node-v0.15.1 - FIX: Resolve issue with styleModifiers not being replaced when the partial has spaces in it. - ADD: Support multiple styleModifier classes using the | pipe syntax +- FIX: Resolve issue with styleModifiers not being applied correctly when mixed with pattern parameters - THX: Thanks @theorise for the issue reports! PL-node-v0.15.0 diff --git a/builder/parameter_hunter.js b/builder/parameter_hunter.js index 5b21008a4..4732ad0a5 100644 --- a/builder/parameter_hunter.js +++ b/builder/parameter_hunter.js @@ -1,10 +1,10 @@ -/* - * patternlab-node - v0.15.1 - 2015 - * +/* + * patternlab-node - v0.15.1 - 2015 + * * Brian Muenzenmeyer, and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. + * Licensed under the MIT license. + * + * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. * */ @@ -28,6 +28,8 @@ //find the partial's name and retrieve it var partialName = pMatch.match(/([\w\-\.\/~]+)/g)[0]; var partialPattern = pattern_assembler.get_pattern_by_key(partialName, patternlab); + //if we retrieved a pattern we should make sure that its extendedTemplate is reset. looks to fix #190 + partialPattern.extendedTemplate = partialPattern.template; if(patternlab.config.debug){ console.log('found patternParameters for ' + partialName); diff --git a/test/files/_patterns/00-test/07-mixed-params.mustache b/test/files/_patterns/00-test/07-mixed-params.mustache new file mode 100644 index 000000000..408b3f1e4 --- /dev/null +++ b/test/files/_patterns/00-test/07-mixed-params.mustache @@ -0,0 +1,6 @@ +
+ {{> test-styled-atom }} + {{> test-styled-atom:test_2(message: '2') }} + {{> test-styled-atom:test_3(message: '3') }} + {{> test-styled-atom:test_4(message: '4') }} +
diff --git a/test/files/_patterns/00-test/08-bookend-params.mustache b/test/files/_patterns/00-test/08-bookend-params.mustache new file mode 100644 index 000000000..d2d0b4081 --- /dev/null +++ b/test/files/_patterns/00-test/08-bookend-params.mustache @@ -0,0 +1,6 @@ +
+ {{> test-styled-atom }} + {{> test-styled-atom:test_2(message: '2') }} + {{> test-styled-atom:test_3(message: '3') }} + {{> test-styled-atom }} +
diff --git a/test/files/_patterns/00-test/09-bookend.mustache b/test/files/_patterns/00-test/09-bookend.mustache new file mode 100644 index 000000000..74fc3451b --- /dev/null +++ b/test/files/_patterns/00-test/09-bookend.mustache @@ -0,0 +1,6 @@ +
+ {{> test-styled-atom }} + {{> test-styled-atom:test_2 }} + {{> test-styled-atom:test_3 }} + {{> test-styled-atom}} +
diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js index af082fda5..87a149c24 100644 --- a/test/pattern_assembler_tests.js +++ b/test/pattern_assembler_tests.js @@ -349,6 +349,106 @@ var expectedValue = '
{{message}} {{message}} {{message}} {{message}}
'; test.equals(mixedPattern.extendedTemplate.replace(/\s\s+/g, ' ').trim(), expectedValue.trim()); test.done(); + }, + 'processPatternRecursive - correctly ignores bookended partials without a style modifier when the same partial has a style modifier between' : function(test){ + //arrange + var fs = require('fs-extra'); + var pattern_assembler = new pa(); + + var pl = {}; + pl.config = {}; + pl.data = {}; + pl.data.link = {}; + pl.config.debug = false; + pl.patterns = []; + var patterns_dir = './test/files/_patterns'; + + var atomPattern = new object_factory.oPattern('test/files/_patterns/00-test/03-styled-atom.mustache', '00-test', '03-styled-atom.mustache'); + atomPattern.template = fs.readFileSync(patterns_dir + '/00-test/03-styled-atom.mustache', 'utf8'); + atomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(atomPattern); + + var bookendPattern = new object_factory.oPattern('test/files/_patterns/00-test/09-bookend.mustache', '00-test', '09-bookend.mustache'); + bookendPattern.template = fs.readFileSync(patterns_dir + '/00-test/09-bookend.mustache', 'utf8'); + bookendPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(bookendPattern); + + pl.patterns.push(atomPattern); + pl.patterns.push(bookendPattern); + + //act + pattern_assembler.process_pattern_recursive('test/files/_patterns/00-test/09-bookend.mustache', pl, {}); + + //assert. here we expect {{styleModifier}} to be in the first and last group, since it was not replaced by anything. rendering with data will then remove this (correctly) + var expectedValue = '
{{message}} {{message}} {{message}} {{message}}
'; + test.equals(bookendPattern.extendedTemplate.replace(/\s\s+/g, ' ').trim(), expectedValue.trim()); + test.done(); + }, + 'processPatternRecursive - correctly ignores a partial without a style modifier when the same partial later has a style modifier and pattern parameters' : function(test){ + //arrange + var fs = require('fs-extra'); + var pattern_assembler = new pa(); + + var pl = {}; + pl.config = {}; + pl.data = {}; + pl.data.link = {}; + pl.config.debug = false; + pl.patterns = []; + var patterns_dir = './test/files/_patterns'; + + var atomPattern = new object_factory.oPattern('test/files/_patterns/00-test/03-styled-atom.mustache', '00-test', '03-styled-atom.mustache'); + atomPattern.template = fs.readFileSync(patterns_dir + '/00-test/03-styled-atom.mustache', 'utf8'); + atomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(atomPattern); + atomPattern.parameteredPartials = pattern_assembler.find_pattern_partials_with_parameters(atomPattern); + + var mixedPattern = new object_factory.oPattern('test/files/_patterns/00-test/07-mixed-params.mustache', '00-test', '07-mixed-params.mustache'); + mixedPattern.template = fs.readFileSync(patterns_dir + '/00-test/07-mixed-params.mustache', 'utf8'); + mixedPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(mixedPattern); + mixedPattern.parameteredPartials = pattern_assembler.find_pattern_partials_with_parameters(mixedPattern); + + pl.patterns.push(atomPattern); + pl.patterns.push(mixedPattern); + + //act + pattern_assembler.process_pattern_recursive('test/files/_patterns/00-test/07-mixed-params.mustache', pl, {}); + + //assert. here we expect {{styleModifier}} to be in the first span, since it was not replaced by anything. rendering with data will then remove this (correctly) + var expectedValue = '
{{message}} 2 3 4
'; + test.equals(mixedPattern.extendedTemplate.replace(/\s\s+/g, ' ').trim(), expectedValue.trim()); + test.done(); + }, + 'processPatternRecursive - correctly ignores bookended partials without a style modifier when the same partial has a style modifier and pattern parameters between' : function(test){ + //arrange + var fs = require('fs-extra'); + var pattern_assembler = new pa(); + + var pl = {}; + pl.config = {}; + pl.data = {}; + pl.data.link = {}; + pl.config.debug = false; + pl.patterns = []; + var patterns_dir = './test/files/_patterns'; + + var atomPattern = new object_factory.oPattern('test/files/_patterns/00-test/03-styled-atom.mustache', '00-test', '03-styled-atom.mustache'); + atomPattern.template = fs.readFileSync(patterns_dir + '/00-test/03-styled-atom.mustache', 'utf8'); + atomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(atomPattern); + atomPattern.parameteredPartials = pattern_assembler.find_pattern_partials_with_parameters(atomPattern); + + var bookendPattern = new object_factory.oPattern('test/files/_patterns/00-test/08-bookend-params.mustache', '00-test', '08-bookend-params.mustache'); + bookendPattern.template = fs.readFileSync(patterns_dir + '/00-test/08-bookend-params.mustache', 'utf8'); + bookendPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(bookendPattern); + bookendPattern.parameteredPartials = pattern_assembler.find_pattern_partials_with_parameters(bookendPattern); + + pl.patterns.push(atomPattern); + pl.patterns.push(bookendPattern); + + //act + pattern_assembler.process_pattern_recursive('test/files/_patterns/00-test/08-bookend-params.mustache', pl, {}); + + //assert. here we expect {{styleModifier}} to be in the first and last span, since it was not replaced by anything. rendering with data will then remove this (correctly) + var expectedValue = '
{{message}} 2 3 {{message}}
'; + test.equals(bookendPattern.extendedTemplate.replace(/\s\s+/g, ' ').trim(), expectedValue.trim()); + test.done(); } }; }()); From 85842319deeef513d1970b1cd164248250f9c96e Mon Sep 17 00:00:00 2001 From: BRIAN MUENZENMEYER Date: Wed, 25 Nov 2015 01:00:33 -0600 Subject: [PATCH 13/25] looks like there are some newlines on the CI box that are not on my Window's env. --- test/pattern_assembler_tests.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js index 87a149c24..36b6c3ea8 100644 --- a/test/pattern_assembler_tests.js +++ b/test/pattern_assembler_tests.js @@ -315,7 +315,7 @@ //assert var expectedValue = '
{{message}} {{message}} {{message}} {{message}}
'; - test.equals(groupPattern.extendedTemplate.replace(/\s\s+/g, ' ').trim(), expectedValue.trim()); + test.equals(groupPattern.extendedTemplate.replace(/\s\s+/g, ' ').replace(/\n/g, ' ').trim(), expectedValue.trim()); test.done(); }, 'processPatternRecursive - correctly ignores a partial without a style modifier when the same partial later has a style modifier' : function(test){ @@ -347,7 +347,7 @@ //assert. here we expect {{styleModifier}} to be in the first group, since it was not replaced by anything. rendering with data will then remove this (correctly) var expectedValue = '
{{message}} {{message}} {{message}} {{message}}
'; - test.equals(mixedPattern.extendedTemplate.replace(/\s\s+/g, ' ').trim(), expectedValue.trim()); + test.equals(mixedPattern.extendedTemplate.replace(/\s\s+/g, ' ').replace(/\n/g, ' ').trim(), expectedValue.trim()); test.done(); }, 'processPatternRecursive - correctly ignores bookended partials without a style modifier when the same partial has a style modifier between' : function(test){ @@ -379,7 +379,7 @@ //assert. here we expect {{styleModifier}} to be in the first and last group, since it was not replaced by anything. rendering with data will then remove this (correctly) var expectedValue = '
{{message}} {{message}} {{message}} {{message}}
'; - test.equals(bookendPattern.extendedTemplate.replace(/\s\s+/g, ' ').trim(), expectedValue.trim()); + test.equals(bookendPattern.extendedTemplate.replace(/\s\s+/g, ' ').replace(/\n/g, ' ').trim(), expectedValue.trim()); test.done(); }, 'processPatternRecursive - correctly ignores a partial without a style modifier when the same partial later has a style modifier and pattern parameters' : function(test){ @@ -413,7 +413,7 @@ //assert. here we expect {{styleModifier}} to be in the first span, since it was not replaced by anything. rendering with data will then remove this (correctly) var expectedValue = '
{{message}} 2 3 4
'; - test.equals(mixedPattern.extendedTemplate.replace(/\s\s+/g, ' ').trim(), expectedValue.trim()); + test.equals(mixedPattern.extendedTemplate.replace(/\s\s+/g, ' ').replace(/\n/g, ' ').trim(), expectedValue.trim()); test.done(); }, 'processPatternRecursive - correctly ignores bookended partials without a style modifier when the same partial has a style modifier and pattern parameters between' : function(test){ @@ -447,7 +447,7 @@ //assert. here we expect {{styleModifier}} to be in the first and last span, since it was not replaced by anything. rendering with data will then remove this (correctly) var expectedValue = '
{{message}} 2 3 {{message}}
'; - test.equals(bookendPattern.extendedTemplate.replace(/\s\s+/g, ' ').trim(), expectedValue.trim()); + test.equals(bookendPattern.extendedTemplate.replace(/\s\s+/g, ' ').replace(/\n/g, ' ').trim(), expectedValue.trim()); test.done(); } }; From e8f99c11570e8aff93490bd14556b4db1a46b342 Mon Sep 17 00:00:00 2001 From: Geoffrey Pursell Date: Tue, 1 Dec 2015 10:51:41 -0600 Subject: [PATCH 14/25] Remove unused module requires -- that's the last of the direct couplings to mustache! Hooray! --- builder/parameter_hunter.js | 7 +++---- builder/pseudopattern_hunter.js | 4 +--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/builder/parameter_hunter.js b/builder/parameter_hunter.js index ef70bead5..c17da993b 100644 --- a/builder/parameter_hunter.js +++ b/builder/parameter_hunter.js @@ -1,6 +1,6 @@ -/* - * patternlab-node - v0.14.0 - 2015 - * +/* + * patternlab-node - v0.14.0 - 2015 + * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. * @@ -15,7 +15,6 @@ var extend = require('util')._extend, pa = require('./pattern_assembler'), - mustache = require('mustache'), smh = require('./style_modifier_hunter'), style_modifier_hunter = new smh(), pattern_assembler = new pa(); diff --git a/builder/pseudopattern_hunter.js b/builder/pseudopattern_hunter.js index 7615d03d6..c3dc07f20 100644 --- a/builder/pseudopattern_hunter.js +++ b/builder/pseudopattern_hunter.js @@ -19,9 +19,7 @@ fs = require('fs-extra'), pa = require('./pattern_assembler'), lh = require('./lineage_hunter'), - of = require('./object_factory'), - patternEngines = require('./pattern_engines/pattern_engines'), - mustache = require('mustache'); + of = require('./object_factory'); var pattern_assembler = new pa(); var lineage_hunter = new lh(); From 96c88c0fc6bf9aa6dd3670c26a2f68018fac40bf Mon Sep 17 00:00:00 2001 From: Geoffrey Pursell Date: Tue, 1 Dec 2015 12:39:30 -0600 Subject: [PATCH 15/25] Migrate some things to a nicer object literal syntax; use the oPattern factory in the pseudopattern hunter; misc. code cleanup; hide some debug console.logs behind the flag --- builder/lineage_hunter.js | 5 +++- builder/object_factory.js | 49 +++++++++++++++++++-------------- builder/patternlab.js | 2 -- builder/pseudopattern_hunter.js | 18 ++++++------ test/pattern_engines_tests.js | 1 - 5 files changed, 41 insertions(+), 34 deletions(-) diff --git a/builder/lineage_hunter.js b/builder/lineage_hunter.js index 5109c03ef..cad37184d 100644 --- a/builder/lineage_hunter.js +++ b/builder/lineage_hunter.js @@ -17,9 +17,12 @@ var pa = require('./pattern_assembler'); var pattern_assembler = new pa(); + var config = require('../config.json'); //find the {{> template-name }} within patterns - console.log('===\n', pattern, '\n==='); + if (config.debug) { + console.log('===\n', pattern, '\n==='); + } var matches = pattern.findPartials(); if(matches !== null){ matches.forEach(function(match, index, matches){ diff --git a/builder/object_factory.js b/builder/object_factory.js index 54b910961..1c32a21da 100644 --- a/builder/object_factory.js +++ b/builder/object_factory.js @@ -19,7 +19,7 @@ // oPattern properties - var oPattern = function(abspath, subdir, filename, data){ + var oPattern = function(abspath, subdir, filename, data) { if (config.debug) { console.log('=== NEW OPATTERN.', '\nabsPath:', abspath, '\nsubdir:', subdir, '\nfilename:', filename, '\ndata:\n', data); } @@ -50,27 +50,34 @@ // oPattern methods - // render method on oPatterns; this acts as a proxy for the PatternEngine's - // render function - oPattern.prototype.render = function (data, partials) { - if (config.debug && this.isPseudoPattern) { - console.log('===', this.name + ' IS A PSEUDO-PATTERN ==='); + oPattern.prototype = { + + // render method on oPatterns; this acts as a proxy for the PatternEngine's + // render function + render: function (data, partials) { + if (config.debug && this.isPseudoPattern) { + console.log('===', this.name + ' IS A PSEUDO-PATTERN ==='); + } + return this.engine.renderPattern(this.extendedTemplate, data, partials); + }, + + // the finders all delegate to the PatternEngine, which also encapsulates all + // appropriate regexes + findPartials: function () { + return this.engine.findPartials(this); + }, + + findPartialsWithStyleModifiers: function () { + return this.engine.findPartialsWithStyleModifiers(this); + }, + + findPartialsWithPatternParameters: function () { + return this.engine.findPartialsWithPatternParameters(this); + }, + + findListItems: function () { + return this.engine.findListItems(this); } - return this.engine.renderPattern(this.extendedTemplate, data, partials); - }; - // the finders all delegate to the PatternEngine, which also encapsulates all - // appropriate regexes - oPattern.prototype.findPartials = function () { - return this.engine.findPartials(this); - }; - oPattern.prototype.findPartialsWithStyleModifiers = function () { - return this.engine.findPartialsWithStyleModifiers(this); - }; - oPattern.prototype.findPartialsWithPatternParameters = function () { - return this.engine.findPartialsWithPatternParameters(this); - }; - oPattern.prototype.findListItems = function () { - return this.engine.findListItems(this); }; // oPattern static methods diff --git a/builder/patternlab.js b/builder/patternlab.js index 1b64e990e..40118b799 100644 --- a/builder/patternlab.js +++ b/builder/patternlab.js @@ -13,9 +13,7 @@ var patternlab_engine = function () { var path = require('path'), fs = require('fs-extra'), - extend = require('util')._extend, diveSync = require('diveSync'), - glob = require('glob'), of = require('./object_factory'), pa = require('./pattern_assembler'), mh = require('./media_hunter'), diff --git a/builder/pseudopattern_hunter.js b/builder/pseudopattern_hunter.js index c3dc07f20..82f88a8f3 100644 --- a/builder/pseudopattern_hunter.js +++ b/builder/pseudopattern_hunter.js @@ -47,22 +47,22 @@ //extend any existing data with variant data variantFileData = pattern_assembler.merge_data(currentPattern.jsonFileData, variantFileData); - // GTP: mustache-specific stuff here var variantName = pseudoPatterns[i].substring(pseudoPatterns[i].indexOf('~') + 1).split('.')[0]; var variantFilePath = 'source/_patterns/' + currentPattern.subdir + '/' + currentPattern.fileName + '~' + variantName + '.json'; var variantFileName = currentPattern.fileName + '-' + variantName + '.'; - var patternVariant = new of.oPattern(variantFilePath, currentPattern.subdir, variantFileName, variantFileData); + var patternVariant = of.oPattern.create(variantFilePath, currentPattern.subdir, variantFileName, variantFileData, { + //use the same template as the non-variant + template: currentPattern.template, + extendedTemplate: currentPattern.extendedTemplate, + isPseudoPattern: true, + basePattern: currentPattern, + // use the same template engine as the non-variant + engine: currentPattern.engine + }); //see if this file has a state pattern_assembler.setPatternState(patternVariant, patternlab); - //use the same template as the non-variant - patternVariant.template = currentPattern.template; - patternVariant.extendedTemplate = currentPattern.extendedTemplate; - patternVariant.isPseudoPattern = true; - patternVariant.basePattern = currentPattern; - patternVariant.engine = patternVariant.basePattern.engine; - //find pattern lineage lineage_hunter.find_lineage(patternVariant, patternlab); diff --git a/test/pattern_engines_tests.js b/test/pattern_engines_tests.js index 34eb5228a..21db4b796 100644 --- a/test/pattern_engines_tests.js +++ b/test/pattern_engines_tests.js @@ -27,7 +27,6 @@ 'getEngineNameForPattern returns "mustache" for an artificial empty template': function (test) { test.expect(1); var emptyPattern = of.oPattern.createEmpty(); - console.log(emptyPattern); test.equals(patternEngines.getEngineNameForPattern(emptyPattern), 'mustache'); test.done(); }, From d7a531e1fc975e2aba724909e349518a2a6fed26 Mon Sep 17 00:00:00 2001 From: Geoffrey Pursell Date: Tue, 1 Dec 2015 12:42:17 -0600 Subject: [PATCH 16/25] factor utility functions out of the pattern assembler and put 'em in our utilities module; move isPatternFile into pattern_engines module since that's where the knowledge it needs is concentrated --- builder/list_item_hunter.js | 7 +- builder/parameter_hunter.js | 5 +- builder/pattern_assembler.js | 83 ++-------------------- builder/pattern_engines/pattern_engines.js | 23 ++++++ builder/patternlab.js | 3 +- builder/pseudopattern_hunter.js | 6 +- builder/util.js | 22 ------ builder/utilities.js | 65 +++++++++++++++++ test/pattern_assembler_tests.js | 3 +- 9 files changed, 109 insertions(+), 108 deletions(-) delete mode 100644 builder/util.js create mode 100644 builder/utilities.js diff --git a/builder/list_item_hunter.js b/builder/list_item_hunter.js index ba9f8097c..40c1bdcfa 100644 --- a/builder/list_item_hunter.js +++ b/builder/list_item_hunter.js @@ -16,6 +16,7 @@ var extend = require('util')._extend, pa = require('./pattern_assembler'), smh = require('./style_modifier_hunter'), + plutils = require('./utilities'), config = require('../config.json'), of = require('./object_factory'); @@ -48,7 +49,7 @@ //check for a local listitems.json file var listData = JSON.parse(JSON.stringify(patternlab.listitems)); - listData = pattern_assembler.merge_data(listData, pattern.listitems); + listData = plutils.mergeData(listData, pattern.listitems); //iterate over each copied block, rendering its contents along with pattenlab.listitems[i] for(var i = 0; i < repeatedBlockTemplate.length; i++){ @@ -61,8 +62,8 @@ var globalData = JSON.parse(JSON.stringify(patternlab.data)); var localData = JSON.parse(JSON.stringify(pattern.jsonFileData)); - var allData = pattern_assembler.merge_data(globalData, localData); - allData = pattern_assembler.merge_data(allData, itemData != undefined ? itemData[i] : {}); //itemData could be undefined if the listblock contains no partial, just markup + var allData = plutils.mergeData(globalData, localData); + allData = plutils.mergeData(allData, itemData != undefined ? itemData[i] : {}); //itemData could be undefined if the listblock contains no partial, just markup allData.link = extend({}, patternlab.data.link); //check for partials within the repeated block diff --git a/builder/parameter_hunter.js b/builder/parameter_hunter.js index c17da993b..5ef4f672b 100644 --- a/builder/parameter_hunter.js +++ b/builder/parameter_hunter.js @@ -16,6 +16,7 @@ var extend = require('util')._extend, pa = require('./pattern_assembler'), smh = require('./style_modifier_hunter'), + plutils = require('./utilities'), style_modifier_hunter = new smh(), pattern_assembler = new pa(); @@ -43,8 +44,8 @@ var globalData = JSON.parse(JSON.stringify(patternlab.data)); var localData = JSON.parse(JSON.stringify(pattern.jsonFileData || {})); - var allData = pattern_assembler.merge_data(globalData, localData); - allData = pattern_assembler.merge_data(allData, paramData); + var allData = plutils.mergeData(globalData, localData); + allData = plutils.mergeData(allData, paramData); //if partial has style modifier data, replace the styleModifier value if(pattern.stylePartials && pattern.stylePartials.length > 0){ diff --git a/builder/pattern_assembler.js b/builder/pattern_assembler.js index e488d8a73..f96fd56b5 100644 --- a/builder/pattern_assembler.js +++ b/builder/pattern_assembler.js @@ -15,19 +15,10 @@ var fs = require('fs-extra'), of = require('./object_factory'), - path = require('path'), + plutils = require('./utilities'), patternEngines = require('./pattern_engines/pattern_engines'), config = fs.readJSONSync('./config.json'); - function isObjectEmpty(obj) { - for(var prop in obj) { - if(obj.hasOwnProperty(prop)) - return false; - } - - return true; - } - function setState(pattern, patternlab){ if(patternlab.config.patternStates[pattern.patternName]){ pattern.patternState = patternlab.config.patternStates[pattern.patternName]; @@ -74,24 +65,6 @@ } } - // takes a filename string, not a full path; a basename (plus extension) - // ignore _underscored patterns, dotfiles, and anything not recognized by a - // loaded pattern engine. Pseudo-pattern .json files ARE considered to be - // pattern files! - function isPatternFile(filename) { - // skip hidden patterns/files without a second thought - var extension = path.extname(filename); - if(filename.charAt(0) === '.' || - filename.charAt(0) === '_' || - (extension === '.json' && filename.indexOf('~') === -1)) { - return false; - } - - // not a hidden pattern, let's dig deeper - var supportedPatternFileExtensions = patternEngines.getSupportedFileExtensions(); - return (supportedPatternFileExtensions.lastIndexOf(extension) !== -1); - } - function processPatternIterative(file, patternlab){ var fs = require('fs-extra'), of = require('./object_factory'), @@ -107,7 +80,7 @@ } // skip non-pattern files - if (!isPatternFile(filename, patternlab)) { return; } + if (!patternEngines.isPatternFile(filename, patternlab)) { return; } if (config.debug) { console.log('processPatternIterative:', 'found pattern', file); } @@ -274,40 +247,7 @@ throw 'Could not find pattern with key ' + key; } - /** - * Recursively merge properties of two objects. - * - * @param {Object} obj1 If obj1 has properties obj2 doesn't, add to obj2. - * @param {Object} obj2 This object's properties have priority over obj1. - * @returns {Object} obj2 - */ - function mergeData(obj1, obj2){ - if(typeof obj2 === 'undefined'){ - obj2 = {}; - } - for(var p in obj1){ - try { - // Only recurse if obj1[p] is an object. - if(obj1[p].constructor === Object){ - // Requires 2 objects as params; create obj2[p] if undefined. - if(typeof obj2[p] === 'undefined'){ - obj2[p] = {}; - } - obj2[p] = mergeData(obj1[p], obj2[p]); - // Pop when recursion meets a non-object. If obj1[p] is a non-object, - // only copy to undefined obj2[p]. This way, obj2 maintains priority. - } else if(typeof obj2[p] === 'undefined'){ - obj2[p] = obj1[p]; - } - } catch(e) { - // Property in destination object not set; create it and set its value. - if(typeof obj2[p] === 'undefined'){ - obj2[p] = obj1[p]; - } - } - } - return obj2; - } + function buildListItems(container){ //combine all list items into one structure @@ -317,7 +257,7 @@ list.push(container.listitems[item]); } } - container.listItemArray = shuffle(list); + container.listItemArray = plutils.shuffle(list); for(var i = 1; i <= container.listItemArray.length; i++){ var tempItems = []; @@ -333,11 +273,7 @@ } } - //http://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array-in-javascript - function shuffle(o){ - for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); - return o; - } + return { find_pattern_partials: function(pattern){ @@ -370,16 +306,9 @@ get_pattern_by_key: function(key, patternlab){ return getpatternbykey(key, patternlab); }, - merge_data: function(existingData, newData){ - return mergeData(existingData, newData); - }, combine_listItems: function(patternlab){ buildListItems(patternlab); - }, - is_object_empty: function(obj){ - return isObjectEmpty(obj); - }, - is_pattern_file: isPatternFile + } }; }; diff --git a/builder/pattern_engines/pattern_engines.js b/builder/pattern_engines/pattern_engines.js index 8a0711f99..32370f154 100644 --- a/builder/pattern_engines/pattern_engines.js +++ b/builder/pattern_engines/pattern_engines.js @@ -12,6 +12,8 @@ (function () { 'use strict'; + var path = require('path'); + // list of supported pattern engines var supportedPatternEngineNames = [ 'mustache', @@ -42,6 +44,7 @@ // accordingly return 'mustache'; }, + getEngineForPattern: function (pattern) { if (pattern.isPseudoPattern) { return this.getEngineForPattern(pattern.basePattern); @@ -50,15 +53,35 @@ return this[engineName]; } }, + getSupportedFileExtensions: function () { var engineNames = Object.keys(PatternEngines); return engineNames.map(function (engineName) { return PatternEngines[engineName].engineFileExtension; }); }, + isFileExtensionSupported: function (fileExtension) { var supportedExtensions = PatternEngines.getSupportedFileExtensions(); return (supportedExtensions.lastIndexOf(fileExtension) !== -1); + }, + + // takes a filename string, not a full path; a basename (plus extension) + // ignore _underscored patterns, dotfiles, and anything not recognized by a + // loaded pattern engine. Pseudo-pattern .json files ARE considered to be + // pattern files! + isPatternFile: function (filename) { + // skip hidden patterns/files without a second thought + var extension = path.extname(filename); + if(filename.charAt(0) === '.' || + filename.charAt(0) === '_' || + (extension === '.json' && filename.indexOf('~') === -1)) { + return false; + } + + // not a hidden pattern, let's dig deeper + var supportedPatternFileExtensions = PatternEngines.getSupportedFileExtensions(); + return (supportedPatternFileExtensions.lastIndexOf(extension) !== -1); } }); diff --git a/builder/patternlab.js b/builder/patternlab.js index 40118b799..3968b4bc0 100644 --- a/builder/patternlab.js +++ b/builder/patternlab.js @@ -19,6 +19,7 @@ var patternlab_engine = function () { mh = require('./media_hunter'), pe = require('./pattern_exporter'), he = require('html-entities').AllHtmlEntities, + plutils = require('./utilities'), patternlab = {}; patternlab.package = fs.readJSONSync('./package.json'); @@ -120,7 +121,7 @@ var patternlab_engine = function () { patternlab.patterns.forEach(function(pattern, index, patterns){ //render the pattern, but first consolidate any data we may have var allData = JSON.parse(JSON.stringify(patternlab.data)); - allData = pattern_assembler.merge_data(allData, pattern.jsonFileData); + allData = plutils.mergeData(allData, pattern.jsonFileData); pattern.patternPartial = pattern_assembler.renderPattern(pattern, allData); diff --git a/builder/pseudopattern_hunter.js b/builder/pseudopattern_hunter.js index 82f88a8f3..a5ab86bf4 100644 --- a/builder/pseudopattern_hunter.js +++ b/builder/pseudopattern_hunter.js @@ -19,7 +19,8 @@ fs = require('fs-extra'), pa = require('./pattern_assembler'), lh = require('./lineage_hunter'), - of = require('./object_factory'); + of = require('./object_factory'), + plutils = require('./utilities'); var pattern_assembler = new pa(); var lineage_hunter = new lh(); @@ -38,6 +39,7 @@ for(var i = 0; i < pseudoPatterns.length; i++){ if(patternlab.config.debug){ + debugger; console.log('found pseudoPattern variant of ' + currentPattern.key); } @@ -45,7 +47,7 @@ var variantFileData = fs.readJSONSync('source/_patterns/' + pseudoPatterns[i]); //extend any existing data with variant data - variantFileData = pattern_assembler.merge_data(currentPattern.jsonFileData, variantFileData); + variantFileData = plutils.mergeData(currentPattern.jsonFileData, variantFileData); var variantName = pseudoPatterns[i].substring(pseudoPatterns[i].indexOf('~') + 1).split('.')[0]; var variantFilePath = 'source/_patterns/' + currentPattern.subdir + '/' + currentPattern.fileName + '~' + variantName + '.json'; diff --git a/builder/util.js b/builder/util.js deleted file mode 100644 index 53ab39a3b..000000000 --- a/builder/util.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * patternlab-node - v0.14.0 - 2015 - * - * Brian Muenzenmeyer, Geoffrey Pursell and the web community. - * Licensed under the MIT license. - * - * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. - * - */ - -(function () { - var path = require('path'); - - var util = { - // takes a string of the filename or path, and will tell you if it refers to - isPseudoPattern: function () { - - } - }; - - module.exports = util; -}()); diff --git a/builder/utilities.js b/builder/utilities.js new file mode 100644 index 000000000..64d5190ed --- /dev/null +++ b/builder/utilities.js @@ -0,0 +1,65 @@ +/* + * patternlab-node - v0.14.0 - 2015 + * + * Brian Muenzenmeyer, Geoffrey Pursell and the web community. + * Licensed under the MIT license. + * + * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. + * + */ + +(function () { + var path = require('path'); + + var util = { + // http://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array-in-javascript + shuffle: function (o) { + for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); + return o; + }, + + /** + * Recursively merge properties of two objects. + * + * @param {Object} obj1 If obj1 has properties obj2 doesn't, add to obj2. + * @param {Object} obj2 This object's properties have priority over obj1. + * @returns {Object} obj2 + */ + mergeData: function (obj1, obj2) { + if(typeof obj2 === 'undefined'){ + obj2 = {}; + } + for(var p in obj1){ + try { + // Only recurse if obj1[p] is an object. + if(obj1[p].constructor === Object){ + // Requires 2 objects as params; create obj2[p] if undefined. + if(typeof obj2[p] === 'undefined'){ + obj2[p] = {}; + } + obj2[p] = util.mergeData(obj1[p], obj2[p]); + // Pop when recursion meets a non-object. If obj1[p] is a non-object, + // only copy to undefined obj2[p]. This way, obj2 maintains priority. + } else if(typeof obj2[p] === 'undefined'){ + obj2[p] = obj1[p]; + } + } catch(e) { + // Property in destination object not set; create it and set its value. + if(typeof obj2[p] === 'undefined'){ + obj2[p] = obj1[p]; + } + } + } + return obj2; + }, + + isObjectEmpty: function (obj) { + for(var prop in obj) { + if(obj.hasOwnProperty(prop)) { return false; } + } + return true; + } + }; + + module.exports = util; +}()); diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js index 5ff9d1ab2..dab6426da 100644 --- a/test/pattern_assembler_tests.js +++ b/test/pattern_assembler_tests.js @@ -3,6 +3,7 @@ var pa = require('../builder/pattern_assembler'); var of = require('../builder/object_factory'); + var patternEngines = require('../builder/pattern_engines/pattern_engines'); exports['pattern_assembler'] = { 'find_pattern_partials finds partials' : function(test){ @@ -341,7 +342,7 @@ // loop over each test case and test it Object.keys(filenames).forEach(function (filename) { var expectedResult = filenames[filename], - actualResult = pattern_assembler.is_pattern_file(filename), + actualResult = patternEngines.isPatternFile(filename), testMessage = 'isPatternFile should return ' + expectedResult + ' for ' + filename; test.strictEqual(actualResult, expectedResult, testMessage); }); From 0ffb2fe97c98b70591fac5447de7627be07a2ae8 Mon Sep 17 00:00:00 2001 From: Geoffrey Pursell Date: Tue, 1 Dec 2015 17:18:11 -0600 Subject: [PATCH 17/25] have eslint chill out about a couple things --- .eslintrc | 4 +++- builder/list_item_hunter.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.eslintrc b/.eslintrc index 61dcc48db..9f090e1e2 100644 --- a/.eslintrc +++ b/.eslintrc @@ -15,7 +15,7 @@ "all" ], "dot-notation": [ - 2, + 1, { "allowKeywords": true } @@ -29,6 +29,7 @@ "never" ], "guard-for-in": 2, + "key-spacing": 1, "new-cap": 0, "no-bitwise": 2, "no-caller": 2, @@ -52,6 +53,7 @@ "no-sequences": 2, "no-shadow": 2, "no-undef": 2, + "no-underscore-dangle": 1, "no-unused-vars": 2, "no-with": 2, "quotes": [ diff --git a/builder/list_item_hunter.js b/builder/list_item_hunter.js index 40c1bdcfa..d2e56f212 100644 --- a/builder/list_item_hunter.js +++ b/builder/list_item_hunter.js @@ -43,7 +43,9 @@ var repeatedBlockTemplate = []; var repeatedBlockHtml = ''; for(var i = 0; i < items.indexOf(loopNumberString); i++){ - console.log('adding', patternBlock, 'to repeatedBlockTemplate'); + if (config.debug) { + console.log('list item(s) in pattern', pattern.patternName, 'adding', patternBlock, 'to repeatedBlockTemplate'); + } repeatedBlockTemplate.push(patternBlock); } From b03bc45f4a6df3666e98bdac12b64b7dc0a98ff1 Mon Sep 17 00:00:00 2001 From: Geoffrey Pursell Date: Tue, 1 Dec 2015 17:21:02 -0600 Subject: [PATCH 18/25] New partial finding regex for Mustache that accounts for regular syntax, verbose syntax, style modifier syntax, pattern parameter syntax, and single and double quoted strings in pattern parameters with escaped quotes. Making this hurt my brain very badly. Update the appropriate unit test with all the test cases used to develop the regex, plus some flown in from other unit tests. --- builder/pattern_engines/engine_mustache.js | 2 +- test/pattern_assembler_tests.js | 45 +++++++++++++++++----- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/builder/pattern_engines/engine_mustache.js b/builder/pattern_engines/engine_mustache.js index 23692ae5d..87be322f2 100644 --- a/builder/pattern_engines/engine_mustache.js +++ b/builder/pattern_engines/engine_mustache.js @@ -19,7 +19,7 @@ engineFileExtension: '.mustache', // regexes, stored here so they're only compiled once - findPartialsRE: /{{>([ ])?([A-Za-z0-9-]+)(?:\:[A-Za-z0-9-]+)?(?:(| )\(.*)?([ ])?}}/g, + findPartialsRE: /{{>\s*((?:\d+-[\w-]+\/)+(\d+-[\w-]+\.\w+)|[A-Za-z0-9-]+)(\:[A-Za-z0-9-]+)?(\(\s*\w+\s*:\s*(?:'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*")\))?\s*}}/g, findPartialsWithStyleModifiersRE: /{{>([ ])?([\w\-\.\/~]+)(?!\()(\:[A-Za-z0-9-_]+)+(?:(| )\(.*)?([ ])?}}/g, findPartialsWithPatternParametersRE: /{{>([ ])?([\w\-\.\/~]+)(?:\:[A-Za-z0-9-_]+)?(?:(| )\(.*)+([ ])?}}/g, findListItemsRE: /({{#( )?)(list(I|i)tems.)(one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty)( )?}}/g, diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js index dab6426da..a3701dff9 100644 --- a/test/pattern_assembler_tests.js +++ b/test/pattern_assembler_tests.js @@ -7,22 +7,47 @@ exports['pattern_assembler'] = { 'find_pattern_partials finds partials' : function(test){ - test.expect(3); + test.expect(13); //setup current pattern from what we would have during execution - var currentPattern = new of.oPattern( + var currentPattern = of.oPattern.create( '/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.mustache', // abspath '01-molecules\\00-testing', // subdir '00-test-mol.mustache', // filename, - null // data + null, // data + { + template: "{{> molecules-comment-header}}asdfasdf" + + "{{> molecules-comment-header}}" + + "{{> \n molecules-comment-header\n}}" + + "{{> }}" + + "{{> molecules-weird-spacing }}" + + "{{> molecules-ba_d-cha*rs }}" + + "{{> molecules-single-comment(description: 'A life isn\\'t like a garden. Perfect moments can be had, but not preserved, except in memory.') }}" + + '{{> molecules-single-comment(description: "A life is like a \\"garden\\". Perfect moments can be had, but not preserved, except in memory.") }}' + + "{{> molecules-single-comment:foo }}" + + "{{> 01-molecules/06-components/03-comment-header.mustache }}" + + "{{> 01-molecules/06-components/02-single-comment.mustache(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}" + + "{{> molecules-single-comment:foo }}" + + "{{>atoms-error(message: 'That\\'s no moon...')}}" + + '{{>atoms-error(message: \'That\\\'s no moon...\')}}' + } ); - currentPattern.template = "

{{> molecules-comment-header}}

{{> molecules-single-comment(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}
"; var results = currentPattern.findPartials(); - test.equals(results.length, 2); - test.equals(results[0], '{{> molecules-comment-header}}'); - test.equals(results[1], '{{> molecules-single-comment(description: \'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.\') }}'); - + console.log(results); + test.equals(results.length, 12); + test.equals(results[0], "{{> molecules-comment-header}}"); + test.equals(results[1], "{{> molecules-comment-header}}"); + test.equals(results[2], "{{> \n molecules-comment-header\n}}"); + test.equals(results[3], "{{> molecules-weird-spacing }}"); + test.equals(results[4], "{{> molecules-single-comment(description: 'A life isn\\'t like a garden. Perfect moments can be had, but not preserved, except in memory.') }}"); + test.equals(results[5], '{{> molecules-single-comment(description: "A life is like a \\"garden\\". Perfect moments can be had, but not preserved, except in memory.") }}'); + test.equals(results[6], "{{> molecules-single-comment:foo }}"); + test.equals(results[7], "{{> 01-molecules/06-components/03-comment-header.mustache }}"); + test.equals(results[8], "{{> 01-molecules/06-components/02-single-comment.mustache(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}"); + test.equals(results[9], "{{> molecules-single-comment:foo }}"); + test.equals(results[10], "{{>atoms-error(message: 'That\\'s no moon...')}}"); + test.equals(results[11], "{{>atoms-error(message: 'That\\'s no moon...')}}"); test.done(); }, @@ -36,12 +61,12 @@ '00-test-mol.mustache', // filename, null // data ); - currentPattern.template = "

{{> 01-molecules/06-components/03-comment-header.mustache }}

{{> 01-molecules/06-components/02-single-comment(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}
"; + currentPattern.template = "

{{> 01-molecules/06-components/03-comment-header.mustache }}

{{> 01-molecules/06-components/02-single-comment.mustache(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}
"; var results = currentPattern.findPartials(); test.equals(results.length, 2); test.equals(results[0], '{{> 01-molecules/06-components/03-comment-header.mustache }}'); - test.equals(results[1], '{{> 01-molecules/06-components/02-single-comment(description: \'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.\') }}'); + test.equals(results[1], '{{> 01-molecules/06-components/02-single-comment.mustache(description: \'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.\') }}'); test.done(); }, From 882aee28fb1d8c146f71987a23f3c9b121a714b4 Mon Sep 17 00:00:00 2001 From: Geoffrey Pursell Date: Tue, 1 Dec 2015 17:23:22 -0600 Subject: [PATCH 19/25] streamline, DRY out and update unit tests --- test/lineage_hunter_tests.js | 40 ++-- test/list_item_hunter_tests.js | 334 ++++++++++----------------------- 2 files changed, 115 insertions(+), 259 deletions(-) diff --git a/test/lineage_hunter_tests.js b/test/lineage_hunter_tests.js index 7c00ce415..b500cb265 100644 --- a/test/lineage_hunter_tests.js +++ b/test/lineage_hunter_tests.js @@ -104,30 +104,22 @@ //setup current pattern from what we would have during execution var currentPattern = createFakeEmptyErrorPattern(); extend(currentPattern, { - "template": "{{> atoms-error(message: 'That\'s no moon...') }}", - "patternPartial": "{{> atoms-error(message: 'That\'s no moon...') }}" + "template": "{{> atoms-error(message: 'That\\'s no moon...') }}", + "patternPartial": "{{> atoms-error(message: 'That\\'s no moon...') }}" }); var patternlab = { patterns: [ - { - "name": "01-atoms-05-alerts-00-error", - "subdir": "01-atoms\\05-alerts", - "filename": "00-error.mustache", - "data": null, - "template": "

{{message}}

", - "patternPartial": "

{{message}}

", - "patternName": "error", - "patternLink": "01-atoms-05-alerts-00-error/01-atoms-05-alerts-00-error.html", - "patternGroup": "atoms", - "patternSubGroup": "atoms\\05-alerts", - "flatPatternPath": "01-atoms\\05-alerts", - "patternState": "", - "lineage": [], - "lineageIndex": [], - "lineageR": [], - "lineageRIndex": [] - } + of.oPattern.create( + '/home/fakeuser/pl/source/_patterns/00-atoms/05-alerts/00-error.mustache', + '00-atoms\\05-alerts', + '00-error.mustache', + null, + { + "template": "

{{message}}

", + "patternPartial": "

{{message}}

" + } + ) ] }; @@ -144,8 +136,8 @@ //setup current pattern from what we would have during execution var currentPattern = createFakeEmptyErrorPattern(); extend(currentPattern, { - "template": "{{>atoms-error(message: 'That\'s no moon...')}}", - "patternPartial": "{{>atoms-error(message: 'That\'s no moon...')}}", + "template": "{{>atoms-error(message: 'That\\'s no moon...')}}", + "patternPartial": "{{>atoms-error(message: 'That\\'s no moon...')}}", }); var patternlab = { @@ -186,8 +178,8 @@ //setup current pattern from what we would have during execution var currentPattern = createFakeEmptyErrorPattern(); extend(currentPattern, { - "template": "{{>atoms-error(message: 'That\'s no moon...')}}", - "patternPartial": "{{>atoms-error(message: 'That\'s no moon...')}}" + "template": "{{>atoms-error(message: 'That\\'s no moon...')}}", + "patternPartial": "{{>atoms-error(message: 'That\\'s no moon...')}}" }); var patternlab = { patterns: [ diff --git a/test/list_item_hunter_tests.js b/test/list_item_hunter_tests.js index aa0529b74..055790602 100644 --- a/test/list_item_hunter_tests.js +++ b/test/list_item_hunter_tests.js @@ -18,8 +18,29 @@ return extend(pattern, customProps); } + function createFakePatternLab(customProps) { + var pl = { + "listitems": { + "1": [ + { "title": "Foo" } + ], + "2": [ + { "title": "Foo" }, + { "title": "Bar" } + ] + }, + "data": { + "link": {}, + "partials": [] + }, + "config": {"debug": false} + }; + + return extend(pl, customProps); + } + exports['list_item_hunter'] = { - 'process_list_item_partials finds and outputs basic repeating blocks' : function(test){ + 'process_list_item_partials finds and outputs basic repeating blocks': function(test){ //arrange //setup current pattern from what we would have during execution var currentPattern = createFakeListPattern({ @@ -27,30 +48,7 @@ "extendedTemplate": "{{#listItems.two}}{{ title }}{{/listItems.two}}", "key": "test-patternName" }); - - var patternlab = { - "listitems": { - "1": [ - { - "title": "Foo" - } - ], - "2": [ - { - "title": "Foo" - }, - { - "title": "Bar" - } - ] - }, - "data": { - "link": {}, - "partials": [] - }, - "config": {"debug": false} - }; - + var patternlab = createFakePatternLab(); var list_item_hunter = new lih(); //act @@ -70,30 +68,7 @@ "extendedTemplate" : "{{#listitems.two}}{{ title }}{{/listitems.two}}", "key": "test-patternName" }); - - var patternlab = { - "listitems": { - "1": [ - { - "title": "Foo" - } - ], - "2": [ - { - "title": "Foo" - }, - { - "title": "Bar" - } - ] - }, - "data": { - "link": {}, - "partials": [] - }, - "config": {"debug": false} - }; - + var patternlab = createFakePatternLab(); var list_item_hunter = new lih(); //act @@ -105,45 +80,25 @@ test.done(); }, - 'process_list_item_partials finds partials and outputs repeated renders' : function(test){ + 'process_list_item_partials finds partials and outputs repeated renders': function(test){ //arrange //setup current pattern from what we would have during execution var currentPattern = createFakeListPattern({ "template": "{{#listItems.two}}{{ title }}{{/listItems.two}}", - "extendedTemplate" : "{{#listItems.two}}{{> test-simple }}{{/listItems.two}}", + "extendedTemplate": "{{#listItems.two}}{{> test-simple }}{{/listItems.two}}", "key": "test-patternName" }); - var patternlab = { - "listitems": { - "1": [ - { - "title": "Foo" - } - ], - "2": [ - { - "title": "Foo" - }, - { - "title": "Bar" - } - ] - }, - "data": { - "link": {}, - "partials": [] - }, - "config": {"debug": false}, + var patternlab = createFakePatternLab({ "patterns": [ { - "template": "{{ title }}", - "extendedTemplate" : "{{ title }}", - "key": "test-simple", - "jsonFileData" : {} + "template": "{{ title }}", + "extendedTemplate" : "{{ title }}", + "key": "test-simple", + "jsonFileData" : {} } ] - }; + }); var list_item_hunter = new lih(); @@ -169,43 +124,23 @@ "key": "test-patternName2" }); - var patternlab = { - "listitems": { - "1": [ - { - "title": "Foo" - } - ], - "2": [ - { - "title": "Foo" - }, - { - "title": "Bar" - } - ] - }, - "data": { - "link": {}, - "partials": [] - }, - "config": {"debug": false}, + var patternlab = createFakePatternLab({ "patterns": [ - of.oPattern.create(null, "00-test", "00-foo", null, { + of.oPattern.create('/home/fakeuser/pl/source/_patterns/00-atoms/00-test/00-foo.mustache', "00-atoms/00-test", "00-foo.mustache", null, { "template": "{{ title }}", - "extendedTemplate": "{{ title }}", - "jsonFileData": {} + "extendedTemplate": "{{ title }}" }), - of.oPattern.create(null, "00-test", "00-bar", null, { + of.oPattern.create('/home/fakeuser/pl/source/_patterns/00-atoms/00-test/00-bar.mustache', "00-atoms/00-test", "00-bar.mustache", null, { "template": "{{ title }}", - "extendedTemplate": "{{ title }}", - "jsonFileData": {} + "extendedTemplate": "{{ title }}" }) ] - }; + }); var list_item_hunter = new lih(); + debugger; + //act list_item_hunter.process_list_item_partials(pattern1, patternlab); list_item_hunter.process_list_item_partials(pattern2, patternlab); @@ -217,57 +152,31 @@ test.done(); }, - 'process_list_item_partials overwrites listItem property if that property is in local .listitem.json' : function(test){ + 'process_list_item_partials overwrites listItem property if that property is in local .listitem.json': function(test) { //arrange //setup current pattern from what we would have during execution - var currentPattern = { - "template": "{{#listItems.two}}{{ title }}{{/listItems.two}}", - "extendedTemplate" : "{{#listItems.two}}{{> test-simple }}{{/listItems.two}}", - "key": "test-patternName", - "jsonFileData" : {}, - "listitems" : { - "2": [ - { - "title": "One" - }, - { - "title": "Two" - }, - ] - } - }; - - var patternlab = { + var currentPattern = createFakeListPattern({ + "template": "{{#listItems.two}}{{ title }}{{/listItems.two}}", + "extendedTemplate": "{{#listItems.two}}{{> test-simple }}{{/listItems.two}}", + "key": "test-patternName", + "jsonFileData": {}, "listitems": { - "1": [ - { - "title": "Foo" - } - ], "2": [ - { - "title": "Foo" - }, - { - "title": "Bar" - } + { "title": "One" }, + { "title": "Two" } ] - }, - "data": { - "link": {}, - "partials": [] - }, - "config": {"debug": false}, + } + }); + var patternlab = createFakePatternLab({ "patterns": [ - { - "template": "{{ title }}", - "extendedTemplate" : "{{ title }}", - "key": "test-simple", - "jsonFileData" : {} - } + createFakeListPattern({ + "template": "{{ title }}", + "extendedTemplate": "{{ title }}", + "key": "test-simple", + "jsonFileData": {} + }) ] - }; - + }); var list_item_hunter = new lih(); //act @@ -282,54 +191,28 @@ 'process_list_item_partials keeps listItem property if that property is not in local .listitem.json' : function(test){ //arrange //setup current pattern from what we would have during execution - var currentPattern = { - "template": "{{#listItems.one}}{{ title }}{{/listItems.one}}", - "extendedTemplate" : "{{#listItems.one}}{{> test-simple }}{{/listItems.one}}", - "key": "test-patternName", - "jsonFileData" : {}, - "listitems" : { - "2": [ - { - "title": "One" - }, - { - "title": "Two" - }, - ] - } - }; - - var patternlab = { + var currentPattern = createFakeListPattern({ + "template": "{{#listItems.one}}{{ title }}{{/listItems.one}}", + "extendedTemplate": "{{#listItems.one}}{{> test-simple }}{{/listItems.one}}", + "key": "test-patternName", + "jsonFileData": {}, "listitems": { - "1": [ - { - "title": "Foo" - } - ], "2": [ - { - "title": "Foo" - }, - { - "title": "Bar" - } + { "title": "One" }, + { "title": "Two" } ] - }, - "data": { - "link": {}, - "partials": [] - }, - "config": {"debug": false}, + } + }); + var patternlab = createFakePatternLab({ "patterns": [ - { - "template": "{{ title }}", - "extendedTemplate" : "{{ title }}", - "key": "test-simple", - "jsonFileData" : {} - } + createFakeListPattern({ + "template": "{{ title }}", + "extendedTemplate": "{{ title }}", + "key": "test-simple", + "jsonFileData" : {} + }) ] - }; - + }); var list_item_hunter = new lih(); //act @@ -344,53 +227,33 @@ 'process_list_item_partials uses local listItem property if that property is not set globally' : function(test){ //arrange //setup current pattern from what we would have during execution - var currentPattern = { - "template": "{{#listItems.one}}{{ title }}{{/listItems.one}}", - "extendedTemplate" : "{{#listItems.one}}{{> test-simple }}{{/listItems.one}}", - "key": "test-patternName", - "jsonFileData" : {}, - "listitems" : { - "1": [ - { - "title": "One" - } - ], - "2": [ - { - "title": "One" - }, - { - "title": "Two" - }, - ] - } - }; - - var patternlab = { + var currentPattern = createFakeListPattern({ + "template": "{{#listItems.one}}{{ title }}{{/listItems.one}}", + "extendedTemplate": "{{#listItems.one}}{{> test-simple }}{{/listItems.one}}", + "key": "test-patternName", + "jsonFileData": {}, "listitems": { + "1": [ + { "title": "One" } + ], "2": [ - { - "title": "Foo" - }, - { - "title": "Bar" - } + { "title": "One" }, + { "title": "Two" } ] - }, - "data": { - "link": {}, - "partials": [] - }, - "config": {"debug": false}, + } + }); + + var patternlab = createFakePatternLab({ "patterns": [ - { - "template": "{{ title }}", - "extendedTemplate" : "{{ title }}", - "key": "test-simple", - "jsonFileData" : {} - } + createFakeListPattern({ + "template": "{{ title }}", + "extendedTemplate": "{{ title }}", + "key": "test-simple", + "jsonFileData": {} + }) ] - }; + }); + delete patternlab.listitems["1"]; // remove the "1" list var list_item_hunter = new lih(); @@ -398,6 +261,7 @@ list_item_hunter.process_list_item_partials(currentPattern, patternlab); //assert + test.equals(typeof patternlab.listitems["1"], "undefined"); test.equals(currentPattern.extendedTemplate, "One" ); test.done(); @@ -405,4 +269,4 @@ }; -}()); +})(); From fd3c4b5bbe45e9a4972d3106a2e027508b398de5 Mon Sep 17 00:00:00 2001 From: Geoffrey Pursell Date: Wed, 2 Dec 2015 10:23:53 -0600 Subject: [PATCH 20/25] unit test fixups, plus a tweak to the Mustache partial finder to permit the verbose partial syntax without the .mustache file extension, as per http://patternlab.io/docs/pattern-including.html --- builder/pattern_engines/engine_mustache.js | 2 +- test/lineage_hunter_tests.js | 4 ++-- test/pattern_assembler_tests.js | 15 ++++++++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/builder/pattern_engines/engine_mustache.js b/builder/pattern_engines/engine_mustache.js index 87be322f2..2ae1970c2 100644 --- a/builder/pattern_engines/engine_mustache.js +++ b/builder/pattern_engines/engine_mustache.js @@ -19,7 +19,7 @@ engineFileExtension: '.mustache', // regexes, stored here so they're only compiled once - findPartialsRE: /{{>\s*((?:\d+-[\w-]+\/)+(\d+-[\w-]+\.\w+)|[A-Za-z0-9-]+)(\:[A-Za-z0-9-]+)?(\(\s*\w+\s*:\s*(?:'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*")\))?\s*}}/g, + findPartialsRE: /{{>\s*((?:\d+-[\w-]+\/)+(\d+-[\w-]+(\.\w+)?)|[A-Za-z0-9-]+)(\:[A-Za-z0-9-]+)?(\(\s*\w+\s*:\s*(?:'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*")\))?\s*}}/g, findPartialsWithStyleModifiersRE: /{{>([ ])?([\w\-\.\/~]+)(?!\()(\:[A-Za-z0-9-_]+)+(?:(| )\(.*)?([ ])?}}/g, findPartialsWithPatternParametersRE: /{{>([ ])?([\w\-\.\/~]+)(?:\:[A-Za-z0-9-_]+)?(?:(| )\(.*)+([ ])?}}/g, findListItemsRE: /({{#( )?)(list(I|i)tems.)(one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty)( )?}}/g, diff --git a/test/lineage_hunter_tests.js b/test/lineage_hunter_tests.js index b500cb265..f95e92de8 100644 --- a/test/lineage_hunter_tests.js +++ b/test/lineage_hunter_tests.js @@ -137,7 +137,7 @@ var currentPattern = createFakeEmptyErrorPattern(); extend(currentPattern, { "template": "{{>atoms-error(message: 'That\\'s no moon...')}}", - "patternPartial": "{{>atoms-error(message: 'That\\'s no moon...')}}", + "patternPartial": "{{>atoms-error(message: 'That\\'s no moon...')}}" }); var patternlab = { @@ -219,4 +219,4 @@ }; -}()); +})(); diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js index a3701dff9..314530d88 100644 --- a/test/pattern_assembler_tests.js +++ b/test/pattern_assembler_tests.js @@ -7,7 +7,13 @@ exports['pattern_assembler'] = { 'find_pattern_partials finds partials' : function(test){ - test.expect(13); + // NOTES from GTP: + // it's nice to have so much test coverage, but it retrospect, I'm not + // happy with the structure I wound up with in this test; it's too + // difficult to add test cases and test failure reporting is not very + // granular. + + test.expect(14); //setup current pattern from what we would have during execution var currentPattern = of.oPattern.create( @@ -29,13 +35,15 @@ "{{> 01-molecules/06-components/02-single-comment.mustache(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}" + "{{> molecules-single-comment:foo }}" + "{{>atoms-error(message: 'That\\'s no moon...')}}" + - '{{>atoms-error(message: \'That\\\'s no moon...\')}}' + '{{>atoms-error(message: \'That\\\'s no moon...\')}}' + + "{{> 00-atoms/00-global/ }}" + + "{{> 00-atoms/00-global/06-test }}" } ); var results = currentPattern.findPartials(); console.log(results); - test.equals(results.length, 12); + test.equals(results.length, 13); test.equals(results[0], "{{> molecules-comment-header}}"); test.equals(results[1], "{{> molecules-comment-header}}"); test.equals(results[2], "{{> \n molecules-comment-header\n}}"); @@ -48,6 +56,7 @@ test.equals(results[9], "{{> molecules-single-comment:foo }}"); test.equals(results[10], "{{>atoms-error(message: 'That\\'s no moon...')}}"); test.equals(results[11], "{{>atoms-error(message: 'That\\'s no moon...')}}"); + test.equals(results[12], "{{> 00-atoms/00-global/06-test }}"); test.done(); }, From ee6fcfc6500fea4be9a07cd005e7c26d95ca1858 Mon Sep 17 00:00:00 2001 From: Geoffrey Pursell Date: Thu, 3 Dec 2015 10:18:22 -0600 Subject: [PATCH 21/25] fix for out-of-order pseudopattern bug -- isPatternFile() should return true for a pseudopattern JSON file and it wasn't. In fact, its unit test was lying to us. Lies! Also: implement a proper isPseudoPatternJSON() function in pattern_engines to centralize that logic and write a unit test for it; move isPatternFile unit test to the right file --- builder/pattern_assembler.js | 2 +- builder/pattern_engines/pattern_engines.js | 12 ++++- test/pattern_assembler_tests.js | 34 ++------------ test/pattern_engines_tests.js | 52 +++++++++++++++++++++- 4 files changed, 66 insertions(+), 34 deletions(-) diff --git a/builder/pattern_assembler.js b/builder/pattern_assembler.js index f96fd56b5..f75c70dda 100644 --- a/builder/pattern_assembler.js +++ b/builder/pattern_assembler.js @@ -89,7 +89,7 @@ var currentPattern = new of.oPattern(file, subdir, filename); //if file is named in the syntax for variants - if(ext === '.json' && filename.indexOf('~') > -1){ + if(patternEngines.isPseudoPatternJSON(filename)){ //add current pattern to patternlab object with minimal data //processPatternRecursive() will run find_pseudopatterns() to fill out //the object in the next diveSync diff --git a/builder/pattern_engines/pattern_engines.js b/builder/pattern_engines/pattern_engines.js index 32370f154..13ef654f0 100644 --- a/builder/pattern_engines/pattern_engines.js +++ b/builder/pattern_engines/pattern_engines.js @@ -66,6 +66,13 @@ return (supportedExtensions.lastIndexOf(fileExtension) !== -1); }, + // given a filename, return a boolean: whether or not the filename indicates + // that the file is pseudopattern JSON + isPseudoPatternJSON: function (filename) { + var extension = path.extname(filename); + return (extension === '.json' && filename.indexOf('~') > -1); + }, + // takes a filename string, not a full path; a basename (plus extension) // ignore _underscored patterns, dotfiles, and anything not recognized by a // loaded pattern engine. Pseudo-pattern .json files ARE considered to be @@ -75,13 +82,14 @@ var extension = path.extname(filename); if(filename.charAt(0) === '.' || filename.charAt(0) === '_' || - (extension === '.json' && filename.indexOf('~') === -1)) { + (extension === '.json' && !PatternEngines.isPseudoPatternJSON(filename))) { return false; } // not a hidden pattern, let's dig deeper var supportedPatternFileExtensions = PatternEngines.getSupportedFileExtensions(); - return (supportedPatternFileExtensions.lastIndexOf(extension) !== -1); + return (supportedPatternFileExtensions.lastIndexOf(extension) !== -1 || + PatternEngines.isPseudoPatternJSON(filename)); } }); diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js index 314530d88..f13545777 100644 --- a/test/pattern_assembler_tests.js +++ b/test/pattern_assembler_tests.js @@ -3,7 +3,6 @@ var pa = require('../builder/pattern_assembler'); var of = require('../builder/object_factory'); - var patternEngines = require('../builder/pattern_engines/pattern_engines'); exports['pattern_assembler'] = { 'find_pattern_partials finds partials' : function(test){ @@ -31,6 +30,9 @@ "{{> molecules-single-comment(description: 'A life isn\\'t like a garden. Perfect moments can be had, but not preserved, except in memory.') }}" + '{{> molecules-single-comment(description: "A life is like a \\"garden\\". Perfect moments can be had, but not preserved, except in memory.") }}' + "{{> molecules-single-comment:foo }}" + + // questionable: is a partial call with a file extension really + // permitted? seems like no, based on + // http://patternlab.io/docs/pattern-including.html "{{> 01-molecules/06-components/03-comment-header.mustache }}" + "{{> 01-molecules/06-components/02-single-comment.mustache(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}" + "{{> molecules-single-comment:foo }}" + @@ -354,35 +356,7 @@ //test that 00-foo.mustache included partial 01-bar.mustache test.equals(fooExtended, 'bar'); - test.done(); - }, - - 'isPatternFile correctly identifies pattern files and rejects non-pattern files': function(test){ - var pattern_assembler = new pa(); - - // each test case - var filenames = { - '00-comment-thread.mustache': true, - '00-comment-thread.fakeextthatdoesntexist': false, - '00-comment-thread': false, - '_00-comment-thread.mustache': false, - '.00-comment-thread.mustache': false, - '00-comment-thread.json': false, - '00-homepage~emergency.json': false - }; - // expect one test per test case - test.expect(Object.keys(filenames).length); - - // loop over each test case and test it - Object.keys(filenames).forEach(function (filename) { - var expectedResult = filenames[filename], - actualResult = patternEngines.isPatternFile(filename), - testMessage = 'isPatternFile should return ' + expectedResult + ' for ' + filename; - test.strictEqual(actualResult, expectedResult, testMessage); - }); - - // done test.done(); } }; -}()); +})(); diff --git a/test/pattern_engines_tests.js b/test/pattern_engines_tests.js index 21db4b796..87810ecf7 100644 --- a/test/pattern_engines_tests.js +++ b/test/pattern_engines_tests.js @@ -39,7 +39,57 @@ var engine = patternEngines.getEngineForPattern(mustacheTestPseudoPattern); test.equals(engine, patternEngines.mustache); test.done(); - } + }, + 'isPseudoPatternJSON correctly identifies pseudo-pattern JSON filenames': function(test) { + // each test case + var filenames = { + '00-homepage~emergency.json': true, + '~emergency.json': true, + '00-homepage~emergency.js': false, + '00-homepage-emergency.js': false, + '00-homepage.hbs': false, + '00-homepage.json': false, + 'greatpic.jpg': false + }; + // expect one test per test case + test.expect(Object.keys(filenames).length); + + // loop over each test case and test it + Object.keys(filenames).forEach(function (filename) { + var expectedResult = filenames[filename], + actualResult = patternEngines.isPseudoPatternJSON(filename), + testMessage = 'isPseudoPatternJSON should return ' + expectedResult + ' for ' + filename; + test.strictEqual(actualResult, expectedResult, testMessage); + }); + + // done + test.done(); + }, + 'isPatternFile correctly identifies pattern files and rejects non-pattern files': function(test){ + // each test case + var filenames = { + '00-comment-thread.mustache': true, + '00-comment-thread.fakeextthatdoesntexist': false, + '00-comment-thread': false, + '_00-comment-thread.mustache': false, + '.00-comment-thread.mustache': false, + '00-comment-thread.json': false, + '00-homepage~emergency.json': true + }; + // expect one test per test case + test.expect(Object.keys(filenames).length); + + // loop over each test case and test it + Object.keys(filenames).forEach(function (filename) { + var expectedResult = filenames[filename], + actualResult = patternEngines.isPatternFile(filename), + testMessage = 'isPatternFile should return ' + expectedResult + ' for ' + filename; + test.strictEqual(actualResult, expectedResult, testMessage); + }); + + // done + test.done(); + } }; // testProps() utility function: given an object, and a hash of expected From 75d90a2b5895f3ba895f916edd04842eeba60d27 Mon Sep 17 00:00:00 2001 From: Geoffrey Pursell Date: Thu, 3 Dec 2015 11:16:58 -0600 Subject: [PATCH 22/25] Note verbose syntax in unit test --- test/pattern_assembler_tests.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js index f13545777..86a6072cc 100644 --- a/test/pattern_assembler_tests.js +++ b/test/pattern_assembler_tests.js @@ -14,8 +14,10 @@ test.expect(14); - //setup current pattern from what we would have during execution - var currentPattern = of.oPattern.create( + // setup current pattern from what we would have during execution + // docs on partial syntax are here: + // http://patternlab.io/docs/pattern-including.html + var currentPattern = of.oPattern.create( '/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.mustache', // abspath '01-molecules\\00-testing', // subdir '00-test-mol.mustache', // filename, @@ -30,15 +32,14 @@ "{{> molecules-single-comment(description: 'A life isn\\'t like a garden. Perfect moments can be had, but not preserved, except in memory.') }}" + '{{> molecules-single-comment(description: "A life is like a \\"garden\\". Perfect moments can be had, but not preserved, except in memory.") }}' + "{{> molecules-single-comment:foo }}" + - // questionable: is a partial call with a file extension really - // permitted? seems like no, based on - // http://patternlab.io/docs/pattern-including.html + // verbose partial syntax, introduced in v0.12.0, with file extension "{{> 01-molecules/06-components/03-comment-header.mustache }}" + "{{> 01-molecules/06-components/02-single-comment.mustache(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}" + "{{> molecules-single-comment:foo }}" + "{{>atoms-error(message: 'That\\'s no moon...')}}" + '{{>atoms-error(message: \'That\\\'s no moon...\')}}' + "{{> 00-atoms/00-global/ }}" + + // verbose partial syntax, introduced in v0.12.0, no file extension "{{> 00-atoms/00-global/06-test }}" } ); From ded322443de00925bdd77786af08579bdacbbee6 Mon Sep 17 00:00:00 2001 From: Geoffrey Pursell Date: Thu, 3 Dec 2015 11:21:03 -0600 Subject: [PATCH 23/25] Fix (?) partial name; unit test now passes --- test/list_item_hunter_tests.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/list_item_hunter_tests.js b/test/list_item_hunter_tests.js index 055790602..1f6430d2c 100644 --- a/test/list_item_hunter_tests.js +++ b/test/list_item_hunter_tests.js @@ -113,14 +113,14 @@ 'process_list_item_partials finds verbose partials and outputs repeated renders' : function(test){ var pattern1 = createFakeListPattern({ - "template": "{{#listItems.one}}{{> 00-test/00-foo }}{{/listItems.one}}", - "extendedTemplate" : "{{#listItems.one}}{{> 00-test/00-foo }}{{/listItems.one}}", + "template": "{{#listItems.one}}{{> atoms-foo }}{{/listItems.one}}", + "extendedTemplate" : "{{#listItems.one}}{{> atoms-foo }}{{/listItems.one}}", "key": "test-patternName1" }); var pattern2 = createFakeListPattern({ - "template": "{{#listItems.two}}{{> 00-test/01-bar.mustache }}{{/listItems.two}}", - "extendedTemplate" : "{{#listItems.two}}{{> 00-test/01-bar.mustache }}{{/listItems.two}}", + "template": "{{#listItems.two}}{{> atoms-bar }}{{/listItems.two}}", + "extendedTemplate" : "{{#listItems.two}}{{> atoms-bar }}{{/listItems.two}}", "key": "test-patternName2" }); @@ -139,8 +139,6 @@ var list_item_hunter = new lih(); - debugger; - //act list_item_hunter.process_list_item_partials(pattern1, patternlab); list_item_hunter.process_list_item_partials(pattern2, patternlab); From b2e1a380a3420182f9be683e6460d1dc233dcf5c Mon Sep 17 00:00:00 2001 From: Geoffrey Pursell Date: Fri, 4 Dec 2015 11:22:52 -0600 Subject: [PATCH 24/25] Fix the 'process_list_item_partials finds verbose partials and outputs repeated renders' test to actually, you know, test verbose partials --- test/list_item_hunter_tests.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/list_item_hunter_tests.js b/test/list_item_hunter_tests.js index 1f6430d2c..a0e583f36 100644 --- a/test/list_item_hunter_tests.js +++ b/test/list_item_hunter_tests.js @@ -113,14 +113,14 @@ 'process_list_item_partials finds verbose partials and outputs repeated renders' : function(test){ var pattern1 = createFakeListPattern({ - "template": "{{#listItems.one}}{{> atoms-foo }}{{/listItems.one}}", - "extendedTemplate" : "{{#listItems.one}}{{> atoms-foo }}{{/listItems.one}}", + "template": "{{#listItems.one}}{{> 00-atoms/00-test/00-foo.mustache }}{{/listItems.one}}", + "extendedTemplate" : "{{#listItems.one}}{{> 00-atoms/00-test/00-foo.mustache }}{{/listItems.one}}", "key": "test-patternName1" }); var pattern2 = createFakeListPattern({ - "template": "{{#listItems.two}}{{> atoms-bar }}{{/listItems.two}}", - "extendedTemplate" : "{{#listItems.two}}{{> atoms-bar }}{{/listItems.two}}", + "template": "{{#listItems.two}}{{> 00-atoms/00-test/00-bar.mustache }}{{/listItems.two}}", + "extendedTemplate" : "{{#listItems.two}}{{> 00-atoms/00-test/00-bar.mustache }}{{/listItems.two}}", "key": "test-patternName2" }); From bdb0b7bf492e6d9ebd17403f336e7b9f80039533 Mon Sep 17 00:00:00 2001 From: Geoffrey Pursell Date: Fri, 4 Dec 2015 15:22:10 -0600 Subject: [PATCH 25/25] Factor getPartialKey out of pattern assembler -- it's Mustache-specific --- builder/object_factory.js | 10 +++++++--- builder/pattern_assembler.js | 4 +++- builder/pattern_engines/engine_mustache.js | 16 ++++++++++++---- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/builder/object_factory.js b/builder/object_factory.js index 761cd59f4..6a9f646e4 100644 --- a/builder/object_factory.js +++ b/builder/object_factory.js @@ -1,6 +1,6 @@ -/* - * patternlab-node - v0.15.1 - 2015 - * +/* + * patternlab-node - v0.15.1 - 2015 + * * Brian Muenzenmeyer, and the web community. * Licensed under the MIT license. * @@ -77,6 +77,10 @@ findListItems: function () { return this.engine.findListItems(this); + }, + + getPartialKey: function (partialString) { + return this.engine.getPartialKey(this, partialString); } }; diff --git a/builder/pattern_assembler.js b/builder/pattern_assembler.js index 66dca48f3..52f975f9b 100644 --- a/builder/pattern_assembler.js +++ b/builder/pattern_assembler.js @@ -145,6 +145,8 @@ addPattern(currentPattern, patternlab); } + + function processPatternRecursive(file, patternlab, additionalData){ var lh = require('./lineage_hunter'), ph = require('./parameter_hunter'), @@ -192,7 +194,7 @@ //do something with the regular old partials for(i = 0; i < foundPatternPartials.length; i++){ - var partialKey = foundPatternPartials[i].replace(/{{>([ ])?([\w\-\.\/~]+)(:[A-z-_|]+)?(?:\:[A-Za-z0-9-_]+)?(?:(| )\(.*)?([ ])?}}/g, '$2'); + var partialKey = currentPattern.getPartialKey(foundPatternPartials[i]); var partialPath; diff --git a/builder/pattern_engines/engine_mustache.js b/builder/pattern_engines/engine_mustache.js index 3a3a3c9e2..a13043a87 100644 --- a/builder/pattern_engines/engine_mustache.js +++ b/builder/pattern_engines/engine_mustache.js @@ -23,6 +23,7 @@ findPartialsWithStyleModifiersRE: /{{>([ ])?([\w\-\.\/~]+)(?!\()(\:[A-Za-z0-9-_|]+)+(?:(| )\(.*)?([ ])?}}/g, findPartialsWithPatternParametersRE: /{{>([ ])?([\w\-\.\/~]+)(?:\:[A-Za-z0-9-_|]+)?(?:(| )\(.*)+([ ])?}}/g, findListItemsRE: /({{#( )?)(list(I|i)tems.)(one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty)( )?}}/g, + getPartialKeyRE: /{{>([ ])?([\w\-\.\/~]+)(:[A-z-_|]+)?(?:\:[A-Za-z0-9-_]+)?(?:(| )\(.*)?([ ])?}}/g, // render it renderPattern: function renderPattern(template, data, partials) { @@ -37,18 +38,25 @@ var matches = pattern.template.match(this.findPartialsRE); return matches; }, - findPartialsWithStyleModifiers: function(pattern){ + findPartialsWithStyleModifiers: function(pattern) { var matches = pattern.template.match(this.findPartialsWithStyleModifiersRE); return matches; }, - // returns any patterns that match {{> value(foo:"bar") }} or {{> value:mod(foo:"bar") }} within the pattern - findPartialsWithPatternParameters: function(pattern){ + // returns any patterns that match {{> value(foo:"bar") }} or {{> + // value:mod(foo:"bar") }} within the pattern + findPartialsWithPatternParameters: function(pattern) { var matches = pattern.template.match(this.findPartialsWithPatternParametersRE); return matches; }, - findListItems: function(pattern){ + findListItems: function(pattern) { var matches = pattern.template.match(this.findListItemsRE); return matches; + }, + // given a pattern, and a partial string, tease out the "pattern key" and + // return it. + getPartialKey: function(pattern, partialString) { + var partialKey = partialString.replace(this.getPartialKeyRE, '$2'); + return partialKey; } };