UNPKG

blogs

Version:
289 lines (277 loc) 6.76 kB
var fs = require('fs'); var path = require('path'); var async = require('async'); var glob = require('glob'); var nunjucks = require('nunjucks'); var bMods = require('blogs_modules'); var removeExt = bMods.removeExt; var camelCase = bMods.camelCase; var forEachKey = bMods.forEachKey; var isUndef = bMods.isUndef; var Cache = bMods.Cache; var rm = bMods.rm; var writeFile = bMods.writeFile; var cp = bMods.cp; var Post = require('./-post'); var EventEmitter = require('events').EventEmitter; class Blog extends EventEmitter { constructor(dir) { super(); this.dir = dir; this.cache = new Cache(); forEachKey(require(this.path('config')), (value, key) => { if (isUndef(this[key])) { this[key] = value; } }); forEachKey({ dest: 'public', static: [], root: '/', permalink: '{{ YYYY }}/{{ MM }}/{{ T }}.html' }, (value, key) => { if (isUndef(this[key])) { this[key] = value; } }); if (!Array.isArray(this.static)) { this.static = [this.static]; } } /** * Wraps path.join * @param {...String} ...paths * @return {String} * @example * var blog = new Blog('/home/jelonzo/blog'); * blog.path('layout'); * // '/home/jelonzo/blog/layout' */ path(...paths) { paths.unshift(this.dir); return path.join.apply(path, paths); } /** * Gets posts * @param {Function} callback * @return {undefined} */ posts(callback) { this.cache.get('posts', (callback) => { async.waterfall([ (callback) => { glob(this.path('posts/**/*'), { nodir: true }, callback); }, (files, callback) => { async.map(files, (file, callback) => { callback(null, new Post(this, file)); }, callback); } ], callback); }, callback); } /** * Gets a list of layout files * @param {Function} callback * @return {undefined} */ layouts(callback) { this.cache.get('layouts', (callback) => { async.waterfall([ (callback) => { async.map([ this.path('layout/**/*'), path.join(__dirname, '../layout/**/*') ], (pattern, callback) => { glob(pattern, { nodir: true }, callback); }, callback); }, (files, callback) => { files = [].concat(...files); callback(null, [ 'error', 'index', 'post' ].reduce((layout, name) => { layout[name] = files.find((file) => { return removeExt(path.basename(file)) === name; }); return layout; }, {})); } ], callback); }, callback); } /** * Gets plugins * @param {String} type - 'processor', 'filter', 'global', 'extension' * @param {Function} callback * @return {undefined} */ plugins(type, callback) { this.cache.get('plugins:' + type, (callback) => { async.waterfall([ (callback) => { var query = type + '-*'; async.map([ path.join(__dirname, '../node_modules', 'blogs-' + query), this.path('scripts', query) ], (pattern, callback) => { glob(pattern, callback); }, callback); }, (files, callback) => { var expr = new RegExp('^.*?' + type + '-'); files = [].concat(...files); callback(null, files.reduce((filters, file) => { var name = camelCase(removeExt(path.basename(file)).replace(expr, '')); filters[name] = require(file); return filters; }, {})); } ], callback); }, callback); } /** * Renders a template string. * @param {String} template * @param {String} dir * @param {Object} bindings * @param {Function} callback * @return {undefined} */ render(template, dir, bindings, callback) { async.waterfall([ (callback) => { this.cache.get('render', (callback) => { async.map([ 'filter', 'extension', 'global' ], (type, callback) => { this.plugins(type, callback); }, callback); }, callback); }, (results, callback) => { var error = null; var [filters, extensions, globals] = results; var Loader = nunjucks.FileSystemLoader; var Environment = nunjucks.Environment; var Template = nunjucks.Template; var env = new Environment(dir && new Loader(dir)); var result; forEachKey(filters, (filter, name) => { env.addFilter(name, filter); }); forEachKey(extensions, (extension, name) => { env.addExtension(name, extension); }); forEachKey(globals, (value, name) => { env.addGlobal(name, value); }); forEachKey(bindings, (value, name) => { env.addGlobal(name, value); }); try { result = new Template(template, env).render(); } catch (err) { error = err; } callback(error, result); } ], callback); } /** * Compiles the blog * @param {Function} callback * @return {undefined} */ compile(callback) { this.cache.get('compile', (callback) => { async.waterfall([ (callback) => { this.posts(callback); }, (posts, callback) => { async.map(posts, (post, callback) => { post.load(callback); }, callback); }, (posts, callback) => { posts.sort((a, b) => { return b.date - a.date; }); posts.forEach((post, index, posts) => { post.newer = posts[index - 1]; post.older = posts[index + 1]; }); this.posts = posts; this.layouts(callback); }, (layouts, callback) => { async.waterfall([ (callback) => { fs.readFile(layouts.post, callback); }, (template, callback) => { template = template.toString(); async.each(this.posts, (post, callback) => { async.waterfall([ (callback) => { this.render(template, path.dirname(layouts.post), { post: post, blog: this }, callback); }, (result, callback) => { post.rendered = result; callback(null, post); } ], callback); }, callback); }, (callback) => { callback(null, this); } ], callback); } ], callback); }, callback); } /** * Save files. * @param {Function} callback * @return {undefined} */ deploy(callback) { async.waterfall([ (callback) => { async.parallel([ (callback) => { this.compile(callback); }, (callback) => { rm(this.path(this.dest), callback); } ], callback); this.compile(callback); }, (results, callback) => { async.each(this.static, (dir, callback) => { cp(this.path(dir), this.path(this.dest, dir), callback); }, callback); }, (callback) => { async.each(this.posts, (post, callback) => { writeFile(this.path(this.dest, post.permalink), post.rendered, callback); }, callback); } ], callback); } } module.exports = Blog;