Skip to content

Commit 86233ca

Browse files
committed
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
1 parent cd4bace commit 86233ca

File tree

3 files changed

+449
-432
lines changed

3 files changed

+449
-432
lines changed

core/index.js

Lines changed: 380 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,380 @@
1+
/*
2+
* patternlab-node https://github.com/pattern-lab/patternlab-node
3+
*
4+
* Brian Muenzenmeyer, Geoff Pursell, Raphael Okon, tburny and the web community.
5+
* Licensed under the MIT license.
6+
*
7+
* Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice.
8+
*
9+
*/
10+
11+
"use strict";
12+
13+
const packageInfo = require('../package.json');
14+
15+
const path = require('path');
16+
const updateNotifier = require('update-notifier');
17+
18+
const logger = require('./lib/log');
19+
const PatternGraph = require('./lib/pattern_graph').PatternGraph;
20+
const CompileState = require('./lib/object_factory').CompileState;
21+
const pa = require('./lib/pattern_assembler');
22+
const pe = require('./lib/pattern_exporter');
23+
const lh = require('./lib/lineage_hunter');
24+
25+
const PatternLab = require('./lib/patternlab');
26+
27+
let fs = require('fs-extra'); // eslint-disable-line
28+
let ui_builder = require('./lib/ui_builder'); // eslint-disable-line
29+
let assetCopier = require('./lib/asset_copy'); // eslint-disable-line
30+
let pattern_exporter = new pe(); // eslint-disable-line
31+
let serve = require('./lib/serve'); // eslint-disable-line
32+
33+
const pattern_assembler = new pa();
34+
const lineage_hunter = new lh();
35+
36+
//bootstrap update notifier
37+
updateNotifier({
38+
pkg: packageInfo,
39+
updateCheckInterval: 1000 * 60 * 60 * 24 // notify at most once a day
40+
}).notify();
41+
42+
const patternlab_module = function (config) {
43+
const patternlab = new PatternLab(config);
44+
const paths = patternlab.config.paths;
45+
46+
function help() {
47+
48+
logger.info('');
49+
50+
logger.info('|=======================================|');
51+
logger.debug(' Pattern Lab Node Help v' + patternlab.package.version);
52+
logger.info('|=======================================|');
53+
54+
logger.info('');
55+
logger.info('API - usually consumed by an edition');
56+
logger.info('');
57+
58+
logger.debug(' patternlab:build');
59+
logger.info(' > Compiles the patterns and frontend, outputting to config.paths.public');
60+
logger.info('');
61+
62+
logger.debug(' patternlab:patternsonly');
63+
logger.info(' > Compiles the patterns only, outputting to config.paths.public');
64+
logger.info('');
65+
66+
logger.debug(' patternlab:version');
67+
logger.info(' > Return the version of patternlab-node you have installed');
68+
logger.info('');
69+
70+
logger.debug(' patternlab:help');
71+
logger.info(' > Get more information about patternlab-node, pattern lab in general, and where to report issues.');
72+
logger.info('');
73+
74+
logger.debug(' patternlab:liststarterkits');
75+
logger.info(' > Returns a url with the list of available starterkits hosted on the Pattern Lab organization Github account');
76+
logger.info('');
77+
78+
logger.debug(' patternlab:loadstarterkit');
79+
logger.info(' > Load a starterkit into config.paths.source/*');
80+
logger.info(' > NOTE: Overwrites existing content, and only cleans out existing directory if --clean=true argument is passed.');
81+
logger.info(' > NOTE: In most cases, `npm install starterkit-name` will precede this call.');
82+
logger.info(' > arguments:');
83+
logger.info(' -- kit ');
84+
logger.info(' > the name of the starter kit to load');
85+
logger.info(' -- clean ');
86+
logger.info(' > removes all files from config.paths.source/ prior to load');
87+
logger.info(' > example (gulp):');
88+
logger.info(' `gulp patternlab:loadstarterkit --kit=starterkit-mustache-demo`');
89+
logger.info('');
90+
91+
logger.info('===============================');
92+
logger.info('');
93+
logger.info('Visit http://patternlab.io/ for more info about Pattern Lab');
94+
logger.info('Visit https://github.com/pattern-lab/patternlab-node/issues to open an issue.');
95+
logger.info('Visit https://github.com/pattern-lab/patternlab-node/wiki to view the changelog, roadmap, and other info.');
96+
logger.info('');
97+
logger.info('===============================');
98+
}
99+
100+
/**
101+
* If a graph was serialized and then {@code deletePatternDir == true}, there is a mismatch in the
102+
* pattern metadata and not all patterns might be recompiled.
103+
* For that reason an empty graph is returned in this case, so every pattern will be flagged as
104+
* "needs recompile". Otherwise the pattern graph is loaded from the meta data.
105+
*
106+
* @param patternlab
107+
* @param {boolean} deletePatternDir When {@code true}, an empty graph is returned
108+
* @return {PatternGraph}
109+
*/
110+
function loadPatternGraph(deletePatternDir) {
111+
// Sanity check to prevent problems when code is refactored
112+
if (deletePatternDir) {
113+
return PatternGraph.empty();
114+
}
115+
return PatternGraph.loadFromFile(patternlab);
116+
}
117+
118+
function cleanBuildDirectory(incrementalBuildsEnabled) {
119+
if (incrementalBuildsEnabled) {
120+
logger.log.info("Incremental builds enabled.");
121+
} else {
122+
// needs to be done BEFORE processing patterns
123+
fs.removeSync(paths.public.patterns);
124+
fs.emptyDirSync(paths.public.patterns);
125+
}
126+
}
127+
128+
function buildPatterns(deletePatternDir) {
129+
patternlab.events.emit('patternlab-build-pattern-start', patternlab);
130+
131+
//
132+
// CHECK INCREMENTAL BUILD GRAPH
133+
//
134+
const graph = patternlab.graph = loadPatternGraph(deletePatternDir);
135+
const graphNeedsUpgrade = !PatternGraph.checkVersion(graph);
136+
if (graphNeedsUpgrade) {
137+
logger.log.info("Due to an upgrade, a complete rebuild is required and the public/patterns directory was deleted. " +
138+
"Incremental build is available again on the next successful run.");
139+
140+
// Ensure that the freshly built graph has the latest version again.
141+
patternlab.graph.upgradeVersion();
142+
}
143+
144+
// Flags
145+
patternlab.incrementalBuildsEnabled = !(deletePatternDir || graphNeedsUpgrade);
146+
147+
//
148+
// CLEAN BUILD DIRECTORY, maybe
149+
//
150+
cleanBuildDirectory(patternlab.incrementalBuildsEnabled);
151+
152+
patternlab.buildGlobalData();
153+
154+
// diveSync once to perform iterative populating of patternlab object
155+
return patternlab.processAllPatternsIterative(paths.source.patterns, patternlab).then(() => {
156+
157+
patternlab.events.emit('patternlab-pattern-iteration-end', patternlab);
158+
159+
//now that all the main patterns are known, look for any links that might be within data and expand them
160+
//we need to do this before expanding patterns & partials into extendedTemplates, otherwise we could lose the data -> partial reference
161+
pattern_assembler.parse_data_links(patternlab);
162+
163+
//diveSync again to recursively include partials, filling out the
164+
//extendedTemplate property of the patternlab.patterns elements
165+
// TODO we can reduce the time needed by only processing changed patterns and their partials
166+
patternlab.processAllPatternsRecursive(paths.source.patterns, patternlab);
167+
168+
//take the user defined head and foot and process any data and patterns that apply
169+
// GTP: should these really be invoked from outside?
170+
patternlab.processHeadPattern();
171+
patternlab.processFootPattern();
172+
173+
//cascade any patternStates
174+
lineage_hunter.cascade_pattern_states(patternlab);
175+
176+
//set pattern-specific header if necessary
177+
let head;
178+
if (patternlab.userHead) {
179+
head = patternlab.userHead;
180+
} else {
181+
head = patternlab.header;
182+
}
183+
184+
//set the pattern-specific header by compiling the general-header with data, and then adding it to the meta header
185+
patternlab.data.patternLabHead = pattern_assembler.renderPattern(patternlab.header, {
186+
cacheBuster: patternlab.cacheBuster
187+
});
188+
189+
// If deletePatternDir == true or graph needs to be updated
190+
// rebuild all patterns
191+
let patternsToBuild = null;
192+
193+
// If deletePatternDir == true or graph needs to be updated
194+
// rebuild all patterns
195+
patternsToBuild = null;
196+
197+
if (patternlab.incrementalBuildsEnabled) {
198+
// When the graph was loaded from file, some patterns might have been moved/deleted between runs
199+
// so the graph data become out of sync
200+
patternlab.graph.sync().forEach(n => {
201+
logger.log.info("[Deleted/Moved] " + n);
202+
});
203+
204+
// TODO Find created or deleted files
205+
const now = new Date().getTime();
206+
pattern_assembler.mark_modified_patterns(now, patternlab);
207+
patternsToBuild = patternlab.graph.compileOrder();
208+
} else {
209+
// build all patterns, mark all to be rebuilt
210+
patternsToBuild = patternlab.patterns;
211+
for (const p of patternsToBuild) {
212+
p.compileState = CompileState.NEEDS_REBUILD;
213+
}
214+
}
215+
216+
//render all patterns last, so lineageR works
217+
return patternsToBuild
218+
.reduce((previousPromise, pattern) => {
219+
return previousPromise.then(() => patternlab.renderSinglePattern(pattern, head));
220+
}, Promise.resolve())
221+
.then(() => {
222+
// Saves the pattern graph when all files have been compiled
223+
PatternGraph.storeToFile(patternlab);
224+
if (patternlab.config.exportToGraphViz) {
225+
PatternGraph.exportToDot(patternlab, "dependencyGraph.dot");
226+
logger.log.info(`Exported pattern graph to ${path.join(config.paths.public.root, "dependencyGraph.dot")}`);
227+
}
228+
229+
//export patterns if necessary
230+
pattern_exporter.export_patterns(patternlab);
231+
});
232+
}).catch((err) => {
233+
logger.info('Error in buildPatterns():', err);
234+
});
235+
}
236+
237+
return {
238+
/**
239+
* logs current version
240+
*
241+
* @returns {void} current patternlab-node version as defined in package.json, as console output
242+
*/
243+
version: function () {
244+
return patternlab.logVersion();
245+
},
246+
247+
/**
248+
* return current version
249+
*
250+
* @returns {string} current patternlab-node version as defined in package.json, as string
251+
*/
252+
v: function () {
253+
return patternlab.getVersion();
254+
},
255+
256+
/**
257+
* build patterns, copy assets, and construct ui
258+
*
259+
* @param {function} callback a function invoked when build is complete
260+
* @param {object} options an object used to control build behavior
261+
* @returns {Promise} a promise fulfilled when build is complete
262+
*/
263+
build: function (callback, options) {
264+
if (patternlab && patternlab.isBusy) {
265+
logger.info('Pattern Lab is busy building a previous run - returning early.');
266+
return Promise.resolve();
267+
}
268+
patternlab.isBusy = true;
269+
return buildPatterns(options.cleanPublic).then(() => {
270+
271+
new ui_builder().buildFrontend(patternlab);
272+
assetCopier().copyAssets(patternlab.config.paths, patternlab, options);
273+
274+
this.events.on('patternlab-pattern-change', () => {
275+
if (!patternlab.isBusy) {
276+
options.cleanPublic = false;
277+
return this.build(callback, options);
278+
}
279+
return Promise.resolve();
280+
});
281+
282+
this.events.on('patternlab-global-change', () => {
283+
if (!patternlab.isBusy) {
284+
options.cleanPublic = true; //rebuild everything
285+
return this.build(callback, options);
286+
}
287+
return Promise.resolve();
288+
});
289+
290+
patternlab.isBusy = false;
291+
callback();
292+
});
293+
},
294+
295+
/**
296+
* logs usage
297+
*
298+
* @returns {void} pattern lab API usage, as console output
299+
*/
300+
help: function () {
301+
help();
302+
},
303+
304+
/**
305+
* build patterns only, leaving existing public files intact
306+
*
307+
* @param {function} callback a function invoked when build is complete
308+
* @param {object} options an object used to control build behavior
309+
* @returns {Promise} a promise fulfilled when build is complete
310+
*/
311+
patternsonly: function (callback, options) {
312+
if (patternlab && patternlab.isBusy) {
313+
logger.info('Pattern Lab is busy building a previous run - returning early.');
314+
return Promise.resolve();
315+
}
316+
patternlab.isBusy = true;
317+
return buildPatterns(options.cleanPublic).then(() => {
318+
patternlab.isBusy = false;
319+
callback();
320+
});
321+
},
322+
323+
/**
324+
* fetches starterkit repos from pattern-lab github org that contain 'starterkit' in their name
325+
*
326+
* @returns {Promise} Returns an Array<{name,url}> for the starterkit repos
327+
*/
328+
liststarterkits: function () {
329+
return patternlab.listStarterkits();
330+
},
331+
332+
/**
333+
* load starterkit already available via `node_modules/`
334+
*
335+
* @param {string} starterkitName name of starterkit
336+
* @param {boolean} clean whether or not to delete contents of source/ before load
337+
* @returns {void}
338+
*/
339+
loadstarterkit: function (starterkitName, clean) {
340+
patternlab.loadStarterKit(starterkitName, clean);
341+
},
342+
343+
344+
/**
345+
* install plugin already available via `node_modules/`
346+
*
347+
* @param {string} pluginName name of plugin
348+
* @returns {void}
349+
*/
350+
installplugin: function (pluginName) {
351+
patternlab.installPlugin(pluginName);
352+
},
353+
354+
/**
355+
* returns all file extensions supported by installed PatternEngines
356+
*
357+
* @returns {Array<string>} all supported file extensions
358+
*/
359+
getSupportedTemplateExtensions: function () {
360+
return patternlab.getSupportedTemplateExtensions();
361+
},
362+
363+
/**
364+
* build patterns, copy assets, and construct ui, watch source files, and serve locally
365+
*
366+
* @param {object} options an object used to control build, copy, and serve behavior
367+
* @returns {Promise} TODO: validate
368+
*/
369+
serve: function (options) {
370+
options.watch = true;
371+
return this.build(() => {}, options).then(function () {
372+
serve(patternlab);
373+
});
374+
},
375+
376+
events: patternlab.events
377+
};
378+
};
379+
380+
module.exports = patternlab_module;

0 commit comments

Comments
 (0)