From 3dfe791c022bfdce7419536bf1e1425a24fb3aa1 Mon Sep 17 00:00:00 2001
From: BRIAN MUENZENMEYER
Date: Thu, 26 Nov 2015 15:11:39 -0600
Subject: [PATCH 01/21] resolve issue with not hiding underscored patterns.
fixes #165
---
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 | 12 ++---
builder/pattern_assembler.js | 16 +++---
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 | 12 ++---
package.gulp.json | 2 +-
package.json | 2 +-
.../00-test/_ignored-pattern.mustache | 1 +
test/pattern_assembler_tests.js | 51 +++++++++++++++++++
17 files changed, 87 insertions(+), 31 deletions(-)
create mode 100644 test/files/_patterns/00-test/_ignored-pattern.mustache
diff --git a/CHANGELOG b/CHANGELOG
index 6e29f56f0..3e8d03629 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,9 @@
THIS CHANGELOG IS AN ATTEMPT TO DOCUMENT CHANGES TO THIS PROJECT.
+PL-node-v0.15.2
+- FIX: Resolve issue with not hiding underscored patterns.
+- THX: Thanks @ivancamilov for reporting this regression.
+
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
diff --git a/builder/lineage_hunter.js b/builder/lineage_hunter.js
index 9056cdeac..bd0951a63 100644
--- a/builder/lineage_hunter.js
+++ b/builder/lineage_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.1 - 2015
+ * patternlab-node - v0.15.2 - 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 87574eee9..7bd98b286 100644
--- a/builder/list_item_hunter.js
+++ b/builder/list_item_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.1 - 2015
+ * patternlab-node - v0.15.2 - 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 d5ada91ce..0293626a0 100644
--- a/builder/media_hunter.js
+++ b/builder/media_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.1 - 2015
+ * patternlab-node - v0.15.2 - 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 d1206866d..fff554e56 100644
--- a/builder/object_factory.js
+++ b/builder/object_factory.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.1 - 2015
+ * patternlab-node - v0.15.2 - 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 4732ad0a5..ea369a729 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.2 - 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.
*
*/
diff --git a/builder/pattern_assembler.js b/builder/pattern_assembler.js
index 5363acbde..d6f5d2dc6 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.2 - 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.
*
*/
@@ -94,8 +94,8 @@
var filename = path.basename(file);
var ext = path.extname(filename);
- //ignore dotfiles and non-variant .json files
- if(filename.charAt(0) === '.' || (ext === '.json' && filename.indexOf('~') === -1)){
+ //ignore dotfiles, underscored files, and non-variant .json files
+ if(filename.charAt(0) === '.' || filename.charAt(0) === '_' || (ext === '.json' && filename.indexOf('~') === -1)){
return;
}
diff --git a/builder/pattern_exporter.js b/builder/pattern_exporter.js
index 90915ec86..b985354f0 100644
--- a/builder/pattern_exporter.js
+++ b/builder/pattern_exporter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.1 - 2015
+ * patternlab-node - v0.15.2 - 2015
*
* Brian Muenzenmeyer, and the web community.
* Licensed under the MIT license.
diff --git a/builder/patternlab.js b/builder/patternlab.js
index 9ea38d604..08d8f5e7e 100644
--- a/builder/patternlab.js
+++ b/builder/patternlab.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.1 - 2015
+ * patternlab-node - v0.15.2 - 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 843dd07f1..b5aefaee5 100644
--- a/builder/patternlab_grunt.js
+++ b/builder/patternlab_grunt.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.1 - 2015
+ * patternlab-node - v0.15.2 - 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 d4014cb79..2c1b9f3d5 100644
--- a/builder/patternlab_gulp.js
+++ b/builder/patternlab_gulp.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.1 - 2015
+ * patternlab-node - v0.15.2 - 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 9f698650f..8db5a215d 100644
--- a/builder/pseudopattern_hunter.js
+++ b/builder/pseudopattern_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.1 - 2015
+ * patternlab-node - v0.15.2 - 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 9baa6f0aa..7ef925160 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.2 - 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.
*
*/
diff --git a/package.gulp.json b/package.gulp.json
index 099c8d907..8aab2cc3b 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.1",
+ "version": "0.15.2",
"devDependencies": {
"browser-sync": "^2.10.0",
"del": "^2.0.2",
diff --git a/package.json b/package.json
index 8d3953d22..f11e8ea3a 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.1",
+ "version": "0.15.2",
"devDependencies": {
"bs-html-injector": "^3.0.0",
"diveSync": "^0.3.0",
diff --git a/test/files/_patterns/00-test/_ignored-pattern.mustache b/test/files/_patterns/00-test/_ignored-pattern.mustache
new file mode 100644
index 000000000..ae9ed0946
--- /dev/null
+++ b/test/files/_patterns/00-test/_ignored-pattern.mustache
@@ -0,0 +1 @@
+These aren't the patterns you are looking for.
diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js
index 36b6c3ea8..9c3b5736d 100644
--- a/test/pattern_assembler_tests.js
+++ b/test/pattern_assembler_tests.js
@@ -449,6 +449,57 @@
var expectedValue = ' {{message}} 2 3 {{message}}
';
test.equals(bookendPattern.extendedTemplate.replace(/\s\s+/g, ' ').replace(/\n/g, ' ').trim(), expectedValue.trim());
test.done();
+ },
+ 'processPatternIterative - ignores files that start with underscore' : function(test){
+ //arrange
+ var diveSync = require('diveSync');
+ var fs = require('fs-extra');
+ var pa = require('../builder/pattern_assembler');
+ var pattern_assembler = new pa();
+ var patterns_dir = './test/files/_patterns';
+ var patternlab = {};
+ patternlab.config = fs.readJSONSync('./config.json');
+ patternlab.config.patterns = {source: patterns_dir};
+ patternlab.data = fs.readJSONSync('./source/_data/data.json');
+ patternlab.listitems = fs.readJSONSync('./source/_data/listitems.json');
+ patternlab.header = fs.readFileSync('./source/_patternlab-files/pattern-header-footer/header.html', 'utf8');
+ patternlab.footer = fs.readFileSync('./source/_patternlab-files/pattern-header-footer/footer.html', 'utf8');
+ patternlab.patterns = [];
+ patternlab.data.link = {};
+ patternlab.partials = {};
+
+ //act
+ diveSync(patterns_dir,
+ {
+ filter: function(path, dir){
+ if(dir){
+ var remainingPath = path.replace(patterns_dir, '');
+ var isValidPath = remainingPath.indexOf('/_') === -1;
+ return isValidPath;
+ }
+ return true;
+ }
+ },
+ function(err, file){
+ //log any errors
+ if(err){
+ console.log(err);
+ return;
+ }
+
+ pattern_assembler.process_pattern_iterative(file.substring(2), patternlab);
+ }
+ );
+
+ //assert
+ var foundIgnoredPattern = false;
+ for(var i = 0; i < patternlab.patterns.length; i++){
+ if(patternlab.patterns[i].fileName[0] === '_'){
+ foundIgnoredPattern = true;
+ }
+ }
+ test.equals(foundIgnoredPattern, false);
+ test.done();
}
};
}());
From 4faa3fae27db8f4a24abeffd3bc8e9f804e96b6a Mon Sep 17 00:00:00 2001
From: johngerome
Date: Wed, 2 Dec 2015 10:08:32 +0800
Subject: [PATCH 02/21] Fix wrong class name for Error Input
---
source/css/scss/base/_forms.scss | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/source/css/scss/base/_forms.scss b/source/css/scss/base/_forms.scss
index d121af100..75e4ad6c8 100644
--- a/source/css/scss/base/_forms.scss
+++ b/source/css/scss/base/_forms.scss
@@ -111,7 +111,7 @@ input[type="search"]::-webkit-search-decoration {
}
/* Validation */
-.has-error {
+.is-error {
border-color: $error;
}
.is-valid {
@@ -135,4 +135,4 @@ input[type="search"]::-webkit-search-decoration {
border: 0;
border-left: 1px solid $gray;
color: $gray;
-}
\ No newline at end of file
+}
From ed4f8dd63e2a5989cf04169e2e1dd1d377f55549 Mon Sep 17 00:00:00 2001
From: BRIAN MUENZENMEYER
Date: Fri, 4 Dec 2015 23:40:21 -0600
Subject: [PATCH 03/21] - ADD: Added a note in the README during installation
to run with elevated privileges - THX: Thanks @RichardBray for the heads up -
ADD: Added a Prerequisites section to the README
---
CHANGELOG | 5 +++++
README.md | 8 ++++++++
2 files changed, 13 insertions(+)
diff --git a/CHANGELOG b/CHANGELOG
index 3e8d03629..4c3f9069c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,6 +3,11 @@ THIS CHANGELOG IS AN ATTEMPT TO DOCUMENT CHANGES TO THIS PROJECT.
PL-node-v0.15.2
- FIX: Resolve issue with not hiding underscored patterns.
- THX: Thanks @ivancamilov for reporting this regression.
+- FIX: Fix misapplied error input class
+- THX: Thanks @johngerome for the pull request!
+- ADD: Added a note in the README during installation to run with elevated privileges
+- THX: Thanks @RichardBray for the heads up
+- ADD: Added a Prerequisites section to the README
PL-node-v0.15.1
- FIX: Resolve issue with styleModifiers not being replaced when the partial has spaces in it.
diff --git a/README.md b/README.md
index c628bf065..b2353e58b 100644
--- a/README.md
+++ b/README.md
@@ -6,12 +6,20 @@ The Node version of [Pattern Lab](http://patternlab.io/) is, at its core, a stat
This repository contains the vanilla builder logic, grunt and gulp configurations, and some sample template/css/data to illustrate the power and flexibility of the tool.
+### Prerequisites
+
+Make sure Node and npm are installed. A great guide can be found here: [https://docs.npmjs.com/getting-started/installing-node](https://docs.npmjs.com/getting-started/installing-node)
+
### Download
* 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 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).
+### Troubleshooting Installs
+
+Make sure you are running your terminal/command line session as administrator. This could mean `sudo`, or opening the window with a right-click option.
+
### 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.
From 800f3b95b1707b971f9a17ec726a2a903052840d Mon Sep 17 00:00:00 2001
From: BRIAN MUENZENMEYER
Date: Sat, 5 Dec 2015 00:53:52 -0600
Subject: [PATCH 04/21] added two patternState unit tests as part of #97
---
test/pattern_assembler_tests.js | 40 +++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js
index 9c3b5736d..d6cdc1e28 100644
--- a/test/pattern_assembler_tests.js
+++ b/test/pattern_assembler_tests.js
@@ -500,6 +500,46 @@
}
test.equals(foundIgnoredPattern, false);
test.done();
+ },
+ 'setState - applies any patternState matching the pattern' : function(test){
+ //arrange
+ var pa = require('../builder/pattern_assembler');
+ var pattern_assembler = new pa();
+ var patternlab = {};
+ patternlab.config = {};
+ patternlab.config.patternStates = {};
+ patternlab.config.patternStates["homepage-emergency"] = "inprogress";
+
+ var pattern = {
+ patternName: "homepage-emergency"
+ };
+
+ //act
+ pattern_assembler.setPatternState(pattern, patternlab);
+
+ //assert
+ test.equals(pattern.patternState, "inprogress");
+ test.done();
+ },
+ 'setState - does not apply any patternState if nothing matches the pattern' : function(test){
+ //arrange
+ var pa = require('../builder/pattern_assembler');
+ var pattern_assembler = new pa();
+ var patternlab = {};
+ patternlab.config = {};
+ patternlab.config.patternStates = {};
+ patternlab.config.patternStates["homepage-emergency"] = "inprogress";
+
+ var pattern = {
+ patternName: "homepage"
+ };
+
+ //act
+ pattern_assembler.setPatternState(pattern, patternlab);
+
+ //assert
+ test.equals(pattern.patternState, "");
+ test.done();
}
};
}());
From 277fdff44d1f651fe6122b2ad6eebfc1c856adfc Mon Sep 17 00:00:00 2001
From: BRIAN MUENZENMEYER
Date: Sat, 5 Dec 2015 01:00:50 -0600
Subject: [PATCH 05/21] delete this file, it's an artifact and should not be
included
---
public/data/annotations.js | 109 -------------------------------------
1 file changed, 109 deletions(-)
delete mode 100644 public/data/annotations.js
diff --git a/public/data/annotations.js b/public/data/annotations.js
deleted file mode 100644
index fc8209531..000000000
--- a/public/data/annotations.js
+++ /dev/null
@@ -1,109 +0,0 @@
-var comments = {
-"comments" : [
- {
- "el": "header[role=banner]",
- "title" : "Masthead",
- "comment": "The main header of the site doesn't take up too much screen real estate in order to keep the focus on the core content. It's using a linear CSS gradient instead of a background image to give greater design flexibility and reduce HTTP requests."
- },
- {
- "el": ".logo",
- "title" : "Logo",
- "comment": "The logo image is an SVG file, which ensures that the logo displays crisply even on high resolution displays. A PNG fallback is provided for browsers that don't support SVG images.
Further reading: Optimizing Web Experiences for High Resolution Screens
"
- },
- {
- "el": "#nav",
- "title" : "Navigation",
- "comment": "Navigation for adaptive web experiences can be tricky. Top navigations are typical on desktop sites, but mobile screen sizes don't give us the luxury of space. We're dealing with this situation by creating a simple menu anchor that toggles the main navigation on small screens. This is just one method. Bagcheck and Contents Magazine add an anchor in the header that jumps users to the navigation which is placed in the footer. This solution works well because it doesn't require any Javascript in order to work. Other methods exist too. For example, ESPN's mobile navigation overlays the main content of the page.
The nav is only hidden when a certain level of javascript is supported in order to ensure that users with little/poor javascript support can still access the navigation. Once the screen size is large enough to accommodate the nav, we show the main navigation links and hide the menu anchor.
See also: Responsive Navigation Patterns
"
- },
- {
- "el": "input[type=search]",
- "title" : "Search",
- "comment": "Search is an incredibly important priority, especially for mobile. It is a great idea to give users the ability to jump directly to what they are looking for without forcing them to wade through your site's navigation. Check out the Burton and Yelp mobile sites for great examples of experiences that prioritize search.
We're also using the HTML5 search input type, which is great for mobile devices that can bring up the appropriate virtual keyboard for many smartphones. And like the main header navigation, we're hiding the search form on small screens to save space. Clicking the search anchor toggles the form.
"
- },
- {
- "el": "#product-img nav",
- "title" : "Image Navigation",
- "comment": "Instead of providing bullets, pagination or text-based image navigation, it's good e-commerce practice to show a preview of the various product views. By default the images simply link through to their larger counterparts, and if adequate javascript support exists, the images get loaded into the main image container.
"
- },
- {
- "el": "#img-container",
- "title" : "Product Image",
- "comment": "The product image is the focal point of the page for good reason. It's typically what the user is there to see. The default markup simply includes the main product image, but that gets replaced with an image gallery if adequate javascript support exists.
We're also using Modernizr to detect if the browser supports touch events and if it does, we load in an excellent lightweight script called Swipe.js to create a touch-friendly image carousel. This allows users to swipe between product photos in a touch-friendly way. Because gestures are invisible, they might get overlooked, but clicking on the image navigation thumbnails animates the slideshow and hints to the user gestural interaction is available.
"
- },
- {
- "el": ".product-main header",
- "title" : "Product Overview",
- "comment": "The product overview appears in the markup before the image container in order to provide the user with the product name, how much it costs and how popular it is. Providing this information as soon as possible can help the user determine whether or not this is the product they're looking for without having to wait for the rest of the page to load."
- },
- {
- "el": ".star",
- "title" : "Rating Stars",
- "comment": "We're using HTML special characters to display the product rating stars. We're using HTML characters instead of images to reduce the amount of images we're requesting and also maintaining crispness on high resolution screens. Not every device supports HTML special characters (Blackberry <=5.0 for example), but support is strong enough and the benefits are many.
See also: Optimizing Web Experiences for High Resolution Screens
"
- },
- {
- "el": ".review-count",
- "title" : "Review Count",
- "comment": "This is a simple anchor link that points to the review section of the page. This may seem like a small detail, but consider a mobile use case. Users can be in stores looking at the physical product, and 79% of smartphone consumers use their phones to help with shopping. They might be interested in buying in-store but turn to their phones to verify its popularity and quality. Making it easy for uses to read product reviews on small screens can help drive more sales, both online and offline.While not incorporated yet, it would be easy to load the reviews for small screens on demand, thereby saving a step.
"
- },
- {
- "el": ".qty-field",
- "title" : "Quantity Field",
- "comment": "We're using the HTML5 number input type, which brings up the appropriate virtual keyboard for many mobile browsers. To increase usability, the input labels are using the \"for\" attribute, which focuses the cursor in the form field when clicked. However, iOS doesn't honor \"for\" default functionality, so we're adding \"cursor: pointer\" to the labels to get Mobile Safari to behave properly."
- },
- {
- "el": ".size-field",
- "title" : "Size Dropdown",
- "comment": "We're using a basic select menu to choose the size, which is commonplace for any e-commerce site. Select menus can be especially difficult to style and can vary greatly in behavior between platforms. Keep this in mind when creating "
- },
- {
- "el": ".submit-form",
- "title" : "Add to Cart button",
- "comment": "The add to cart button is the primary user action on the page. That's why it's large and in charge and very prominently placed on the page. The button is using box-shadows and rounded corners to create an attractive button that will hopefully get plenty of clicks."
- },
- {
- "el": ".share-btn",
- "title" : "Share button",
- "comment": "It seems like everything has a share button on it these days. And for good reason. Sharing content and products on social networks can be a great way to increase exposure. However, tacking on tons of social widgets adds a lot of overhead, which can be extremely detrimental to the site's performance. Including a simple share link that loads the heavy widgets only when requested is one way to keep pages fast and social. Check out Target's mobile site for an example of a site that isolates share functionality in a separate page fragment."
- },
- {
- "el": ".find-nearby",
- "title" : "Geolocation",
- "comment": "One of the most important aspects of the mobile context is location. We carry our mobile devices with us everywhere. Using geolocation we can tap into the user's location to deliver an enhanced experience. In this case we're giving them a chance to check out what stores nearby might sell this product. The geolocation API is well supported in mobile browsers as well as desktop browsers. We're using Modernizr to detect for geolocation support and if its support, we ask the user for their latitude and longitude. If the browser does not support geolocation, the default experience could take the user to a simple web form asking for a ZIP code. Check out Tiffany's mobile site store locator for an example of geolocation in action."
- },
- {
- "el": "#p-desc",
- "title" : "Product Description",
- "comment": "A product description is an essential part of any e-commerce experience. Descriptions offer tangible details that inform and persuade, and the tone can help support the physical product. Provide relevant information clearly and concisely. Check out the Android design guide for some tips on how to keep copy short and extremely effective."
- },
- {
- "el": "#related-products",
- "title" : "Related Products",
- "comment": "Related products are obviously an important aspect of e-commerce sites as they drive awareness of other similar products and can lead to more purchases. However, including a lot of auxiliary content can bog down the site performance, which is especially crucial on mobile. On slow connections, the presence of this extra content might slow down the user experience enough that the user gives up.
We're handling the issue by conditionally loading the auxiliary content.
By default, the related item link simply clicks through to an HTML fragment containing the related products. The content is still accessible, even on devices with poor or no javascript support. When the user clicks on the related products on small screens, the content gets dynamically loaded inline and the link becomes a toggler for the content. Once the experience reaches a certain width breakpoint, we then load in the content. However, screen size != fast connection, so we should keep our eyes on the emerging navigator.connection to better gauge real connection speed.
See also: An Ajax-Include Pattern for Modular Content
All these wonderful t-shirts are retired/rejected Busted Tees, graciously donated to this demo by Will Schneider.
"
- },
- {
- "el": "#reviews",
- "title" : "Reviews",
- "comment": "Reviews are incredibly influential on a user's decision to purchase a product or pass on it. Also, because we carry our mobile phones with us everywhere, we use them to inform our in-store purchased. 70% of smartphone owners use them while in brick and mortar stores, and often times they're looking for reviews to give them the green light to buy.Only the primary product content gets loaded by default, and the reviews exist as their own separate HTML fragment. The reviews remain accessible and don't get loaded until we conditionally load them when the screen is large enough or small screen users click the reviews link. This keeps things nimble while still providing access to the valuable reviews.
See also: An Ajax-Include Pattern for Modular Content
"
- },
- {
- "el": "#p-reviews .btn",
- "title" : "More Reviews Button",
- "comment": "All reviews aren't loaded by default in order to keep the site performance in top shape. Ultimately, this button could be replaced with a lazy-loading solution to remove the need for the button.
"
- },
- {
- "el": ".footer .nav",
- "title" : "Footer Nav",
- "comment": "Repetition of elements isn't a bad thing, especially with potentially long scrolling pages on mobile. Providing access to the main site navigation is a good way for the user to jump off to another section and avoids leaving them with a dead end. Also, some mobile sites like Bagcheck and Contents Magazine keep the primary navigation at the footer and simply link to it with an anchor in the header. That way the nav stays accessible but the focus stays on on the core page content.
"
- },
- {
- "el": ".tel",
- "title" : "Customer Service Number",
- "comment": "We sometimes forget that mobile phones can make phone calls. Whether a user is having trouble with the site or simply has some questions about the product he's about to buy, it's a smart decision to provide a clickable phone number to facilitate that call. What happens when desktops and other non-phone devices click on the tel link? Well, some devices (like iPads and other tablets) ask the user if they'd like to add the number to their contact list, other desktops open 3rd party VoIP programs like Skype, and others simply give an error message.
"
- },
- {
- "el": ".top",
- "title" : "Back to Top Link",
- "comment": "Back to top links are simple yet underrated. They provide users with an easy way back up to the top of the page with minimum effort. This is especially helpful on mobile devices, which tend to have long scrolling pages.
We're using an HTML character for the back to top arrow in order to reduce image elements and keep things looking crisp on high res displays.
"
- }
-]
-};
\ No newline at end of file
From bb47678cb9a114376fc78afdc47ccedc09ab63fb Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Mon, 7 Dec 2015 15:14:26 -0600
Subject: [PATCH 06/21] Generate the list of supported pattern engines based on
the presence to real plugin files rather than a hard-coded list. Also, better
organize the PatternEngine setup functions to be cleaner, more functional and
more testable
---
builder/pattern_engines/pattern_engines.js | 117 ++++++++++++++-------
1 file changed, 81 insertions(+), 36 deletions(-)
diff --git a/builder/pattern_engines/pattern_engines.js b/builder/pattern_engines/pattern_engines.js
index 13ef654f0..71e68c39b 100644
--- a/builder/pattern_engines/pattern_engines.js
+++ b/builder/pattern_engines/pattern_engines.js
@@ -13,25 +13,88 @@
'use strict';
var path = require('path');
+ var diveSync = require('diveSync');
+ var engineMatcher = /^engine_(.*)\.js/;
+ var enginesDirectory = __dirname;
- // list of supported pattern engines
- var supportedPatternEngineNames = [
- 'mustache',
- 'handlebars'
- ];
+ var PatternEngines; // the main export object
+ var engineNameForExtension; // generated mapping of extension to engine name
- // mapping of file extensions to engine names, for lookup use
- var engineNameForExtension = {};
- // Object/hash of all loaded pattern engines, empty at first.
- // My intention here is to make this return an object that can be used to
- // obtain any loaded PatternEngine by addressing them like this:
+ // free private functions, for internal setup only
+
+ function findSupportedPatternEngineNames() {
+ var foundPatternEngineNames = [];
+
+ console.log('patternEngines: begin diveSync ====================');
+
+ // find
+ diveSync(enginesDirectory, {
+ recursive: false,
+ filter: function (filePath, dir) {
+ var baseName = path.basename(filePath),
+ engineMatch = baseName.match(engineMatcher);
+
+ if (dir || engineMatch !== null) { return true; }
+ return false;
+ }
+ }, function (err, filePath) {
+ if (err) { throw err; }
+ var baseName = path.basename(filePath),
+ engineMatch = baseName.match(engineMatcher),
+ foundEngineName = engineMatch[1];
+
+ console.log('patternEngines: FOUND ENGINE', foundEngineName);
+ foundPatternEngineNames.push(foundEngineName);
+ });
+
+ return foundPatternEngineNames;
+ }
+
+ // try to load all supported engines
+ function loadAllEngines(enginesObject) {
+ enginesObject.supportedPatternEngineNames.forEach(function (engineName) {
+ try {
+ enginesObject[engineName] = require('./engine_' + engineName);
+ } catch (err) {
+ console.log(err, 'pattern engine "' + engineName + '" not loaded. Did you install its dependency with npm?');
+ }
+ });
+ }
+
+ // produce a mapping between file extension and engine name for each of the
+ // loaded engines
+ function createFileExtensionToEngineNameMap(enginesObject) {
+ var mapping = {};
+
+ Object.keys(enginesObject).forEach(function (engineName) {
+ var extensionForEngine = enginesObject[engineName].engineFileExtension;
+ mapping[extensionForEngine] = engineName;
+ });
+
+ return mapping;
+ }
+
+
+ //
+ // PatternEngines: the main export of this module
+ //
+ // It's an Object/hash of all loaded pattern engines, empty at first. My
+ // intention here is to make this return an object that can be used to obtain
+ // any loaded PatternEngine by addressing them like this:
//
- // var PatternEngines = require('./pattern_engines/pattern_engines');
- // var Mustache = PatternEngines['mustache'];
+ // var PatternEngines = require('./pattern_engines/pattern_engines');
+ // var Mustache = PatternEngines['mustache'];
//
- var PatternEngines = Object.create({
- supportedPatternEngineNames: supportedPatternEngineNames,
+ // Object.create lets us create an object with a specified prototype. We want
+ // this here because we would like the object's "own properties" to include
+ // only the engine names so we can easily iterate over them; all the handy
+ // methods and properites below should therefore be on its prototype.
+
+ PatternEngines = Object.create({
+ // build the list of supported pattern engines based on what plugins we have
+ // in the pattern_engines directory
+ supportedPatternEngineNames: findSupportedPatternEngineNames(),
getEngineNameForPattern: function (pattern) {
// avoid circular dependency by putting this in here. TODO: is this slow?
@@ -93,30 +156,12 @@
}
});
- // try to load all supported engines
- (function loadAllEngines() {
- supportedPatternEngineNames.forEach(function (engineName) {
- try {
- PatternEngines[engineName] = require('./engine_' + engineName);
- } catch (err) {
- console.log(err, 'pattern engine "' + engineName + '" not loaded. Did you install its dependency with npm?');
- }
- });
- })();
-
- // produce a mapping between file extension and engine name for each of the
- // loaded engines
- engineNameForExtension = (function () {
- var mapping = {};
- Object.keys(PatternEngines).forEach(function (engineName) {
- var extensionForEngine = PatternEngines[engineName].engineFileExtension;
- mapping[extensionForEngine] = engineName;
- });
-
- return mapping;
- })();
+ // load up the engines we found
+ loadAllEngines(PatternEngines);
+ // mapping of file extensions to engine names, for lookup use
+ engineNameForExtension = createFileExtensionToEngineNameMap(PatternEngines);
module.exports = PatternEngines;
})();
From a66d0a850bbeb547785013b05f10508ed568705d Mon Sep 17 00:00:00 2001
From: BRIAN MUENZENMEYER
Date: Mon, 7 Dec 2015 23:53:13 -0600
Subject: [PATCH 07/21] Made pseudopattern_hunter a bit less hardcoded added
unit test for pseudopattern generation fixed a bug where pseudopatterns were
being added to the UI twice explicitly sorted patterns before UI build
setting version to v0.16.0
---
.gitignore | 1 +
CHANGELOG | 5 +-
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 | 12 ++--
builder/pattern_exporter.js | 2 +-
builder/patternlab.js | 14 +++-
builder/patternlab_grunt.js | 2 +-
builder/patternlab_gulp.js | 2 +-
builder/pseudopattern_hunter.js | 10 +--
builder/style_modifier_hunter.js | 2 +-
package.gulp.json | 2 +-
package.json | 2 +-
.../_patterns/00-test/03-styled-atom~alt.json | 3 +
test/pattern_assembler_tests.js | 66 +++++++++++++++++--
test/pseudopattern_hunter_tests.js | 45 +++++++++++++
19 files changed, 147 insertions(+), 31 deletions(-)
create mode 100644 test/files/_patterns/00-test/03-styled-atom~alt.json
create mode 100644 test/pseudopattern_hunter_tests.js
diff --git a/.gitignore b/.gitignore
index 7200b256a..08b9a2f83 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ public/index.html
public/styleguide.html
public/styleguide/html/styleguide.html
public/css/*
+public/data/*
public/fonts/*
public/js/*
public/images/*
diff --git a/CHANGELOG b/CHANGELOG
index 4c3f9069c..fd73f8cc4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,6 @@
THIS CHANGELOG IS AN ATTEMPT TO DOCUMENT CHANGES TO THIS PROJECT.
-PL-node-v0.15.2
+PL-node-v0.16.0
- FIX: Resolve issue with not hiding underscored patterns.
- THX: Thanks @ivancamilov for reporting this regression.
- FIX: Fix misapplied error input class
@@ -8,6 +8,9 @@ PL-node-v0.15.2
- ADD: Added a note in the README during installation to run with elevated privileges
- THX: Thanks @RichardBray for the heads up
- ADD: Added a Prerequisites section to the README
+- ADD: Added unit tests for pattern states and pseudopatterns
+- CHG: Changed pseudopattern generation to use config.patterns.source directory instead of hardcode
+- CHG: Explicitly sorting patterns by name prior to building the UI
PL-node-v0.15.1
- FIX: Resolve issue with styleModifiers not being replaced when the partial has spaces in it.
diff --git a/builder/lineage_hunter.js b/builder/lineage_hunter.js
index bd0951a63..a01d2c641 100644
--- a/builder/lineage_hunter.js
+++ b/builder/lineage_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.2 - 2015
+ * patternlab-node - v0.16.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 7bd98b286..690079561 100644
--- a/builder/list_item_hunter.js
+++ b/builder/list_item_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.2 - 2015
+ * patternlab-node - v0.16.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 0293626a0..b939e3b39 100644
--- a/builder/media_hunter.js
+++ b/builder/media_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.2 - 2015
+ * patternlab-node - v0.16.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 fff554e56..d53587ec1 100644
--- a/builder/object_factory.js
+++ b/builder/object_factory.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.2 - 2015
+ * patternlab-node - v0.16.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 ea369a729..a0e78e6f8 100644
--- a/builder/parameter_hunter.js
+++ b/builder/parameter_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.2 - 2015
+ * patternlab-node - v0.16.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 d6f5d2dc6..ccf85e144 100644
--- a/builder/pattern_assembler.js
+++ b/builder/pattern_assembler.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.2 - 2015
+ * patternlab-node - v0.16.0 - 2015
*
* Brian Muenzenmeyer, and the web community.
* Licensed under the MIT license.
@@ -46,7 +46,7 @@
}
function setState(pattern, patternlab){
- if(patternlab.config.patternStates[pattern.patternName]){
+ if(patternlab.config.patternStates && patternlab.config.patternStates[pattern.patternName]){
pattern.patternState = patternlab.config.patternStates[pattern.patternName];
} else{
pattern.patternState = "";
@@ -102,13 +102,9 @@
//make a new Pattern Object
var currentPattern = new of.oPattern(file, subdir, filename);
- //if file is named in the syntax for variants
+ //if file is named in the syntax for variants, no need to process further
+ //processPatternRecursive() will run find_pseudopatterns() and look at each pattern for a variant
if(ext === '.json' && filename.indexOf('~') > -1){
- //add current pattern to patternlab object with minimal data
- //processPatternRecursive() will run find_pseudopatterns() to fill out
- //the object in the next diveSync
- addPattern(currentPattern, patternlab);
- //no need to process further
return;
}
diff --git a/builder/pattern_exporter.js b/builder/pattern_exporter.js
index b985354f0..7fc1e37ad 100644
--- a/builder/pattern_exporter.js
+++ b/builder/pattern_exporter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.2 - 2015
+ * patternlab-node - v0.16.0 - 2015
*
* Brian Muenzenmeyer, and the web community.
* Licensed under the MIT license.
diff --git a/builder/patternlab.js b/builder/patternlab.js
index 08d8f5e7e..d40956ca2 100644
--- a/builder/patternlab.js
+++ b/builder/patternlab.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.2 - 2015
+ * patternlab-node - v0.16.0 - 2015
*
* Brian Muenzenmeyer, and the web community.
* Licensed under the MIT license.
@@ -211,6 +211,18 @@ var patternlab_engine = function () {
//build the patternlab website
var patternlabSiteTemplate = fs.readFileSync('./source/_patternlab-files/index.mustache', 'utf8');
+ //sort all patterns explicitly.
+ patternlab.patterns = patternlab.patterns.sort(function(a,b){
+ if (a.name > b.name) {
+ return 1;
+ }
+ if (a.name < b.name) {
+ return -1;
+ }
+ // a must be equal to b
+ return 0;
+ });
+
//loop through all patterns.to build the navigation
//todo: refactor this someday
for(var i = 0; i < patternlab.patterns.length; i++){
diff --git a/builder/patternlab_grunt.js b/builder/patternlab_grunt.js
index b5aefaee5..606127614 100644
--- a/builder/patternlab_grunt.js
+++ b/builder/patternlab_grunt.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.2 - 2015
+ * patternlab-node - v0.16.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 2c1b9f3d5..5db996f66 100644
--- a/builder/patternlab_gulp.js
+++ b/builder/patternlab_gulp.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.2 - 2015
+ * patternlab-node - v0.16.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 8db5a215d..b4e69867e 100644
--- a/builder/pseudopattern_hunter.js
+++ b/builder/pseudopattern_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.2 - 2015
+ * patternlab-node - v0.16.0 - 2015
*
* Brian Muenzenmeyer, and the web community.
* Licensed under the MIT license.
@@ -28,7 +28,7 @@
//look for a pseudo pattern by checking if there is a file containing same name, with ~ in it, ending in .json
var needle = currentPattern.subdir + '/' + currentPattern.fileName + '~*.json';
var pseudoPatterns = glob.sync(needle, {
- cwd: 'source/_patterns/', //relative to gruntfile
+ cwd: patternlab.config.patterns.source + '/',
debug: false,
nodir: true,
});
@@ -41,14 +41,14 @@
console.log('found pseudoPattern variant of ' + currentPattern.key);
}
- //we want to do everything we normally would here, except instead head the pseudoPattern data
- var variantFileData = fs.readJSONSync('source/_patterns/' + pseudoPatterns[i]);
+ //we want to do everything we normally would here, except instead read the pseudoPattern data
+ var variantFileData = fs.readJSONSync(patternlab.config.patterns.source + '/' + pseudoPatterns[i]);
//extend any existing data with variant data
variantFileData = pattern_assembler.merge_data(currentPattern.jsonFileData, variantFileData);
var variantName = pseudoPatterns[i].substring(pseudoPatterns[i].indexOf('~') + 1).split('.')[0];
- var variantFilePath = 'source/_patterns/' + currentPattern.subdir + '/' + currentPattern.fileName + '~' + variantName + '.json';
+ var variantFilePath = patternlab.config.patterns.source + '/' + currentPattern.subdir + '/' + currentPattern.fileName + '~' + variantName + '.json';
var variantFileName = currentPattern.fileName + '-' + variantName + '.';
var patternVariant = new of.oPattern(variantFilePath, currentPattern.subdir, variantFileName, variantFileData);
diff --git a/builder/style_modifier_hunter.js b/builder/style_modifier_hunter.js
index 7ef925160..c38ada4fd 100644
--- a/builder/style_modifier_hunter.js
+++ b/builder/style_modifier_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.15.2 - 2015
+ * patternlab-node - v0.16.0 - 2015
*
* Brian Muenzenmeyer, and the web community.
* Licensed under the MIT license.
diff --git a/package.gulp.json b/package.gulp.json
index 8aab2cc3b..313d4edf1 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.2",
+ "version": "0.16.0",
"devDependencies": {
"browser-sync": "^2.10.0",
"del": "^2.0.2",
diff --git a/package.json b/package.json
index f11e8ea3a..516481505 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.2",
+ "version": "0.16.0",
"devDependencies": {
"bs-html-injector": "^3.0.0",
"diveSync": "^0.3.0",
diff --git a/test/files/_patterns/00-test/03-styled-atom~alt.json b/test/files/_patterns/00-test/03-styled-atom~alt.json
new file mode 100644
index 000000000..d4595ffd0
--- /dev/null
+++ b/test/files/_patterns/00-test/03-styled-atom~alt.json
@@ -0,0 +1,3 @@
+{
+ "message": "alternateMessage"
+}
diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js
index d6cdc1e28..2519b87ba 100644
--- a/test/pattern_assembler_tests.js
+++ b/test/pattern_assembler_tests.js
@@ -290,6 +290,7 @@
//arrange
var fs = require('fs-extra');
var pattern_assembler = new pa();
+ var patterns_dir = './test/files/_patterns';
var pl = {};
pl.config = {};
@@ -297,7 +298,7 @@
pl.data.link = {};
pl.config.debug = false;
pl.patterns = [];
- var patterns_dir = './test/files/_patterns';
+ pl.config.patterns = { source: patterns_dir};
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');
@@ -322,6 +323,7 @@
//arrange
var fs = require('fs-extra');
var pattern_assembler = new pa();
+ var patterns_dir = './test/files/_patterns';
var pl = {};
pl.config = {};
@@ -329,7 +331,7 @@
pl.data.link = {};
pl.config.debug = false;
pl.patterns = [];
- var patterns_dir = './test/files/_patterns';
+ pl.config.patterns = { source: patterns_dir};
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');
@@ -354,6 +356,7 @@
//arrange
var fs = require('fs-extra');
var pattern_assembler = new pa();
+ var patterns_dir = './test/files/_patterns';
var pl = {};
pl.config = {};
@@ -361,7 +364,7 @@
pl.data.link = {};
pl.config.debug = false;
pl.patterns = [];
- var patterns_dir = './test/files/_patterns';
+ pl.config.patterns = { source: patterns_dir};
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');
@@ -386,6 +389,7 @@
//arrange
var fs = require('fs-extra');
var pattern_assembler = new pa();
+ var patterns_dir = './test/files/_patterns';
var pl = {};
pl.config = {};
@@ -393,7 +397,7 @@
pl.data.link = {};
pl.config.debug = false;
pl.patterns = [];
- var patterns_dir = './test/files/_patterns';
+ pl.config.patterns = { source: patterns_dir};
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');
@@ -420,6 +424,7 @@
//arrange
var fs = require('fs-extra');
var pattern_assembler = new pa();
+ var patterns_dir = './test/files/_patterns';
var pl = {};
pl.config = {};
@@ -427,7 +432,7 @@
pl.data.link = {};
pl.config.debug = false;
pl.patterns = [];
- var patterns_dir = './test/files/_patterns';
+ pl.config.patterns = { source: patterns_dir};
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');
@@ -501,6 +506,57 @@
test.equals(foundIgnoredPattern, false);
test.done();
},
+ 'processPatternIterative - ignores files that are variants' : function(test){
+ //arrange
+ var diveSync = require('diveSync');
+ var fs = require('fs-extra');
+ var pa = require('../builder/pattern_assembler');
+ var pattern_assembler = new pa();
+ var patterns_dir = './test/files/_patterns';
+ var patternlab = {};
+ patternlab.config = fs.readJSONSync('./config.json');
+ patternlab.config.patterns = {source: patterns_dir};
+ patternlab.data = fs.readJSONSync('./source/_data/data.json');
+ patternlab.listitems = fs.readJSONSync('./source/_data/listitems.json');
+ patternlab.header = fs.readFileSync('./source/_patternlab-files/pattern-header-footer/header.html', 'utf8');
+ patternlab.footer = fs.readFileSync('./source/_patternlab-files/pattern-header-footer/footer.html', 'utf8');
+ patternlab.patterns = [];
+ patternlab.data.link = {};
+ patternlab.partials = {};
+
+ //act
+ diveSync(patterns_dir,
+ {
+ filter: function(path, dir){
+ if(dir){
+ var remainingPath = path.replace(patterns_dir, '');
+ var isValidPath = remainingPath.indexOf('/_') === -1;
+ return isValidPath;
+ }
+ return true;
+ }
+ },
+ function(err, file){
+ //log any errors
+ if(err){
+ console.log(err);
+ return;
+ }
+
+ pattern_assembler.process_pattern_iterative(file.substring(2), patternlab);
+ }
+ );
+
+ //assert
+ var foundVariant = false;
+ for(var i = 0; i < patternlab.patterns.length; i++){
+ if(patternlab.patterns[i].fileName.indexOf('~') > -1){
+ foundVariant = true;
+ }
+ }
+ test.equals(foundVariant, false);
+ test.done();
+ },
'setState - applies any patternState matching the pattern' : function(test){
//arrange
var pa = require('../builder/pattern_assembler');
diff --git a/test/pseudopattern_hunter_tests.js b/test/pseudopattern_hunter_tests.js
new file mode 100644
index 000000000..5a50dbcdd
--- /dev/null
+++ b/test/pseudopattern_hunter_tests.js
@@ -0,0 +1,45 @@
+(function () {
+ "use strict";
+
+ var pha = require('../builder/pseudopattern_hunter');
+ var pa = require('../builder/pattern_assembler');
+ var object_factory = require('../builder/object_factory');
+
+ exports['pseudopattern_hunter'] = {
+ 'pseudpattern found and added as a pattern' : function(test){
+ //arrange
+ var fs = require('fs-extra');
+ var pattern_assembler = new pa();
+ var pseudopattern_hunter = new pha();
+ var patterns_dir = './test/files/_patterns/';
+
+ var pl = {};
+ pl.config = {};
+ pl.data = {};
+ pl.data.link = {};
+ pl.config.debug = false;
+ pl.patterns = [];
+ pl.config.patterns = { source: patterns_dir};
+ pl.config.patternStates = {};
+
+ 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.extendedTemplate = atomPattern.template;
+ atomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(atomPattern);
+
+ pl.patterns.push(atomPattern);
+
+ //act
+ var patternCountBefore = pl.patterns.length;
+ pseudopattern_hunter.find_pseudopatterns(atomPattern, pl);
+
+ //assert
+ test.equals(patternCountBefore + 1, pl.patterns.length);
+ test.equals(pl.patterns[1].key, 'test-styled-atom-alt');
+ test.equals(pl.patterns[1].extendedTemplate.replace(/\s\s+/g, ' ').replace(/\n/g, ' ').trim(), ' {{message}} ');
+ test.equals(JSON.stringify(pl.patterns[1].jsonFileData), JSON.stringify({"message": "alternateMessage"}));
+
+ test.done();
+ }
+ }
+}());
From e4b73e8fcc62dfe9f0505c4553ea017b62756b00 Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Tue, 8 Dec 2015 09:47:58 -0600
Subject: [PATCH 08/21] further streamline engine loading process and logging
---
builder/pattern_engines/pattern_engines.js | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/builder/pattern_engines/pattern_engines.js b/builder/pattern_engines/pattern_engines.js
index 71e68c39b..e00700a56 100644
--- a/builder/pattern_engines/pattern_engines.js
+++ b/builder/pattern_engines/pattern_engines.js
@@ -21,13 +21,11 @@
var engineNameForExtension; // generated mapping of extension to engine name
- // free private functions, for internal setup only
+ // free "private" functions, for internal setup only
function findSupportedPatternEngineNames() {
var foundPatternEngineNames = [];
- console.log('patternEngines: begin diveSync ====================');
-
// find
diveSync(enginesDirectory, {
recursive: false,
@@ -44,7 +42,6 @@
engineMatch = baseName.match(engineMatcher),
foundEngineName = engineMatch[1];
- console.log('patternEngines: FOUND ENGINE', foundEngineName);
foundPatternEngineNames.push(foundEngineName);
});
@@ -53,13 +50,25 @@
// try to load all supported engines
function loadAllEngines(enginesObject) {
+ console.log('\nLoading engines...');
+
enginesObject.supportedPatternEngineNames.forEach(function (engineName) {
+ var notLoaded = false;
+
try {
enginesObject[engineName] = require('./engine_' + engineName);
} catch (err) {
- console.log(err, 'pattern engine "' + engineName + '" not loaded. Did you install its dependency with npm?');
+ // Handle errors loading each pattern engine. This will usually be
+ // because the engine's renderer hasn't been installed by the end user
+ // -- we don't include any of them (except mustache) by default as
+ // depedencies in package.json.
+ notLoaded = (err.code === 'MODULE_NOT_FOUND');
+ } finally {
+ console.log('-', engineName, 'engine:',
+ notLoaded ? 'renderer not installed; engine disabled' : 'good to go');
}
});
+ console.log('Done loading engines.\n');
}
// produce a mapping between file extension and engine name for each of the
From d80df26d99b3a3cca5fe2d037be81c15b3b78741 Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Tue, 8 Dec 2015 17:00:03 -0600
Subject: [PATCH 09/21] clean up require()s
---
builder/pattern_assembler.js | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/builder/pattern_assembler.js b/builder/pattern_assembler.js
index 52f975f9b..9451b2485 100644
--- a/builder/pattern_assembler.js
+++ b/builder/pattern_assembler.js
@@ -12,12 +12,13 @@
"use strict";
var pattern_assembler = function(){
-
- var fs = require('fs-extra'),
+ var path = require('path'),
+ fs = require('fs-extra'),
of = require('./object_factory'),
plutils = require('./utilities'),
- patternEngines = require('./pattern_engines/pattern_engines'),
- config = fs.readJSONSync('./config.json');
+ patternEngines = require('./pattern_engines/pattern_engines');
+
+ var config = fs.readJSONSync('./config.json');
function setState(pattern, patternlab){
if(patternlab.config.patternStates[pattern.patternName]){
@@ -66,10 +67,6 @@
}
function processPatternIterative(file, patternlab){
- var fs = require('fs-extra'),
- of = require('./object_factory'),
- path = require('path');
-
//extract some information
var subdir = path.dirname(path.relative(patternlab.config.patterns.source, file)).replace('\\', '/');
var filename = path.basename(file);
From c7adf8adc3ed915bf2e03e2c464e4a2da6814f1b Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Tue, 8 Dec 2015 17:00:19 -0600
Subject: [PATCH 10/21] slight polish to engine loader console output
---
builder/pattern_engines/pattern_engines.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/builder/pattern_engines/pattern_engines.js b/builder/pattern_engines/pattern_engines.js
index e00700a56..2968de9a8 100644
--- a/builder/pattern_engines/pattern_engines.js
+++ b/builder/pattern_engines/pattern_engines.js
@@ -68,7 +68,7 @@
notLoaded ? 'renderer not installed; engine disabled' : 'good to go');
}
});
- console.log('Done loading engines.\n');
+ console.log('...done loading engines.\n');
}
// produce a mapping between file extension and engine name for each of the
From 5cab80b0a4d89123ba82dadc11e013e455690997 Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Tue, 8 Dec 2015 17:00:42 -0600
Subject: [PATCH 11/21] clean up eslintrc a bit
---
.eslintrc | 3 ---
1 file changed, 3 deletions(-)
diff --git a/.eslintrc b/.eslintrc
index 9f090e1e2..eb695c29c 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,9 +1,6 @@
{
"env": {
- "jasmine": true,
"node": true,
- "mocha": true,
- "browser": true,
"builtin": true
},
"globals": {},
From fe6206b130ce1fc5aee241c94a1ed2e16da0828d Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Tue, 8 Dec 2015 17:01:37 -0600
Subject: [PATCH 12/21] make process_pattern_iterative() return the oPattern to
make it a little more functional, which seems nice for unit tests;
incorporate @sghoweri's fix to the pattern_assembler
---
builder/pattern_assembler.js | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/builder/pattern_assembler.js b/builder/pattern_assembler.js
index 9451b2485..a26715351 100644
--- a/builder/pattern_assembler.js
+++ b/builder/pattern_assembler.js
@@ -77,7 +77,7 @@
}
// skip non-pattern files
- if (!patternEngines.isPatternFile(filename, patternlab)) { return; }
+ if (!patternEngines.isPatternFile(filename, patternlab)) { return null; }
if (config.debug) {
console.log('processPatternIterative:', 'found pattern', file);
}
@@ -92,15 +92,15 @@
//the object in the next diveSync
addPattern(currentPattern, patternlab);
//no need to process further
- return;
+ return currentPattern;
}
- //can ignore all non-mustache files at this point
- if(ext !== '.mustache'){
+ //can ignore all non-supported files at this point
+ if(patternEngines.isFileExtensionSupported(ext) === false){
if (config.debug) {
console.log('==================== FOUND NON-MUSTACHE FILE');
}
- return;
+ return currentPattern;
}
//see if this file has a state
@@ -140,6 +140,8 @@
//add currentPattern to patternlab.patterns array
addPattern(currentPattern, patternlab);
+
+ return currentPattern;
}
@@ -297,7 +299,7 @@
return renderPattern(template, data, partials);
},
process_pattern_iterative: function(file, patternlab){
- processPatternIterative(file, patternlab);
+ return processPatternIterative(file, patternlab);
},
process_pattern_recursive: function(file, patternlab, additionalData){
processPatternRecursive(file, patternlab, additionalData);
From 865b38dc38f85f5a4827caf0517bdf4482e2f077 Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Tue, 8 Dec 2015 17:05:23 -0600
Subject: [PATCH 13/21] Handlebars engine, plus unit tests and proposed pattern
tree for file-based unit testing
---
builder/pattern_engines/engine_handlebars.js | 66 ++++++
test/engine_handlebars_tests.js | 205 ++++++++++++++++++
.../00-atoms/00-global/00-foo.hbs | 0
.../00-atoms/00-global/00-helloworld.hbs | 1 +
.../00-atoms/00-global/01-helloworlds.hbs | 1 +
5 files changed, 273 insertions(+)
create mode 100644 builder/pattern_engines/engine_handlebars.js
create mode 100644 test/engine_handlebars_tests.js
create mode 100644 test/files/_handlebars-test-patterns/00-atoms/00-global/00-foo.hbs
create mode 100644 test/files/_handlebars-test-patterns/00-atoms/00-global/00-helloworld.hbs
create mode 100644 test/files/_handlebars-test-patterns/00-atoms/00-global/01-helloworlds.hbs
diff --git a/builder/pattern_engines/engine_handlebars.js b/builder/pattern_engines/engine_handlebars.js
new file mode 100644
index 000000000..8fe2ff52e
--- /dev/null
+++ b/builder/pattern_engines/engine_handlebars.js
@@ -0,0 +1,66 @@
+/*
+ * handlebars pattern engine for patternlab-node - v0.15.1 - 2015
+ *
+ * Geoffrey Pursell, Brian Muenzenmeyer, and the web community.
+ * Licensed under the MIT license.
+ *
+ * Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice.
+ *
+ */
+
+(function () {
+ "use strict";
+
+ var Handlebars = require('handlebars');
+
+ var engine_handlebars = {
+ engine: Handlebars,
+ engineName: 'handlebars',
+ engineFileExtension: '.hbs',
+
+ // regexes, stored here so they're only compiled once
+ // GTP warning: unchanged copypasta from mustache engine
+ findPartialsRE: /{{>\s*((?:\d+-[\w-]+\/)+(\d+-[\w-]+(\.\w+)?)|[A-Za-z0-9-]+)(\:[\w-]+)?(\(\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,
+ getPartialKeyRE: /{{>([ ])?([\w\-\.\/~]+)(:[A-z-_|]+)?(?:\:[A-Za-z0-9-_]+)?(?:(| )\(.*)?([ ])?}}/g,
+
+ // render it
+ renderPattern: function renderPattern(template, data, partials) {
+ if (partials) {
+ Handlebars.registerPartial(partials);
+ }
+ var compiled = Handlebars.compile(template);
+ return compiled(data);
+ },
+
+ // find and return any {{> template-name }} within pattern
+ findPartials: function findPartials(pattern) {
+ var matches = pattern.template.match(this.findPartialsRE);
+ return matches;
+ },
+ 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) {
+ var matches = pattern.template.match(this.findPartialsWithPatternParametersRE);
+ return matches;
+ },
+ 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;
+ }
+ };
+
+ module.exports = engine_handlebars;
+})();
diff --git a/test/engine_handlebars_tests.js b/test/engine_handlebars_tests.js
new file mode 100644
index 000000000..9f2e851e7
--- /dev/null
+++ b/test/engine_handlebars_tests.js
@@ -0,0 +1,205 @@
+(function () {
+ "use strict";
+
+ var path = require('path');
+ var pa = require('../builder/pattern_assembler');
+ var object_factory = require('../builder/object_factory');
+ var testPatternsPath = path.resolve(__dirname, 'files', '_handlebars-test-patterns');
+
+ // fake pattern lab constructor:
+ // sets up a fake patternlab object, which is needed by the pattern processing
+ // apparatus.
+ function fakePatternLab() {
+ var fpl = {
+ partials: {},
+ patterns: [],
+ footer: '',
+ header: '',
+ listitems: {},
+ listItemArray: [],
+ data: {
+ link: {}
+ },
+ config: require('../config.json'),
+ package: {}
+ };
+
+ // patch the pattern source so the pattern assembler can correctly determine
+ // the "subdir"
+ fpl.config.patterns.source = './test/files/_handlebars-test-patterns';
+
+ return fpl;
+ }
+
+
+ exports['engine_handlebars'] = {
+ 'hello world handlebars pattern renders': function (test) {
+ test.expect(1);
+
+ var patternPath = path.resolve(
+ testPatternsPath,
+ '00-atoms',
+ '00-global',
+ '00-helloworld.hbs'
+ );
+
+ // do all the normal processing of the pattern
+ var patternlab = new fakePatternLab();
+ var assembler = new pa();
+ var helloWorldPattern = assembler.process_pattern_iterative(patternPath, patternlab);
+ assembler.process_pattern_recursive(patternPath, patternlab);
+
+ test.equals(helloWorldPattern.render(), 'Hello world!\n');
+ test.done();
+ },
+ 'hello worlds handlebars pattern can see the atoms-helloworld partial and renders it twice': function (test) {
+ test.expect(1);
+
+ // pattern paths
+ var pattern1Path = path.resolve(
+ testPatternsPath,
+ '00-atoms',
+ '00-global',
+ '00-helloworld.hbs'
+ );
+ var pattern2Path = path.resolve(
+ testPatternsPath,
+ '00-atoms',
+ '00-global',
+ '01-helloworlds.hbs'
+ );
+
+ // set up environment
+ var patternlab = new fakePatternLab(); // environment
+ var assembler = new pa();
+
+ // do all the normal processing of the pattern
+ assembler.process_pattern_iterative(pattern1Path, patternlab);
+ var helloWorldsPattern = assembler.process_pattern_iterative(pattern2Path, patternlab);
+ assembler.process_pattern_recursive(pattern1Path, patternlab);
+ assembler.process_pattern_recursive(pattern2Path, patternlab);
+
+ // test
+ test.equals(helloWorldsPattern.render(), 'Hello world!\n and Hello world!\n\n');
+ test.done();
+ },
+ // GTP warning: unchanged copypasta from mustache engine
+ 'find_pattern_partials finds partials': function(test){
+ // 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(16);
+
+ // 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 = object_factory.oPattern.create(
+ '/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.hbs', // abspath
+ '01-molecules\\00-testing', // subdir
+ '00-test-mol.hbs', // filename,
+ 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 }}" +
+ // verbose partial syntax, introduced in v0.12.0, with file extension
+ "{{> 01-molecules/06-components/03-comment-header.hbs }}" +
+ "{{> 01-molecules/06-components/02-single-comment.hbs(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 }}" +
+ "{{> molecules-single-comment:foo_1 }}" +
+ "{{> molecules-single-comment:foo-1 }}"
+ }
+ );
+
+ var results = currentPattern.findPartials();
+ console.log(results);
+ test.equals(results.length, 15);
+ 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.hbs }}");
+ test.equals(results[8], "{{> 01-molecules/06-components/02-single-comment.hbs(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.equals(results[12], "{{> 00-atoms/00-global/06-test }}");
+ test.equals(results[13], '{{> molecules-single-comment:foo_1 }}');
+ test.equals(results[14], '{{> molecules-single-comment:foo-1 }}');
+ test.done();
+ },
+ // GTP warning: unchanged copypasta from mustache engine
+ 'find_pattern_partials finds verbose partials': function(test){
+ test.expect(3);
+
+ //setup current pattern from what we would have during execution
+ var currentPattern = new object_factory.oPattern(
+ '/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.hbs', // abspath
+ '01-molecules\\00-testing', // subdir
+ '00-test-mol.hbs', // filename,
+ null // data
+ );
+ currentPattern.template = "{{> 01-molecules/06-components/03-comment-header.hbs }}
{{> 01-molecules/06-components/02-single-comment.hbs(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.hbs }}');
+ test.equals(results[1], '{{> 01-molecules/06-components/02-single-comment.hbs(description: \'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.\') }}');
+ test.done();
+ },
+ // GTP warning: unchanged copypasta from mustache engine
+ 'find_pattern_partials_with_parameters finds parameters with verbose partials': function(test){
+ test.expect(2);
+
+ //setup current pattern from what we would have during execution
+ var currentPattern = new object_factory.oPattern(
+ '/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.hbs', // abspath
+ '01-molecules\\00-testing', // subdir
+ '00-test-mol.hbs', // filename,
+ null // data
+ );
+ currentPattern.template = "{{> 01-molecules/06-components/molecules-comment-header}}
{{> 01-molecules/06-components/molecules-single-comment(bar:'baz') }}
";
+
+ var results = currentPattern.findPartialsWithPatternParameters();
+ test.equals(results.length, 1);
+ test.equals(results[0], "{{> 01-molecules/06-components/molecules-single-comment(bar:'baz') }}");
+
+ test.done();
+ },
+ // GTP warning: unchanged copypasta from mustache engine
+ 'find_pattern_partials_with_parameters finds no style modifiers when only partials present': function(test){
+ test.expect(1);
+
+ //setup current pattern from what we would have during execution
+ var currentPattern = new object_factory.oPattern(
+ '/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.hbs', // abspath
+ '01-molecules\\00-testing', // subdir
+ '00-test-mol.hbs', // filename,
+ null // data
+ );
+ currentPattern.template = "{{> molecules-comment-header}}
{{> molecules-single-comment }}
";
+
+ var results = currentPattern.findPartialsWithPatternParameters();
+ test.equals(results, null);
+
+ test.done();
+ }
+ };
+})();
diff --git a/test/files/_handlebars-test-patterns/00-atoms/00-global/00-foo.hbs b/test/files/_handlebars-test-patterns/00-atoms/00-global/00-foo.hbs
new file mode 100644
index 000000000..e69de29bb
diff --git a/test/files/_handlebars-test-patterns/00-atoms/00-global/00-helloworld.hbs b/test/files/_handlebars-test-patterns/00-atoms/00-global/00-helloworld.hbs
new file mode 100644
index 000000000..cd0875583
--- /dev/null
+++ b/test/files/_handlebars-test-patterns/00-atoms/00-global/00-helloworld.hbs
@@ -0,0 +1 @@
+Hello world!
diff --git a/test/files/_handlebars-test-patterns/00-atoms/00-global/01-helloworlds.hbs b/test/files/_handlebars-test-patterns/00-atoms/00-global/01-helloworlds.hbs
new file mode 100644
index 000000000..1df8b851d
--- /dev/null
+++ b/test/files/_handlebars-test-patterns/00-atoms/00-global/01-helloworlds.hbs
@@ -0,0 +1 @@
+{{> atoms-helloworld}} and {{> atoms-helloworld}}
From c741d185d3ce02956c50897ab7259e1efa8411a4 Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Tue, 8 Dec 2015 17:11:45 -0600
Subject: [PATCH 14/21] I am now realizing that I called an atom from inside an
atom. The SHAME. "hello worlds" promoted to molecule!
---
test/engine_handlebars_tests.js | 4 ++--
.../_handlebars-test-patterns/00-atoms/00-global/00-foo.hbs | 0
.../00-atoms/00-global/01-helloworlds.hbs | 1 -
3 files changed, 2 insertions(+), 3 deletions(-)
delete mode 100644 test/files/_handlebars-test-patterns/00-atoms/00-global/00-foo.hbs
delete mode 100644 test/files/_handlebars-test-patterns/00-atoms/00-global/01-helloworlds.hbs
diff --git a/test/engine_handlebars_tests.js b/test/engine_handlebars_tests.js
index 9f2e851e7..26ac85576 100644
--- a/test/engine_handlebars_tests.js
+++ b/test/engine_handlebars_tests.js
@@ -64,9 +64,9 @@
);
var pattern2Path = path.resolve(
testPatternsPath,
- '00-atoms',
+ '00-molecules',
'00-global',
- '01-helloworlds.hbs'
+ '00-helloworlds.hbs'
);
// set up environment
diff --git a/test/files/_handlebars-test-patterns/00-atoms/00-global/00-foo.hbs b/test/files/_handlebars-test-patterns/00-atoms/00-global/00-foo.hbs
deleted file mode 100644
index e69de29bb..000000000
diff --git a/test/files/_handlebars-test-patterns/00-atoms/00-global/01-helloworlds.hbs b/test/files/_handlebars-test-patterns/00-atoms/00-global/01-helloworlds.hbs
deleted file mode 100644
index 1df8b851d..000000000
--- a/test/files/_handlebars-test-patterns/00-atoms/00-global/01-helloworlds.hbs
+++ /dev/null
@@ -1 +0,0 @@
-{{> atoms-helloworld}} and {{> atoms-helloworld}}
From 5302a4da1c88ff386a73203c559d378b3d86635e Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Tue, 8 Dec 2015 17:32:02 -0600
Subject: [PATCH 15/21] oops, add molecules directory and pattern
---
.../00-molecules/00-global/00-helloworlds.hbs | 1 +
1 file changed, 1 insertion(+)
create mode 100644 test/files/_handlebars-test-patterns/00-molecules/00-global/00-helloworlds.hbs
diff --git a/test/files/_handlebars-test-patterns/00-molecules/00-global/00-helloworlds.hbs b/test/files/_handlebars-test-patterns/00-molecules/00-global/00-helloworlds.hbs
new file mode 100644
index 000000000..1df8b851d
--- /dev/null
+++ b/test/files/_handlebars-test-patterns/00-molecules/00-global/00-helloworlds.hbs
@@ -0,0 +1 @@
+{{> atoms-helloworld}} and {{> atoms-helloworld}}
From 5c745c2c04ec4c4115c94538338e3ca27f360979 Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Tue, 8 Dec 2015 17:32:19 -0600
Subject: [PATCH 16/21] add Handlebars as an npm dependency (for now)
---
package.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/package.json b/package.json
index 8d3953d22..7ba715db5 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"grunt-contrib-copy": "^0.8.2",
"grunt-contrib-nodeunit": "^0.4.1",
"grunt-contrib-watch": "^0.6.1",
+ "handlebars": "^4.0.5",
"html-entities": "^1.2.0",
"matchdep": "^1.0.0",
"mustache": "^2.2.0"
From ddfd363baa94ab49855fa0ba4e84fa747e054c59 Mon Sep 17 00:00:00 2001
From: BRIAN MUENZENMEYER
Date: Tue, 8 Dec 2015 23:42:24 -0600
Subject: [PATCH 17/21] Support for pattern link.* shortcut inside pattern
data objects. Unit test to cover this
Resolves #171
Marking this version 1.0.0 - having achieved broad feature parity with PL PHP v1
---
CHANGELOG | 5 +-
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 | 36 +++++++++++-
builder/pattern_exporter.js | 2 +-
builder/patternlab.js | 7 ++-
builder/patternlab_grunt.js | 2 +-
builder/patternlab_gulp.js | 2 +-
builder/pseudopattern_hunter.js | 2 +-
builder/style_modifier_hunter.js | 2 +-
package.gulp.json | 2 +-
package.json | 2 +-
test/files/_patterns/00-test/link.mustache | 1 +
test/files/_patterns/00-test/nav.json | 11 ++++
test/files/_patterns/00-test/nav.mustache | 9 +++
test/pattern_assembler_tests.js | 66 ++++++++++++++++++++++
19 files changed, 144 insertions(+), 15 deletions(-)
create mode 100644 test/files/_patterns/00-test/link.mustache
create mode 100644 test/files/_patterns/00-test/nav.json
create mode 100644 test/files/_patterns/00-test/nav.mustache
diff --git a/CHANGELOG b/CHANGELOG
index fd73f8cc4..c01f4e782 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,6 @@
THIS CHANGELOG IS AN ATTEMPT TO DOCUMENT CHANGES TO THIS PROJECT.
-PL-node-v0.16.0
+PL-node-v1.0.0
- FIX: Resolve issue with not hiding underscored patterns.
- THX: Thanks @ivancamilov for reporting this regression.
- FIX: Fix misapplied error input class
@@ -11,6 +11,9 @@ PL-node-v0.16.0
- ADD: Added unit tests for pattern states and pseudopatterns
- CHG: Changed pseudopattern generation to use config.patterns.source directory instead of hardcode
- CHG: Explicitly sorting patterns by name prior to building the UI
+- ADD: Added ability to specify link.* urls inside data objects
+- CHG: Incremented version to 1.0.0. Achieved near-parity with PL PHP 1!
+- THX: Thanks to each and every person who cared about Pattern Lab Node! - Brian
PL-node-v0.15.1
- FIX: Resolve issue with styleModifiers not being replaced when the partial has spaces in it.
diff --git a/builder/lineage_hunter.js b/builder/lineage_hunter.js
index a01d2c641..916eddb93 100644
--- a/builder/lineage_hunter.js
+++ b/builder/lineage_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.16.0 - 2015
+ * patternlab-node - v1.0.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 690079561..f553f32c2 100644
--- a/builder/list_item_hunter.js
+++ b/builder/list_item_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.16.0 - 2015
+ * patternlab-node - v1.0.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 b939e3b39..e7eb70a15 100644
--- a/builder/media_hunter.js
+++ b/builder/media_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.16.0 - 2015
+ * patternlab-node - v1.0.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 d53587ec1..6823d94a7 100644
--- a/builder/object_factory.js
+++ b/builder/object_factory.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.16.0 - 2015
+ * patternlab-node - v1.0.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 a0e78e6f8..e38481beb 100644
--- a/builder/parameter_hunter.js
+++ b/builder/parameter_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.16.0 - 2015
+ * patternlab-node - v1.0.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 ccf85e144..f175dc115 100644
--- a/builder/pattern_assembler.js
+++ b/builder/pattern_assembler.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.16.0 - 2015
+ * patternlab-node - v1.0.0 - 2015
*
* Brian Muenzenmeyer, and the web community.
* Licensed under the MIT license.
@@ -54,6 +54,7 @@
}
function addPattern(pattern, patternlab){
+ //add the link to the global object
patternlab.data.link[pattern.patternGroup + '-' + pattern.patternName] = '/patterns/' + pattern.patternLink;
//only push to array if the array doesn't contain this pattern
@@ -317,6 +318,36 @@
return o;
}
+ //look for pattern links included in data files.
+ //these will be in the form of link.* WITHOUT {{}}, which would still be there from direct pattern inclusion
+ function parseDataLinks(patternlab){
+
+ //loop through all patterns
+ for (var i = 0; i < patternlab.patterns.length; i++){
+ var pattern = patternlab.patterns[i];
+ //look for link.* such as link.pages-blog as a value
+ var linkRE = /link.[A-z0-9-_]+/g;
+ //convert to string for easier searching
+ var dataObjAsString = JSON.stringify(pattern.jsonFileData);
+ var linkMatches = dataObjAsString.match(linkRE);
+
+ //if no matches found, escape current loop iteration
+ if(linkMatches === null) { continue; }
+
+ for(var i = 0; i < linkMatches.length; i++){
+ //for each match, find the expanded link within the already constructed patternlab.data.link object
+ var expandedLink = patternlab.data.link[linkMatches[i].split('.')[1]];
+ if(patternlab.config.debug){
+ console.log('expanded data link from ' + linkMatches[i] + ' to ' + expandedLink + ' inside ' + pattern.key);
+ }
+ //replace value with expandedLink on the pattern
+ dataObjAsString = dataObjAsString.replace(linkMatches[i], expandedLink);
+ }
+ //write back to data on the pattern
+ pattern.jsonFileData = JSON.parse(dataObjAsString);
+ }
+ }
+
return {
find_pattern_partials: function(pattern){
return findPartials(pattern);
@@ -356,6 +387,9 @@
},
is_object_empty: function(obj){
return isObjectEmpty(obj);
+ },
+ parse_data_links: function(patternlab){
+ parseDataLinks(patternlab);
}
};
diff --git a/builder/pattern_exporter.js b/builder/pattern_exporter.js
index 7fc1e37ad..ddbf74023 100644
--- a/builder/pattern_exporter.js
+++ b/builder/pattern_exporter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.16.0 - 2015
+ * patternlab-node - v1.0.0 - 2015
*
* Brian Muenzenmeyer, and the web community.
* Licensed under the MIT license.
diff --git a/builder/patternlab.js b/builder/patternlab.js
index d40956ca2..01850e29c 100644
--- a/builder/patternlab.js
+++ b/builder/patternlab.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.16.0 - 2015
+ * patternlab-node - v1.0.0 - 2015
*
* Brian Muenzenmeyer, and the web community.
* Licensed under the MIT license.
@@ -91,6 +91,10 @@ var patternlab_engine = function () {
pattern_assembler.process_pattern_iterative(file.substring(2), patternlab);
});
+ //now that all the main patterns are known, look for any links that might be within data and expand them
+ //we need to do this before expanding patterns & partials into extendedTemplates, otherwise we could lose the data -> partial reference
+ pattern_assembler.parse_data_links(patternlab);
+
//diveSync again to recursively include partials, filling out the
//extendedTemplate property of the patternlab.patterns elements
diveSync(patterns_dir, {
@@ -124,6 +128,7 @@ var patternlab_engine = function () {
var allData = JSON.parse(JSON.stringify(patternlab.data));
allData = pattern_assembler.merge_data(allData, pattern.jsonFileData);
+ //render the extendedTemplate with all data
pattern.patternPartial = pattern_assembler.renderPattern(pattern.extendedTemplate, allData);
//add footer info before writing
diff --git a/builder/patternlab_grunt.js b/builder/patternlab_grunt.js
index 606127614..f56e58074 100644
--- a/builder/patternlab_grunt.js
+++ b/builder/patternlab_grunt.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.16.0 - 2015
+ * patternlab-node - v1.0.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 5db996f66..e20e1e1a7 100644
--- a/builder/patternlab_gulp.js
+++ b/builder/patternlab_gulp.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.16.0 - 2015
+ * patternlab-node - v1.0.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 b4e69867e..0969c7ac1 100644
--- a/builder/pseudopattern_hunter.js
+++ b/builder/pseudopattern_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.16.0 - 2015
+ * patternlab-node - v1.0.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 c38ada4fd..8a42889bd 100644
--- a/builder/style_modifier_hunter.js
+++ b/builder/style_modifier_hunter.js
@@ -1,5 +1,5 @@
/*
- * patternlab-node - v0.16.0 - 2015
+ * patternlab-node - v1.0.0 - 2015
*
* Brian Muenzenmeyer, and the web community.
* Licensed under the MIT license.
diff --git a/package.gulp.json b/package.gulp.json
index 313d4edf1..dbece6e4b 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.16.0",
+ "version": "1.0.0",
"devDependencies": {
"browser-sync": "^2.10.0",
"del": "^2.0.2",
diff --git a/package.json b/package.json
index 516481505..157d4cc66 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.16.0",
+ "version": "1.0.0",
"devDependencies": {
"bs-html-injector": "^3.0.0",
"diveSync": "^0.3.0",
diff --git a/test/files/_patterns/00-test/link.mustache b/test/files/_patterns/00-test/link.mustache
new file mode 100644
index 000000000..b34269824
--- /dev/null
+++ b/test/files/_patterns/00-test/link.mustache
@@ -0,0 +1 @@
+Cool Dude
diff --git a/test/files/_patterns/00-test/nav.json b/test/files/_patterns/00-test/nav.json
new file mode 100644
index 000000000..32a6c0952
--- /dev/null
+++ b/test/files/_patterns/00-test/nav.json
@@ -0,0 +1,11 @@
+{
+ "brad" : {
+ "url" : "link.twitter-brad"
+ },
+ "dave" : {
+ "url" : "link.twitter-dave"
+ },
+ "brian" : {
+ "url" : "link.twitter-brian"
+ }
+}
diff --git a/test/files/_patterns/00-test/nav.mustache b/test/files/_patterns/00-test/nav.mustache
new file mode 100644
index 000000000..b76f77dfc
--- /dev/null
+++ b/test/files/_patterns/00-test/nav.mustache
@@ -0,0 +1,9 @@
+{{# brad }}
+ {{> test-link }}
+{{/ brad }}
+{{# dave }}
+ {{> test-link }}
+{{/ dave }}
+{{# brian }}
+ {{> test-link }}
+{{/ brian }}
diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js
index 2519b87ba..6953e2324 100644
--- a/test/pattern_assembler_tests.js
+++ b/test/pattern_assembler_tests.js
@@ -514,6 +514,7 @@
var pattern_assembler = new pa();
var patterns_dir = './test/files/_patterns';
var patternlab = {};
+ //THIS IS BAD.
patternlab.config = fs.readJSONSync('./config.json');
patternlab.config.patterns = {source: patterns_dir};
patternlab.data = fs.readJSONSync('./source/_data/data.json');
@@ -596,6 +597,71 @@
//assert
test.equals(pattern.patternState, "");
test.done();
+ },
+ 'parseDataLinks - replaces found link.* data for their expanded links' : function(test){
+ //arrange
+ var diveSync = require('diveSync');
+ var fs = require('fs-extra');
+ var pa = require('../builder/pattern_assembler');
+ var pattern_assembler = new pa();
+ var patterns_dir = './test/files/_patterns/';
+ var patternlab = {};
+ //THIS IS BAD
+ patternlab.config = fs.readJSONSync('./config.json');
+ patternlab.config.patterns = {source: patterns_dir};
+ patternlab.data = fs.readJSONSync('./source/_data/data.json');
+ patternlab.listitems = fs.readJSONSync('./source/_data/listitems.json');
+ patternlab.header = fs.readFileSync('./source/_patternlab-files/pattern-header-footer/header.html', 'utf8');
+ patternlab.footer = fs.readFileSync('./source/_patternlab-files/pattern-header-footer/footer.html', 'utf8');
+ patternlab.patterns = [];
+ patternlab.data.link = {};
+ patternlab.partials = {};
+
+ diveSync(patterns_dir,
+ {
+ filter: function(path, dir){
+ if(dir){
+ var remainingPath = path.replace(patterns_dir, '');
+ var isValidPath = remainingPath.indexOf('/_') === -1;
+ return isValidPath;
+ }
+ return true;
+ }
+ },
+ function(err, file){
+ //log any errors
+ if(err){
+ console.log(err);
+ return;
+ }
+ pattern_assembler.process_pattern_iterative(file.substring(2), patternlab);
+ }
+ );
+
+ //for the sake of the test, also imagining I have the following pages...
+ patternlab.data.link['twitter-brad'] = 'https://twitter.com/brad_frost';
+ patternlab.data.link['twitter-dave'] = 'https://twitter.com/dmolsen';
+ patternlab.data.link['twitter-brian'] = 'https://twitter.com/bmuenzenmeyer';
+
+ var pattern;
+ for(var i = 0; i < patternlab.patterns.length; i++){
+ if(patternlab.patterns[i].key === 'test-nav'){
+ pattern = patternlab.patterns[i];
+ }
+ }
+ //assert before
+ test.equals(pattern.jsonFileData.brad.url, "link.twitter-brad");
+ test.equals(pattern.jsonFileData.dave.url, "link.twitter-dave");
+ test.equals(pattern.jsonFileData.brian.url, "link.twitter-brian");
+
+ //act
+ pattern_assembler.parse_data_links(patternlab);
+
+ //assert after
+ test.equals(pattern.jsonFileData.brad.url, "https://twitter.com/brad_frost");
+ test.equals(pattern.jsonFileData.dave.url, "https://twitter.com/dmolsen");
+ test.equals(pattern.jsonFileData.brian.url, "https://twitter.com/bmuenzenmeyer");
+ test.done();
}
};
}());
From 8ac9b6e7d3b76872686198dc2b1f618e838cb719 Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Fri, 11 Dec 2015 09:29:57 -0600
Subject: [PATCH 18/21] (mostly?) working handlebars rendering on a real tree
-- there's a problem now with patternlab.json having cyclical references that
needs to be fixed.
---
builder/lineage_hunter.js | 3 -
builder/object_factory.js | 9 +-
builder/pattern_assembler.js | 14 +-
builder/pattern_engines/engine_handlebars.js | 10 +-
builder/patternlab.js | 20 +-
test/engine_handlebars_tests.js | 183 +++++++------------
6 files changed, 107 insertions(+), 132 deletions(-)
diff --git a/builder/lineage_hunter.js b/builder/lineage_hunter.js
index a3f92e8b2..0ee3bda24 100644
--- a/builder/lineage_hunter.js
+++ b/builder/lineage_hunter.js
@@ -20,9 +20,6 @@
var config = require('../config.json');
//find the {{> template-name }} within patterns
- 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 2c7ef80cf..d929db6e9 100644
--- a/builder/object_factory.js
+++ b/builder/object_factory.js
@@ -20,9 +20,6 @@
// oPattern properties
var oPattern = function(abspath, subdir, filename, data) {
- if (config.debug) {
- console.log('=== NEW OPATTERN.', '\nabsPath:', abspath, '\nsubdir:', subdir, '\nfilename:', filename, '\ndata:\n', data);
- }
this.fileName = filename.substring(0, filename.indexOf('.'));
this.fileExtension = path.extname(abspath);
this.abspath = abspath;
@@ -61,6 +58,12 @@
return this.engine.renderPattern(this.extendedTemplate, data, partials);
},
+ registerPartial: function () {
+ if (typeof this.engine.registerPartial === 'function') {
+ this.engine.registerPartial(this);
+ }
+ },
+
// the finders all delegate to the PatternEngine, which also encapsulates all
// appropriate regexes
findPartials: function () {
diff --git a/builder/pattern_assembler.js b/builder/pattern_assembler.js
index 14c00347e..d09b5edae 100644
--- a/builder/pattern_assembler.js
+++ b/builder/pattern_assembler.js
@@ -31,6 +31,8 @@
function addPattern(pattern, patternlab){
//add the link to the global object
patternlab.data.link[pattern.patternGroup + '-' + pattern.patternName] = '/patterns/' + pattern.patternLink;
+ if (!patternlab.patternsByKey) { patternlab.patternsByKey = {}; }
+ if (!patternlab.patternsByAbsPath) { patternlab.patternsByAbsPath = {}; }
//only push to array if the array doesn't contain this pattern
var isNew = true;
@@ -45,7 +47,12 @@
}
//if the pattern is new, just push to the array
if(isNew){
+ // do global registration
patternlab.patterns.push(pattern);
+ patternlab.patternsByKey[pattern.key] = pattern;
+ patternlab.patternsByAbsPath[pattern.asbpath] = pattern;
+ // do plugin-specific registration
+ pattern.registerPartial();
}
}
@@ -74,14 +81,11 @@
var ext = path.extname(filename);
if (config.debug) {
- console.log('processPatternIterative:', 'filename:', filename);
+ console.log('processPatternIterative:', filename);
}
// skip non-pattern files
if (!patternEngines.isPatternFile(filename, patternlab)) { return null; }
- if (config.debug) {
- console.log('processPatternIterative:', 'found pattern', file);
- }
//make a new Pattern Object
var currentPattern = new of.oPattern(file, subdir, filename);
@@ -104,7 +108,7 @@
var jsonFilename = patternlab.config.patterns.source + currentPattern.subdir + '/' + currentPattern.fileName + ".json";
currentPattern.jsonFileData = fs.readJSONSync(jsonFilename.substring(2));
if(patternlab.config.debug){
- console.log('found pattern-specific data.json for ' + currentPattern.key);
+ console.log('processPatternIterative: found pattern-specific data.json for ' + currentPattern.key);
}
}
catch(e) {
diff --git a/builder/pattern_engines/engine_handlebars.js b/builder/pattern_engines/engine_handlebars.js
index 8fe2ff52e..9cdd83b13 100644
--- a/builder/pattern_engines/engine_handlebars.js
+++ b/builder/pattern_engines/engine_handlebars.js
@@ -20,11 +20,11 @@
// regexes, stored here so they're only compiled once
// GTP warning: unchanged copypasta from mustache engine
- findPartialsRE: /{{>\s*((?:\d+-[\w-]+\/)+(\d+-[\w-]+(\.\w+)?)|[A-Za-z0-9-]+)(\:[\w-]+)?(\(\s*\w+\s*:\s*(?:'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*")\))?\s*}}/g,
+ // findPartialsRE: /{{>\s*((?:\d+-[\w-]+\/)+(\d+-[\w-]+(\.\w+)?)|[A-Za-z0-9-]+)(\:[\w-]+)?(\(\s*\w+\s*:\s*(?:'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*")\))?\s*}}/g,
+ findPartialsRE: /{{#?>\s*([\w-\/.]+)(?:.|\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,
- getPartialKeyRE: /{{>([ ])?([\w\-\.\/~]+)(:[A-z-_|]+)?(?:\:[A-Za-z0-9-_]+)?(?:(| )\(.*)?([ ])?}}/g,
// render it
renderPattern: function renderPattern(template, data, partials) {
@@ -35,6 +35,10 @@
return compiled(data);
},
+ registerPartial: function (oPattern) {
+ Handlebars.registerPartial(oPattern.key, oPattern.template);
+ },
+
// find and return any {{> template-name }} within pattern
findPartials: function findPartials(pattern) {
var matches = pattern.template.match(this.findPartialsRE);
@@ -57,7 +61,7 @@
// 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');
+ var partialKey = partialString.replace(this.findPartialsRE, '$1');
return partialKey;
}
};
diff --git a/builder/patternlab.js b/builder/patternlab.js
index 9185a6516..1d99f09d4 100644
--- a/builder/patternlab.js
+++ b/builder/patternlab.js
@@ -1,6 +1,6 @@
-/*
- * patternlab-node - v1.0.0 - 2015
- *
+/*
+ * patternlab-node - v1.0.0 - 2015
+ *
* Brian Muenzenmeyer, and the web community.
* Licensed under the MIT license.
*
@@ -19,6 +19,7 @@ var patternlab_engine = function () {
mh = require('./media_hunter'),
pe = require('./pattern_exporter'),
he = require('html-entities').AllHtmlEntities,
+ util = require('util'),
plutils = require('./utilities'),
patternlab = {};
@@ -49,7 +50,10 @@ var patternlab_engine = function () {
//debug file can be written by setting flag on config.json
if(patternlab.config.debug){
console.log('writing patternlab debug file to ./patternlab.json');
- fs.outputFileSync('./patternlab.json', JSON.stringify(patternlab, null, 3));
+ // fs.outputFileSync('./patternlab.json', JSON.stringify(patternlab, null, 3));
+ fs.outputFileSync('./patternlab.json', util.inspect(patternlab, {
+ depth: null
+ }));
}
}
@@ -114,7 +118,12 @@ var patternlab_engine = function () {
}
pattern_assembler.process_pattern_recursive(file.substring(2), patternlab);
- });
+ });
+
+ if (patternlab.config.debug) {
+ console.log('pattern keys:', Object.keys(patternlab.patternsByKey));
+ console.log('by keys length:', Object.keys(patternlab.patternsByKey).length, 'array length:', patternlab.patterns.length);
+ }
//delete the contents of config.patterns.public before writing
if(deletePatternDir){
@@ -145,7 +154,6 @@ var patternlab_engine = function () {
//export patterns if necessary
pattern_exporter.export_patterns(patternlab);
-
}
function buildFrontEnd(){
diff --git a/test/engine_handlebars_tests.js b/test/engine_handlebars_tests.js
index 26ac85576..7a50c1460 100644
--- a/test/engine_handlebars_tests.js
+++ b/test/engine_handlebars_tests.js
@@ -32,6 +32,35 @@
}
+ // function for testing sets of partials
+ function testFindPartials(test, partialTests) {
+ test.expect(partialTests.length + 1);
+
+ // 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 = object_factory.oPattern.create(
+ '/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.hbs', // abspath
+ '01-molecules\\00-testing', // subdir
+ '00-test-mol.hbs', // filename,
+ null, // data
+ {
+ template: partialTests.join()
+ }
+ );
+
+ // act
+ var results = currentPattern.findPartials();
+
+ // assert
+ test.equals(results.length, partialTests.length);
+ partialTests.forEach(function(testString, index) {
+ test.equals(results[index], testString);
+ });
+
+ test.done();
+ }
+
exports['engine_handlebars'] = {
'hello world handlebars pattern renders': function (test) {
test.expect(1);
@@ -83,123 +112,53 @@
test.equals(helloWorldsPattern.render(), 'Hello world!\n and Hello world!\n\n');
test.done();
},
- // GTP warning: unchanged copypasta from mustache engine
'find_pattern_partials finds partials': function(test){
- // 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(16);
-
- // 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 = object_factory.oPattern.create(
- '/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.hbs', // abspath
- '01-molecules\\00-testing', // subdir
- '00-test-mol.hbs', // filename,
- 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 }}" +
- // verbose partial syntax, introduced in v0.12.0, with file extension
- "{{> 01-molecules/06-components/03-comment-header.hbs }}" +
- "{{> 01-molecules/06-components/02-single-comment.hbs(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 }}" +
- "{{> molecules-single-comment:foo_1 }}" +
- "{{> molecules-single-comment:foo-1 }}"
- }
- );
-
- var results = currentPattern.findPartials();
- console.log(results);
- test.equals(results.length, 15);
- 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.hbs }}");
- test.equals(results[8], "{{> 01-molecules/06-components/02-single-comment.hbs(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.equals(results[12], "{{> 00-atoms/00-global/06-test }}");
- test.equals(results[13], '{{> molecules-single-comment:foo_1 }}');
- test.equals(results[14], '{{> molecules-single-comment:foo-1 }}');
- test.done();
+ testFindPartials(test, [
+ "{{> molecules-comment-header}}",
+ "{{> molecules-comment-header}}",
+ "{{> \n molecules-comment-header\n}}",
+ "{{> molecules-weird-spacing }}",
+ "{{> molecules-ba_d-cha*rs }}"
+ ]);
},
- // GTP warning: unchanged copypasta from mustache engine
'find_pattern_partials finds verbose partials': function(test){
- test.expect(3);
-
- //setup current pattern from what we would have during execution
- var currentPattern = new object_factory.oPattern(
- '/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.hbs', // abspath
- '01-molecules\\00-testing', // subdir
- '00-test-mol.hbs', // filename,
- null // data
- );
- currentPattern.template = "{{> 01-molecules/06-components/03-comment-header.hbs }}
{{> 01-molecules/06-components/02-single-comment.hbs(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.hbs }}');
- test.equals(results[1], '{{> 01-molecules/06-components/02-single-comment.hbs(description: \'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.\') }}');
- test.done();
+ testFindPartials(test, [
+ '{{> 01-molecules/06-components/03-comment-header.hbs }}',
+ "{{> 01-molecules/06-components/02-single-comment.hbs(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/06-test }}'
+ ]);
},
- // GTP warning: unchanged copypasta from mustache engine
- 'find_pattern_partials_with_parameters finds parameters with verbose partials': function(test){
- test.expect(2);
-
- //setup current pattern from what we would have during execution
- var currentPattern = new object_factory.oPattern(
- '/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.hbs', // abspath
- '01-molecules\\00-testing', // subdir
- '00-test-mol.hbs', // filename,
- null // data
- );
- currentPattern.template = "{{> 01-molecules/06-components/molecules-comment-header}}
{{> 01-molecules/06-components/molecules-single-comment(bar:'baz') }}
";
-
- var results = currentPattern.findPartialsWithPatternParameters();
- test.equals(results.length, 1);
- test.equals(results[0], "{{> 01-molecules/06-components/molecules-single-comment(bar:'baz') }}");
-
- test.done();
+ 'find_pattern_partials finds simple partials with parameters': function(test){
+ testFindPartials(test, [
+ "{{> 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.") }}'
+ ]);
},
- // GTP warning: unchanged copypasta from mustache engine
- 'find_pattern_partials_with_parameters finds no style modifiers when only partials present': function(test){
- test.expect(1);
-
- //setup current pattern from what we would have during execution
- var currentPattern = new object_factory.oPattern(
- '/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.hbs', // abspath
- '01-molecules\\00-testing', // subdir
- '00-test-mol.hbs', // filename,
- null // data
- );
- currentPattern.template = "{{> molecules-comment-header}}
{{> molecules-single-comment }}
";
-
- var results = currentPattern.findPartialsWithPatternParameters();
- test.equals(results, null);
-
- test.done();
+ 'find_pattern_partials finds simple partials with style modifiers': function(test){
+ testFindPartials(test, [
+ '{{> molecules-single-comment:foo }}'
+ ]);
+ },
+ 'find_pattern_partials finds partials with handlebars parameters': function(test){
+ testFindPartials(test, [
+ '{{> atoms-title title="bravo" headingLevel="2" headingSize="bravo" position="left"}}',
+ '{{> atoms-title title="bravo"\n headingLevel="2"\n headingSize="bravo"\n position="left"}}',
+ '{{> atoms-title title="color midnight blue" headingSize="charlie"}}',
+ '{{> atoms-input label="city" required=true}}',
+ '{{> organisms-product-filter filterData}}',
+ '{{> atoms-input email required=true}}',
+ '{{> molecules-storycard variants.flex }}',
+ '{{> myPartial name=../name }}'
+ ]);
+ },
+
+ 'find_pattern_partials finds handlebars block partials': function(test){
+ testFindPartials(test, [
+ '{{#> myPartial }}'
+ ]);
}
};
})();
From 82c76edd33fc669b0cc4f5a00131b71a96f9279e Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Mon, 14 Dec 2015 14:53:22 -0600
Subject: [PATCH 19/21] Fix the circular reference problem in the log file,
which occurred when the Handlebars engine was in use.
---
builder/patternlab.js | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/builder/patternlab.js b/builder/patternlab.js
index 1d99f09d4..d6ae6a51b 100644
--- a/builder/patternlab.js
+++ b/builder/patternlab.js
@@ -47,13 +47,21 @@ var patternlab_engine = function () {
}
function printDebug() {
+ // A replacer function to pass to stringify below; this is here to prevent
+ // the debug output from blowing up into a massive fireball of circular
+ // references. This happens specifically with the Handlebars engine. Remove
+ // if you like 180MB log files.
+ function propertyStringReplacer(key, value) {
+ if (key === 'engine' && value.engineName) {
+ return '{' + value.engineName + ' engine object}';
+ }
+ return value;
+ }
+
//debug file can be written by setting flag on config.json
if(patternlab.config.debug){
console.log('writing patternlab debug file to ./patternlab.json');
- // fs.outputFileSync('./patternlab.json', JSON.stringify(patternlab, null, 3));
- fs.outputFileSync('./patternlab.json', util.inspect(patternlab, {
- depth: null
- }));
+ fs.outputFileSync('./patternlab.json', JSON.stringify(patternlab, propertyStringReplacer, 3));
}
}
From 6693ee2d91e9ab1cb51492b138a9144fd4239a24 Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Mon, 14 Dec 2015 15:18:58 -0600
Subject: [PATCH 20/21] remove speculative alternate pattern data structures
---
builder/pattern_assembler.js | 4 ----
builder/patternlab.js | 4 ----
2 files changed, 8 deletions(-)
diff --git a/builder/pattern_assembler.js b/builder/pattern_assembler.js
index d09b5edae..9b9074b03 100644
--- a/builder/pattern_assembler.js
+++ b/builder/pattern_assembler.js
@@ -31,8 +31,6 @@
function addPattern(pattern, patternlab){
//add the link to the global object
patternlab.data.link[pattern.patternGroup + '-' + pattern.patternName] = '/patterns/' + pattern.patternLink;
- if (!patternlab.patternsByKey) { patternlab.patternsByKey = {}; }
- if (!patternlab.patternsByAbsPath) { patternlab.patternsByAbsPath = {}; }
//only push to array if the array doesn't contain this pattern
var isNew = true;
@@ -49,8 +47,6 @@
if(isNew){
// do global registration
patternlab.patterns.push(pattern);
- patternlab.patternsByKey[pattern.key] = pattern;
- patternlab.patternsByAbsPath[pattern.asbpath] = pattern;
// do plugin-specific registration
pattern.registerPartial();
}
diff --git a/builder/patternlab.js b/builder/patternlab.js
index d6ae6a51b..646ed146d 100644
--- a/builder/patternlab.js
+++ b/builder/patternlab.js
@@ -128,10 +128,6 @@ var patternlab_engine = function () {
pattern_assembler.process_pattern_recursive(file.substring(2), patternlab);
});
- if (patternlab.config.debug) {
- console.log('pattern keys:', Object.keys(patternlab.patternsByKey));
- console.log('by keys length:', Object.keys(patternlab.patternsByKey).length, 'array length:', patternlab.patterns.length);
- }
//delete the contents of config.patterns.public before writing
if(deletePatternDir){
From a2c6c714e0b98a31302a71a3722ea001dd2dad4a Mon Sep 17 00:00:00 2001
From: Geoffrey Pursell
Date: Mon, 14 Dec 2015 15:55:13 -0600
Subject: [PATCH 21/21] Add pattern-engines branch to .travis.yml
---
.travis.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.travis.yml b/.travis.yml
index 611cfd7d6..1a080079e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,3 +16,4 @@ branches:
only:
- master
- dev
+ - pattern-engines