diff --git a/core/lib/list_item_hunter.js b/core/lib/list_item_hunter.js
index 35c077f5d..1deecb505 100644
--- a/core/lib/list_item_hunter.js
+++ b/core/lib/list_item_hunter.js
@@ -5,15 +5,58 @@ var list_item_hunter = function () {
var extend = require('util')._extend,
JSON5 = require('json5'),
pa = require('./pattern_assembler'),
- smh = require('./style_modifier_hunter'),
- plutils = require('./utilities'),
- Pattern = require('./object_factory').Pattern;
+ plutils = require('./utilities');
var pattern_assembler = new pa(),
- style_modifier_hunter = new smh(),
items = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty'];
+ function getEnd(liMatch) {
+ return liMatch.replace('#', '/');
+ }
+
+ function getPatternBlock(pattern, liMatch, end) {
+ return pattern.extendedTemplate.substring(pattern.extendedTemplate.indexOf(liMatch) + liMatch.length, pattern.extendedTemplate.indexOf(end));
+ }
+
+ function preprocessListItemPartials(pattern) {
+ //find any listitem blocks
+ var matches = pattern.findListItems();
+
+ if (matches !== null) {
+ matches.forEach(function (liMatch) {
+ var end = getEnd(liMatch);
+ var patternBlock = getPatternBlock(pattern, liMatch, end);
+ var partials = pattern.engine.findPartials(patternBlock);
+
+ //escape listitem blocks with partials
+ if (partials) {
+ var liMatchEscaped = '\u0002' + liMatch.slice(2);
+ var endEscaped = '\u0002' + end.slice(2);
+ var find = liMatch + patternBlock + end;
+ var replace = liMatchEscaped + patternBlock + endEscaped;
+
+ pattern.extendedTemplate = pattern.extendedTemplate.replace(find, replace);
+ }
+ });
+ }
+ }
+
+ function postprocessListItemPartials(pattern) {
+ //find any listitem blocks
+ var matches = pattern.extendedTemplate.match(/\u0002(.|\s)*?\}\}/g);
+
+ if (matches !== null) {
+ matches.forEach(function (liMatch) {
+ var replace = '{{' + liMatch.slice(1);
+
+ pattern.extendedTemplate = pattern.extendedTemplate.replace(liMatch, replace);
+ });
+ }
+ }
+
function processListItemPartials(pattern, patternlab) {
+ preprocessListItemPartials(pattern);
+
//find any listitem blocks
var matches = pattern.findListItems();
@@ -26,8 +69,8 @@ var list_item_hunter = function () {
//find the boundaries of the block
var loopNumberString = liMatch.split('.')[1].split('}')[0].trim();
- var end = liMatch.replace('#', '/');
- var patternBlock = pattern.template.substring(pattern.template.indexOf(liMatch) + liMatch.length, pattern.template.indexOf(end)).trim();
+ var end = getEnd(liMatch);
+ var patternBlock = getPatternBlock(pattern, liMatch, end).trim();
//build arrays that repeat the block, however large we need to
var repeatedBlockTemplate = [];
@@ -59,59 +102,11 @@ var list_item_hunter = function () {
//combine listItem data with pattern data with global data
var itemData = listData['' + items.indexOf(loopNumberString)]; //this is a property like "2"
- var globalData;
- var localData;
- try {
- globalData = JSON5.parse(JSON5.stringify(patternlab.data));
- localData = JSON5.parse(JSON5.stringify(pattern.jsonFileData));
- } catch (err) {
- console.log('There was an error parsing JSON for ' + pattern.relPath);
- console.log(err);
- }
-
- var allData = plutils.mergeData(globalData, localData);
- allData = plutils.mergeData(allData, itemData !== undefined ? itemData[i] : {}); //itemData could be undefined if the listblock contains no partial, just markup
+ var allData = plutils.mergeData(pattern.allData, itemData !== undefined ? itemData[i] : {}); //itemData could be undefined if the listblock contains no partial, just markup
allData.link = extend({}, patternlab.data.link);
- //check for partials within the repeated block
- var foundPartials = Pattern.createEmpty({'template': thisBlockTemplate}).findPartials();
-
- if (foundPartials && foundPartials.length > 0) {
-
- for (var j = 0; j < foundPartials.length; j++) {
-
- //get the partial
- var partialName = foundPartials[j].match(/([\w\-\.\/~]+)/g)[0];
- var partialPattern = pattern_assembler.findPartial(partialName, patternlab);
-
- //create a copy of the partial so as to not pollute it after the get_pattern_by_key call.
- var cleanPartialPattern;
- try {
- cleanPartialPattern = JSON5.parse(JSON5.stringify(partialPattern));
- } catch (err) {
- console.log('There was an error parsing JSON for ' + pattern.relPath);
- console.log(err);
- }
-
- //if we retrieved a pattern we should make sure that its extendedTemplate is reset. looks to fix #356
- cleanPartialPattern.extendedTemplate = cleanPartialPattern.template;
-
- //if partial has style modifier data, replace the styleModifier value
- if (foundPartials[j].indexOf(':') > -1) {
- style_modifier_hunter.consume_style_modifier(cleanPartialPattern, foundPartials[j], patternlab);
- }
-
- //replace its reference within the block with the extended template
- thisBlockTemplate = thisBlockTemplate.replace(foundPartials[j], cleanPartialPattern.extendedTemplate);
- }
-
- //render with data
- thisBlockHTML = pattern_assembler.renderPattern(thisBlockTemplate, allData, patternlab.partials);
-
- } else {
- //just render with mergedData
- thisBlockHTML = pattern_assembler.renderPattern(thisBlockTemplate, allData, patternlab.partials);
- }
+ //just render with mergedData
+ thisBlockHTML = pattern_assembler.renderPattern(thisBlockTemplate, allData);
//add the rendered HTML to our string
repeatedBlockHtml = repeatedBlockHtml + thisBlockHTML;
@@ -121,17 +116,17 @@ var list_item_hunter = function () {
var repeatingBlock = pattern.extendedTemplate.substring(pattern.extendedTemplate.indexOf(liMatch), pattern.extendedTemplate.indexOf(end) + end.length);
pattern.extendedTemplate = pattern.extendedTemplate.replace(repeatingBlock, repeatedBlockHtml);
- //update the extendedTemplate in the partials object in case this pattern is consumed later
- patternlab.partials[pattern.patternPartial] = pattern.extendedTemplate;
-
});
}
+
+ postprocessListItemPartials(pattern);
}
return {
process_list_item_partials: function (pattern, patternlab) {
processListItemPartials(pattern, patternlab);
- }
+ },
+ items: items
};
};
diff --git a/core/lib/object_factory.js b/core/lib/object_factory.js
index 74b62f39e..d7e125b1b 100644
--- a/core/lib/object_factory.js
+++ b/core/lib/object_factory.js
@@ -56,6 +56,9 @@ var Pattern = function (relPath, data) {
this.lineageRIndex = [];
this.isPseudoPattern = false;
this.engine = patternEngines.getEngineForPattern(this);
+ this.patternPartials = null;
+ this.allData = null;
+ this.dataKeys = [];
};
// Pattern methods
@@ -71,9 +74,9 @@ Pattern.prototype = {
return null;
},
- registerPartial: function () {
+ registerPartial: function (patternlab) {
if (this.engine && typeof this.engine.registerPartial === 'function') {
- this.engine.registerPartial(this);
+ this.engine.registerPartial(this, patternlab);
}
},
diff --git a/core/lib/parameter_hunter.js b/core/lib/parameter_hunter.js
deleted file mode 100644
index b2b9832e2..000000000
--- a/core/lib/parameter_hunter.js
+++ /dev/null
@@ -1,301 +0,0 @@
-"use strict";
-
-var parameter_hunter = function () {
-
- var extend = require('util')._extend,
- JSON5 = require('json5'),
- pa = require('./pattern_assembler'),
- smh = require('./style_modifier_hunter'),
- plutils = require('./utilities'),
- style_modifier_hunter = new smh(),
- pattern_assembler = new pa();
-
- /**
- * This function is really to accommodate the lax JSON-like syntax allowed by
- * Pattern Lab PHP for parameter submissions to partials. Unfortunately, no
- * easily searchable library was discovered for this. What we had to do was
- * write a custom script to crawl through the parameter string, and wrap the
- * keys and values in double-quotes as necessary.
- * The steps on a high-level are as follows:
- * * Further escape all escaped quotes and colons. Use the string
- * representation of their unicodes for this. This has the added bonus
- * of being interpreted correctly by JSON5.parse() without further
- * modification. This will be useful later in the function.
- * * Once escaped quotes are out of the way, we know the remaining quotes
- * are either key/value wrappers or wrapped within those wrappers. We know
- * that remaining commas and colons are either delimiters, or wrapped
- * within quotes to not be recognized as such.
- * * A do-while loop crawls paramString to write keys to a keys array and
- * values to a values array.
- * * Start by parsing the first key. Determine the type of wrapping quote,
- * if any.
- * * By knowing the open wrapper, we know that the next quote of that kind
- * (if the key is wrapped in quotes), HAS to be the close wrapper.
- * Similarly, if the key is unwrapped, we know the next colon HAS to be
- * the delimiter between key and value.
- * * Save the key to the keys array.
- * * Next, search for a value. It will either be the next block wrapped in
- * quotes, or a string of alphanumerics, decimal points, or minus signs.
- * * Save the value to the values array.
- * * The do-while loop truncates the paramString value while parsing. Its
- * condition for completion is when the paramString is whittled down to an
- * empty string.
- * * After the keys and values arrays are built, a for loop iterates through
- * them to build the final paramStringWellFormed string.
- * * No quote substitution had been done prior to this loop. In this loop,
- * all keys are ensured to be wrapped in double-quotes. String values are
- * also ensured to be wrapped in double-quotes.
- * * Unescape escaped unicodes except for double-quotes. Everything beside
- * double-quotes will be wrapped in double-quotes without need for escape.
- * * Return paramStringWellFormed.
- *
- * @param {string} pString
- * @returns {string} paramStringWellFormed
- */
- function paramToJson(pString) {
- var colonPos = -1;
- var keys = [];
- var paramString = pString; // to not reassign param
- var paramStringWellFormed;
- var quotePos = -1;
- var regex;
- var values = [];
- var wrapper;
-
- //replace all escaped double-quotes with escaped unicode
- paramString = paramString.replace(/\\"/g, '\\u0022');
-
- //replace all escaped single-quotes with escaped unicode
- paramString = paramString.replace(/\\'/g, '\\u0027');
-
- //replace all escaped colons with escaped unicode
- paramString = paramString.replace(/\\:/g, '\\u0058');
-
- //with escaped chars out of the way, crawl through paramString looking for
- //keys and values
- do {
-
- //check if searching for a key
- if (paramString[0] === '{' || paramString[0] === ',') {
- paramString = paramString.substring(1, paramString.length).trim();
-
- //search for end quote if wrapped in quotes. else search for colon.
- //everything up to that position will be saved in the keys array.
- switch (paramString[0]) {
-
- //need to search for end quote pos in case the quotes wrap a colon
- case '"':
- case '\'':
- wrapper = paramString[0];
- quotePos = paramString.indexOf(wrapper, 1);
- break;
-
- default:
- colonPos = paramString.indexOf(':');
- }
-
- if (quotePos > -1) {
- keys.push(paramString.substring(0, quotePos + 1).trim());
-
- //truncate the beginning from paramString and look for a value
- paramString = paramString.substring(quotePos + 1, paramString.length).trim();
-
- //unset quotePos
- quotePos = -1;
-
- } else if (colonPos > -1) {
- keys.push(paramString.substring(0, colonPos).trim());
-
- //truncate the beginning from paramString and look for a value
- paramString = paramString.substring(colonPos, paramString.length);
-
- //unset colonPos
- colonPos = -1;
-
- //if there are no more colons, and we're looking for a key, there is
- //probably a problem. stop any further processing.
- } else {
- paramString = '';
- break;
- }
- }
-
- //now, search for a value
- if (paramString[0] === ':') {
- paramString = paramString.substring(1, paramString.length).trim();
-
- //the only reason we're using regexes here, instead of indexOf(), is
- //because we don't know if the next delimiter is going to be a comma or
- //a closing curly brace. since it's not much of a performance hit to
- //use regexes as sparingly as here, and it's much more concise and
- //readable, we'll use a regex for match() and replace() instead of
- //performing conditional logic with indexOf().
- switch (paramString[0]) {
-
- //since a quote of same type as its wrappers would be escaped, and we
- //escaped those even further with their unicodes, it is safe to look
- //for wrapper pairs and conclude that their contents are values
- case '"':
- regex = /^"(.|\s)*?"/;
- break;
- case '\'':
- regex = /^'(.|\s)*?'/;
- break;
-
- //if there is no value wrapper, regex for alphanumerics, decimal
- //points, and minus signs for exponential notation.
- default:
- regex = /^[\w\-\.]*/;
- }
- values.push(paramString.match(regex)[0].trim());
-
- //truncate the beginning from paramString and continue either
- //looking for a key, or returning
- paramString = paramString.replace(regex, '').trim();
-
- //exit do while if the final char is '}'
- if (paramString === '}') {
- paramString = '';
- break;
- }
-
- //if there are no more colons, and we're looking for a value, there is
- //probably a problem. stop any further processing.
- } else {
- paramString = '';
- break;
- }
- } while (paramString);
-
- //build paramStringWellFormed string for JSON parsing
- paramStringWellFormed = '{';
- for (var i = 0; i < keys.length; i++) {
-
- //keys
- //replace single-quote wrappers with double-quotes
- if (keys[i][0] === '\'' && keys[i][keys[i].length - 1] === '\'') {
- paramStringWellFormed += '"';
-
- //any enclosed double-quotes must be escaped
- paramStringWellFormed += keys[i].substring(1, keys[i].length - 1).replace(/"/g, '\\"');
- paramStringWellFormed += '"';
- } else {
-
- //open wrap with double-quotes if no wrapper
- if (keys[i][0] !== '"' && keys[i][0] !== '\'') {
- paramStringWellFormed += '"';
-
- //this is to clean up vestiges from Pattern Lab PHP's escaping scheme.
- //F.Y.I. Pattern Lab PHP would allow special characters like question
- //marks in parameter keys so long as the key was unwrapped and the
- //special character escaped with a backslash. In Node, we need to wrap
- //those keys and unescape those characters.
- keys[i] = keys[i].replace(/\\/g, '');
- }
-
- paramStringWellFormed += keys[i];
-
- //close wrap with double-quotes if no wrapper
- if (keys[i][keys[i].length - 1] !== '"' && keys[i][keys[i].length - 1] !== '\'') {
- paramStringWellFormed += '"';
- }
- }
-
- //colon delimiter.
- paramStringWellFormed += ':';
-
- //values
- //replace single-quote wrappers with double-quotes
- if (values[i][0] === '\'' && values[i][values[i].length - 1] === '\'') {
- paramStringWellFormed += '"';
-
- //any enclosed double-quotes must be escaped
- paramStringWellFormed += values[i].substring(1, values[i].length - 1).replace(/"/g, '\\"');
- paramStringWellFormed += '"';
-
- //for everything else, just add the value however it's wrapped
- } else {
- paramStringWellFormed += values[i];
- }
-
- //comma delimiter
- if (i < keys.length - 1) {
- paramStringWellFormed += ',';
- }
- }
- paramStringWellFormed += '}';
-
- //unescape escaped unicode except for double-quotes
- paramStringWellFormed = paramStringWellFormed.replace(/\\u0027/g, '\'');
- paramStringWellFormed = paramStringWellFormed.replace(/\\u0058/g, ':');
-
- return paramStringWellFormed;
- }
-
- function findparameters(pattern, patternlab) {
-
- if (pattern.parameteredPartials && pattern.parameteredPartials.length > 0) {
-
- //compile this partial immeadiately, essentially consuming it.
- pattern.parameteredPartials.forEach(function (pMatch) {
- //find the partial's name and retrieve it
- var partialName = pMatch.match(/([\w\-\.\/~]+)/g)[0];
- var partialPattern = pattern_assembler.findPartial(partialName, patternlab);
-
- //if we retrieved a pattern we should make sure that its extendedTemplate is reset. looks to fix #190
- partialPattern.extendedTemplate = partialPattern.template;
-
- if (patternlab.config.debug) {
- console.log('found patternParameters for ' + partialName);
- }
-
- //strip out the additional data, convert string to JSON.
- var leftParen = pMatch.indexOf('(');
- var rightParen = pMatch.lastIndexOf(')');
- var paramString = '{' + pMatch.substring(leftParen + 1, rightParen) + '}';
- var paramStringWellFormed = paramToJson(paramString);
-
- var paramData = {};
- var globalData = {};
- var localData = {};
-
- try {
- paramData = JSON5.parse(paramStringWellFormed);
- globalData = JSON5.parse(JSON5.stringify(patternlab.data));
- localData = JSON5.parse(JSON5.stringify(pattern.jsonFileData || {}));
- } catch (err) {
- console.log('There was an error parsing JSON for ' + pattern.relPath);
- console.log(err);
- }
-
- var allData = plutils.mergeData(globalData, localData);
- allData = plutils.mergeData(allData, paramData);
-
- //if partial has style modifier data, replace the styleModifier value
- if (pattern.stylePartials && pattern.stylePartials.length > 0) {
- style_modifier_hunter.consume_style_modifier(partialPattern, pMatch, patternlab);
- }
-
- //extend pattern data links into link for pattern link shortcuts to work. we do this locally and globally
- allData.link = extend({}, patternlab.data.link);
-
- var renderedPartial = pattern_assembler.renderPattern(partialPattern.extendedTemplate, allData, patternlab.partials);
-
- //remove the parameter from the partial and replace it with the rendered partial + paramData
- pattern.extendedTemplate = pattern.extendedTemplate.replace(pMatch, renderedPartial);
-
- //update the extendedTemplate in the partials object in case this pattern is consumed later
- patternlab.partials[pattern.patternPartial] = pattern.extendedTemplate;
- });
- }
- }
-
- return {
- find_parameters: function (pattern, patternlab) {
- findparameters(pattern, patternlab);
- }
- };
-
-};
-
-module.exports = parameter_hunter;
diff --git a/core/lib/partial_hunter.js b/core/lib/partial_hunter.js
new file mode 100644
index 000000000..6dc17076b
--- /dev/null
+++ b/core/lib/partial_hunter.js
@@ -0,0 +1,116 @@
+"use strict";
+
+var partial_hunter = function () {
+
+ var smh = require('./style_modifier_hunter');
+ var style_modifier_hunter = new smh();
+
+ function replacePartials(pattern, patternlab) {
+ var dataKey;
+ var dataKeysRegex;
+ var escapedKeys = '';
+ var i;
+ var j;
+ var newTemplate = pattern.extendedTemplate;
+ var partialContent;
+ var partials;
+ var partialsUnique;
+ var pMatch;
+ var regex;
+ var regexStr;
+ var tag;
+ var tmpPattern;
+
+ if (!pattern.engine) {
+ return;
+ }
+
+ //escape all tags that match keys in the JSON data.
+ //it can be significantly faster to process large dataKey arrays in one read
+ //with a large regex than to read many times and process with small regexes.
+ //this is especially true with large templates.
+ //escape global data keys
+ escapedKeys = patternlab.dataKeysEscape;
+
+ //escape local data keys
+ for (i = 0; i < pattern.dataKeys.length; i++) {
+ dataKey = pattern.dataKeys[i];
+ if (typeof pattern.engine.escapeReservedRegexChars === 'function') {
+ dataKey = pattern.engine.escapeReservedRegexChars(dataKey);
+ }
+ if (i > 0) {
+ escapedKeys += '|';
+ }
+ escapedKeys += dataKey;
+ }
+
+ //apply replacement based on allowable characters from lines 78 and 79 of mustache.js
+ //of the Mustache for JS project.
+ dataKeysRegex = new RegExp('\\{\\{([\\{#\\^\\/&]?(\\s*|[^\\}]*\\.)(' + escapedKeys + ')\\s*)\\}\\}', 'g');
+
+ newTemplate = newTemplate.replace(dataKeysRegex, '\u0002$1}}');
+
+ //removing empty lines for some reason reduces rendering time considerably.
+ newTemplate = newTemplate.replace(/^\s*$\n/gm, '');
+
+ //escape partial includes so they are not erased by a render
+ newTemplate = newTemplate.replace(/\{\{>/g, '\u0002>');
+
+ //render this pattern immediately, so as to delete blocks not keyed to allData
+ newTemplate = pattern.engine.renderPattern(newTemplate, pattern.allData);
+
+ //unescape data keys and partial includes
+ newTemplate = newTemplate.replace(/\u0002/g, '{{');
+
+ //find all remaining partial tags
+ partials = pattern.engine.findPartials(newTemplate) || [];
+
+ //create array of unique elements so the tags can be use for global replace
+ partialsUnique = partials.filter(function (value, index, thisArray) {
+ return thisArray.indexOf(value) === index;
+ });
+
+ //replace remaining partials with their content
+ for (i = 0; i < partialsUnique.length; i++) {
+ pMatch = partialsUnique[i];
+
+ for (j in patternlab.partials) {
+ if (patternlab.partials.hasOwnProperty(j)) {
+ tag = j;
+ partialContent = patternlab.partials[j].content;
+
+ if (pMatch === tag) {
+
+ //check if this tag has any style modifiers
+ if (tag.search(pattern.engine.findPartialsWithStyleModifiersRE) > -1) {
+ //if so, add the style modifiers to partialContent
+ tmpPattern = {extendedTemplate: partialContent};
+ style_modifier_hunter.consume_style_modifier(tmpPattern, tag, patternlab);
+ partialContent = tmpPattern.extendedTemplate;
+ }
+
+ //we want to globally replace instances of this tag in case it was
+ //included within a partial from within this for loop
+ if (typeof pattern.engine.escapeReservedRegexChars === 'function') {
+ regexStr = pattern.engine.escapeReservedRegexChars(tag);
+ } else {
+ regexStr = tag;
+ }
+ regex = new RegExp(regexStr, 'g');
+ newTemplate = newTemplate.replace(regex, partialContent);
+ }
+ }
+ }
+ }
+
+ pattern.extendedTemplate = newTemplate;
+ }
+
+ return {
+ replace_partials: function (pattern, patternlab) {
+ replacePartials(pattern, patternlab);
+ }
+ };
+};
+
+module.exports = partial_hunter;
diff --git a/core/lib/pattern_assembler.js b/core/lib/pattern_assembler.js
index 8fc82ab78..2e5335ac5 100644
--- a/core/lib/pattern_assembler.js
+++ b/core/lib/pattern_assembler.js
@@ -9,8 +9,7 @@ var path = require('path'),
patternEngines = require('./pattern_engines'),
lh = require('./lineage_hunter'),
lih = require('./list_item_hunter'),
- smh = require('./style_modifier_hunter'),
- ph = require('./parameter_hunter'),
+ ph = require('./partial_hunter'),
JSON5 = require('json5');
var markdown_parser = new mp();
@@ -29,8 +28,8 @@ var pattern_assembler = function () {
//else look by verbose syntax
for (var i = 0; i < patternlab.patterns.length; i++) {
switch (partialName) {
+ case patternlab.patterns[i].relPath:
case patternlab.patterns[i].subdir + '/' + patternlab.patterns[i].fileName:
- case patternlab.patterns[i].subdir + '/' + patternlab.patterns[i].fileName + '.mustache':
return patternlab.patterns[i];
}
}
@@ -100,7 +99,6 @@ var pattern_assembler = function () {
if (pattern.relPath === patternlab.patterns[i].relPath) {
//if relPath already exists, overwrite that element
patternlab.patterns[i] = pattern;
- patternlab.partials[pattern.patternPartial] = pattern.extendedTemplate || pattern.template;
isNew = false;
break;
}
@@ -117,12 +115,8 @@ var pattern_assembler = function () {
if (pattern.isPattern) {
- patternlab.partials[pattern.patternPartial] = pattern.extendedTemplate || pattern.template;
-
// do plugin-specific registration
- pattern.registerPartial();
- } else {
- patternlab.partials[pattern.patternPartial] = pattern.patternDesc;
+ pattern.registerPartial(patternlab);
}
patternlab.patterns.push(pattern);
@@ -141,11 +135,20 @@ var pattern_assembler = function () {
if (pattern instanceof Pattern) {
return pattern.render(data, partials);
} else {
- // otherwise, assume it's a plain mustache template string, and we
- // therefore just need to create a dummpy pattern to be able to render
+ // otherwise, check for the first loaded templating engine, and we
+ // therefore just need to create a dummy pattern to be able to render
// it
var dummyPattern = Pattern.createEmpty({extendedTemplate: pattern});
- return patternEngines.mustache.renderPattern(dummyPattern, data, partials);
+ var engine;
+ var engineName = Object.keys(patternEngines)[0];
+ if (engineName) {
+ engine = patternEngines[engineName];
+ }
+ if (engine) {
+ return engine.renderPattern(dummyPattern, data, partials);
+ } else {
+ return pattern;
+ }
}
}
@@ -199,7 +202,36 @@ var pattern_assembler = function () {
}
}
+ /**
+ * Recursively get all the property keys from the JSON data for a pattern.
+ *
+ * @param {object} data
+ * @param {array} uniqueKeysParam The array of unique keys to be added to and returned.
+ * @returns {array} keys A flat, one-dimensional array.
+ */
+ function getDataKeys(data, uniqueKeysParam) {
+ var uniqueKeys = uniqueKeysParam || [];
+
+ for (var key in data) {
+ if (data.hasOwnProperty(key)) {
+ if (data.constructor !== Array) {
+ if (uniqueKeys.indexOf(key) === -1) {
+ uniqueKeys.push(key);
+ } else {
+ continue;
+ }
+ }
+ if (typeof data[key] === 'object') {
+ getDataKeys(data[key], uniqueKeys);
+ }
+ }
+ }
+
+ return uniqueKeys;
+ }
+
function processPatternIterative(relPath, patternlab) {
+ var list_item_hunter = new lih();
//check if the found file is a top-level markdown file
var fileObject = path.parse(relPath);
@@ -230,8 +262,6 @@ var pattern_assembler = function () {
}
- var pseudopattern_hunter = new pph();
-
//extract some information
var filename = fileObject.base;
var ext = fileObject.ext;
@@ -257,24 +287,31 @@ var pattern_assembler = function () {
setState(currentPattern, patternlab, true);
//look for a json file for this template
+ var jsonFilename = '';
+ var jsonFilenameStats;
+ var jsonFileStr = '';
try {
- var jsonFilename = path.resolve(patternsPath, currentPattern.subdir, currentPattern.fileName + ".json");
+ var jsonFilename = path.resolve(patternsPath, currentPattern.subdir, currentPattern.fileName + '.json');
+ var jsonFilenameStats = fs.statSync(jsonFilename);
+ } catch (err) {
+ //not a file
+ }
+
+ if (jsonFilenameStats && jsonFilenameStats.isFile()) {
try {
- var jsonFilenameStats = fs.statSync(jsonFilename);
- } catch (err) {
- //not a file
- }
- if (jsonFilenameStats && jsonFilenameStats.isFile()) {
- currentPattern.jsonFileData = fs.readJSONSync(jsonFilename);
+ jsonFileStr = fs.readFileSync(jsonFilename, 'utf8');
+ currentPattern.jsonFileData = JSON5.parse(jsonFileStr);
if (patternlab.config.debug) {
console.log('processPatternIterative: found pattern-specific data.json for ' + currentPattern.patternPartial);
}
+ } catch (err) {
+ console.log('There was an error parsing sibling JSON for ' + currentPattern.relPath);
+ console.log(err);
}
}
- catch (err) {
- console.log('There was an error parsing sibling JSON for ' + currentPattern.relPath);
- console.log(err);
- }
+
+ //add allData keys to currentPattern.dataKeys
+ currentPattern.dataKeys = getDataKeys(currentPattern.jsonFileData);
//look for a listitems.json file for this template
try {
@@ -302,33 +339,36 @@ var pattern_assembler = function () {
//add the raw template to memory
currentPattern.template = fs.readFileSync(path.resolve(patternsPath, relPath), 'utf8');
+ currentPattern.extendedTemplate = currentPattern.template;
+
+ //find any listItem blocks within the pattern
+ list_item_hunter.process_list_item_partials(currentPattern, patternlab);
//find any stylemodifiers that may be in the current pattern
currentPattern.stylePartials = currentPattern.findPartialsWithStyleModifiers();
- //find any pattern parameters that may be in the current pattern
- currentPattern.parameteredPartials = currentPattern.findPartialsWithPatternParameters();
-
//add currentPattern to patternlab.patterns array
addPattern(currentPattern, patternlab);
- //look for a pseudo pattern by checking if there is a file containing same name, with ~ in it, ending in .json
- pseudopattern_hunter.find_pseudopatterns(currentPattern, patternlab);
-
return currentPattern;
}
- function processPatternRecursive(file, patternlab) {
-
- var lineage_hunter = new lh(),
- list_item_hunter = new lih();
+ function processPatternRecursive(relPath, patternlab, origPatternParam, levelParam) {
+ var lineage_hunter = new lh();
+ var pseudopattern_hunter = new pph();
+ var currentPattern;
+ var i;
- //find current pattern in patternlab object using var file as a partial
- var currentPattern, i;
+ //find current pattern in patternlab object either as passed as a param
+ //or by identifying by relPath
+ if (origPatternParam) {
+ currentPattern = origPatternParam;
- for (i = 0; i < patternlab.patterns.length; i++) {
- if (patternlab.patterns[i].relPath === file) {
- currentPattern = patternlab.patterns[i];
+ } else {
+ for (i = 0; i < patternlab.patterns.length; i++) {
+ if (patternlab.patterns[i].relPath === relPath) {
+ currentPattern = patternlab.patterns[i];
+ }
}
}
@@ -338,74 +378,49 @@ var pattern_assembler = function () {
//we are processing a markdown only pattern
if (currentPattern.engine === null) { return; }
- currentPattern.extendedTemplate = currentPattern.template;
+ //merge global data into local data after iterating through all patterns
+ //but not after first recursion
+ if (!currentPattern.allData) {
+ currentPattern.allData = plutils.mergeData(patternlab.data, currentPattern.jsonFileData);
+ }
- //find how many partials there may be for the given pattern
- var foundPatternPartials = currentPattern.findPartials();
+ //look for a pseudo pattern by checking if there is a file containing same name, with ~ in it, ending in .json
+ //only do this at the top level of recursion
+ var level = levelParam ? levelParam : 0;
+ if (!level) {
+ pseudopattern_hunter.find_pseudopatterns(currentPattern, patternlab);
+ }
+ level++;
- //find any listItem blocks that within the pattern, even if there are no partials
- list_item_hunter.process_list_item_partials(currentPattern, patternlab);
+ //find how many partials there may be for the given pattern
+ currentPattern.patternPartials = currentPattern.findPartials();
- // expand any partials present in this pattern; that is, drill down into
- // the template and replace their calls in this template with rendered
- // results
+ //expand any partials present in this pattern; that is, drill down into the
+ //template and replace their calls in this template with rendered results
+ if (currentPattern.engine.expandPartials && (currentPattern.patternPartials !== null && currentPattern.patternPartials.length > 0)) {
+ //find pattern lineage
+ lineage_hunter.find_lineage(currentPattern, patternlab);
- if (currentPattern.engine.expandPartials && (foundPatternPartials !== null && foundPatternPartials.length > 0)) {
// eslint-disable-next-line
- expandPartials(foundPatternPartials, list_item_hunter, patternlab, currentPattern);
-
- // update the extendedTemplate in the partials object in case this
- // pattern is consumed later
- patternlab.partials[currentPattern.patternPartial] = currentPattern.extendedTemplate;
+ expandPartials(patternlab, currentPattern, level);
}
-
- //find pattern lineage
- lineage_hunter.find_lineage(currentPattern, patternlab);
-
- //add to patternlab object so we can look these up later.
- addPattern(currentPattern, patternlab);
}
- function expandPartials(foundPatternPartials, list_item_hunter, patternlab, currentPattern) {
+ function expandPartials(patternlab, currentPattern, level) {
- var style_modifier_hunter = new smh(),
- parameter_hunter = new ph();
+ var partial_hunter = new ph();
+ var list_item_hunter = new lih();
if (patternlab.config.debug) {
console.log('found partials for ' + currentPattern.patternPartial);
}
- // determine if the template contains any pattern parameters. if so they
- // must be immediately consumed
- parameter_hunter.find_parameters(currentPattern, patternlab);
-
- //do something with the regular old partials
- for (var i = 0; i < foundPatternPartials.length; i++) {
- var partial = currentPattern.findPartial(foundPatternPartials[i]);
- var partialPath;
-
- //identify which pattern this partial corresponds to
- for (var j = 0; j < patternlab.patterns.length; j++) {
- if (patternlab.patterns[j].patternPartial === partial ||
- patternlab.patterns[j].relPath.indexOf(partial) > -1)
- {
- partialPath = patternlab.patterns[j].relPath;
- }
- }
-
- //recurse through nested partials to fill out this extended template.
- processPatternRecursive(partialPath, patternlab);
+ partial_hunter.replace_partials(currentPattern, patternlab);
- //complete assembly of extended template
- var partialPattern = getPartial(partial, patternlab);
-
- //if partial has style modifier data, replace the styleModifier value
- if (currentPattern.stylePartials && currentPattern.stylePartials.length > 0) {
- style_modifier_hunter.consume_style_modifier(partialPattern, foundPatternPartials[i], patternlab);
- }
+ //find any listItem blocks within the pattern
+ list_item_hunter.process_list_item_partials(currentPattern, patternlab);
- currentPattern.extendedTemplate = currentPattern.extendedTemplate.replace(foundPatternPartials[i], partialPattern.extendedTemplate);
- }
+ processPatternRecursive(currentPattern.relPath, patternlab, currentPattern, level);
}
function parseDataLinksHelper(patternlab, obj, key) {
@@ -419,7 +434,6 @@ var pattern_assembler = function () {
for (var i = 0; i < linkMatches.length; i++) {
expandedLink = patternlab.data.link[linkMatches[i].split('.')[1]];
if (expandedLink) {
- expandedLink = expandedLink.replace('\\', '/');
if (patternlab.config.debug) {
console.log('expanded data link from ' + linkMatches[i] + ' to ' + expandedLink + ' inside ' + key);
}
@@ -497,6 +511,9 @@ var pattern_assembler = function () {
},
parse_pattern_markdown: function (pattern, patternlab) {
parsePatternMarkdown(pattern, patternlab);
+ },
+ get_data_keys: function (data, uniqueKeys) {
+ return getDataKeys(data, uniqueKeys);
}
};
diff --git a/core/lib/patternlab.js b/core/lib/patternlab.js
index d721de09f..9c3da14e3 100644
--- a/core/lib/patternlab.js
+++ b/core/lib/patternlab.js
@@ -53,6 +53,44 @@ function processAllPatternsIterative(pattern_assembler, patterns_dir, patternlab
);
}
+function preprocessPatternlabPartials(pattern_assembler, list_item_hunter, patternlab) {
+ // get the templating engine used for this implementation of pattern lab
+ var engine;
+ var i;
+ for (i = 0; i < patternlab.patterns.length; i++) {
+ if (patternlab.patterns[i].isPattern) {
+ engine = patternlab.patterns[i].engine;
+ break;
+ }
+ }
+
+ // push list item keywords into dataKeys property
+ for (i = 0; i < list_item_hunter.items.length; i++) {
+ patternlab.dataKeys.push('listItems.' + list_item_hunter.items[i]);
+ patternlab.dataKeys.push('listitems.' + list_item_hunter.items[i]);
+ }
+
+ // create properties to later escape all tags that match keys in the JSON
+ // data so these tags persist through recursive partial includes.
+ patternlab.dataKeysEscape = '';
+ var dataKey;
+ for (var i = 0; i < patternlab.dataKeys.length; i++) {
+ dataKey = patternlab.dataKeys[i];
+ if (engine && typeof engine.escapeReservedRegexChars === 'function') {
+ dataKey = engine.escapeReservedRegexChars(dataKey);
+ }
+ if (i > 0) {
+ patternlab.dataKeysEscape += '|';
+ }
+ patternlab.dataKeysEscape += dataKey;
+ }
+
+ // preprocess partials so they can be recursively included respecting any parameters they may be submitting
+ if (engine && typeof engine.preprocessPartials === 'function') {
+ engine.preprocessPartials(pattern_assembler, patternlab);
+ }
+}
+
function processAllPatternsRecursive(pattern_assembler, patterns_dir, patternlab) {
diveSync(
patterns_dir,
@@ -80,11 +118,11 @@ function processAllPatternsRecursive(pattern_assembler, patterns_dir, patternlab
var patternlab_engine = function (config) {
'use strict';
- var JSON5 = require('json5'),
- fs = require('fs-extra'),
+ var fs = require('fs-extra'),
pa = require('./pattern_assembler'),
pe = require('./pattern_exporter'),
lh = require('./lineage_hunter'),
+ lih = require('./list_item_hunter'),
buildFrontEnd = require('./ui_builder'),
plutils = require('./utilities'),
sm = require('./starterkit_manager'),
@@ -195,12 +233,23 @@ var patternlab_engine = function (config) {
}
function buildPatterns(deletePatternDir) {
+ var list_item_hunter = new lih();
+ var pattern_assembler = new pa();
+
try {
patternlab.data = buildPatternData(paths.source.data, fs);
} catch (ex) {
plutils.logRed('missing or malformed' + paths.source.data + 'data.json Pattern Lab may not work without this file.');
patternlab.data = {};
}
+ try {
+ //create this properties to later escape all tags that match keys in the
+ //JSON data so these tags persist through recursive partial includes.
+ patternlab.dataKeys = pattern_assembler.get_data_keys(patternlab.data);
+ } catch (ex) {
+ plutils.logRed(ex);
+ patternlab.dataKeys = [];
+ }
try {
patternlab.listitems = fs.readJSONSync(path.resolve(paths.source.data, 'listitems.json'));
} catch (ex) {
@@ -235,6 +284,8 @@ var patternlab_engine = function (config) {
// diveSync once to perform iterative populating of patternlab object
processAllPatternsIterative(pattern_assembler, patterns_dir, patternlab);
+ preprocessPatternlabPartials(pattern_assembler, list_item_hunter, patternlab);
+
//diveSync again to recursively include partials, filling out the
//extendedTemplate property of the patternlab.patterns elements
processAllPatternsRecursive(pattern_assembler, patterns_dir, patternlab);
@@ -301,21 +352,13 @@ var patternlab_engine = function (config) {
pattern.patternLineageRExists = pattern.lineageR.length > 0;
pattern.patternLineageEExists = pattern.patternLineageExists || pattern.patternLineageRExists;
- //render the pattern, but first consolidate any data we may have
- var allData;
- try {
- allData = JSON5.parse(JSON5.stringify(patternlab.data));
- } catch (err) {
- console.log('There was an error parsing JSON for ' + pattern.relPath);
- console.log(err);
- }
- allData = plutils.mergeData(allData, pattern.jsonFileData);
- allData.cacheBuster = patternlab.cacheBuster;
+ //set cacheBuster property
+ pattern.allData.cacheBuster = patternlab.cacheBuster;
- var headHTML = pattern_assembler.renderPattern(pattern.header, allData);
+ var headHTML = pattern_assembler.renderPattern(pattern.header, pattern.allData);
//render the extendedTemplate with all data
- pattern.patternPartialCode = pattern_assembler.renderPattern(pattern, allData);
+ pattern.patternPartialCode = pattern_assembler.renderPattern(pattern, pattern.allData);
//todo see if this is still needed
//pattern.patternPartialCodeE = entity_encoder.encode(pattern.patternPartialCode);
@@ -415,6 +458,7 @@ var patternlab_engine = function (config) {
// have a better way to do this
patternlab_engine.build_pattern_data = buildPatternData;
patternlab_engine.process_all_patterns_iterative = processAllPatternsIterative;
+patternlab_engine.preprocess_patternlab_partials = preprocessPatternlabPartials;
patternlab_engine.process_all_patterns_recursive = processAllPatternsRecursive;
module.exports = patternlab_engine;
diff --git a/core/lib/pseudopattern_hunter.js b/core/lib/pseudopattern_hunter.js
index eb8753be2..f7718e686 100644
--- a/core/lib/pseudopattern_hunter.js
+++ b/core/lib/pseudopattern_hunter.js
@@ -9,8 +9,8 @@ var pseudopattern_hunter = function () {
lh = require('./lineage_hunter'),
Pattern = require('./object_factory').Pattern,
plutils = require('./utilities'),
- path = require('path');
-
+ path = require('path'),
+ JSON5 = require('json5');
var pattern_assembler = new pa();
var lineage_hunter = new lh();
@@ -32,25 +32,36 @@ var pseudopattern_hunter = function () {
}
//we want to do everything we normally would here, except instead read the pseudoPattern data
+ var variantFilename = path.resolve(paths.source.patterns, pseudoPatterns[i]);
+ var variantFileStr = '';
+ var variantLocalData = {};
+ var variantAllData = {};
try {
- var variantFileData = fs.readJSONSync(path.resolve(paths.source.patterns, pseudoPatterns[i]));
+ variantFileStr = fs.readFileSync(variantFilename, 'utf8');
+ variantLocalData = JSON5.parse(variantFileStr);
+
+ //clone. do not reference
+ variantAllData = JSON5.parse(variantFileStr);
} catch (err) {
console.log('There was an error parsing pseudopattern JSON for ' + currentPattern.relPath);
console.log(err);
}
//extend any existing data with variant data
- variantFileData = plutils.mergeData(currentPattern.jsonFileData, variantFileData);
+ plutils.mergeData(currentPattern.jsonFileData, variantLocalData);
+ plutils.mergeData(currentPattern.allData, variantAllData);
var variantName = pseudoPatterns[i].substring(pseudoPatterns[i].indexOf('~') + 1).split('.')[0];
var variantFilePath = path.join(currentPattern.subdir, currentPattern.fileName + '~' + variantName + '.json');
- var patternVariant = Pattern.create(variantFilePath, variantFileData, {
+ var patternVariant = Pattern.create(variantFilePath, variantLocalData, {
//use the same template as the non-variant
template: currentPattern.template,
fileExtension: currentPattern.fileExtension,
extendedTemplate: currentPattern.extendedTemplate,
isPseudoPattern: true,
basePattern: currentPattern,
+ allData: variantAllData,
+ dataKeys: pattern_assembler.get_data_keys(variantLocalData),
// use the same template engine as the non-variant
engine: currentPattern.engine
diff --git a/core/lib/style_modifier_hunter.js b/core/lib/style_modifier_hunter.js
index 8c0b7413f..4bef7460a 100644
--- a/core/lib/style_modifier_hunter.js
+++ b/core/lib/style_modifier_hunter.js
@@ -16,9 +16,6 @@ var style_modifier_hunter = function () {
//replace the stylemodifier placeholder with the class name
pattern.extendedTemplate = pattern.extendedTemplate.replace(/{{[ ]?styleModifier[ ]?}}/i, styleModifier);
-
- //update the extendedTemplate in the partials object in case this pattern is consumed later
- patternlab.partials[pattern.patternPartial] = pattern.extendedTemplate;
}
}
diff --git a/test/engine_mustache_tests.js b/test/engine_mustache_tests.js
index da811be0e..cfc28454b 100644
--- a/test/engine_mustache_tests.js
+++ b/test/engine_mustache_tests.js
@@ -42,7 +42,8 @@ function testFindPartials(test, partialTests) {
'01-molecules/00-testing/00-test-mol.mustache', // relative path now
null, // data
{
- template: partialTests.join(eol)
+ template: partialTests.join(eol),
+ extendedTemplate: partialTests.join(eol)
}
);
@@ -68,7 +69,8 @@ function testFindPartialsWithStyleModifiers(test, partialTests) {
'01-molecules/00-testing/00-test-mol.mustache', // relative path now
null, // data
{
- template: partialTests.join(eol)
+ template: partialTests.join(eol),
+ extendedTemplate: partialTests.join(eol)
}
);
@@ -94,7 +96,8 @@ function testFindPartialsWithPatternParameters(test, partialTests) {
'01-molecules/00-testing/00-test-mol.mustache', // relative path now
null, // data
{
- template: partialTests.join(eol)
+ template: partialTests.join(eol),
+ extendedTemplate: partialTests.join(eol)
}
);
diff --git a/test/files/_patterns/01-test1/00-simple.mustache b/test/files/_patterns/01-test1/00-simple.mustache
new file mode 100644
index 000000000..6d5a89f8a
--- /dev/null
+++ b/test/files/_patterns/01-test1/00-simple.mustache
@@ -0,0 +1 @@
+{{ title }}
diff --git a/test/files/_patterns/01-test1/01-listitem-partial.mustache b/test/files/_patterns/01-test1/01-listitem-partial.mustache
new file mode 100644
index 000000000..41c3b013e
--- /dev/null
+++ b/test/files/_patterns/01-test1/01-listitem-partial.mustache
@@ -0,0 +1 @@
+{{#listItems.two}}{{> test1-simple }}{{/listItems.two}}
diff --git a/test/files/_patterns/01-test1/02-listitem-verbose-foo.mustache b/test/files/_patterns/01-test1/02-listitem-verbose-foo.mustache
new file mode 100644
index 000000000..b4e988359
--- /dev/null
+++ b/test/files/_patterns/01-test1/02-listitem-verbose-foo.mustache
@@ -0,0 +1 @@
+{{#listItems.one}}{{> 00-test/00-foo.mustache }}{{/listItems.one}}
diff --git a/test/files/_patterns/01-test1/03-listitem-verbose-bar.mustache b/test/files/_patterns/01-test1/03-listitem-verbose-bar.mustache
new file mode 100644
index 000000000..a6d988fa5
--- /dev/null
+++ b/test/files/_patterns/01-test1/03-listitem-verbose-bar.mustache
@@ -0,0 +1 @@
+{{#listItems.two}}{{> 00-test/01-bar.mustache }}{{/listItems.two}}
diff --git a/test/files/_patterns/01-test1/04-parametered-partial.mustache b/test/files/_patterns/01-test1/04-parametered-partial.mustache
new file mode 100644
index 000000000..c6bf814e8
--- /dev/null
+++ b/test/files/_patterns/01-test1/04-parametered-partial.mustache
@@ -0,0 +1,7 @@
+{{> test1-simple(title: 'foo') }}
+{{# foo }}
+ {{> test1-parametered-partial(bar: true) }}
+{{/ foo }}
+{{# bar }}
+ {{> test1-simple(title: 'bar') }}
+{{/ bar }}
diff --git a/test/files/_patterns/01-test1/05-recursive-includer.mustache b/test/files/_patterns/01-test1/05-recursive-includer.mustache
new file mode 100644
index 000000000..2c970de51
--- /dev/null
+++ b/test/files/_patterns/01-test1/05-recursive-includer.mustache
@@ -0,0 +1 @@
+{{> test1-parametered-partial }}
diff --git a/test/files/_patterns/01-test1/06-anti-infinity-tester.mustache b/test/files/_patterns/01-test1/06-anti-infinity-tester.mustache
new file mode 100644
index 000000000..f29b81561
--- /dev/null
+++ b/test/files/_patterns/01-test1/06-anti-infinity-tester.mustache
@@ -0,0 +1 @@
+{{> test1-parametered-partial(foo: true) }}
diff --git a/test/lineage_hunter_tests.js b/test/lineage_hunter_tests.js
index f689d049b..4445f98bd 100644
--- a/test/lineage_hunter_tests.js
+++ b/test/lineage_hunter_tests.js
@@ -49,6 +49,7 @@ exports['lineage hunter '] = {
);
extend(currentPattern, {
"template": "\r\n\tSearch\r\n\tMenu\r\n\r\n
",
+ "extendedTemplate": "
",
"patternPartialCode": "
",
"patternBaseName": "logo",
"patternLink": "00-atoms-03-images-00-logo/00-atoms-03-images-00-logo.html",
@@ -79,6 +81,7 @@ exports['lineage hunter '] = {
"filename": "00-primary-nav.mustache",
"data": null,
"template": "\r\n",
+ "extendedTemplate": "\r\n",
"patternPartialCode": "\r\n",
"patternBaseName": "primary-nav",
"patternLink": "01-molecules-05-navigation-00-primary-nav/01-molecules-05-navigation-00-primary-nav.html",
@@ -98,6 +101,7 @@ exports['lineage hunter '] = {
"filename": "00-search.mustache",
"data": null,
"template": "
{{description}}
", - "extendedTemplate": "{{description}}
" - } - ], - config: { - debug: false - }, - data: { - description: 'Not a quote from a smart man' - }, - partials: {} - } - }; - - exports['parameter_hunter'] = { - 'parameter hunter finds and extends templates' : function(test){ - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.
'); - - test.done(); - }, - - 'parameter hunter finds and extends templates with mixed parameter and global data' : function(test){ - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - patternlab.patterns[0].template = "{{description}}
"; - patternlab.patterns[0].extendedTemplate = patternlab.patterns[0].template; - patternlab.data.foo = 'Bar'; - patternlab.data.description = 'Baz'; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.
'); - - test.done(); - }, - - 'parameter hunter finds and extends templates with verbose partials' : function(test){ - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - currentPattern.template = "{{> 01-molecules/06-components/02-single-comment(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = "{{> 01-molecules/06-components/02-single-comment(description: 'We are all in the gutter, but some of us are looking at the stars.') }}"; - currentPattern.parameteredPartials[1] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.
'); - - test.done(); - }, - - 'parameter hunter finds and extends templates with fully-pathed partials' : function(test){ - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - currentPattern.template = "{{> 01-molecules/06-components/02-single-comment.mustache(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = "{{> 01-molecules/06-components/02-single-comment.mustache(description: 'We are all in the gutter, but some of us are looking at the stars.') }}"; - currentPattern.parameteredPartials[1] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.
'); - - test.done(); - }, - - //previous tests were for unquoted parameter keys and single-quoted values. - //test other quoting options. - 'parameter hunter parses parameters with unquoted keys and unquoted values' : function(test){ - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - currentPattern.template = "{{> molecules-single-comment(description: true) }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'true
'); - - test.done(); - }, - - 'parameter hunter parses parameters with unquoted keys and double-quoted values' : function(test){ - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - currentPattern.template = "{{> molecules-single-comment(description: \"true\") }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'true
'); - - test.done(); - }, - - 'parameter hunter parses parameters with single-quoted keys and unquoted values' : function(test){ - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - currentPattern.template = "{{> molecules-single-comment('description': true) }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'true
'); - - test.done(); - }, - - 'parameter hunter parses parameters with single-quoted keys and single-quoted values wrapping internal escaped single-quotes' : function(test){ - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - currentPattern.template = "{{> molecules-single-comment('description': 'true not,\\'true\\'') }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'true not,'true'
'); - - test.done(); - }, - - 'parameter hunter parses parameters with single-quoted keys and double-quoted values wrapping internal single-quotes' : function(test){ - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - currentPattern.template = "{{> molecules-single-comment('description': \"true not:'true'\") }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'true not:'true'
'); - - test.done(); - }, - - 'parameter hunter parses parameters with double-unquoted keys and unquoted values' : function(test){ - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - currentPattern.template = "{{> molecules-single-comment(\"description\": true) }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'true
'); - - test.done(); - }, - - 'parameter hunter parses parameters with double-quoted keys and single-quoted values wrapping internal double-quotes' : function(test){ - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - currentPattern.template = "{{> molecules-single-comment(\"description\": 'true not{\"true\"') }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'true not{"true"
'); - - test.done(); - }, - - 'parameter hunter parses parameters with double-quoted keys and double-quoted values wrapping internal escaped double-quotes' : function(test){ - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - currentPattern.template = "{{> molecules-single-comment(\"description\": \"true not}\\\"true\\\"\") }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'true not}"true"
'); - - test.done(); - }, - - 'parameter hunter parses parameters with combination of quoting schemes for keys and values' : function(test){ - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - currentPattern.template = "{{> molecules-single-comment(description: true, 'foo': false, \"bar\": false, 'single': true, 'singlesingle': 'true', 'singledouble': \"true\", \"double\": true, \"doublesingle\": 'true', \"doubledouble\": \"true\") }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'true
'); - - test.done(); - }, - - 'parameter hunter parses parameters with values containing a closing parenthesis' : function(test){ - // From issue #291 https://github.com/pattern-lab/patternlab-node/issues/291 - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - currentPattern.template = "{{> molecules-single-comment(description: 'Hello ) World') }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'Hello ) World
'); - - test.done(); - }, - - 'parameter hunter parses parameters that follow a non-quoted value' : function(test){ - // From issue #291 https://github.com/pattern-lab/patternlab-node/issues/291 - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - patternlab.patterns[0].template = "{{foo}}
{{bar}}
"; - patternlab.patterns[0].extendedTemplate = patternlab.patterns[0].template; - - currentPattern.template = "{{> molecules-single-comment(foo: true, bar: \"Hello World\") }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'true
Hello World
'); - - test.done(); - }, - - 'parameter hunter parses parameters whose keys contain escaped quotes' : function(test){ - // From issue #291 https://github.com/pattern-lab/patternlab-node/issues/291 - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - patternlab.patterns[0].template = "{{ silly'key }}
{{bar}}
{{ another\"silly-key }}
"; - patternlab.patterns[0].extendedTemplate = patternlab.patterns[0].template; - - currentPattern.template = "{{> molecules-single-comment('silly\\\'key': true, bar: \"Hello World\", \"another\\\"silly-key\": 42 ) }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'true
Hello World
42
'); - - test.done(); - }, - - 'parameter hunter skips malformed parameters' : function(test){ - // From issue #291 https://github.com/pattern-lab/patternlab-node/issues/291 - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - patternlab.patterns[0].template = "{{foo}}
"; - patternlab.patterns[0].extendedTemplate = patternlab.patterns[0].template; - - currentPattern.abspath = __filename; - currentPattern.template = "{{> molecules-single-comment( missing-val: , : missing-key, : , , foo: \"Hello World\") }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - console.log('\nPattern Lab should catch JSON.parse() errors and output useful debugging information...'); - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, ''); - - test.done(); - }, - - 'parameter hunter parses parameters containing html tags' : function(test){ - // From issue #145 https://github.com/pattern-lab/patternlab-node/issues/145 - var currentPattern = currentPatternClosure(); - var patternlab = patternlabClosure(); - var parameter_hunter = new ph(); - - patternlab.patterns[0].template = "{{{ tag1 }}}
{{{ tag2 }}}
{{{ tag3 }}}
"; - patternlab.patterns[0].extendedTemplate = patternlab.patterns[0].template; - - currentPattern.template = "{{> molecules-single-comment(tag1: 'Single-quoted', tag2: \"Double-quoted\", tag3: 'With attributes') }}"; - currentPattern.extendedTemplate = currentPattern.template; - currentPattern.parameteredPartials[0] = currentPattern.template; - - parameter_hunter.find_parameters(currentPattern, patternlab); - test.equals(currentPattern.extendedTemplate, 'Single-quoted
Double-quoted
With attributes
'); - - test.done(); - } - - - }; - -}()); diff --git a/test/partial_hunter_tests.js b/test/partial_hunter_tests.js new file mode 100644 index 000000000..9199e131a --- /dev/null +++ b/test/partial_hunter_tests.js @@ -0,0 +1,486 @@ +(function () { + "use strict"; + + var lih = require('../core/lib/list_item_hunter'); + var pa = require('../core/lib/pattern_assembler'); + var ph = require('../core/lib/partial_hunter'); + var Pattern = require('../core/lib/object_factory').Pattern; + var patternEngine = require('../core/lib/patternlab'); + + //setup current pattern from what we would have during execution + function currentPatternClosure() { + return Pattern.createEmpty({ + "relPath": "02-organisms/02-comments/01-sticky-comment.mustache", + "fileName": "01-sticky-comment", + "subdir": "02-organisms/02-comments", + "name": "02-organisms-02-comments-01-sticky-comment", + "patternBaseName": "sticky-comment", + "patternLink": "02-organisms-02-comments-01-sticky-comment/02-organisms-02-comments-01-sticky-comment.html", + "patternGroup": "organisms", + "patternSubGroup": "comments", + "flatPatternPath": "02-organisms-02-comments", + "patternPartial": "organisms-sticky-comment", + "template": "{{> molecules-single-comment(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}", + "extendedTemplate": "{{> molecules-single-comment(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}" + }); + } + + function patternlabClosure() { + return { + patterns: [ + { + "relPath": "01-molecules/06-components/02-single-comment.mustache", + "fileName": "02-single-comment", + "subdir": "01-molecules/06-components", + "name": "01-molecules-06-components-02-single-comment", + "patternBaseName": "single-comment", + "patternLink": "01-molecules-06-components-02-single-comment/01-molecules-06-components-02-single-comment.html", + "patternGroup": "molecules", + "patternSubGroup": "components", + "flatPatternPath": "01-molecules-06-components", + "patternPartial": "molecules-single-comment", + "template": "{{description}}
", + "extendedTemplate": "{{description}}
" + } + ], + config: { + debug: false + }, + data: { + description: 'Not a quote from a smart man' + }, + partials: {} + } + }; + + exports['partial_hunter'] = { + 'partial hunter finds and extends templates' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.
'); + + test.done(); + }, + + 'partial hunter finds and extends templates with global data' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.template = "{{> molecules-single-comment }}"; + currentPattern.extendedTemplate = currentPattern.template; + patternlab.patterns[0].template = "{{description}}
"; + patternlab.patterns[0].extendedTemplate = patternlab.patterns[0].template; + currentPattern.allData = { + foo: 'Bar', + description: 'Baz' + }; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + currentPattern.extendedTemplate = pattern_assembler.renderPattern(currentPattern, currentPattern.allData); + test.equals(currentPattern.extendedTemplate, 'Baz
'); + + test.done(); + }, + + 'partial hunter finds and extends templates with mixed parameter and global data' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + patternlab.patterns[0].template = "{{description}}
"; + patternlab.patterns[0].extendedTemplate = patternlab.patterns[0].template; + currentPattern.allData = { + foo: 'Bar', + description: 'Baz' + }; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + currentPattern.extendedTemplate = pattern_assembler.renderPattern(currentPattern, currentPattern.allData); + test.equals(currentPattern.extendedTemplate, 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.
'); + + test.done(); + }, + + 'partial hunter finds and extends templates with verbose partials' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.template = "{{> 01-molecules/06-components/02-single-comment(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.
'); + + test.done(); + }, + + 'partial hunter finds and extends templates with fully-pathed partials' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.template = "{{> 01-molecules/06-components/02-single-comment.mustache(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.
'); + + test.done(); + }, + + //previous tests were for unquoted parameter keys and single-quoted values. + //test other quoting options. + 'partial hunter parses parameters with unquoted keys and unquoted values' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.template = "{{> molecules-single-comment(description: true) }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'true
'); + + test.done(); + }, + + 'partial hunter parses parameters with unquoted keys and double-quoted values' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.template = "{{> molecules-single-comment(description: \"true\") }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'true
'); + + test.done(); + }, + + 'partial hunter parses parameters with single-quoted keys and unquoted values' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.template = "{{> molecules-single-comment('description': true) }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'true
'); + + test.done(); + }, + + 'partial hunter parses parameters with single-quoted keys and single-quoted values wrapping internal escaped single-quotes' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.template = "{{> molecules-single-comment('description': 'true not,\\'true\\'') }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'true not,'true'
'); + + test.done(); + }, + + 'partial hunter parses parameters with single-quoted keys and double-quoted values wrapping internal single-quotes' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.template = "{{> molecules-single-comment('description': \"true not:'true'\") }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'true not:'true'
'); + + test.done(); + }, + + 'partial hunter parses parameters with double-unquoted keys and unquoted values' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.template = "{{> molecules-single-comment(\"description\": true) }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'true
'); + + test.done(); + }, + + 'partial hunter parses parameters with double-quoted keys and single-quoted values wrapping internal double-quotes' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.template = "{{> molecules-single-comment(\"description\": 'true not{\"true\"') }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'true not{"true"
'); + + test.done(); + }, + + 'partial hunter parses parameters with double-quoted keys and double-quoted values wrapping internal escaped double-quotes' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.template = "{{> molecules-single-comment(\"description\": \"true not}\\\"true\\\"\") }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'true not}"true"
'); + + test.done(); + }, + + 'partial hunter parses parameters with combination of quoting schemes for keys and values' : function(test){ + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.template = "{{> molecules-single-comment(description: true, 'foo': false, \"bar\": false, 'single': true, 'singlesingle': 'true', 'singledouble': \"true\", \"double\": true, \"doublesingle\": 'true', \"doubledouble\": \"true\") }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'true
'); + + test.done(); + }, + + 'partial hunter parses parameters with values containing a closing parenthesis' : function(test){ + // From issue #291 https://github.com/pattern-lab/patternlab-node/issues/291 + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + currentPattern.template = "{{> molecules-single-comment(description: 'Hello ) World') }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'Hello ) World
'); + + test.done(); + }, + + 'partial hunter parses parameters that follow a non-quoted value' : function(test){ + // From issue #291 https://github.com/pattern-lab/patternlab-node/issues/291 + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + patternlab.patterns[0].template = "{{foo}}
{{bar}}
"; + patternlab.patterns[0].extendedTemplate = patternlab.patterns[0].template; + + currentPattern.template = "{{> molecules-single-comment(foo: true, bar: \"Hello World\") }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'true
Hello World
'); + + test.done(); + }, + + 'partial hunter parses parameters whose keys contain escaped quotes' : function(test){ + // From issue #291 https://github.com/pattern-lab/patternlab-node/issues/291 + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + patternlab.patterns[0].template = "{{ silly'key }}
{{bar}}
{{ another\"silly-key }}
"; + patternlab.patterns[0].extendedTemplate = patternlab.patterns[0].template; + + currentPattern.template = "{{> molecules-single-comment('silly\\\'key': true, bar: \"Hello World\", \"another\\\"silly-key\": 42 ) }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'true
Hello World
42
'); + + test.done(); + }, + + 'partial hunter skips malformed parameters' : function(test){ + // From issue #291 https://github.com/pattern-lab/patternlab-node/issues/291 + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + patternlab.patterns[0].template = "{{foo}}
"; + patternlab.patterns[0].extendedTemplate = patternlab.patterns[0].template; + + currentPattern.abspath = __filename; + currentPattern.template = "{{> molecules-single-comment( missing-val: , : missing-key, : , , foo: \"Hello World\") }}"; + currentPattern.extendedTemplate = currentPattern.template; + + console.log('\nPattern Lab should catch JSON.parse() errors and output useful debugging information...'); + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + currentPattern.extendedTemplate = pattern_assembler.renderPattern(currentPattern.extendedTemplate, patternlab.data); + test.equals(currentPattern.extendedTemplate, ''); + + test.done(); + }, + + 'partial hunter parses parameters containing html tags' : function(test){ + // From issue #145 https://github.com/pattern-lab/patternlab-node/issues/145 + var currentPattern = currentPatternClosure(); + var patternlab = patternlabClosure(); + var pattern_assembler = new pa(); + var partial_hunter = new ph(); + + patternlab.patterns[0].template = "{{{ tag1 }}}
{{{ tag2 }}}
{{{ tag3 }}}
"; + patternlab.patterns[0].extendedTemplate = patternlab.patterns[0].template; + + currentPattern.template = "{{> molecules-single-comment(tag1: 'Single-quoted', tag2: \"Double-quoted\", tag3: 'With attributes') }}"; + currentPattern.extendedTemplate = currentPattern.template; + + currentPattern.registerPartial(patternlab); + currentPattern.engine.preprocessPartials(pattern_assembler, patternlab); + partial_hunter.replace_partials(currentPattern, patternlab); + test.equals(currentPattern.extendedTemplate, 'Single-quoted
Double-quoted
With attributes
'); + + test.done(); + }, + + 'partial hunter correctly parses partial parameters for recursion beyond a single level' : function(test){ + var list_item_hunter = new lih(); + var pattern_assembler = new pa(); + var patterns_dir = './test/files/_patterns'; + + // this test utilizes pattern_assembler for the heavy lifting, but the actual code being tested resides inside pattern_hunter.js + //arrange + var pl = {}; + pl.config = { + debug: false, + paths: { + source: { + patterns: patterns_dir + } + } + }; + pl.data = { + link: {} + } + pl.dataKeys = []; + pl.partials = {}; + pl.patterns = []; + + var pattern1 = pattern_assembler.process_pattern_iterative("01-test1/05-recursive-includer.mustache", pl); + var pattern2 = pattern_assembler.process_pattern_iterative("01-test1/04-parametered-partial.mustache", pl); + var pattern3 = pattern_assembler.process_pattern_iterative("01-test1/00-simple.mustache", pl); + patternEngine.preprocess_patternlab_partials(pattern_assembler, list_item_hunter, pl); + pattern_assembler.process_pattern_recursive(pattern1.relPath, pl, pattern1); + pattern_assembler.process_pattern_recursive(pattern2.relPath, pl, pattern2); + pattern_assembler.process_pattern_recursive(pattern3.relPath, pl, pattern3); + + //assert. + var expectedValue = 'foo'; + test.equals(pattern1.extendedTemplate.trim(), expectedValue); + test.done(); + }, + + 'partial hunter correctly limits recursion on partials that call themselves but within restricted conditions' : function(test){ + var list_item_hunter = new lih(); + var pattern_assembler = new pa(); + var patterns_dir = './test/files/_patterns'; + + // this test utilizes pattern_assembler for the heavy lifting, but the actual code being tested resides inside pattern_hunter.js + //arrange + var pl = {}; + pl.config = { + debug: false, + paths: { + source: { + patterns: patterns_dir + } + } + }; + pl.data = { + link: {} + } + pl.dataKeys = []; + pl.partials = {}; + pl.patterns = []; + + var pattern1 = pattern_assembler.process_pattern_iterative("01-test1/06-anti-infinity-tester.mustache", pl); + var pattern2 = pattern_assembler.process_pattern_iterative("01-test1/04-parametered-partial.mustache", pl); + var pattern3 = pattern_assembler.process_pattern_iterative("01-test1/00-simple.mustache", pl); + patternEngine.preprocess_patternlab_partials(pattern_assembler, list_item_hunter, pl); + pattern_assembler.process_pattern_recursive(pattern1.relPath, pl, pattern1); + pattern_assembler.process_pattern_recursive(pattern2.relPath, pl, pattern2); + pattern_assembler.process_pattern_recursive(pattern3.relPath, pl, pattern3); + + //assert. + var expectedValue = 'foo\n foo\n\n bar'; + test.equals(pattern1.extendedTemplate.trim(), expectedValue); + test.done(); + } + }; + +}()); diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js index b0fa5e4a0..4b2f7d7d4 100644 --- a/test/pattern_assembler_tests.js +++ b/test/pattern_assembler_tests.js @@ -1,494 +1,426 @@ (function () { - "use strict"; + "use strict"; - var pa = require('../core/lib/pattern_assembler'); + var pa = require('../core/lib/pattern_assembler'); var Pattern = require('../core/lib/object_factory').Pattern; var path = require('path'); - exports['pattern_assembler'] = { - 'process_pattern_recursive recursively includes partials' : function(test){ + exports['pattern_assembler'] = { + 'process_pattern_recursive recursively includes partials' : function(test){ test.expect(3); - //tests inclusion of partial that will be discovered by diveSync later in iteration than parent - //prepare to diveSync - var diveSync = require('diveSync'); - var fs = require('fs-extra'); - var pa = require('../core/lib/pattern_assembler'); + //tests inclusion of partial that will be discovered by diveSync later in iteration than parent + //prepare to diveSync + var fs = require('fs-extra'); var plMain = require('../core/lib/patternlab'); - var pattern_assembler = new pa(); - var patterns_dir = './test/files/_patterns'; - var patternlab = {}; - patternlab.config = fs.readJSONSync('./patternlab-config.json'); - patternlab.config.paths.source.patterns = patterns_dir; + var pattern_assembler = new pa(); + var patterns_dir = './test/files/_patterns'; + var patternlab = {}; + patternlab.config = fs.readJSONSync('./patternlab-config.json'); + patternlab.config.paths.source.patterns = patterns_dir; - //patternlab.data = fs.readJSONSync(path.resolve(patternlab.config.paths.source.data, 'data.json')); + //patternlab.data = fs.readJSONSync(path.resolve(patternlab.config.paths.source.data, 'data.json')); patternlab.data = {}; - //patternlab.listitems = fs.readJSONSync(path.resolve(patternlab.config.paths.source.data, 'listitems.json')); + //patternlab.listitems = fs.readJSONSync(path.resolve(patternlab.config.paths.source.data, 'listitems.json')); patternlab.listitems = {}; - //patternlab.header = fs.readFileSync(path.resolve(patternlab.config.paths.source.patternlabFiles, 'templates/pattern-header-footer/header.html'), 'utf8'); + //patternlab.header = fs.readFileSync(path.resolve(patternlab.config.paths.source.patternlabFiles, 'templates/pattern-header-footer/header.html'), 'utf8'); patternlab.header = ''; - //patternlab.footer = fs.readFileSync(path.resolve(patternlab.config.paths.source.patternlabFiles, 'templates/pattern-header-footer/footer.html'), 'utf8'); + //patternlab.footer = fs.readFileSync(path.resolve(patternlab.config.paths.source.patternlabFiles, 'templates/pattern-header-footer/footer.html'), 'utf8'); patternlab.footer = ''; - patternlab.patterns = []; - patternlab.data.link = {}; - patternlab.partials = {}; - - //diveSync once to perform iterative populating of patternlab object - plMain.process_all_patterns_iterative(pattern_assembler, patterns_dir, patternlab); - - //diveSync again to recursively include partials, filling out the - //extendedTemplate property of the patternlab.patterns elements - plMain.process_all_patterns_recursive(pattern_assembler, patterns_dir, patternlab); - - //get test output for comparison - var foo = fs.readFileSync(patterns_dir + '/00-test/00-foo.mustache', 'utf8').trim(); - var bar = fs.readFileSync(patterns_dir + '/00-test/01-bar.mustache', 'utf8').trim(); - var fooExtended; - - //get extended pattern - for(var i = 0; i < patternlab.patterns.length; i++){ - if(patternlab.patterns[i].fileName === '00-foo'){ - fooExtended = patternlab.patterns[i].extendedTemplate.trim(); - break; - } - } - - //check initial values - test.equals(foo, '{{> test-bar }}'); - test.equals(bar, 'bar'); - //test that 00-foo.mustache included partial 01-bar.mustache - test.equals(fooExtended, 'bar'); - - test.done(); - }, - 'processPatternRecursive - correctly replaces all stylemodifiers when multiple duplicate patterns with different stylemodifiers found' : function(test){ - //arrange - var fs = require('fs-extra'); - var pattern_assembler = new pa(); - var patterns_dir = './test/files/_patterns'; - - var pl = {}; - pl.config = { - paths: { - source: { - patterns: patterns_dir - } - } - }; - pl.data = {}; - pl.data.link = {}; - pl.config.debug = false; - pl.patterns = []; - pl.partials = {}; - - var atomPattern = new Pattern('00-test/03-styled-atom.mustache'); - atomPattern.template = fs.readFileSync(patterns_dir + '/00-test/03-styled-atom.mustache', 'utf8'); - atomPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(atomPattern); - - var groupPattern = new Pattern('00-test/04-group.mustache'); - groupPattern.template = fs.readFileSync(patterns_dir + '/00-test/04-group.mustache', 'utf8'); - groupPattern.stylePartials = pattern_assembler.find_pattern_partials_with_style_modifiers(groupPattern); - - pattern_assembler.addPattern(atomPattern, pl); - pattern_assembler.addPattern(groupPattern, pl); - - //act - - pattern_assembler.process_pattern_recursive('00-test' + path.sep + '04-group.mustache', pl, {}); - - //assert - var expectedValue = '