-
Notifications
You must be signed in to change notification settings - Fork 407
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(patternlab): Separate patternlab.js into two files
* index.js will contain the trimmed down cleaner public entry point * patternlab.js is now just the class * no more free functions * code is rendering correctly, with only 5 unit test failures * still work to do to potentially move and re-org some files
- Loading branch information
1 parent
cd4bace
commit 86233ca
Showing
3 changed files
with
449 additions
and
432 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,380 @@ | ||
/* | ||
* patternlab-node https://github.com/pattern-lab/patternlab-node | ||
* | ||
* Brian Muenzenmeyer, Geoff Pursell, Raphael Okon, tburny and the web community. | ||
* Licensed under the MIT license. | ||
* | ||
* Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice. | ||
* | ||
*/ | ||
|
||
"use strict"; | ||
|
||
const packageInfo = require('../package.json'); | ||
|
||
const path = require('path'); | ||
const updateNotifier = require('update-notifier'); | ||
|
||
const logger = require('./lib/log'); | ||
const PatternGraph = require('./lib/pattern_graph').PatternGraph; | ||
const CompileState = require('./lib/object_factory').CompileState; | ||
const pa = require('./lib/pattern_assembler'); | ||
const pe = require('./lib/pattern_exporter'); | ||
const lh = require('./lib/lineage_hunter'); | ||
|
||
const PatternLab = require('./lib/patternlab'); | ||
|
||
let fs = require('fs-extra'); // eslint-disable-line | ||
let ui_builder = require('./lib/ui_builder'); // eslint-disable-line | ||
let assetCopier = require('./lib/asset_copy'); // eslint-disable-line | ||
let pattern_exporter = new pe(); // eslint-disable-line | ||
let serve = require('./lib/serve'); // eslint-disable-line | ||
|
||
const pattern_assembler = new pa(); | ||
const lineage_hunter = new lh(); | ||
|
||
//bootstrap update notifier | ||
updateNotifier({ | ||
pkg: packageInfo, | ||
updateCheckInterval: 1000 * 60 * 60 * 24 // notify at most once a day | ||
}).notify(); | ||
|
||
const patternlab_module = function (config) { | ||
const patternlab = new PatternLab(config); | ||
const paths = patternlab.config.paths; | ||
|
||
function help() { | ||
|
||
logger.info(''); | ||
|
||
logger.info('|=======================================|'); | ||
logger.debug(' Pattern Lab Node Help v' + patternlab.package.version); | ||
logger.info('|=======================================|'); | ||
|
||
logger.info(''); | ||
logger.info('API - usually consumed by an edition'); | ||
logger.info(''); | ||
|
||
logger.debug(' patternlab:build'); | ||
logger.info(' > Compiles the patterns and frontend, outputting to config.paths.public'); | ||
logger.info(''); | ||
|
||
logger.debug(' patternlab:patternsonly'); | ||
logger.info(' > Compiles the patterns only, outputting to config.paths.public'); | ||
logger.info(''); | ||
|
||
logger.debug(' patternlab:version'); | ||
logger.info(' > Return the version of patternlab-node you have installed'); | ||
logger.info(''); | ||
|
||
logger.debug(' patternlab:help'); | ||
logger.info(' > Get more information about patternlab-node, pattern lab in general, and where to report issues.'); | ||
logger.info(''); | ||
|
||
logger.debug(' patternlab:liststarterkits'); | ||
logger.info(' > Returns a url with the list of available starterkits hosted on the Pattern Lab organization Github account'); | ||
logger.info(''); | ||
|
||
logger.debug(' patternlab:loadstarterkit'); | ||
logger.info(' > Load a starterkit into config.paths.source/*'); | ||
logger.info(' > NOTE: Overwrites existing content, and only cleans out existing directory if --clean=true argument is passed.'); | ||
logger.info(' > NOTE: In most cases, `npm install starterkit-name` will precede this call.'); | ||
logger.info(' > arguments:'); | ||
logger.info(' -- kit '); | ||
logger.info(' > the name of the starter kit to load'); | ||
logger.info(' -- clean '); | ||
logger.info(' > removes all files from config.paths.source/ prior to load'); | ||
logger.info(' > example (gulp):'); | ||
logger.info(' `gulp patternlab:loadstarterkit --kit=starterkit-mustache-demo`'); | ||
logger.info(''); | ||
|
||
logger.info('==============================='); | ||
logger.info(''); | ||
logger.info('Visit http://patternlab.io/ for more info about Pattern Lab'); | ||
logger.info('Visit https://github.com/pattern-lab/patternlab-node/issues to open an issue.'); | ||
logger.info('Visit https://github.com/pattern-lab/patternlab-node/wiki to view the changelog, roadmap, and other info.'); | ||
logger.info(''); | ||
logger.info('==============================='); | ||
} | ||
|
||
/** | ||
* If a graph was serialized and then {@code deletePatternDir == true}, there is a mismatch in the | ||
* pattern metadata and not all patterns might be recompiled. | ||
* For that reason an empty graph is returned in this case, so every pattern will be flagged as | ||
* "needs recompile". Otherwise the pattern graph is loaded from the meta data. | ||
* | ||
* @param patternlab | ||
* @param {boolean} deletePatternDir When {@code true}, an empty graph is returned | ||
* @return {PatternGraph} | ||
*/ | ||
function loadPatternGraph(deletePatternDir) { | ||
// Sanity check to prevent problems when code is refactored | ||
if (deletePatternDir) { | ||
return PatternGraph.empty(); | ||
} | ||
return PatternGraph.loadFromFile(patternlab); | ||
} | ||
|
||
function cleanBuildDirectory(incrementalBuildsEnabled) { | ||
if (incrementalBuildsEnabled) { | ||
logger.log.info("Incremental builds enabled."); | ||
} else { | ||
// needs to be done BEFORE processing patterns | ||
fs.removeSync(paths.public.patterns); | ||
fs.emptyDirSync(paths.public.patterns); | ||
} | ||
} | ||
|
||
function buildPatterns(deletePatternDir) { | ||
patternlab.events.emit('patternlab-build-pattern-start', patternlab); | ||
|
||
// | ||
// CHECK INCREMENTAL BUILD GRAPH | ||
// | ||
const graph = patternlab.graph = loadPatternGraph(deletePatternDir); | ||
const graphNeedsUpgrade = !PatternGraph.checkVersion(graph); | ||
if (graphNeedsUpgrade) { | ||
logger.log.info("Due to an upgrade, a complete rebuild is required and the public/patterns directory was deleted. " + | ||
"Incremental build is available again on the next successful run."); | ||
|
||
// Ensure that the freshly built graph has the latest version again. | ||
patternlab.graph.upgradeVersion(); | ||
} | ||
|
||
// Flags | ||
patternlab.incrementalBuildsEnabled = !(deletePatternDir || graphNeedsUpgrade); | ||
|
||
// | ||
// CLEAN BUILD DIRECTORY, maybe | ||
// | ||
cleanBuildDirectory(patternlab.incrementalBuildsEnabled); | ||
|
||
patternlab.buildGlobalData(); | ||
|
||
// diveSync once to perform iterative populating of patternlab object | ||
return patternlab.processAllPatternsIterative(paths.source.patterns, patternlab).then(() => { | ||
|
||
patternlab.events.emit('patternlab-pattern-iteration-end', patternlab); | ||
|
||
//now that all the main patterns are known, look for any links that might be within data and expand them | ||
//we need to do this before expanding patterns & partials into extendedTemplates, otherwise we could lose the data -> partial reference | ||
pattern_assembler.parse_data_links(patternlab); | ||
|
||
//diveSync again to recursively include partials, filling out the | ||
//extendedTemplate property of the patternlab.patterns elements | ||
// TODO we can reduce the time needed by only processing changed patterns and their partials | ||
patternlab.processAllPatternsRecursive(paths.source.patterns, patternlab); | ||
|
||
//take the user defined head and foot and process any data and patterns that apply | ||
// GTP: should these really be invoked from outside? | ||
patternlab.processHeadPattern(); | ||
patternlab.processFootPattern(); | ||
|
||
//cascade any patternStates | ||
lineage_hunter.cascade_pattern_states(patternlab); | ||
|
||
//set pattern-specific header if necessary | ||
let head; | ||
if (patternlab.userHead) { | ||
head = patternlab.userHead; | ||
} else { | ||
head = patternlab.header; | ||
} | ||
|
||
//set the pattern-specific header by compiling the general-header with data, and then adding it to the meta header | ||
patternlab.data.patternLabHead = pattern_assembler.renderPattern(patternlab.header, { | ||
cacheBuster: patternlab.cacheBuster | ||
}); | ||
|
||
// If deletePatternDir == true or graph needs to be updated | ||
// rebuild all patterns | ||
let patternsToBuild = null; | ||
|
||
// If deletePatternDir == true or graph needs to be updated | ||
// rebuild all patterns | ||
patternsToBuild = null; | ||
|
||
if (patternlab.incrementalBuildsEnabled) { | ||
// When the graph was loaded from file, some patterns might have been moved/deleted between runs | ||
// so the graph data become out of sync | ||
patternlab.graph.sync().forEach(n => { | ||
logger.log.info("[Deleted/Moved] " + n); | ||
}); | ||
|
||
// TODO Find created or deleted files | ||
const now = new Date().getTime(); | ||
pattern_assembler.mark_modified_patterns(now, patternlab); | ||
patternsToBuild = patternlab.graph.compileOrder(); | ||
} else { | ||
// build all patterns, mark all to be rebuilt | ||
patternsToBuild = patternlab.patterns; | ||
for (const p of patternsToBuild) { | ||
p.compileState = CompileState.NEEDS_REBUILD; | ||
} | ||
} | ||
|
||
//render all patterns last, so lineageR works | ||
return patternsToBuild | ||
.reduce((previousPromise, pattern) => { | ||
return previousPromise.then(() => patternlab.renderSinglePattern(pattern, head)); | ||
}, Promise.resolve()) | ||
.then(() => { | ||
// Saves the pattern graph when all files have been compiled | ||
PatternGraph.storeToFile(patternlab); | ||
if (patternlab.config.exportToGraphViz) { | ||
PatternGraph.exportToDot(patternlab, "dependencyGraph.dot"); | ||
logger.log.info(`Exported pattern graph to ${path.join(config.paths.public.root, "dependencyGraph.dot")}`); | ||
} | ||
|
||
//export patterns if necessary | ||
pattern_exporter.export_patterns(patternlab); | ||
}); | ||
}).catch((err) => { | ||
logger.info('Error in buildPatterns():', err); | ||
}); | ||
} | ||
|
||
return { | ||
/** | ||
* logs current version | ||
* | ||
* @returns {void} current patternlab-node version as defined in package.json, as console output | ||
*/ | ||
version: function () { | ||
return patternlab.logVersion(); | ||
}, | ||
|
||
/** | ||
* return current version | ||
* | ||
* @returns {string} current patternlab-node version as defined in package.json, as string | ||
*/ | ||
v: function () { | ||
return patternlab.getVersion(); | ||
}, | ||
|
||
/** | ||
* build patterns, copy assets, and construct ui | ||
* | ||
* @param {function} callback a function invoked when build is complete | ||
* @param {object} options an object used to control build behavior | ||
* @returns {Promise} a promise fulfilled when build is complete | ||
*/ | ||
build: function (callback, options) { | ||
if (patternlab && patternlab.isBusy) { | ||
logger.info('Pattern Lab is busy building a previous run - returning early.'); | ||
return Promise.resolve(); | ||
} | ||
patternlab.isBusy = true; | ||
return buildPatterns(options.cleanPublic).then(() => { | ||
|
||
new ui_builder().buildFrontend(patternlab); | ||
assetCopier().copyAssets(patternlab.config.paths, patternlab, options); | ||
|
||
this.events.on('patternlab-pattern-change', () => { | ||
if (!patternlab.isBusy) { | ||
options.cleanPublic = false; | ||
return this.build(callback, options); | ||
} | ||
return Promise.resolve(); | ||
}); | ||
|
||
this.events.on('patternlab-global-change', () => { | ||
if (!patternlab.isBusy) { | ||
options.cleanPublic = true; //rebuild everything | ||
return this.build(callback, options); | ||
} | ||
return Promise.resolve(); | ||
}); | ||
|
||
patternlab.isBusy = false; | ||
callback(); | ||
}); | ||
}, | ||
|
||
/** | ||
* logs usage | ||
* | ||
* @returns {void} pattern lab API usage, as console output | ||
*/ | ||
help: function () { | ||
help(); | ||
}, | ||
|
||
/** | ||
* build patterns only, leaving existing public files intact | ||
* | ||
* @param {function} callback a function invoked when build is complete | ||
* @param {object} options an object used to control build behavior | ||
* @returns {Promise} a promise fulfilled when build is complete | ||
*/ | ||
patternsonly: function (callback, options) { | ||
if (patternlab && patternlab.isBusy) { | ||
logger.info('Pattern Lab is busy building a previous run - returning early.'); | ||
return Promise.resolve(); | ||
} | ||
patternlab.isBusy = true; | ||
return buildPatterns(options.cleanPublic).then(() => { | ||
patternlab.isBusy = false; | ||
callback(); | ||
}); | ||
}, | ||
|
||
/** | ||
* fetches starterkit repos from pattern-lab github org that contain 'starterkit' in their name | ||
* | ||
* @returns {Promise} Returns an Array<{name,url}> for the starterkit repos | ||
*/ | ||
liststarterkits: function () { | ||
return patternlab.listStarterkits(); | ||
}, | ||
|
||
/** | ||
* load starterkit already available via `node_modules/` | ||
* | ||
* @param {string} starterkitName name of starterkit | ||
* @param {boolean} clean whether or not to delete contents of source/ before load | ||
* @returns {void} | ||
*/ | ||
loadstarterkit: function (starterkitName, clean) { | ||
patternlab.loadStarterKit(starterkitName, clean); | ||
}, | ||
|
||
|
||
/** | ||
* install plugin already available via `node_modules/` | ||
* | ||
* @param {string} pluginName name of plugin | ||
* @returns {void} | ||
*/ | ||
installplugin: function (pluginName) { | ||
patternlab.installPlugin(pluginName); | ||
}, | ||
|
||
/** | ||
* returns all file extensions supported by installed PatternEngines | ||
* | ||
* @returns {Array<string>} all supported file extensions | ||
*/ | ||
getSupportedTemplateExtensions: function () { | ||
return patternlab.getSupportedTemplateExtensions(); | ||
}, | ||
|
||
/** | ||
* build patterns, copy assets, and construct ui, watch source files, and serve locally | ||
* | ||
* @param {object} options an object used to control build, copy, and serve behavior | ||
* @returns {Promise} TODO: validate | ||
*/ | ||
serve: function (options) { | ||
options.watch = true; | ||
return this.build(() => {}, options).then(function () { | ||
serve(patternlab); | ||
}); | ||
}, | ||
|
||
events: patternlab.events | ||
}; | ||
}; | ||
|
||
module.exports = patternlab_module; |
Oops, something went wrong.