neutron-adt
Version:
Neutron Atomic Design Tool
441 lines (363 loc) • 10.4 kB
JavaScript
var fse = require('fs-extra');
var klaw = require('klaw');
var path = require('path');
var util = require('util');
var handlebars = require('handlebars');
var object_merge = require('object-merge');
var sort_object = require('sort-object');
var beautify_html = require('js-beautify').html;
var settings = require('./settings');
var u = require('./utilities');
var partials = require('./partials')();
var layoutHandler = require('./layouts')();
var markup = require('./markup')();
var helpers = require('./helpers');
var engine = function (cb) {
'use strict';
var globalData = {};
var patternFiles = [];
var patternsData = {};
var outputCache = [];
var patternsTree = {
atoms: {},
molecules: {},
organisms: {},
templates: {},
pages: {}
};
u.log('Neutron: Start render', 'info');
function init () {
globalData = JSON.parse(fse.readFileSync(u.getAppPath(settings.paths.src.data, 'global.json'), settings.encode));
registerHelpers();
cleanPaths();
walkPartials();
}
/**
* Register all base helpers into handlebars
* */
function registerHelpers() {
handlebars.registerHelper(helpers);
}
function onEnd() {
partials.resetReverseDependencies();
if (cb) {
cb();
}
console.log('\x1b[32m')
console.timeEnd(' render duration');
console.log('\x1b[0m')
}
/**
* Returns true when extension is a pattern extension
* @return Boolean
*/
function isPatternExtension(extension) {
return extension === settings.fileExtension;
}
function getPatterns() {
setRenderHelper();
return klaw(u.getAppPath(settings.paths.src.patterns))
.on('data', function (file) {
if (isPatternExtension(path.extname(file.path))) {
patternFiles.push(file);
}
})
.on('end', writeFiles);
}
/**
* Return true when the file path should be ignored for render
* @partam path file path
* @return Boolen
*/
function shouldIgnoreFile(path) {
return path[0] === "_";
}
function writeFiles() {
try {
var totalFiles = patternFiles.length;
if (totalFiles === 0) {
return onEnd();
}
patternFiles.forEach(function (file, i) {
getData(file.path, function (data) {
fse.readFile(file.path, settings.encode, function(err, source) {
if (source) {
var extendedData = Object.assign({}, globalData, data);
if (shouldIgnoreFile(path.basename(file.path))) {
return true;
}
handlePattern({
file: file,
source: source,
data: extendedData
}, i === totalFiles - 1);
}
});
});
});
} catch (e) {
u.log(e, 'error');
}
}
function handlePattern(pattern, end) {
var partialData = partials.getPartialsData(pattern);
var newData = partialData.data;
var partialsList = partialData.partials;
var partialName = partials.getPartialName(pattern.file.path);
newData.cssTheme = settings.cssTheme;
newData.partialClass = partials.getPatternFolder(partialName);
newData.patternName = partialName;
newData.dependencies = addEngineSnippets(partialsList);
newData.menuBehavior = settings.menuBehavior || "overlay";
newData.language = settings.language || "en";
helpers.resetHelpers();
try {
var source = handlebars.compile(pattern.source);
var compiledSource = getHtml(source, newData);
var fullLayout = layoutHandler.renderLayout(newData, compiledSource);
var markups = markup.addMarkup(pattern.source, compiledSource, newData);
var output = {
partialName: partialName,
html: fullLayout,
markup: markups
};
outputCache.push(output);
if (end) {
saveToDisk();
}
} catch(e) {
u.log('Error in ' + partialName, 'error');
console.log(e.message)
process.exit();
}
}
function saveToDisk() {
outputCache.forEach(function (file, i) {
var end = i === (outputCache.length - 1);
if (partials.reverseDependencies.hasOwnProperty(file.partialName)) {
var dependenciesList = partials.reverseDependencies[file.partialName]
.sort(function (a, b) {
return a.partial > b.partial;
});
file.html = file.html.replace('reverseDependencies: []', 'reverseDependencies: ' + JSON.stringify(dependenciesList) + '');
}
addToTree(file.partialName, end);
renderFile(file, end);
});
}
function addEngineSnippets(partialsArr) {
if (!partialsArr) {
return [];
}
var dependencies = [];
partialsArr.forEach(function (k, i) {
dependencies.push({
partial: k,
path: partials.getPatternFolder(k) + '/index.html'
});
});
return JSON.stringify(dependencies);
}
function getData(fileName, callback) {
var jsonFile = fileName.replace(settings.fileExtension, '.json');
fse.stat(jsonFile, function(err, stat) {
var jsonData;
var partialName = partials.getPartialName(fileName);
if (!err) {
delete require.cache[require.resolve(jsonFile)];
try {
jsonData = require(jsonFile);
patternsData[partialName] = jsonData;
}
catch(e) {
u.log('JSON error: ' + partialName, 'error');
console.log(e.message)
}
}
if (callback) {
callback(jsonData);
}
});
}
function renderFile(fileInfo, end) {
var DS = path.sep;
var partialName = partials.getPatternFolder(fileInfo.partialName);
var filePath = u.getAppPath(settings.paths.public.patterns + partialName, '/index.html');
var filePathMarkups = u.getAppPath(settings.paths.public.patterns + partialName, '/markups.html');
fse.outputFileSync(filePath, fileInfo.html);
fse.outputFileSync(filePathMarkups, fileInfo.markup);
if (end) {
onEnd();
}
}
function getHtml(template, data) {
try {
var result = template(data);
if (settings.beautifyHTML) {
result = beautify_html(result, {
indent_size: 2,
type: ['html'],
brace_style: ['collapse'],
end_with_newline: true,
max_preserve_newlines: 1,
preserve_newlines: true,
unformatted: [
"span",
"img",
"code",
"pre",
"sub",
"sup",
"em",
"strong",
"b",
"i",
"u",
"strike",
"big",
"small",
"pre",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"meta"
]
});
}
return result;
}
catch(err) {
u.log(err.message, 'error');
return 'ERROR: ' + err.message;
}
}
function cleanPaths() {
fse.mkdirsSync(u.getAppPath(settings.paths.public.patterns));
fse.mkdirsSync(u.getAppPath(settings.paths.public.data));
}
function addToTree(partial, end) {
var arr = partial.split('/');
var url = partials.getPatternFolder(partial) + '/index.html';
var status = patternsData[partial] &&
patternsData[partial].hasOwnProperty('_status') ? patternsData[partial]._status : null;
var tree = arr.reduceRight(function(previousValue, currentValue, currentIndex, array) {
var obj = {}
if (array.length - 2 === currentIndex && !obj.hasOwnProperty(currentValue)) {
obj[currentValue] = {};
}
if (array.length - 2 === currentIndex) {
obj[currentValue][previousValue] = {
url: url,
status: status
}
}
else {
obj[currentValue] = previousValue;
}
return obj;
});
patternsTree = object_merge(patternsTree, tree);
if (end) {
renderData();
}
}
function renderData() {
for (var obj in patternsTree) {
if (Object.keys(patternsTree[obj]).length > 0) {
patternsTree[obj] = sort_object(patternsTree[obj], {sortOrder: 'ASC'});
}
else {
delete patternsTree[obj];
}
}
fse.outputFileSync(u.getAppPath(settings.paths.public.data, 'patterns.json'), JSON.stringify(patternsTree));
renderTemplate();
}
function walkPartials() {
return klaw(u.getAppPath(settings.paths.src.patterns))
.on('data', function (file) {
if (isPatternExtension(path.extname(file.path))) {
partials.registeredPartials.push(file);
}
})
.on('end', registerPartials);
};
function registerPartials() {
var totalPartials = partials.registeredPartials.length;
if (totalPartials > 0) {
return partials.registeredPartials.forEach(function (file, i) {
fse.readFile(file.path, settings.encode, function(err, source) {
if (source) {
var partialName = partials.getPartialName(file.path);
if (partials.isHiddenPartial(partialName)) {
partialName = partialName.replace('/_', '/');
partials.hiddenPartials.push(partialName);
}
partials.setPartial(partialName, source);
if (i === totalPartials - 1) {
getPatterns();
}
}
});
});
} else {
getPatterns();
}
return renderData();
}
function renderTemplate() {
var indexSource = fse.readFileSync(u.getPath(settings.paths.core.templates, 'index' + settings.fileExtension), settings.encode);
helpers.resetHelpers();
var indexTemplate = handlebars.compile(indexSource);
var indexHTML = layoutHandler.renderLayout({
partialClass: 'neutron-welcome-page',
assetsPath: '',
cssTheme: settings.cssTheme,
dependencies: '[]',
menuBehavior: settings.menuBehavior || 'overlay',
language: settings.language || 'en'
}, indexTemplate());
fse.outputFileSync(u.getAppPath(settings.paths.public.root, 'index.html'), indexHTML);
}
function isTemplate(partial) {
var regex = /^(template)/;
return regex.test(partial);
}
function wrapWithComments(html, patternName) {
var output = "";
output += "<!-- start " + patternName + " -->\n";
output += html;
output += "\n<!-- end " + patternName + " -->\n";
return output;
}
function setRenderHelper() {
handlebars.registerHelper('render', function (partial, params) {
var data;
var partialArr = partial.split(':');
var partialName = partialArr[0];
var styleModifier = partialArr.length > 1 ? partialArr[1] : null;
var hasHash = params.hasOwnProperty('hash');
var partialData = !isTemplate(partial) && patternsData.hasOwnProperty(partialName) ? patternsData[partialName] : {};
var source = '{{> '+ partialName + '}}';
if (this && hasHash) {
data = Object.assign({}, partialData, this, params.hash);
}
if (styleModifier) {
data.styleModifier = styleModifier;
}
var template = handlebars.compile(source);
var result = template(data);
if (settings.htmlComments) {
result = wrapWithComments(result, partialName);
}
return new handlebars.SafeString(result);
});
}
console.time(' render duration');
init();
return true;
}
module.exports = engine;