From 4ff84d00bd15089e4b43432a669c3ac7f10101c9 Mon Sep 17 00:00:00 2001 From: Maximilian Franzke <787658+mfranzke@users.noreply.github.com> Date: Mon, 29 May 2023 15:40:11 +0200 Subject: [PATCH] refactor: remove parameter hunter (#1467) * refactor: remove parameter hunter * refactor: remove parameter hunter * fix: issue in mustache runable * docs: adapted documentation --------- Co-authored-by: Josef Bredreck <13408112+JosefBredereck@users.noreply.github.com> Co-authored-by: Josef Bredreck --- packages/core/src/lib/expandPartials.js | 80 ++-- packages/core/src/lib/parameter_hunter.js | 339 -------------- packages/core/test/parameter_hunter_tests.js | 462 ------------------- packages/docs/src/docs/pattern-parameters.md | 95 +--- 4 files changed, 35 insertions(+), 941 deletions(-) delete mode 100644 packages/core/src/lib/parameter_hunter.js delete mode 100644 packages/core/test/parameter_hunter_tests.js diff --git a/packages/core/src/lib/expandPartials.js b/packages/core/src/lib/expandPartials.js index d9f7fad8e..e4976fe8b 100644 --- a/packages/core/src/lib/expandPartials.js +++ b/packages/core/src/lib/expandPartials.js @@ -1,12 +1,10 @@ 'use strict'; const logger = require('./log'); -const ph = require('./parameter_hunter'); const jsonCopy = require('./json_copy'); const getPartial = require('./get'); -const parameter_hunter = new ph(); - +// TODO: remove when removing mustache module.exports = function (currentPattern, patternlab) { const processRecursive = require('./processRecursive'); @@ -23,56 +21,46 @@ module.exports = function (currentPattern, patternlab) { ) { logger.debug(`found partials for ${currentPattern.patternPartial}`); - // determine if the template contains any pattern parameters. if so they - // must be immediately consumed - return parameter_hunter - .find_parameters(currentPattern, patternlab) - .then(() => { - //do something with the regular old partials - foundPatternPartials.forEach((foundPartial) => { - const partial = currentPattern.findPartial(foundPartial); - const partialPattern = getPartial(partial, patternlab); + //do something with the regular old partials + foundPatternPartials.forEach((foundPartial) => { + const partial = currentPattern.findPartial(foundPartial); + const partialPattern = getPartial(partial, patternlab); - //recurse through nested partials to fill out this extended template. - return processRecursive(partialPattern.relPath, patternlab) - .then(() => { - //eslint-disable-line no-loop-func + //recurse through nested partials to fill out this extended template. + return processRecursive(partialPattern.relPath, patternlab) + .then(() => { + //eslint-disable-line no-loop-func - //complete assembly of extended template - //create a copy of the partial so as to not pollute it after the getPartial call. - const cleanPartialPattern = jsonCopy( - partialPattern, - `partial pattern ${partial}` - ); + //complete assembly of extended template + //create a copy of the partial so as to not pollute it after the getPartial call. + const cleanPartialPattern = jsonCopy( + partialPattern, + `partial pattern ${partial}` + ); - //this is what we came here for - logger.debug( - `within ${currentPattern.patternPartial}, replacing extendedTemplate partial ${foundPartial} with ${cleanPartialPattern.patternPartial}'s extendedTemplate` - ); + //this is what we came here for + logger.debug( + `within ${currentPattern.patternPartial}, replacing extendedTemplate partial ${foundPartial} with ${cleanPartialPattern.patternPartial}'s extendedTemplate` + ); - currentPattern.extendedTemplate = - currentPattern.extendedTemplate.replace( - foundPartial, - cleanPartialPattern.extendedTemplate - ); + currentPattern.extendedTemplate = + currentPattern.extendedTemplate.replace( + foundPartial, + cleanPartialPattern.extendedTemplate + ); - // update the extendedTemplate in the partials object in case this - // pattern is consumed later - patternlab.partials[currentPattern.patternPartial] = - currentPattern.extendedTemplate; + // update the extendedTemplate in the partials object in case this + // pattern is consumed later + patternlab.partials[currentPattern.patternPartial] = + currentPattern.extendedTemplate; - return Promise.resolve(); - }) - .catch((reason) => { - console.log(reason); - logger.error(reason); - }); + return Promise.resolve(); + }) + .catch((reason) => { + console.log(reason); + logger.error(reason); }); - }) - .catch((reason) => { - console.log(reason); - logger.error(reason); - }); + }); } return Promise.resolve(); }; diff --git a/packages/core/src/lib/parameter_hunter.js b/packages/core/src/lib/parameter_hunter.js deleted file mode 100644 index 01a260ffc..000000000 --- a/packages/core/src/lib/parameter_hunter.js +++ /dev/null @@ -1,339 +0,0 @@ -'use strict'; - -const getPartial = require('./get'); -const logger = require('./log'); -const parseLink = require('./parseLink'); -const jsonCopy = require('./json_copy'); -const replaceParameter = require('./replaceParameter'); - -const parameter_hunter = function () { - /** - * 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 JSON.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) { - let colonPos = -1; - const keys = []; - let paramString = pString; // to not reassign param - let paramStringWellFormed; - let quotePos = -1; - let regex; - const values = []; - let wrapper; - - // attempt to parse the data in case it is already well formed JSON - try { - paramStringWellFormed = JSON.stringify(JSON.parse(pString)); - return paramStringWellFormed; - } catch (err) { - logger.debug( - `Not valid JSON found for passed pattern parameter ${pString} will attempt to parse manually...` - ); - } - - //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 (let 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; - } - - //compile this partial immeadiately, essentially consuming it. - function findparameters(pattern, patternlab) { - if (pattern.parameteredPartials && pattern.parameteredPartials.length > 0) { - logger.debug(`processing patternParameters for ${pattern.patternName}`); - - return pattern.parameteredPartials.reduce((previousPromise, pMatch) => { - return previousPromise - .then(() => { - logger.debug(`processing patternParameter ${pMatch}`); - - //find the partial's name and retrieve it - const partialName = pMatch.match(/([\w\-\.\/~]+)/g)[0]; - const partialPattern = jsonCopy( - getPartial( - partialName, - patternlab, - `partial pattern ${partialName}` - ) - ); - - //if we retrieved a pattern we should make sure that its extendedTemplate is reset. looks to fix #190 - if (!partialPattern.extendedTemplate) { - partialPattern.extendedTemplate = partialPattern.template; - } - - if (!pattern.extendedTemplate) { - pattern.extendedTemplate = pattern.template; - } - - logger.debug(`retrieved pattern ${partialName}`); - - //strip out the additional data, convert string to JSON. - const leftParen = pMatch.indexOf('('); - const rightParen = pMatch.lastIndexOf(')'); - const paramString = - '{' + pMatch.substring(leftParen + 1, rightParen) + '}'; - const paramStringWellFormed = paramToJson(paramString); - - let paramData = {}; - - try { - paramData = JSON.parse(paramStringWellFormed); - } catch (err) { - logger.warning( - `There was a problem parsing JSON parameters for ${pattern.relPath}` - ); - logger.warning(err); - } - - // resolve any pattern links that might be present - paramData = parseLink( - patternlab, - paramData, - pattern.patternPartial - ); - - // for each property in paramData - for (const prop in paramData) { - if (paramData.hasOwnProperty(prop)) { - // find it within partialPattern.extendedTemplate and replace its value - partialPattern.extendedTemplate = replaceParameter( - partialPattern.extendedTemplate, - prop, - paramData[prop] - ); - } - } - - // set pattern.extendedTemplate pMatch with replacedPartial - pattern.extendedTemplate = pattern.extendedTemplate.replace( - pMatch, - partialPattern.extendedTemplate - ); - - //todo: this no longer needs to be a promise - return Promise.resolve(); - }) - .catch((reason) => { - console.log(reason); - logger.error(reason); - }); - }, Promise.resolve()); - } - logger.debug(`pattern has no partials ${pattern.patternPartial}`); - return Promise.resolve(); - } - - return { - find_parameters: function (pattern, patternlab) { - return findparameters(pattern, patternlab); - }, - }; -}; - -module.exports = parameter_hunter; diff --git a/packages/core/test/parameter_hunter_tests.js b/packages/core/test/parameter_hunter_tests.js deleted file mode 100644 index b19314d10..000000000 --- a/packages/core/test/parameter_hunter_tests.js +++ /dev/null @@ -1,462 +0,0 @@ -'use strict'; - -const path = require('path'); -const util = require('./util/test_utils.js'); -const tap = require('tap'); - -const loadPattern = require('../src/lib/loadPattern'); -const ph = require('../src/lib/parameter_hunter'); -const processIterative = require('../src/lib/processIterative'); - -const parameter_hunter = new ph(); - -const config = require('./util/patternlab-config.json'); -const engineLoader = require('../src/lib/pattern_engines'); -engineLoader.loadAllEngines(config); - -const testPatternsPath = path.resolve(__dirname, 'files', '_patterns'); - -tap.test('parameter hunter finds and extends templates', function (test) { - //arrange - const pl = util.fakePatternLab(testPatternsPath); - - var commentPath = path.join('test', 'comment.mustache'); - var commentPattern = loadPattern(commentPath, pl); - - var testPatternPath = path.join('test', 'sticky-comment.mustache'); - var testPattern = loadPattern(testPatternPath, pl); - - var p1 = processIterative(commentPattern, pl); - var p2 = processIterative(testPattern, pl); - - Promise.all([p1, p2]) - .then(() => { - //act - parameter_hunter - .find_parameters(testPattern, pl) - .then(() => { - //assert - test.equal( - util.sanitized(testPattern.extendedTemplate), - util.sanitized( - '

{{foo}}

A life is like a garden. Perfect moments can be had, but not preserved, except in memory.

' - ) - ); - test.end(); - }) - .catch(test.threw); - }) - .catch(test.threw); -}); - -tap.test( - 'parameter hunter finds and extends templates with verbose partials', - function (test) { - //arrange - const pl = util.fakePatternLab(testPatternsPath); - - var commentPath = path.join('test', 'comment.mustache'); - var commentPattern = loadPattern(commentPath, pl); - - var testPatternPath = path.join('test', 'sticky-comment-verbose.mustache'); - var testPattern = loadPattern(testPatternPath, pl); - - var p1 = processIterative(commentPattern, pl); - var p2 = processIterative(testPattern, pl); - - Promise.all([p1, p2]) - .then(() => { - //act - parameter_hunter - .find_parameters(testPattern, pl) - .then(() => { - //assert - test.equal( - util.sanitized(testPattern.extendedTemplate), - util.sanitized( - '

{{foo}}

A life is like a garden. Perfect moments can be had, but not preserved, except in memory.

' - ) - ); - test.end(); - }) - .catch(test.threw); - }) - .catch(test.threw); - } -); - -//previous tests were for unquoted parameter keys and single-quoted values. -//test other quoting options. -tap.test( - 'parameter hunter parses parameters with unquoted keys and unquoted values', - function (test) { - //arrange - const pl = util.fakePatternLab(testPatternsPath); - - var commentPath = path.join('test', 'comment.mustache'); - var commentPattern = loadPattern(commentPath, pl); - - var testPatternPath = path.join('test', 'sticky-comment.mustache'); - var testPattern = loadPattern(testPatternPath, pl); - - //override the file - testPattern.template = '{{> test-comment(description: true) }}'; - testPattern.extendedTemplate = testPattern.template; - testPattern.parameteredPartials[0] = testPattern.template; - - var p1 = processIterative(commentPattern, pl); - var p2 = processIterative(testPattern, pl); - - Promise.all([p1, p2]).then(() => { - //act - parameter_hunter.find_parameters(testPattern, pl).then(() => { - //assert - test.equal( - util.sanitized(testPattern.extendedTemplate), - util.sanitized('

{{foo}}

true

') - ); - test.end(); - }); - }); - } -); - -tap.test( - 'parameter hunter parses parameters with unquoted keys and double-quoted values', - function (test) { - //arrange - const pl = util.fakePatternLab(testPatternsPath); - - var commentPath = path.join('test', 'comment.mustache'); - var commentPattern = loadPattern(commentPath, pl); - - var testPatternPath = path.join('test', 'sticky-comment.mustache'); - var testPattern = loadPattern(testPatternPath, pl); - - //override the file - testPattern.template = '{{> test-comment(description: "true") }}'; - testPattern.extendedTemplate = testPattern.template; - testPattern.parameteredPartials[0] = testPattern.template; - - var p1 = processIterative(commentPattern, pl); - var p2 = processIterative(testPattern, pl); - - Promise.all([p1, p2]).then(() => { - //act - parameter_hunter.find_parameters(testPattern, pl).then(() => { - //assert - test.equal( - util.sanitized(testPattern.extendedTemplate), - util.sanitized('

{{foo}}

true

') - ); - test.end(); - }); - }); - } -); - -tap.test( - 'parameter hunter parses parameters with single-quoted keys and unquoted values', - function (test) { - //arrange - const pl = util.fakePatternLab(testPatternsPath); - - var commentPath = path.join('test', 'comment.mustache'); - var commentPattern = loadPattern(commentPath, pl); - - var testPatternPath = path.join('test', 'sticky-comment.mustache'); - var testPattern = loadPattern(testPatternPath, pl); - - //override the file - testPattern.template = "{{> test-comment('description': true) }}"; - testPattern.extendedTemplate = testPattern.template; - testPattern.parameteredPartials[0] = testPattern.template; - - var p1 = processIterative(commentPattern, pl); - var p2 = processIterative(testPattern, pl); - - Promise.all([p1, p2]).then(() => { - //act - parameter_hunter.find_parameters(testPattern, pl).then(() => { - //assert - test.equal( - util.sanitized(testPattern.extendedTemplate), - util.sanitized('

{{foo}}

true

') - ); - test.end(); - }); - }); - } -); - -tap.test( - 'parameter hunter parses parameters with single-quoted keys and single-quoted values wrapping internal escaped single-quotes', - function (test) { - //arrange - const pl = util.fakePatternLab(testPatternsPath); - - var commentPath = path.join('test', 'comment.mustache'); - var commentPattern = loadPattern(commentPath, pl); - - var testPatternPath = path.join('test', 'sticky-comment.mustache'); - var testPattern = loadPattern(testPatternPath, pl); - - //override the file - testPattern.template = - "{{> test-comment('description': 'true not,\\'true\\'') }}"; - testPattern.extendedTemplate = testPattern.template; - testPattern.parameteredPartials[0] = testPattern.template; - - var p1 = processIterative(commentPattern, pl); - var p2 = processIterative(testPattern, pl); - - Promise.all([p1, p2]).then(() => { - //act - parameter_hunter.find_parameters(testPattern, pl).then(() => { - //assert - test.equal( - util.sanitized(testPattern.extendedTemplate), - util.sanitized(`

{{foo}}

true not,'true'

`) - ); - test.end(); - }); - }); - } -); - -tap.test( - 'parameter hunter parses parameters with single-quoted keys and double-quoted values wrapping internal single-quotes', - function (test) { - //arrange - const pl = util.fakePatternLab(testPatternsPath); - - var commentPath = path.join('test', 'comment.mustache'); - var commentPattern = loadPattern(commentPath, pl); - - var testPatternPath = path.join('test', 'sticky-comment.mustache'); - var testPattern = loadPattern(testPatternPath, pl); - - //override the file - testPattern.template = - "{{> test-comment('description': \"true not:'true'\") }}"; - testPattern.extendedTemplate = testPattern.template; - testPattern.parameteredPartials[0] = testPattern.template; - - var p1 = processIterative(commentPattern, pl); - var p2 = processIterative(testPattern, pl); - - Promise.all([p1, p2]).then(() => { - //act - parameter_hunter.find_parameters(testPattern, pl).then(() => { - //assert - test.equal( - util.sanitized(testPattern.extendedTemplate), - util.sanitized(`

{{foo}}

true not:'true'

`) - ); - test.end(); - }); - }); - } -); - -tap.test( - 'parameter hunter parses parameters with double-unquoted keys and unquoted values', - function (test) { - //arrange - const pl = util.fakePatternLab(testPatternsPath); - - var commentPath = path.join('test', 'comment.mustache'); - var commentPattern = loadPattern(commentPath, pl); - - var testPatternPath = path.join('test', 'sticky-comment.mustache'); - var testPattern = loadPattern(testPatternPath, pl); - - //override the file - testPattern.template = '{{> test-comment("description": true) }}'; - testPattern.extendedTemplate = testPattern.template; - testPattern.parameteredPartials[0] = testPattern.template; - - var p1 = processIterative(commentPattern, pl); - var p2 = processIterative(testPattern, pl); - - Promise.all([p1, p2]).then(() => { - //act - parameter_hunter.find_parameters(testPattern, pl).then(() => { - //assert - test.equal( - util.sanitized(testPattern.extendedTemplate), - util.sanitized('

{{foo}}

true

') - ); - test.end(); - }); - }); - } -); - -tap.test( - 'parameter hunter parses parameters with double-quoted keys and single-quoted values wrapping internal double-quotes', - function (test) { - //arrange - const pl = util.fakePatternLab(testPatternsPath); - - var commentPath = path.join('test', 'comment.mustache'); - var commentPattern = loadPattern(commentPath, pl); - - var testPatternPath = path.join('test', 'sticky-comment.mustache'); - var testPattern = loadPattern(testPatternPath, pl); - - //override the file - testPattern.template = - '{{> test-comment("description": \'true not{"true"\') }}'; - testPattern.extendedTemplate = testPattern.template; - testPattern.parameteredPartials[0] = testPattern.template; - - var p1 = processIterative(commentPattern, pl); - var p2 = processIterative(testPattern, pl); - - Promise.all([p1, p2]).then(() => { - //act - parameter_hunter.find_parameters(testPattern, pl).then(() => { - //assert - test.equal( - util.sanitized(testPattern.extendedTemplate), - util.sanitized('

{{foo}}

true not{"true"

') - ); - test.end(); - }); - }); - } -); - -tap.test( - 'parameter hunter parses parameters with double-quoted keys and double-quoted values wrapping internal escaped double-quotes', - function (test) { - //arrange - const pl = util.fakePatternLab(testPatternsPath); - - var commentPath = path.join('test', 'comment.mustache'); - var commentPattern = loadPattern(commentPath, pl); - - var testPatternPath = path.join('test', 'sticky-comment.mustache'); - var testPattern = loadPattern(testPatternPath, pl); - - //override the file - testPattern.template = - '{{> test-comment("description": "true not}\\"true\\"") }}'; - testPattern.extendedTemplate = testPattern.template; - testPattern.parameteredPartials[0] = testPattern.template; - - var p1 = processIterative(commentPattern, pl); - var p2 = processIterative(testPattern, pl); - - Promise.all([p1, p2]).then(() => { - //act - parameter_hunter.find_parameters(testPattern, pl).then(() => { - //assert - test.equal( - util.sanitized(testPattern.extendedTemplate), - util.sanitized('

{{foo}}

true not}"true"

') - ); - test.end(); - }); - }); - } -); - -tap.test( - 'parameter hunter parses parameters with combination of quoting schemes for keys and values', - function (test) { - //arrange - const pl = util.fakePatternLab(testPatternsPath); - - var commentPath = path.join('test', 'comment.mustache'); - var commentPattern = loadPattern(commentPath, pl); - - var testPatternPath = path.join('test', 'sticky-comment.mustache'); - var testPattern = loadPattern(testPatternPath, pl); - - //override the file - testPattern.template = - '{{> test-comment(description: true, \'foo\': false, "bar": false, \'single\': true, \'singlesingle\': \'true\', \'singledouble\': "true", "double": true, "doublesingle": \'true\', "doubledouble": "true") }}'; - testPattern.extendedTemplate = testPattern.template; - testPattern.parameteredPartials[0] = testPattern.template; - - var p1 = processIterative(commentPattern, pl); - var p2 = processIterative(testPattern, pl); - - Promise.all([p1, p2]).then(() => { - //act - parameter_hunter.find_parameters(testPattern, pl).then(() => { - //assert - test.equal( - util.sanitized(testPattern.extendedTemplate), - util.sanitized('

false

true

') - ); - test.end(); - }); - }); - } -); - -//todo https://github.com/pattern-lab/patternlab-node/issues/673 -// tap.test('parameter hunter parses parameters with values containing a closing parenthesis', function (test) { -// //arrange -// const pl = util.fakePatternLab(testPatternsPath); - -// var commentPath = path.join('test', 'comment.mustache'); -// var commentPattern = loadPattern(commentPath, pl); - -// var testPatternPath = path.join('test', 'sticky-comment.mustache'); -// var testPattern = loadPattern(testPatternPath, pl); - -// //override the file -// testPattern.template = "{{> test-comment(description: 'Hello ) World') }}"; -// testPattern.extendedTemplate = testPattern.template; -// testPattern.parameteredPartials[0] = testPattern.template; - -// var p1 = processIterative(commentPattern, pl); -// var p2 = processIterative(testPattern, pl); - -// Promise.all([p1, p2]).then(() => { -// //act -// parameter_hunter.find_parameters(testPattern, pl).then(() => { -// //assert -// test.equal(util.sanitized(testPattern.extendedTemplate), util.sanitized('

Hello ) World

')); -// test.end(); -// }); -// }); -// }); - -tap.test('parameter hunter skips malformed parameters', function (test) { - const pl = util.fakePatternLab(testPatternsPath); - - var commentPath = path.join('test', 'comment.mustache'); - var commentPattern = loadPattern(commentPath, pl); - - var testPatternPath = path.join('test', 'sticky-comment.mustache'); - var testPattern = loadPattern(testPatternPath, pl); - - //override the file - testPattern.template = - '{{> test-comment( missing-val: , : missing-key, : , , foo: "Hello World") }}'; - testPattern.extendedTemplate = testPattern.template; - testPattern.parameteredPartials[0] = testPattern.template; - - var p1 = processIterative(commentPattern, pl); - var p2 = processIterative(testPattern, pl); - - Promise.all([p1, p2]).then(() => { - //act - parameter_hunter.find_parameters(testPattern, pl).then(() => { - //assert - console.log( - '\nPattern Lab should catch JSON.parse() errors and output useful debugging information...' - ); - test.equal( - util.sanitized(testPattern.extendedTemplate), - util.sanitized('

{{foo}}

{{description}}

') - ); - test.end(); - }); - }); -}); diff --git a/packages/docs/src/docs/pattern-parameters.md b/packages/docs/src/docs/pattern-parameters.md index 5b040e926..c41130647 100644 --- a/packages/docs/src/docs/pattern-parameters.md +++ b/packages/docs/src/docs/pattern-parameters.md @@ -11,97 +11,4 @@ sitemapPriority: '0.8' sitemapChangefreq: 'monthly' --- -**Important:** Pattern parameters are supported by the Node Mustache PatternEngines. Other template languages provide better solutions to this problem. - -Pattern parameters are a **simple** mechanism for replacing Mustache variables in an included pattern. They are limited to replacing variables in the included pattern and **only** the included pattern. They are especially useful when including a single pattern multiple times in a molecule, template, or page and you want to supply unique data to that pattern each time it's included. Pattern parameters **do not** currently support the following: - -- sub-lists (_e.g. iteration of a section_), -- long strings of text (_can be unwieldy_) -- modifying/replacing variables in patterns included _within_ the targeted pattern - -Pattern parameters are Pattern Lab-specific, have no relationship to Mustache, and are implemented outside of Mustache. Learn more about pattern parameters: - -- [The Pattern Parameter Syntax](#pattern-parameter-syntax) -- [Adding Pattern Parameters to Your Pattern Partial](#adding-pattern-parameters) -- [Toggling Sections with Pattern Parameters](#toggling-sections) - -## The Pattern Parameter Syntax - -The attributes listed in the pattern parameters need to match Mustache variable names in your pattern. The values listed for each attribute will replace the Mustache variables. For example: - - {% raw %}{{> patternGroup-pattern(attribute1: value, attribute2: "value string") }}{% endraw %} - -Again, pattern parameters are a simple find and replace of Mustache variables with the supplied values. - -## Adding Pattern Parameters to Your Pattern Partial - -Let's look at a simple example for how we might use pattern parameters. First we'll set-up a pattern that might be included multiple times. We'll make it a simple "message" pattern with a single Mustache variable of `message`. - -```html -
{% raw %}{{ message }}{% endraw %}
-``` - -We'll organize it under Atoms > Global and call it "message" so it'll have the pattern partial of `atoms-message`. - -Now let's create a pattern that includes our message pattern partial multiple times. It might look like this. - -```html -
- {% raw %}{{> atoms-message }}{% endraw %} -
- A bunch of extra information -
- {% raw %}{{> atoms-message }}{% endraw %} -
-``` - -Using `data.json` or a pattern-specific JSON file we wouldn't be able to supply separate messages to each pattern partial. For example, if we defined `message` in our `data.json` as "this is an alert" then "this is an alert" would show up twice when our parent pattern was rendered. - -Instead we can use pattern parameters to supply unique messages for each instance. So let's show what that would look like: - -```html -{% raw %} -
- {{> atoms-message(message: "this is an alert message") }} -
- A bunch of extra information -
- {{> atoms-message(message: "this is an informational message") }} -
-{% endraw %} -``` - -Now each pattern would have its very own message. - -## Toggling Sections with Pattern Parameters - -While pattern parameters are not a one-to-one match for Mustache they do offer the ability to toggle sections of content. For example we might have the following in a generic header pattern called `organisms-header`: - -```html -{% raw %} -
- {{# emergency }} -
Emergency!!!
- {{/ emergency }} -
- ... stuff ... -
-
-{% endraw %} -``` - -We call the header pattern in a template like so: - -``` -{% raw %}{{> organisms-header }}{% endraw %} -... stuff ... -``` - -By default, if we don't have an `emergency` attribute in our `data.json` or the pattern-specific JSON file for the template the emergency alert will never be rendered. Instead of modifying either of those two files we can use a boolean pattern param to show it instead like so: - -``` -{% raw %}{{> organisms-header(emergency: true) }}{% endraw %} -... stuff ... -``` - -Again, because pattern parameters aren't leveraging Mustache this may not fit the normal Mustache workflow. We still wanted to offer a way to quickly turn on and off sections of an included pattern. +Passing parameters parameters to included patterns are a **simple** mechanism for replacing variables in an included pattern, by each of the standard ways of how to do it either in handlebars ([Partial Parameters](https://handlebarsjs.com/guide/partials.html#partial-parameters)) or twig ([include with `with` keyword](https://twig.symfony.com/doc/3.x/tags/include.html#:~:text=You%20can%20add%20additional%20variables%20by%20passing%20them%20after%20the%20with%20keyword%3A)) template language. They are limited to replacing variables in the included pattern and **only** the included pattern. They are especially useful when including a single pattern multiple times in a molecule, template, or page and you want to supply unique data to that pattern each time it's included.