diff --git a/.travis.yml b/.travis.yml index 90d48e964..c185e08d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,6 @@ branches: only: - master - dev - - dev-2.0-core notifications: webhooks: diff --git a/README.md b/README.md index 067e6369b..63dfc975b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[](https://travis-ci.org/pattern-lab/patternlab-node)   [](https://gitter.im/pattern-lab/node) +[](https://travis-ci.org/pattern-lab/patternlab-node)   [](https://gitter.im/pattern-lab/node) # Pattern Lab Node Core diff --git a/core/lib/lineage_hunter.js b/core/lib/lineage_hunter.js index 2c5fb0112..e74e4a6c9 100644 --- a/core/lib/lineage_hunter.js +++ b/core/lib/lineage_hunter.js @@ -13,7 +13,7 @@ var lineage_hunter = function () { if (matches !== null) { matches.forEach(function (match) { //get the ancestorPattern - var ancestorPattern = pattern_assembler.findPartial(pattern.findPartial(match), patternlab); + var ancestorPattern = pattern_assembler.getPartial(pattern.findPartial(match), patternlab); if (ancestorPattern && pattern.lineageIndex.indexOf(ancestorPattern.patternPartial) === -1) { //add it since it didnt exist @@ -83,7 +83,7 @@ var lineage_hunter = function () { //find all lineage - patterns being consumed by this one for (var h = 0; h < pattern.lineageIndex.length; h++) { - var lineagePattern = pattern_assembler.findPartial(pattern.lineageIndex[h], patternlab); + var lineagePattern = pattern_assembler.getPartial(pattern.lineageIndex[h], patternlab); setPatternState('fromFuture', lineagePattern, pattern); } } @@ -93,7 +93,7 @@ var lineage_hunter = function () { //find all reverse lineage - that is, patterns consuming this one for (var j = 0; j < pattern.lineageRIndex.length; j++) { - var lineageRPattern = pattern_assembler.findPartial(pattern.lineageRIndex[j], patternlab); + var lineageRPattern = pattern_assembler.getPartial(pattern.lineageRIndex[j], patternlab); //only set patternState if pattern.patternState "is less than" the lineageRPattern.patternstate //or if lineageRPattern.patternstate (the consuming pattern) does not have a state diff --git a/core/lib/list_item_hunter.js b/core/lib/list_item_hunter.js index 35c077f5d..be55f35c5 100644 --- a/core/lib/list_item_hunter.js +++ b/core/lib/list_item_hunter.js @@ -82,7 +82,7 @@ var list_item_hunter = function () { //get the partial var partialName = foundPartials[j].match(/([\w\-\.\/~]+)/g)[0]; - var partialPattern = pattern_assembler.findPartial(partialName, patternlab); + var partialPattern = pattern_assembler.getPartial(partialName, patternlab); //create a copy of the partial so as to not pollute it after the get_pattern_by_key call. var cleanPartialPattern; diff --git a/core/lib/markdown_parser.js b/core/lib/markdown_parser.js index c7e764b8f..495e0d181 100644 --- a/core/lib/markdown_parser.js +++ b/core/lib/markdown_parser.js @@ -1,9 +1,15 @@ "use strict"; var md = require('markdown-it')(); +var yaml = require('js-yaml'); var markdown_parser = function () { + /** + * Converts a markdown block with frontmatter (each is optional, technically) to a well-formed object. + * @param block - the ".md" file, which can contain frontmatter or not, or only frontmatter. + * @returns an object with any frontmatter keys, plus a .markdown key + */ function parseMarkdownBlock(block) { var returnObject = {}; @@ -11,31 +17,23 @@ var markdown_parser = function () { //for each block process the yaml frontmatter and markdown var frontmatterRE = /---\r?\n{1}([\s\S]*)---\r?\n{1}([\s\S]*)+/gm; var chunks = frontmatterRE.exec(block); - if (chunks && chunks[1]) { - - //convert each yaml frontmatter key / value into an object key - var frontmatter = chunks[1]; - var frontmatterLines = frontmatter.split(/\n/gm); - for (var j = 0; j < frontmatterLines.length; j++) { - - var frontmatterLine = frontmatterLines[j]; - if (frontmatterLine.length > 0) { - - var frontmatterLineChunks = frontmatterLine.split(':'); //test this - var frontmatterKey = frontmatterLineChunks[0].toLowerCase().trim(); - var frontmatterValueString = frontmatterLineChunks[1].trim(); - - returnObject[frontmatterKey] = frontmatterValueString; - } + if (chunks) { + //we got some frontmatter + if (chunks && chunks[1]) { + //parse the yaml if we got it + var frontmatter = chunks[1]; + returnObject = yaml.safeLoad(frontmatter); } - } - if (chunks && chunks[2]) { - //parse the actual markdown - returnObject.markdown = md.render(chunks[2]); + if (chunks[2]) { + //parse the actual markdown if it exists + returnObject.markdown = md.render(chunks[2]); + } else { + returnObject.markdown = ''; + } } else { - //assume the passed in block is raw markdown + //assume the block was only markdown returnObject.markdown = md.render(block); } } catch (ex) { diff --git a/core/lib/object_factory.js b/core/lib/object_factory.js index 74b62f39e..e277c510d 100644 --- a/core/lib/object_factory.js +++ b/core/lib/object_factory.js @@ -16,7 +16,7 @@ var Pattern = function (relPath, data) { this.fileExtension = pathObj.ext; // '.mustache' // this is the unique name, subDir + fileName (sans extension) - this.name = this.subdir.replace(/[\/\\]/g, '-') + '-' + this.fileName.replace('~', '-'); // '00-atoms-00-global-00-colors' + this.name = this.subdir.replace(path.sep, '-') + '-' + this.fileName.replace('~', '-'); // '00-atoms-00-global-00-colors' // the JSON used to render values in the pattern this.jsonFileData = data || {}; @@ -36,10 +36,16 @@ var Pattern = function (relPath, data) { // the top-level pattern group this pattern belongs to. 'atoms' this.patternGroup = this.subdir.split(path.sep)[0].replace(/^\d*-/, ''); + //00-atoms if needed + this.patternType = this.subdir.split(path.sep)[0]; + // the sub-group this pattern belongs to. this.patternSubGroup = path.basename(this.subdir).replace(/^\d*-/, ''); // 'global' - // Not sure what this is used for. + //00-colors if needed + this.patternSubType = path.basename(this.subdir); + + // the joined pattern group and subgroup directory this.flatPatternPath = this.subdir.replace(/[\/\\]/g, '-'); // '00-atoms-00-global' // The canonical "key" by which this pattern is known. This is the callable @@ -47,6 +53,7 @@ var Pattern = function (relPath, data) { this.patternPartial = this.patternGroup + '-' + this.patternBaseName; this.isPattern = true; + this.isFlatPattern = this.patternGroup === this.patternSubGroup; this.patternState = ''; this.template = ''; this.patternPartialCode = ''; @@ -117,41 +124,6 @@ Pattern.create = function (relPath, data, customProps) { return extend(newPattern, customProps); }; - -var oPatternType = function (name) { - this.patternTypeLC = name; - this.patternTypeUC = name.split('-').reduce(function (val, working) { - return val.charAt(0).toUpperCase() + val.slice(1) + ' ' + working.charAt(0).toUpperCase() + working.slice(1); - }, '').trim(); - this.patternTypeItems = []; - this.patternTypeItemsIndex = []; - this.patternItems = []; - this.patternItemsIndex = []; -}; - - -var oPatternSubType = function (name) { - this.patternSubtypeLC = name; - this.patternSubtypeUC = name.split('-').reduce(function (val, working) { - return val.charAt(0).toUpperCase() + val.slice(1) + ' ' + working.charAt(0).toUpperCase() + working.slice(1); - }, '').trim(); - this.patternSubtypeItems = []; - this.patternSubtypeItemsIndex = []; -}; - - -var oPatternSubTypeItem = function (name) { - this.patternPath = ''; - this.patternPartialCode = ''; - this.patternName = name.split(' ').reduce(function (val, working) { - return val.charAt(0).toUpperCase() + val.slice(1) + ' ' + working.charAt(0).toUpperCase() + working.slice(1); - }, '').trim(); -}; - - module.exports = { - Pattern: Pattern, - oPatternType: oPatternType, - oPatternSubType: oPatternSubType, - oPatternSubTypeItem: oPatternSubTypeItem + Pattern: Pattern }; diff --git a/core/lib/parameter_hunter.js b/core/lib/parameter_hunter.js index b2b9832e2..6f2d50f1e 100644 --- a/core/lib/parameter_hunter.js +++ b/core/lib/parameter_hunter.js @@ -240,7 +240,7 @@ var parameter_hunter = function () { 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); + var partialPattern = pattern_assembler.getPartial(partialName, patternlab); //if we retrieved a pattern we should make sure that its extendedTemplate is reset. looks to fix #190 partialPattern.extendedTemplate = partialPattern.template; diff --git a/core/lib/pattern_assembler.js b/core/lib/pattern_assembler.js index 9d355063c..5ba703d62 100644 --- a/core/lib/pattern_assembler.js +++ b/core/lib/pattern_assembler.js @@ -29,8 +29,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]; } } @@ -45,7 +45,8 @@ var pattern_assembler = function () { return patternlab.patterns[i]; } } - throw 'Could not find pattern with partial ' + partialName; + console.error('Could not find pattern with partial ' + partialName); + return undefined; } function buildListItems(container) { @@ -91,7 +92,7 @@ var pattern_assembler = function () { function addPattern(pattern, patternlab) { //add the link to the global object - patternlab.data.link[pattern.patternGroup + '-' + pattern.patternBaseName] = '/patterns/' + pattern.patternLink; + patternlab.data.link[pattern.patternPartial] = '/patterns/' + pattern.patternLink; //only push to array if the array doesn't contain this pattern var isNew = true; @@ -214,7 +215,7 @@ var pattern_assembler = function () { subTypePattern.patternSectionSubtype = true; subTypePattern.patternLink = subTypePattern.name + '/index.html'; subTypePattern.patternDesc = subTypeMarkdown.markdown; - subTypePattern.patternPartial = 'viewall-' + subTypePattern.patternPartial; + subTypePattern.flatPatternPath = subTypePattern.flatPatternPath + '-' + subTypePattern.fileName; subTypePattern.isPattern = false; subTypePattern.engine = null; @@ -417,8 +418,9 @@ var pattern_assembler = function () { if (linkMatches) { for (var i = 0; i < linkMatches.length; i++) { - expandedLink = patternlab.data.link[linkMatches[i].split('.')[1]]; + expandedLink = encodeURI(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); } @@ -482,7 +484,7 @@ var pattern_assembler = function () { process_pattern_recursive: function (file, patternlab, additionalData) { processPatternRecursive(file, patternlab, additionalData); }, - findPartial: function (partial, patternlab) { + getPartial: function (partial, patternlab) { return getPartial(partial, patternlab); }, combine_listItems: function (patternlab) { diff --git a/core/lib/patternlab.js b/core/lib/patternlab.js index d721de09f..be5d78be3 100644 --- a/core/lib/patternlab.js +++ b/core/lib/patternlab.js @@ -1,5 +1,5 @@ /* - * patternlab-node - v2.3.0 - 2016 + * patternlab-node - v2.4.0 - 2016 * * Brian Muenzenmeyer, Geoff Pursell, and the web community. * Licensed under the MIT license. @@ -21,27 +21,17 @@ var diveSync = require('diveSync'), function buildPatternData(dataFilesPath, fs) { var dataFilesPath = dataFilesPath; var dataFiles = glob.sync(dataFilesPath + '*.json', {"ignore" : [dataFilesPath + 'listitems.json']}); - var mergeObject = {} + var mergeObject = {}; dataFiles.forEach(function (filePath) { - var jsonData = fs.readJSONSync(path.resolve(filePath), 'utf8') - mergeObject = _.merge(mergeObject, jsonData) - }) + var jsonData = fs.readJSONSync(path.resolve(filePath), 'utf8'); + mergeObject = _.merge(mergeObject, jsonData); + }); return mergeObject; } function processAllPatternsIterative(pattern_assembler, patterns_dir, patternlab) { diveSync( patterns_dir, - { - filter: function (thisPath, dir) { - if (dir) { - var remainingPath = thisPath.replace(patterns_dir, ''); - var isValidPath = remainingPath.indexOf('/_') === -1; - return isValidPath; - } - return true; - } - }, function (err, file) { //log any errors if (err) { @@ -56,16 +46,6 @@ function processAllPatternsIterative(pattern_assembler, patterns_dir, patternlab function processAllPatternsRecursive(pattern_assembler, patterns_dir, patternlab) { diveSync( patterns_dir, - { - filter: function (thisPath, dir) { - if (dir) { - var remainingPath = thisPath.replace(patterns_dir, ''); - var isValidPath = remainingPath.indexOf('/_') === -1; - return isValidPath; - } - return true; - } - }, function (err, file) { //log any errors if (err) { @@ -85,7 +65,7 @@ var patternlab_engine = function (config) { pa = require('./pattern_assembler'), pe = require('./pattern_exporter'), lh = require('./lineage_hunter'), - buildFrontEnd = require('./ui_builder'), + ui = require('./ui_builder'), plutils = require('./utilities'), sm = require('./starterkit_manager'), patternlab = {}; @@ -99,7 +79,6 @@ var patternlab_engine = function (config) { console.log(patternlab.package.version); } - function help() { console.log(''); @@ -185,7 +164,7 @@ var patternlab_engine = function (config) { } function listStarterkits() { - var starterkit_manager = new sm(patternlab); + var starterkit_manager = new sm(patternlab.config); return starterkit_manager.list_starterkits(); } @@ -244,19 +223,17 @@ var patternlab_engine = function (config) { patternlab.userHead = fs.readFileSync(path.resolve(paths.source.meta, '_00-head.mustache'), 'utf8'); } catch (ex) { - if (patternlab.config.debug) { - console.log(ex); - console.log('Could not find optional user-defined header, usually found at ./source/_meta/_00-head.mustache. It was likely deleted.'); - } + plutils.logRed('\nWARNING: Could not find the user-editable header template, currently configured to be at ' + path.join(config.paths.source.meta, '_00-head.mustache') + '. Your configured path may be incorrect (check paths.source.meta in your config file), the file may have been deleted, or it may have been left in the wrong place during a migration or update.\n'); + if (patternlab.config.debug) { console.log(ex); } + process.exit(1); } try { patternlab.userFoot = fs.readFileSync(path.resolve(paths.source.meta, '_01-foot.mustache'), 'utf8'); } catch (ex) { - if (patternlab.config.debug) { - console.log(ex); - console.log('Could not find optional user-defined footer, usually found at ./source/_meta/_01-foot.mustache. It was likely deleted.'); - } + plutils.logRed('\nWARNING: Could not find the user-editable footer template, currently configured to be at ' + path.join(config.paths.source.meta, '_01-foot.mustache') + '. Your configured path may be incorrect (check paths.source.meta in your config file), the file may have been deleted, or it may have been left in the wrong place during a migration or update.\n'); + if (patternlab.config.debug) { console.log(ex); } + process.exit(1); } //now that all the main patterns are known, look for any links that might be within data and expand them @@ -275,7 +252,7 @@ var patternlab_engine = function (config) { //set pattern-specific header if necessary var head; if (patternlab.userHead) { - head = patternlab.userHead.replace('{% pattern-lab-head %}', patternlab.header); + head = patternlab.userHead; } else { head = patternlab.header; } @@ -389,7 +366,7 @@ var patternlab_engine = function (config) { }, build: function (callback, deletePatternDir) { buildPatterns(deletePatternDir); - buildFrontEnd(patternlab); + new ui().buildFrontend(patternlab); printDebug(); callback(); }, diff --git a/core/lib/starterkit_manager.js b/core/lib/starterkit_manager.js index e14503d45..5be59c776 100644 --- a/core/lib/starterkit_manager.js +++ b/core/lib/starterkit_manager.js @@ -1,15 +1,15 @@ "use strict"; -var starterkit_manager = function (pl) { +var starterkit_manager = function (config) { var path = require('path'), fs = require('fs-extra'), util = require('./utilities'), - paths = pl.config.paths; + paths = config.paths; function loadStarterKit(starterkitName, clean) { try { var kitPath = path.resolve( - path.join(process.cwd(), 'node_modules', starterkitName, pl.config.starterkitSubDir) + path.join(process.cwd(), 'node_modules', starterkitName, config.starterkitSubDir) ); console.log('Attempting to load starterkit from', kitPath); try { @@ -49,6 +49,15 @@ var starterkit_manager = function (pl) { } + function detectStarterKits() { + var node_modules_path = path.join(process.cwd(), 'node_modules'); + var npm_modules = fs.readdirSync(node_modules_path).filter(function (dir) { + var module_path = path.join(process.cwd(), 'node_modules', dir); + return fs.statSync(module_path).isDirectory() && dir.indexOf('starterkit-') === 0; + }); + return npm_modules; + } + return { load_starterkit: function (starterkitName, clean) { loadStarterKit(starterkitName, clean); @@ -58,6 +67,9 @@ var starterkit_manager = function (pl) { }, pack_starterkit: function () { packStarterkit(); + }, + detect_starterkits: function () { + return detectStarterKits(); } }; diff --git a/core/lib/ui_builder.js b/core/lib/ui_builder.js index e8845bc6c..d4b268240 100644 --- a/core/lib/ui_builder.js +++ b/core/lib/ui_builder.js @@ -7,522 +7,620 @@ var of = require('./object_factory'); var Pattern = of.Pattern; var pa = require('./pattern_assembler'); var pattern_assembler = new pa(); +var plutils = require('./utilities'); var eol = require('os').EOL; +var _ = require('lodash'); + +var ui_builder = function () { + + /** + * Registers the pattern to the patternPaths object for the appropriate patternGroup and basename + * patternGroup + patternBaseName are what comprise the patternPartial (atoms-colors) + * @param patternlab - global data store + * @param pattern - the pattern to add + */ + function addToPatternPaths(patternlab, pattern) { + if (!patternlab.patternPaths) { + patternlab.patternPaths = {}; + } -// PRIVATE FUNCTIONS + if (!patternlab.patternPaths[pattern.patternGroup]) { + patternlab.patternPaths[pattern.patternGroup] = {}; + } -function addToPatternPaths(patternlab, pattern) { - if (!patternlab.patternPaths[pattern.patternGroup]) { - patternlab.patternPaths[pattern.patternGroup] = {}; + //only add real patterns + if (pattern.isPattern && !pattern.isDocPattern) { + patternlab.patternPaths[pattern.patternGroup][pattern.patternBaseName] = pattern.name; + } } - patternlab.patternPaths[pattern.patternGroup][pattern.patternBaseName] = pattern.name; -} - -//todo: refactor this as a method on the pattern object itself once we merge dev with pattern-engines branch -function isPatternExcluded(pattern) { - // returns whether or not the first character of the pattern filename is an underscore, or excluded - return pattern.isPattern && pattern.fileName.charAt(0) === '_'; -} - -// Returns the array of patterns to be rendered in the styleguide view and -// linked to in the pattern navigation. Checks if patterns are excluded. -function assembleStyleguidePatterns(patternlab) { - var styleguideExcludes = patternlab.config.styleGuideExcludes; - var styleguidePatterns = []; - - //todo this loop can be made more efficient - if (styleguideExcludes && styleguideExcludes.length) { - for (var i = 0; i < patternlab.patterns.length; i++) { - - var pattern = patternlab.patterns[i]; - - // skip underscore-prefixed files - if (isPatternExcluded(pattern)) { - if (patternlab.config.debug) { - console.log('Omitting ' + pattern.patternPartial + " from styleguide pattern exclusion."); - } - continue; - } - //this is meant to be a homepage that is not present anywhere else - if (pattern.patternPartial === patternlab.config.defaultPattern) { - if (patternlab.config.debug) { - console.log('omitting ' + pattern.patternPartial + ' from styleguide patterns because it is defined as a defaultPattern'); - } - continue; - } + /** + * Registers the pattern with the viewAllPaths object for the appropriate patternGroup and patternSubGroup + * @param patternlab - global data store + * @param pattern - the pattern to add + */ + function addToViewAllPaths(patternlab, pattern) { + if (!patternlab.viewAllPaths) { + patternlab.viewAllPaths = {}; + } - var partial = pattern.patternPartial; - var partialType = partial.substring(0, partial.indexOf('-')); - var isExcluded = (styleguideExcludes.indexOf(partialType) > -1); - if (!isExcluded) { - styleguidePatterns.push(pattern); - } + if (!patternlab.viewAllPaths[pattern.patternGroup]) { + patternlab.viewAllPaths[pattern.patternGroup] = {}; } - } else { - for (i = 0; i < patternlab.patterns.length; i++) { - var pattern = patternlab.patterns[i]; - - // skip underscore-prefixed files - if (isPatternExcluded(pattern)) { - if (patternlab.config.debug) { - console.log('Omitting ' + pattern.patternPartial + " from styleguide pattern exclusion."); - } - continue; - } - //this is meant to be a homepage that is not present anywhere else - if (pattern.patternPartial === patternlab.config.defaultPattern) { - if (patternlab.config.debug) { - console.log('omitting ' + pattern.patternPartial + ' from styleguide patterns because it is defined as a defaultPattern'); - } - continue; - } + if (!patternlab.viewAllPaths[pattern.patternGroup][pattern.patternSubGroup]) { + patternlab.viewAllPaths[pattern.patternGroup][pattern.patternSubGroup] = {}; + } - styleguidePatterns.push(pattern); + //note these retain any number prefixes if present, because these paths match the filesystem + patternlab.viewAllPaths[pattern.patternGroup][pattern.patternSubGroup] = pattern.patternType + '-' + pattern.patternSubType; + + //add all if it does not exist yet + if (!patternlab.viewAllPaths[pattern.patternGroup].all) { + patternlab.viewAllPaths[pattern.patternGroup].all = pattern.patternType; } } - return styleguidePatterns; -} -function buildNavigation(patternlab) { - for (var i = 0; i < patternlab.patterns.length; i++) { + /** + * Writes a file to disk, with an optional callback + * @param filePath - the path to write to with filename + * @param data - the file contents + * @param callback - an optional callback + */ + function writeFile(filePath, data, callback) { + if (callback) { + fs.outputFile(filePath, data, callback); + } else { + fs.outputFile(filePath, data); + } + } - var pattern = patternlab.patterns[i]; + /** + * Returns whether or not the pattern should be excluded from direct rendering or navigation on the front end + * @param pattern - the pattern to test for inclusion/exclusion + * @param patternlab - global data store + * @returns boolean - whether or not the pattern is excluded + */ + function isPatternExcluded(pattern, patternlab) { + var styleGuideExcludes = patternlab.config.styleGuideExcludes; + var isOmitted; - //exclude any named defaultPattern from the navigation. - //this is meant to be a homepage that is not navigable - if (pattern.patternPartial === patternlab.config.defaultPattern) { + // skip underscore-prefixed files + isOmitted = pattern.isPattern && pattern.fileName.charAt(0) === '_'; + if (isOmitted) { if (patternlab.config.debug) { - console.log('omitting ' + pattern.patternPartial + ' from navigation because it is defined as a defaultPattern'); + console.log('Omitting ' + pattern.patternPartial + " from styleguide patterns because it has an underscore suffix."); } + return true; + } - //add to patternPaths before continuing - addToPatternPaths(patternlab, pattern); - - continue; + //this is meant to be a homepage that is not present anywhere else + isOmitted = pattern.patternPartial === patternlab.config.defaultPattern; + if (isOmitted) { + if (patternlab.config.debug) { + console.log('Omitting ' + pattern.patternPartial + ' from styleguide patterns because it is defined as a defaultPattern.'); + } + return true; } - // skip underscore-prefixed files. don't create a patternType on account of an underscored pattern - if (isPatternExcluded(pattern)) { - continue; + //this pattern is a member of any excluded pattern groups + isOmitted = styleGuideExcludes && styleGuideExcludes.length && _.some(styleGuideExcludes, function (exclude) { + return exclude === pattern.patternGroup; }); + if (isOmitted) { + if (patternlab.config.debug) { + console.log('Omitting ' + pattern.patternPartial + ' from styleguide patterns its patternGroup is specified in styleguideExcludes.'); + } + return true; } - var patternSubTypeName; - var patternSubTypeItemName; - var flatPatternItem; - var patternSubType; - var patternSubTypeItem; - var viewAllPatternSubTypeItem; - - //get the patternSubType. - //if there is one or more slashes in the subdir, get everything after - //the last slash. if no slash, get the whole subdir string and strip - //any numeric + hyphen prefix - patternSubTypeName = pattern.subdir.split(path.sep).pop().replace(/^\d*\-/, ''); - - //get the patternSubTypeItem - patternSubTypeItemName = pattern.patternName.replace(/-/g, ' '); - - //assume the patternSubTypeItem does not exist. - patternSubTypeItem = new of.oPatternSubTypeItem(patternSubTypeItemName); - patternSubTypeItem.patternPath = pattern.patternLink; - patternSubTypeItem.patternPartial = pattern.patternPartial; - - //check if the patternType already exists - var patternTypeIndex = patternlab.patternTypeIndex.indexOf(pattern.patternGroup); - if (patternTypeIndex === -1) { - //add the patternType - var patternType = new of.oPatternType(pattern.patternGroup); - - //add patternPath and viewAllPath - patternlab.patternPaths[pattern.patternGroup] = patternlab.patternPaths[pattern.patternGroup] || {}; - patternlab.viewAllPaths[pattern.patternGroup] = {}; + //this pattern is contained with a directory prefixed with an underscore (a handy way to hide whole directories from the nav + isOmitted = pattern.relPath.charAt(0) === '_' || pattern.relPath.indexOf('/_') > -1; + if (isOmitted) { + if (patternlab.config.debug) { + console.log('Omitting ' + pattern.patternPartial + ' from styleguide patterns its contained within an underscored directory.'); + } + return true; + } - //test whether the pattern structure is flat or not - usually due to a template or page - flatPatternItem = patternSubTypeName === pattern.patternGroup; + //yay, let's include this on the front end + return isOmitted; + } - //assume the patternSubType does not exist. - patternSubType = new of.oPatternSubType(patternSubTypeName); + /** + * For the given pattern, find or construct the view-all pattern block for the group + * @param pattern - the pattern to derive our documentation pattern from + * @param patternlab - global data store + * @param isSubtypePattern - whether or not this is a subtypePattern or a typePattern (typePatterns not supported yet) + * @returns the found or created pattern object + */ + function injectDocumentationBlock(pattern, patternlab, isSubtypePattern) { + //first see if pattern_assembler processed one already + var docPattern = patternlab.subtypePatterns[pattern.patternGroup + (isSubtypePattern ? '-' + pattern.patternSubGroup : '')]; + if (docPattern) { + docPattern.isDocPattern = true; + return docPattern; + } - //add the patternState if it exists - if (pattern.patternState) { - patternSubTypeItem.patternState = pattern.patternState; + //if not, create one now + docPattern = new Pattern.createEmpty( + { + name: pattern.flatPatternPath, + patternName: isSubtypePattern ? pattern.patternSubGroup : pattern.patternGroup, + patternDesc: '', + patternPartial: 'viewall-' + pattern.patternGroup + (isSubtypePattern ? '-' + pattern.patternSubGroup : ''), + patternSectionSubtype : isSubtypePattern, + patternLink: pattern.flatPatternPath + path.sep + 'index.html', + isPattern: false, + engine: null, + flatPatternPath: pattern.flatPatternPath, + isDocPattern: true } + ); + return docPattern; + } - //if it is flat - we should not add the pattern to patternPaths - if (flatPatternItem) { + /** + * Registers flat patterns with the patternTypes object + * This is a new menu group like atoms + * @param patternlab - global data store + * @param pattern - the pattern to register + */ + function addPatternType(patternlab, pattern) { + patternlab.patternTypes.push( + { + patternTypeLC: pattern.patternGroup.toLowerCase(), + patternTypeUC: pattern.patternGroup.charAt(0).toUpperCase() + pattern.patternGroup.slice(1), + patternType: pattern.patternType, + patternTypeDash: pattern.patternGroup, //todo verify + patternTypeItems: [] + } + ); + } - patternType.patternItems.push(patternSubTypeItem); + /** + * Return the patternType object for the given pattern. Exits application if not found. + * @param patternlab - global data store + * @param pattern - the pattern to derive the pattern Type from + * @returns the found pattern type object + */ + function getPatternType(patternlab, pattern) { + var patternType = _.find(patternlab.patternTypes, ['patternType', pattern.patternType]); + + if (!patternType) { + plutils.logRed('Could not find patternType' + pattern.patternType + '. This is a critical error.'); + console.trace(); + process.exit(1); + } - //add to patternPaths - addToPatternPaths(patternlab, pattern); + return patternType; + } - } else { + /** + * Return the patternSubType object for the given pattern. Exits application if not found. + * @param patternlab - global data store + * @param pattern - the pattern to derive the pattern subType from + * @returns the found patternSubType object + */ + function getPatternSubType(patternlab, pattern) { + var patternType = getPatternType(patternlab, pattern); + var patternSubType = _.find(patternType.patternTypeItems, ['patternSubtype', pattern.patternSubType]); + + if (!patternSubType) { + plutils.logRed('Could not find patternType ' + pattern.patternType + '-' + pattern.patternType + '. This is a critical error.'); + console.trace(); + process.exit(1); + } - patternType.patternTypeItems.push(patternSubType); - patternType.patternTypeItemsIndex.push(patternSubTypeName); - patternSubType.patternSubtypeItems.push(patternSubTypeItem); - patternSubType.patternSubtypeItemsIndex.push(patternSubTypeItemName); + return patternSubType; + } - //add to patternPaths - addToPatternPaths(patternlab, pattern); + /** + * Registers the pattern with the appropriate patternType.patternTypeItems object + * This is a new menu group like atoms/global + * @param patternlab - global data store + * @param pattern - the pattern to register + */ + function addPatternSubType(patternlab, pattern) { + var patternType = getPatternType(patternlab, pattern); + patternType.patternTypeItems.push( + { + patternSubtypeLC: pattern.patternSubGroup.toLowerCase(), + patternSubtypeUC: pattern.patternSubGroup.charAt(0).toUpperCase() + pattern.patternSubGroup.slice(1), + patternSubtype: pattern.patternSubType, + patternSubtypeDash: pattern.patternSubGroup, //todo verify + patternSubtypeItems: [] + } + ); + } - //add the view all PatternSubTypeItem - viewAllPatternSubTypeItem = new of.oPatternSubTypeItem("View All"); - viewAllPatternSubTypeItem.patternPath = pattern.subdir.slice(0, pattern.subdir.indexOf(pattern.patternGroup) + pattern.patternGroup.length) + "/index.html"; - viewAllPatternSubTypeItem.patternPartial = "viewall-" + pattern.patternGroup; + /** + * Creates a patternSubTypeItem object from a pattern + * This is a menu item you click on + * @param pattern - the pattern to derive the subtypeitem from + * @returns {{patternPartial: string, patternName: (*|string), patternState: string, patternSrcPath: string, patternPath: string}} + */ + function createPatternSubTypeItem(pattern) { + return { + patternPartial: pattern.patternPartial, + patternName: pattern.patternName, + patternState: pattern.patternState, + patternSrcPath: encodeURI(pattern.subdir + pattern.filename), + patternPath: encodeURI(pattern.flatPatternPath + '/' + pattern.flatPatternPath + '.html') + } + } - patternType.patternItems.push(viewAllPatternSubTypeItem); - patternlab.viewAllPaths[pattern.patternGroup].viewall = pattern.subdir.slice(0, pattern.subdir.indexOf(pattern.patternGroup) + pattern.patternGroup.length); + /** + * Registers the pattern with the appropriate patternType.patternSubType.patternSubtypeItems array + * These are the actual menu items you click on + * @param patternlab - global data store + * @param pattern - the pattern to derive the subtypeitem from + * @param createViewAllVariant - whether or not to create the special view all item + */ + function addPatternSubTypeItem(patternlab, pattern, createSubtypeViewAllVarient) { + var patternSubType = getPatternSubType(patternlab, pattern); + if (createSubtypeViewAllVarient) { + patternSubType.patternSubtypeItems.push( + { + patternPartial: 'viewall-' + pattern.patternGroup + '-' + pattern.patternSubGroup, + patternName: 'View All', + patternPath: encodeURI(pattern.flatPatternPath + '/index.html'), + patternType: pattern.patternType, + patternSubtype: pattern.patternSubtype + } + ); + } + else { + patternSubType.patternSubtypeItems.push( + createPatternSubTypeItem(pattern) + ); + } + } - } + /** + * Registers flat patterns to the appropriate type + * @param patternlab - global data store + * @param pattern - the pattern to add + */ + function addPatternItem(patternlab, pattern, isViewAllVariant) { + var patternType = getPatternType(patternlab, pattern); + if (!patternType) { + plutils.logRed('Could not find patternType' + pattern.patternType + '. This is a critical error.'); + console.trace(); + process.exit(1); + } - //add the patternType. - patternlab.patternTypes.push(patternType); - patternlab.patternTypeIndex.push(pattern.patternGroup); + if (!patternType.patternItems) { + patternType.patternItems = []; + } - //done + if (isViewAllVariant) { + if (!pattern.isFlatPattern) { + //todo: it'd be nice if we could get this into createPatternSubTypeItem someday + patternType.patternItems.push({ + patternPartial: 'viewall-' + pattern.patternGroup + '-all', + patternName: 'View All', + patternSrcPath: encodeURI(pattern.patternType + '/index.html'), + patternPath: encodeURI(pattern.patternType + '/index.html') + }); + } } else { - //find the patternType - patternType = patternlab.patternTypes[patternTypeIndex]; + patternType.patternItems.push(createPatternSubTypeItem(pattern)); + } + } - //add the patternState if it exists - if (pattern.patternState) { - patternSubTypeItem.patternState = pattern.patternState; + // function getPatternItems(patternlab, patternType) { + // var patternType = _.find(patternlab.patternTypes, ['patternTypeLC', patternType]); + // if (patternType) { + // return patternType.patternItems; + // } + // return []; + // } + + /** + * Sorts patterns based on name. + * Will be expanded to use explicit order in the near future + * @param patternsArray - patterns to sort + * @returns sorted patterns + */ + function sortPatterns(patternsArray) { + return patternsArray.sort(function (a, b) { + + if (a.name > b.name) { + return 1; + } + if (a.name < b.name) { + return -1; } + return 0; + }); + } - //test whether the pattern structure is flat or not - usually due to a template or page - flatPatternItem = patternSubTypeName === pattern.patternGroup; + /** + * Returns an object representing how the front end styleguide and navigation is structured + * @param patternlab - global data store + * @returns ptterns grouped by type -> subtype like atoms -> global -> pattern, pattern, pattern + */ + function groupPatterns(patternlab) { + var groupedPatterns = { + patternGroups: {} + }; + + if (!patternlab.patternTypes) { + patternlab.patternTypes = []; + } - //if it is flat - we should not add the pattern to patternPaths - if (flatPatternItem) { - //add the patternSubType to patternItems - patternType.patternItems.push(patternSubTypeItem); + _.forEach(sortPatterns(patternlab.patterns), function (pattern) { - //add to patternPaths - addToPatternPaths(patternlab, pattern); + //ignore patterns we can omit from rendering directly + pattern.omitFromStyleguide = isPatternExcluded(pattern, patternlab); + if (pattern.omitFromStyleguide) { return; } - } else { + if (!groupedPatterns.patternGroups[pattern.patternGroup]) { - // only do this if pattern is included - //check to see if patternSubType exists - var patternTypeItemsIndex = patternType.patternTypeItemsIndex.indexOf(patternSubTypeName); - if (patternTypeItemsIndex === -1) { - patternSubType = new of.oPatternSubType(patternSubTypeName); - - //add the patternSubType and patternSubTypeItem - patternSubType.patternSubtypeItems.push(patternSubTypeItem); - patternSubType.patternSubtypeItemsIndex.push(patternSubTypeItemName); - patternType.patternTypeItems.push(patternSubType); - patternType.patternTypeItemsIndex.push(patternSubTypeName); - - } else { - //add the patternSubTypeItem - patternSubType = patternType.patternTypeItems[patternTypeItemsIndex]; - patternSubType.patternSubtypeItems.push(patternSubTypeItem); - patternSubType.patternSubtypeItemsIndex.push(patternSubTypeItemName); - } + groupedPatterns.patternGroups[pattern.patternGroup] = {}; + pattern.isSubtypePattern = false; + addPatternType(patternlab, pattern); + + //todo: Pattern Type View All and Documentation + //groupedPatterns.patternGroups[pattern.patternGroup]['viewall-' + pattern.patternGroup] = injectDocumentationBlock(pattern, patternlab, false); + addPatternItem(patternlab, pattern, true); + } + + //continue building navigation for nested patterns + if (pattern.patternGroup !== pattern.patternSubGroup) { - //check if we are moving to a new subgroup in the next loop - if (!patternlab.patterns[i + 1] || pattern.patternSubGroup !== patternlab.patterns[i + 1].patternSubGroup) { + if (!groupedPatterns.patternGroups[pattern.patternGroup][pattern.patternSubGroup]) { - //add the viewall SubTypeItem - var viewAllPatternSubTypeItem = new of.oPatternSubTypeItem("View All"); - viewAllPatternSubTypeItem.patternPath = pattern.flatPatternPath + "/index.html"; - viewAllPatternSubTypeItem.patternPartial = "viewall-" + pattern.patternGroup + "-" + pattern.patternSubGroup; + addPatternSubType(patternlab, pattern); + + pattern.isSubtypePattern = !pattern.isPattern; + groupedPatterns.patternGroups[pattern.patternGroup][pattern.patternSubGroup] = {}; + groupedPatterns.patternGroups[pattern.patternGroup][pattern.patternSubGroup]['viewall-' + pattern.patternGroup + '-' + pattern.patternSubGroup] = injectDocumentationBlock(pattern, patternlab, true); + + addToViewAllPaths(patternlab, pattern); + addPatternSubTypeItem(patternlab, pattern, true); - patternSubType.patternSubtypeItems.push(viewAllPatternSubTypeItem); - patternSubType.patternSubtypeItemsIndex.push("View All"); } - // just add to patternPaths + groupedPatterns.patternGroups[pattern.patternGroup][pattern.patternSubGroup][pattern.patternBaseName] = pattern; + + addToPatternPaths(patternlab, pattern); + addPatternSubTypeItem(patternlab, pattern); + } else { + addPatternItem(patternlab, pattern); addToPatternPaths(patternlab, pattern); } - } - patternlab.viewAllPaths[pattern.patternGroup][pattern.patternSubGroup] = pattern.flatPatternPath; - } - return patternTypeIndex; -} - -function buildFooterHTML(patternlab, patternPartial) { - //set the pattern-specific footer by compiling the general-footer with data, and then adding it to the meta footer - var footerPartial = pattern_assembler.renderPattern(patternlab.footer, { - patternData: JSON.stringify({ - patternPartial: patternPartial, - }), - cacheBuster: patternlab.cacheBuster - }); - var footerHTML = pattern_assembler.renderPattern(patternlab.userFoot, { - patternLabFoot : footerPartial - }); - return footerHTML; -} - -function insertPatternSubtypeDocumentationPattern(patternlab, patterns, patternPartial) { - //attempt to find a subtype pattern before rendering - var subtypePattern = patternlab.subtypePatterns[patternPartial]; - if (subtypePattern) { - patterns.unshift(subtypePattern); - } else { - var stubbedSubtypePattern = Pattern.createEmpty({ - patternSectionSubtype: true, - isPattern: false, - patternPartial: 'viewall-' + patternPartial, - patternName: patterns[0].patternSubGroup, - patternLink: patterns[0].flatPatternPath + '/index.html' }); - patterns.unshift(stubbedSubtypePattern); - } - return patterns; -} - -function buildViewAllHTML(patternlab, patterns, patternPartial) { - var patternsPlusSubtpe = insertPatternSubtypeDocumentationPattern(patternlab, patterns, patternPartial); + return groupedPatterns; + } - var viewAllHTML = pattern_assembler.renderPattern(patternlab.viewAll, - { - partials: patternsPlusSubtpe, - patternPartial: patternPartial, + /** + * Builds footer HTML from the general footer and user-defined footer + * @param patternlab - global data store + * @param patternPartial - the partial key to build this for, either viewall-patternPartial or a viewall-patternType-all + * @returns HTML + */ + function buildFooterHTML(patternlab, patternPartial) { + //first render the general footer + var footerPartial = pattern_assembler.renderPattern(patternlab.footer, { + patternData: JSON.stringify({ + patternPartial: patternPartial, + }), cacheBuster: patternlab.cacheBuster - }, { - patternSection: patternlab.patternSection, - patternSectionSubtype: patternlab.patternSectionSubType }); - return viewAllHTML; -} - -function buildViewAllPages(mainPageHeadHtml, patternlab, styleguidePatterns) { - var paths = patternlab.config.paths; - var prevSubdir = ''; - var prevGroup = ''; - var i; - for (i = 0; i < styleguidePatterns.length; i++) { + //then add it to the user footer + var footerHTML = pattern_assembler.renderPattern(patternlab.userFoot, { + patternLabFoot : footerPartial + }); + return footerHTML; + } - var pattern = styleguidePatterns[i]; + /** + * Takes a set of patterns and builds a viewall HTML page for them + * Used by the type and subtype viewall sets + * @param patternlab - global data store + * @param patterns - the set of patterns to build the viewall page for + * @param patternPartial - a key used to identify the viewall page + * @returns HTML + */ + function buildViewAllHTML(patternlab, patterns, patternPartial) { + var viewAllHTML = pattern_assembler.renderPattern(patternlab.viewAll, + { + partials: patterns, + patternPartial: 'viewall-' + patternPartial, + cacheBuster: patternlab.cacheBuster + }, { + patternSection: patternlab.patternSection, + patternSectionSubtype: patternlab.patternSectionSubType + }); + return viewAllHTML; + } - // skip underscore-prefixed files - if (isPatternExcluded(pattern)) { - if (patternlab.config.debug) { - console.log('Omitting ' + pattern.patternPartial + " from view all rendering."); - } - continue; - } + /** + * Constructs viewall pages for each set of grouped patterns + * @param mainPageHeadHtml - the already built main page HTML + * @param patternlab - global data store + * @param styleguidePatterns - the grouped set of patterns + * @returns every built pattern and set of viewall patterns, so the styleguide can use it + */ + function buildViewAllPages(mainPageHeadHtml, patternlab, styleguidePatterns) { + var paths = patternlab.config.paths; + var patterns = []; + var writeViewAllFile = true; - //this is meant to be a homepage that is not present anywhere else - if (pattern.patternPartial === patternlab.config.defaultPattern) { - if (patternlab.config.debug) { - console.log('Omitting ' + pattern.patternPartial + ' from view all rendering because it is defined as a defaultPattern'); - } - continue; - } + //loop through the grouped styleguide patterns, building at each level + _.forEach(styleguidePatterns.patternGroups, function (patternTypeObj, patternType) { - //create the view all for the section - // check if the current section is different from the previous one - if (pattern.patternGroup !== prevGroup) { - prevGroup = pattern.patternGroup; + var p; + var typePatterns = []; + _.forOwn(patternTypeObj, function (patternSubtypes, patternSubtype) { - var viewAllPatterns = []; - var patternPartial = "viewall-" + pattern.patternGroup; - var j; + var patternPartial = patternType + '-' + patternSubtype; + //do not create a viewall page for flat patterns + if (patternType === patternSubtype) { + writeViewAllFile = false; + return false; + } - for (j = 0; j < styleguidePatterns.length; j++) { + //render the footer needed for the viewall template + var footerHTML = buildFooterHTML(patternlab, 'viewall-' + patternPartial); + //render the viewall template + var subtypePatterns = _.values(patternSubtypes); - if (styleguidePatterns[j].patternGroup === pattern.patternGroup) { - //again, skip any sibling patterns to the current one that may have underscores + //determine if we should write at this time by checking if these are flat patterns or grouped patterns + p = _.find(subtypePatterns, function (pat) { + return pat.isDocPattern; + }); - if (isPatternExcluded(styleguidePatterns[j])) { - if (patternlab.config.debug) { - console.log('Omitting ' + styleguidePatterns[j].patternPartial + " from view all sibling rendering."); - } - continue; - } + typePatterns = typePatterns.concat(subtypePatterns); - //this is meant to be a homepage that is not present anywhere else - if (styleguidePatterns[j].patternPartial === patternlab.config.defaultPattern) { - if (patternlab.config.debug) { - console.log('Omitting ' + pattern.patternPartial + ' from view all sibling rendering because it is defined as a defaultPattern'); - } - continue; - } + var viewAllHTML = buildViewAllHTML(patternlab, subtypePatterns, patternPartial); + writeFile(paths.public.patterns + p.flatPatternPath + '/index.html', mainPageHeadHtml + viewAllHTML + footerHTML); + return true; //stop yelling at us eslint we know we know + }); - viewAllPatterns.push(styleguidePatterns[j]); - } + //do not create a viewall page for flat patterns + if (!writeViewAllFile || !p) { + return false; } //render the footer needed for the viewall template - var footerHTML = buildFooterHTML(patternlab, patternPartial); + var footerHTML = buildFooterHTML(patternlab, 'viewall-' + patternType + '-all'); + + //add any flat patterns + //todo this isn't quite working yet + //typePatterns = typePatterns.concat(getPatternItems(patternlab, patternType)); //render the viewall template - var viewAllHTML = buildViewAllHTML(patternlab, viewAllPatterns, patternPartial); + var viewAllHTML = buildViewAllHTML(patternlab, typePatterns, patternType); + writeFile(paths.public.patterns + p.subdir + '/index.html', mainPageHeadHtml + viewAllHTML + footerHTML); - fs.outputFileSync(paths.public.patterns + pattern.subdir.slice(0, pattern.subdir.indexOf(pattern.patternGroup) + pattern.patternGroup.length) + '/index.html', mainPageHeadHtml + viewAllHTML + footerHTML); - } + patterns = patterns.concat(typePatterns); + return true; //stop yelling at us eslint we know we know + }); + return patterns; + } - //create the view all for the subsection - // check if the current sub section is different from the previous one - if (pattern.subdir !== prevSubdir) { - prevSubdir = pattern.subdir; - - viewAllPatterns = []; - patternPartial = "viewall-" + pattern.patternGroup + "-" + pattern.patternSubGroup; - - for (j = 0; j < styleguidePatterns.length; j++) { - - if (styleguidePatterns[j].subdir === pattern.subdir) { - //again, skip any sibling patterns to the current one that may have underscores - if (isPatternExcluded(styleguidePatterns[j])) { - if (patternlab.config.debug) { - console.log('Omitting ' + styleguidePatterns[j].patternPartial + " from view all sibling rendering."); - } - continue; - } - - //this is meant to be a homepage that is not present anywhere else - if (styleguidePatterns[j].patternPartial === patternlab.config.defaultPattern) { - if (patternlab.config.debug) { - console.log('Omitting ' + pattern.patternPartial + ' from view all sibling rendering because it is defined as a defaultPattern'); - } - continue; - } - - viewAllPatterns.push(styleguidePatterns[j]); - } - } + /** + * Write out our pattern information for use by the front end + * @param patternlab - global data store + */ + function exportData(patternlab) { + var annotation_exporter = new ae(patternlab); + var paths = patternlab.config.paths; - //render the footer needed for the viewall template - var footerHTML = buildFooterHTML(patternlab, patternPartial); + //write out the data + var output = ''; - //render the viewall template - var viewAllHTML = buildViewAllHTML(patternlab, viewAllPatterns, patternPartial); + //config + output += 'var config = ' + JSON.stringify(patternlab.config) + ';\n'; - fs.outputFileSync(paths.public.patterns + pattern.flatPatternPath + '/index.html', mainPageHeadHtml + viewAllHTML + footerHTML); - } - } -} + //ishControls + output += 'var ishControls = {"ishControlsHide":' + JSON.stringify(patternlab.config.ishControlsHide) + '};' + eol; -function sortPatterns(patternsArray) { - return patternsArray.sort(function (a, b) { + //navItems + output += 'var navItems = {"patternTypes": ' + JSON.stringify(patternlab.patternTypes) + '};' + eol; - if (a.name > b.name) { - return 1; - } - if (a.name < b.name) { - return -1; - } + //patternPaths + output += 'var patternPaths = ' + JSON.stringify(patternlab.patternPaths) + ';' + eol; - // a must be equal to b - return 0; - }); -} - - -// MAIN BUILDER FUNCTION - -function buildFrontEnd(patternlab) { - var annotation_exporter = new ae(patternlab); - var styleguidePatterns = []; - var paths = patternlab.config.paths; - - patternlab.patternTypes = []; - patternlab.patternTypeIndex = []; - patternlab.patternPaths = {}; - patternlab.viewAllPaths = {}; - - // check if patterns are excluded, if not add them to styleguidePatterns - styleguidePatterns = assembleStyleguidePatterns(patternlab); - - //sort all patterns explicitly. - styleguidePatterns = sortPatterns(styleguidePatterns); - - //set the pattern-specific header by compiling the general-header with data, and then adding it to the meta header - var headerPartial = pattern_assembler.renderPattern(patternlab.header, { - cacheBuster: patternlab.cacheBuster - }); - var headerHTML = pattern_assembler.renderPattern(patternlab.userHead, { - patternLabHead : headerPartial - }); - - //set the pattern-specific footer by compiling the general-footer with data, and then adding it to the meta footer - var footerPartial = pattern_assembler.renderPattern(patternlab.footer, { - patternData: '{}', - cacheBuster: patternlab.cacheBuster - }); - var footerHTML = pattern_assembler.renderPattern(patternlab.userFoot, { - patternLabFoot : footerPartial - }); - - //build the styleguide - var styleguideHtml = pattern_assembler.renderPattern(patternlab.viewAll, - { - partials: styleguidePatterns, - cacheBuster: patternlab.cacheBuster - }, { - patternSection: patternlab.patternSection, - patternSectionSubType: patternlab.patternSectionSubType - }); + //viewAllPaths + output += 'var viewAllPaths = ' + JSON.stringify(patternlab.viewAllPaths) + ';' + eol; - fs.outputFileSync(path.resolve(paths.public.styleguide, 'html/styleguide.html'), headerHTML + styleguideHtml + footerHTML); + //plugins someday + output += 'var plugins = [];' + eol; - //build the viewall pages - buildViewAllPages(headerHTML, patternlab, styleguidePatterns); + //smaller config elements + output += 'var defaultShowPatternInfo = ' + (patternlab.config.defaultShowPatternInfo ? patternlab.config.defaultShowPatternInfo : 'false') + ';' + eol; + output += 'var defaultPattern = "' + (patternlab.config.defaultPattern ? patternlab.config.defaultPattern : 'all') + '";' + eol; - //build the patternlab website - buildNavigation(patternlab); + //write all output to patternlab-data + writeFile(path.resolve(paths.public.data, 'patternlab-data.js'), output); - //move the index file from its asset location into public root - var patternlabSiteHtml; - try { - patternlabSiteHtml = fs.readFileSync(path.resolve(paths.source.styleguide, 'index.html'), 'utf8'); - } catch (error) { - console.log(error); - console.log("\nERROR: Could not load one or more styleguidekit assets from", paths.source.styleguide, '\n'); - process.exit(1); + //annotations + var annotationsJSON = annotation_exporter.gather(); + var annotations = 'var comments = { "comments" : ' + JSON.stringify(annotationsJSON) + '};'; + writeFile(path.resolve(paths.public.annotations, 'annotations.js'), annotations); } - fs.outputFileSync(path.resolve(paths.public.root, 'index.html'), patternlabSiteHtml); - - //write out the data - var output = ''; - - //config - output += 'var config = ' + JSON.stringify(patternlab.config) + ';\n'; - //ishControls - output += 'var ishControls = {"ishControlsHide":' + JSON.stringify(patternlab.config.ishControlsHide) + '};' + eol; + /** + * The main entry point for ui_builder + * @param patternlab - global data store + */ + function buildFrontend(patternlab) { - //navItems - output += 'var navItems = {"patternTypes": ' + JSON.stringify(patternlab.patternTypes) + '};' + eol; + var paths = patternlab.config.paths; - //patternPaths - output += 'var patternPaths = ' + JSON.stringify(patternlab.patternPaths) + ';' + eol; + //determine which patterns should be included in the front-end rendering + var styleguidePatterns = groupPatterns(patternlab); - //viewAllPaths - output += 'var viewAllPaths = ' + JSON.stringify(patternlab.viewAllPaths) + ';' + eol; + //set the pattern-specific header by compiling the general-header with data, and then adding it to the meta header + var headerPartial = pattern_assembler.renderPattern(patternlab.header, { + cacheBuster: patternlab.cacheBuster + }); + var headerHTML = pattern_assembler.renderPattern(patternlab.userHead, { + patternLabHead : headerPartial, + cacheBuster: patternlab.cacheBuster + }); - //plugins someday - output += 'var plugins = [];' + eol; + //set the pattern-specific footer by compiling the general-footer with data, and then adding it to the meta footer + var footerPartial = pattern_assembler.renderPattern(patternlab.footer, { + patternData: '{}', + cacheBuster: patternlab.cacheBuster + }); + var footerHTML = pattern_assembler.renderPattern(patternlab.userFoot, { + patternLabFoot : footerPartial + }); - //smaller config elements - output += 'var defaultShowPatternInfo = ' + (patternlab.config.defaultShowPatternInfo ? patternlab.config.defaultShowPatternInfo : 'false') + ';' + eol; - output += 'var defaultPattern = "' + (patternlab.config.defaultPattern ? patternlab.config.defaultPattern : 'all') + '";' + eol; + //build the viewall pages + var allPatterns = buildViewAllPages(headerHTML, patternlab, styleguidePatterns); + + //build the main styleguide page + var styleguideHtml = pattern_assembler.renderPattern(patternlab.viewAll, + { + partials: allPatterns + }, { + patternSection: patternlab.patternSection, + patternSectionSubtype: patternlab.patternSectionSubType + }); + writeFile(path.resolve(paths.public.styleguide, 'html/styleguide.html'), headerHTML + styleguideHtml + footerHTML); + + //move the index file from its asset location into public root + var patternlabSiteHtml; + try { + patternlabSiteHtml = fs.readFileSync(path.resolve(paths.source.styleguide, 'index.html'), 'utf8'); + } catch (error) { + console.log(error); + console.log("\nERROR: Could not load one or more styleguidekit assets from", paths.source.styleguide, '\n'); + process.exit(1); + } + writeFile(path.resolve(paths.public.root, 'index.html'), patternlabSiteHtml); - //write all ouytput to patternlab-data - fs.outputFileSync(path.resolve(paths.public.data, 'patternlab-data.js'), output); + //write out patternlab.data object to be read by the client + exportData(patternlab); + } - //annotations - var annotationsJSON = annotation_exporter.gather(); - var annotations = 'var comments = { "comments" : ' + JSON.stringify(annotationsJSON) + '};'; - fs.outputFileSync(path.resolve(paths.public.annotations, 'annotations.js'), annotations); + return { + buildFrontend: function (patternlab) { + buildFrontend(patternlab) + }, + isPatternExcluded: function (pattern, patternlab) { + return isPatternExcluded(pattern, patternlab); + }, + groupPatterns: function (patternlab) { + return groupPatterns(patternlab); + } + }; -} +}; -module.exports = buildFrontEnd; +module.exports = ui_builder; diff --git a/core/lib/utilities.js b/core/lib/utilities.js index 4765e874b..d05eec876 100644 --- a/core/lib/utilities.js +++ b/core/lib/utilities.js @@ -15,6 +15,10 @@ var util = { console.log('\x1b[32m', message, '\x1b[0m'); }, + logOrange: function (message) { + console.log('\x1b[33m', message, '\x1b[0m'); + }, + logRed: function (message) { console.log('\x1b[41m', message, '\x1b[0m'); }, diff --git a/core/scripts/postinstall.js b/core/scripts/postinstall.js new file mode 100644 index 000000000..ea546658e --- /dev/null +++ b/core/scripts/postinstall.js @@ -0,0 +1,33 @@ +"use strict"; +try{ + + console.log('Beginning Pattern Lab postinstall...'); + + var sm = require('../lib/starterkit_manager.js'); + var u = require('../lib/utilities.js'); + var path = require('path'); + var fs = require('fs-extra'); + + //get the config + var configPath = path.resolve(process.cwd(), 'patternlab-config.json'); + var config = fs.readJSONSync(path.resolve(configPath), 'utf8'); + + //determine if any starterkits are already installed + var starterkit_manager = new sm(config); + var foundStarterkits = starterkit_manager.detect_starterkits(); + + //todo - enhance to support multiple kits with prompt for each or all + if(foundStarterkits && foundStarterkits.length > 0) { + starterkit_manager.load_starterkit(foundStarterkits[0], true); + } else { + console.log('No starterkits found to automatically load.') + } + u.logGreen('Pattern Lab postinstall complete.'); + +} catch (ex) { + u.logOrange(ex); + u.logOrange('An error occurred during Pattern Lab Node postinstall.'); + u.logOrange('Pattern Lab postinstall completed with errors.'); +} + + diff --git a/package.json b/package.json index 9dabe425c..656129208 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,13 @@ { "name": "patternlab-node", "description": "Pattern Lab is a collection of tools to help you create atomic design systems. This is the node command line interface (CLI).", - "version": "2.3.0", + "version": "2.4.0", "main": "./core/lib/patternlab.js", "dependencies": { "diveSync": "^0.3.0", "fs-extra": "^0.30.0", "glob": "^7.0.0", + "js-yaml": "^3.6.1", "json5": "^0.5.0", "lodash": "~4.13.1", "markdown-it": "^6.0.1", diff --git a/test/engine_underscore_tests.js b/test/engine_underscore_tests.js index b5beb95e3..06583de00 100644 --- a/test/engine_underscore_tests.js +++ b/test/engine_underscore_tests.js @@ -72,6 +72,17 @@ exports['engine_underscore'] = { // test test.equals(helloWorldWithData.render(), 'Hello world!' + eol + 'Yeah, we got the subtitle from the JSON.' + eol); test.done(); + }, + 'findPartial return the ID of the partial, given a whole partial call': function (test) { + var engineLoader = require('../core/lib/pattern_engines'); + var underscoreEngine = engineLoader.underscore; + + test.expect(1); + + // do all the normal processing of the pattern + // test + test.equals(underscoreEngine.findPartial("<%= _.renderNamedPartial('molecules-details', obj) %>"), 'molecules-details'); + test.done(); } }; diff --git a/test/files/_patterns/00-test/03-styled-atom.md b/test/files/_patterns/00-test/03-styled-atom.md new file mode 100644 index 000000000..23371c488 --- /dev/null +++ b/test/files/_patterns/00-test/03-styled-atom.md @@ -0,0 +1,3 @@ +--- +status: inprogress +--- diff --git a/test/files/_patterns/patternType1/patternSubType1/blue.mustache b/test/files/_patterns/patternType1/patternSubType1/blue.mustache new file mode 100644 index 000000000..24560d9b8 --- /dev/null +++ b/test/files/_patterns/patternType1/patternSubType1/blue.mustache @@ -0,0 +1 @@ +blue diff --git a/test/files/_patterns/patternType1/patternSubType1/red.mustache b/test/files/_patterns/patternType1/patternSubType1/red.mustache new file mode 100644 index 000000000..a9d1386a1 --- /dev/null +++ b/test/files/_patterns/patternType1/patternSubType1/red.mustache @@ -0,0 +1 @@ +red diff --git a/test/files/_patterns/patternType1/patternSubType1/yellow.mustache b/test/files/_patterns/patternType1/patternSubType1/yellow.mustache new file mode 100644 index 000000000..d1ed081df --- /dev/null +++ b/test/files/_patterns/patternType1/patternSubType1/yellow.mustache @@ -0,0 +1 @@ +yellow diff --git a/test/files/_patterns/patternType1/patternType2/black.mustache b/test/files/_patterns/patternType1/patternType2/black.mustache new file mode 100644 index 000000000..7e66a17d4 --- /dev/null +++ b/test/files/_patterns/patternType1/patternType2/black.mustache @@ -0,0 +1 @@ +black diff --git a/test/files/_patterns/patternType1/patternType2/grey.mustache b/test/files/_patterns/patternType1/patternType2/grey.mustache new file mode 100644 index 000000000..546ef5d97 --- /dev/null +++ b/test/files/_patterns/patternType1/patternType2/grey.mustache @@ -0,0 +1 @@ +grey diff --git a/test/files/_patterns/patternType1/patternType2/white.mustache b/test/files/_patterns/patternType1/patternType2/white.mustache new file mode 100644 index 000000000..57d4625e3 --- /dev/null +++ b/test/files/_patterns/patternType1/patternType2/white.mustache @@ -0,0 +1 @@ +white diff --git a/test/files/nav.md b/test/files/nav.md index 00de05203..fcf153aa0 100644 --- a/test/files/nav.md +++ b/test/files/nav.md @@ -1,5 +1,5 @@ --- -el: #nav +el: '#nav' title : Navigation --- Navigation for adaptive web experiences can be tricky. Refer to [these repsonsive patterns](https://bradfrost.github.io/this-is-responsive/patterns.html#navigation) when evaluating solutions. diff --git a/test/lineage_hunter_tests.js b/test/lineage_hunter_tests.js index f689d049b..dac3f3f97 100644 --- a/test/lineage_hunter_tests.js +++ b/test/lineage_hunter_tests.js @@ -178,7 +178,7 @@ exports['lineage hunter '] = { lineage_hunter.cascade_pattern_states(pl); //assert - var consumerPatternReturned = pattern_assembler.findPartial('test-foo', pl); + var consumerPatternReturned = pattern_assembler.getPartial('test-foo', pl); test.equals(consumerPatternReturned.patternState, 'inreview'); test.done(); }, @@ -206,7 +206,7 @@ exports['lineage hunter '] = { lineage_hunter.cascade_pattern_states(pl); //assert - var consumerPatternReturned = pattern_assembler.findPartial('test-foo', pl); + var consumerPatternReturned = pattern_assembler.getPartial('test-foo', pl); test.equals(consumerPatternReturned.lineage[0].lineageState, 'inreview'); test.done(); }, @@ -233,7 +233,7 @@ exports['lineage hunter '] = { lineage_hunter.cascade_pattern_states(pl); //assert - var consumedPatternReturned = pattern_assembler.findPartial('test-bar', pl); + var consumedPatternReturned = pattern_assembler.getPartial('test-bar', pl); test.equals(consumedPatternReturned.lineageR[0].lineageState, 'inreview'); test.done(); @@ -261,7 +261,7 @@ exports['lineage hunter '] = { lineage_hunter.cascade_pattern_states(pl); //assert - var consumerPatternReturned = pattern_assembler.findPartial('test-foo', pl); + var consumerPatternReturned = pattern_assembler.getPartial('test-foo', pl); test.equals(consumerPatternReturned.lineage.length, 1); test.equals(consumerPatternReturned.lineage[0].lineageState, 'inreview'); test.equals(consumerPatternReturned.patternState, 'inreview'); diff --git a/test/list_item_hunter_tests.js b/test/list_item_hunter_tests.js index 2ab591d6f..8b5aac2df 100644 --- a/test/list_item_hunter_tests.js +++ b/test/list_item_hunter_tests.js @@ -151,24 +151,28 @@ var pattern1 = createFakeListPattern({ "template": "{{#listItems.one}}{{> 00-test/00-foo.mustache }}{{/listItems.one}}", "extendedTemplate" : "{{#listItems.one}}{{> 00-test/00-foo.mustache }}{{/listItems.one}}", - "key": "test-patternName1" + "patternPartial": "test-patternName1", + "relPath": "00-test/02-patternName1.mustache" }); var pattern2 = createFakeListPattern({ "template": "{{#listItems.two}}{{> 00-test/00-bar.mustache }}{{/listItems.two}}", "extendedTemplate" : "{{#listItems.two}}{{> 00-test/00-bar.mustache }}{{/listItems.two}}", - "key": "test-patternName2" + "patternPartial": "test-patternName2", + "relPath": "00-test/03-patternName2.mustache" }); var patternlab = createFakePatternLab({ "patterns": [ Pattern.create('00-test/00-foo.mustache', null, { "template": "{{ title }}", - "extendedTemplate": "{{ title }}" + "extendedTemplate": "{{ title }}", + "relPath": "00-test/00-foo.mustache" }), Pattern.create('00-test/00-bar.mustache', null, { "template": "{{ title }}", - "extendedTemplate": "{{ title }}" + "extendedTemplate": "{{ title }}", + "relPath": "00-test/00-bar.mustache" }) ] }); diff --git a/test/markdown_parser_tests.js b/test/markdown_parser_tests.js index 9a9998818..60ab16762 100644 --- a/test/markdown_parser_tests.js +++ b/test/markdown_parser_tests.js @@ -2,34 +2,46 @@ var path = require('path'); var fs = require('fs-extra'); -var eol = require('os').EOL; var mp = require('../core/lib/markdown_parser'); var markdown_parser = new mp(); exports['markdown_parser'] = { - 'parses pattern description block correctly when frontmatter not present' : function(test){ - //arrange + 'parses pattern description block correctly when frontmatter not present' : function(test) { + //arrange var markdownFileName = path.resolve("./test/files/_patterns/00-test/00-foo.md"); var markdownFileContents = fs.readFileSync(markdownFileName, 'utf8'); - //act - var returnObject = markdown_parser.parse(markdownFileContents) + //act + var returnObject = markdown_parser.parse(markdownFileContents); - //assert + //assert test.equals(returnObject.markdown, '
This pattern contains an include of test-bar. It also has this markdown file, which does not have frontmatter.
Foo cannot get simpler than bar, amiright?
\n'); test.equals(returnObject.status, 'complete'); test.done(); + }, + 'parses frontmatter only when no markdown present': function (test) { + //arrange + var markdownFileName = path.resolve("./test/files/_patterns/00-test/03-styled-atom.md"); + var markdownFileContents = fs.readFileSync(markdownFileName, 'utf8'); + + //act + var returnObject = markdown_parser.parse(markdownFileContents); + + //assert + test.equals(returnObject.markdown, ''); + test.equals(returnObject.status, 'inprogress'); + test.done(); } }; diff --git a/test/object_factory_tests.js b/test/object_factory_tests.js index cec3172ae..b3fb433cb 100644 --- a/test/object_factory_tests.js +++ b/test/object_factory_tests.js @@ -71,55 +71,4 @@ } }; - exports['oPatternType initialization'] = { - 'test oPatternType initializes correctly' : function(test){ - var b = new of.oPatternType('test'); - test.equals(b.patternTypeLC, 'test'); - test.equals(b.patternTypeUC, 'Test'); - test.equals(b.patternTypeItems.length, 0); - test.equals(b.patternTypeItemsIndex.length, 0); - test.equals(b.patternItems.length, 0); - test.equals(b.patternItemsIndex.length, 0); - test.done(); - }, - 'test oPatternType capitalizes patternTypeUC' : function(test){ - var b = new of.oPatternType('page-templates'); - test.equals(b.patternTypeLC, 'page-templates'); - test.equals(b.patternTypeUC, 'Page Templates'); - test.done(); - } - }; - - exports['oPatternSubType initialization'] = { - 'test oPatternSubType initializes correctly' : function(test){ - var ni = new of.oPatternSubType('test'); - test.equals(ni.patternSubtypeLC, 'test'); - test.equals(ni.patternSubtypeUC, 'Test'); - test.equals(ni.patternSubtypeItems.length, 0); - test.equals(ni.patternSubtypeItemsIndex.length, 0); - test.done(); - }, - 'test oPatternSubType correctly capitalizes sectionNameUC' : function(test){ - var ni = new of.oPatternSubType('global-concepts'); - test.equals(ni.patternSubtypeLC, 'global-concepts'); - test.equals(ni.patternSubtypeUC, 'Global Concepts'); - test.done(); - } - }; - - exports['oPatternSubTypeItem initialization'] = { - 'test oPatternSubTypeItem initializes correctly' : function(test){ - var sni = new of.oPatternSubTypeItem('test'); - test.equals(sni.patternName, 'Test'); - test.equals(sni.patternPath, ''); - test.equals(sni.patternPartialCode, ''); - test.done(); - }, - 'test oPatternSubTypeItem capitalizes patternName' : function(test){ - var sni = new of.oPatternSubTypeItem('nav button'); - test.equals(sni.patternName, 'Nav Button'); - test.done(); - } - }; - }()); diff --git a/test/parameter_hunter_tests.js b/test/parameter_hunter_tests.js index 1019d5569..7db9ec85d 100644 --- a/test/parameter_hunter_tests.js +++ b/test/parameter_hunter_tests.js @@ -6,6 +6,7 @@ //setup current pattern from what we would have during execution function currentPatternClosure() { return { + "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", @@ -28,6 +29,7 @@ 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", diff --git a/test/pattern_assembler_tests.js b/test/pattern_assembler_tests.js index b0fa5e4a0..4bb4f8690 100644 --- a/test/pattern_assembler_tests.js +++ b/test/pattern_assembler_tests.js @@ -419,7 +419,7 @@ }); //act - var result = pattern_assembler.findPartial('character-han', patternlab); + var result = pattern_assembler.getPartial('character-han', patternlab); //assert test.equals(result, patternlab.patterns[0]); test.done(); @@ -441,7 +441,7 @@ }); //act - var result = pattern_assembler.findPartial('molecules-primary-nav', patternlab); + var result = pattern_assembler.getPartial('molecules-primary-nav', patternlab); //assert test.equals(result, patternlab.patterns[1]); test.done(); diff --git a/test/ui_builder_tests.js b/test/ui_builder_tests.js new file mode 100644 index 000000000..2273f0010 --- /dev/null +++ b/test/ui_builder_tests.js @@ -0,0 +1,237 @@ +"use strict"; + +var eol = require('os').EOL; +var Pattern = require('../core/lib/object_factory').Pattern; +var extend = require('util')._extend; + +function createFakePatternLab(customProps) { + var pl = { + config: { + paths: { + source: { + patterns: './test/files/_patterns' + }, + public: { + patterns: './test/output' + } + }, + styleGuideExcludes: [ 'templates' ], + debug: false + } + }; + return extend(pl, customProps); +} + +var ui = require('../core/lib/ui_builder')(); + +exports['ui_builder'] = { + + 'isPatternExcluded - returns true when pattern filename starts with underscore': function (test) { + //arrange + var patternlab = createFakePatternLab({}); + var pattern = new Pattern('00-test/_ignored-pattern.mustache'); + + //act + var result = ui.isPatternExcluded(pattern, patternlab); + + //assert + test.equals(result, true); + test.done(); + }, + + 'isPatternExcluded - returns true when pattern is defaultPattern': function (test) { + //arrange + var patternlab = createFakePatternLab({}); + var pattern = new Pattern('00-test/foo.mustache'); + patternlab.config.defaultPattern = 'test-foo'; + + //act + var result = ui.isPatternExcluded(pattern, patternlab); + + //assert + test.equals(result, true); + test.done(); + }, + + 'isPatternExcluded - returns true when patterngroup is specified in styleguideExcludes': function (test) { + //arrange + var patternlab = createFakePatternLab({}); + var pattern = new Pattern('00-test/foo.mustache'); + patternlab.config.defaultPattern = 'test-boaz'; + patternlab.config.styleGuideExcludes.push('test'); + + //act + var result = ui.isPatternExcluded(pattern, patternlab); + + //assert + test.equals(result, true); + test.done(); + }, + + 'isPatternExcluded - returns true when pattern within underscored directory - top level': function (test) { + //arrange + var patternlab = createFakePatternLab({}); + var pattern = Pattern.createEmpty({ + relPath: '_hidden/patternsubtype/foo.mustache', + isPattern: true, + fileName : 'foo.mustache', + patternPartial: 'hidden-foo' + }); + + //act + var result = ui.isPatternExcluded(pattern, patternlab); + + //assert + test.equals(result, true); + test.done(); + }, + + 'isPatternExcluded - returns true when pattern within underscored directory - subtype level': function (test) { + //arrange + var patternlab = createFakePatternLab({}); + var pattern = Pattern.createEmpty({ + relPath: 'shown/_patternsubtype/foo.mustache', + isPattern: true, + fileName : 'foo.mustache', + patternPartial: 'shown-foo' + }); + + //act + var result = ui.isPatternExcluded(pattern, patternlab); + + //assert + test.equals(result, true); + test.done(); + }, + + 'groupPatterns - creates pattern groups correctly': function (test) { + //arrange + var patternlab = createFakePatternLab({ + patterns: [], + patternGroups: {}, + subtypePatterns: {} + }); + + patternlab.patterns.push( + new Pattern('00-test/foo.mustache'), + new Pattern('00-test/bar.mustache'), + new Pattern('patternType1/patternSubType1/blue.mustache'), + new Pattern('patternType1/patternSubType1/red.mustache'), + new Pattern('patternType1/patternSubType1/yellow.mustache'), + new Pattern('patternType1/patternSubType2/black.mustache'), + new Pattern('patternType1/patternSubType2/grey.mustache'), + new Pattern('patternType1/patternSubType2/white.mustache') + ); + + //act + var result = ui.groupPatterns(patternlab); + + //assert + test.equals(result.patternGroups.patternType1.patternSubType1.blue.patternPartial, 'patternType1-blue'); + test.equals(result.patternGroups.patternType1.patternSubType1.red.patternPartial, 'patternType1-red'); + test.equals(result.patternGroups.patternType1.patternSubType1.yellow.patternPartial, 'patternType1-yellow'); + test.equals(result.patternGroups.patternType1.patternSubType2.black.patternPartial, 'patternType1-black'); + test.equals(result.patternGroups.patternType1.patternSubType2.grey.patternPartial, 'patternType1-grey'); + test.equals(result.patternGroups.patternType1.patternSubType2.white.patternPartial, 'patternType1-white'); + + test.equals(patternlab.patternTypes[0].patternItems[0].patternPartial, 'test-bar'); + test.equals(patternlab.patternTypes[0].patternItems[1].patternPartial, 'test-foo'); + + //todo: patternlab.patternTypes[0].patternItems[1] looks malformed + + test.done(); + }, + + 'groupPatterns - creates documentation patterns for each type and subtype if not exists': function (test) { + //arrange + var patternlab = createFakePatternLab({ + patterns: [], + patternGroups: {}, + subtypePatterns: {} + }); + + patternlab.patterns.push( + new Pattern('00-test/foo.mustache'), + new Pattern('00-test/bar.mustache'), + new Pattern('patternType1/patternSubType1/blue.mustache'), + new Pattern('patternType1/patternSubType1/red.mustache'), + new Pattern('patternType1/patternSubType1/yellow.mustache'), + new Pattern('patternType1/patternSubType2/black.mustache'), + new Pattern('patternType1/patternSubType2/grey.mustache'), + new Pattern('patternType1/patternSubType2/white.mustache') + ); + + //act + var result = ui.groupPatterns(patternlab); + + //assert + test.equals(result.patternGroups.patternType1.patternSubType1['viewall-patternType1-patternSubType1'].patternPartial, 'viewall-patternType1-patternSubType1'); + test.equals(result.patternGroups.patternType1.patternSubType2['viewall-patternType1-patternSubType2'].patternPartial, 'viewall-patternType1-patternSubType2'); + + test.done(); + }, + + 'groupPatterns - adds each pattern to the patternPaths object': function (test) { + //arrange + var patternlab = createFakePatternLab({ + patterns: [], + patternGroups: {}, + subtypePatterns: {} + }); + + patternlab.patterns.push( + new Pattern('00-test/foo.mustache'), + new Pattern('00-test/bar.mustache'), + new Pattern('patternType1/patternSubType1/blue.mustache'), + new Pattern('patternType1/patternSubType1/red.mustache'), + new Pattern('patternType1/patternSubType1/yellow.mustache'), + new Pattern('patternType1/patternSubType2/black.mustache'), + new Pattern('patternType1/patternSubType2/grey.mustache'), + new Pattern('patternType1/patternSubType2/white.mustache') + ); + + //act + var result = ui.groupPatterns(patternlab); + + //assert + test.equals(patternlab.patternPaths['test']['foo'], '00-test-foo'); + test.equals(patternlab.patternPaths['test']['bar'], '00-test-bar'); + test.equals(patternlab.patternPaths['patternType1']['blue'], 'patternType1-patternSubType1-blue'); + test.equals(patternlab.patternPaths['patternType1']['red'], 'patternType1-patternSubType1-red'); + test.equals(patternlab.patternPaths['patternType1']['yellow'], 'patternType1-patternSubType1-yellow'); + test.equals(patternlab.patternPaths['patternType1']['black'], 'patternType1-patternSubType2-black'); + test.equals(patternlab.patternPaths['patternType1']['grey'], 'patternType1-patternSubType2-grey'); + test.equals(patternlab.patternPaths['patternType1']['white'], 'patternType1-patternSubType2-white'); + + test.done(); + }, + + 'groupPatterns - adds each pattern to the view all paths object': function (test) { + //arrange + var patternlab = createFakePatternLab({ + patterns: [], + patternGroups: {}, + subtypePatterns: {} + }); + + patternlab.patterns.push( + new Pattern('00-test/foo.mustache'), + new Pattern('00-test/bar.mustache'), + new Pattern('patternType1/patternSubType1/blue.mustache'), + new Pattern('patternType1/patternSubType1/red.mustache'), + new Pattern('patternType1/patternSubType1/yellow.mustache'), + new Pattern('patternType1/patternSubType2/black.mustache'), + new Pattern('patternType1/patternSubType2/grey.mustache'), + new Pattern('patternType1/patternSubType2/white.mustache') + ); + + //act + var result = ui.groupPatterns(patternlab); + + //assert + test.equals('todo', 'todo'); + + test.done(); + } + +};