bookmaker
Version:
Create books from a folder of markdown files
201 lines (178 loc) • 4.79 kB
JavaScript
var EventEmitter = require('events').EventEmitter
var parser = require('markdown-parse')
var fs = require('fs')
var async = require('async')
var util = require('util')
var globby = require('globby')
var resize = require('imagemagickresizer')()
var path = require('path')
var mkdirp = require('mkdirp')
function BookMaker(source, options){
EventEmitter.call(this)
this._source = source
this._options = options
}
util.inherits(BookMaker, EventEmitter)
// an array of relative glob results
BookMaker.prototype.globs = function(glob, done){
var self = this;
if(typeof(glob)==='string'){
glob = [glob]
}
globby(glob, {
cwd:self._source
}, done)
}
// an array of absolute file paths from globs
BookMaker.prototype.files = function(glob, done){
var self = this;
this.globs(glob, function(err, files){
if(err) return done(err)
files = files.map(function(file){
return path.normalize(self._source + '/' + file)
})
done(null, files)
})
}
BookMaker.prototype.fileCopies = function(glob, dest, done){
var self = this;
this.globs(glob, function(err, files){
if(err) return done(err)
files = files.map(function(file){
return {
source:path.normalize(self._source + '/' + file),
dest:path.normalize(dest + '/' + file)
}
})
done(null, files)
})
}
BookMaker.prototype.load = function(configGlob, pagesGlob, done){
var self = this;
async.parallel({
config:function(next){
self.loadConfigs(configGlob, next)
},
pages:function(next){
self.loadPages(pagesGlob, next)
}
}, function(err, result){
if(err) return done(err)
var book = result.config
book.pages = result.pages
done(null, book)
})
}
BookMaker.prototype.loadPages = function(glob, done){
var self = this;
self.files(glob, function(err, files){
async.map(files, function(file, nextFile){
fs.readFile(file, 'utf8', function(err, content){
if(err) return nextFile(err)
parser(content, nextFile)
})
}, done)
})
}
BookMaker.prototype.loadConfigs = function(glob, done){
var self = this;
self.files(glob, function(err, files){
if(err) return done(err)
var config = {}
files.filter(function(file){
return file.match(/\.json/)
}).map(function(file){
return require(file)
}).forEach(function(data){
Object.keys(data || {}).forEach(function(key){
config[key] = data[key]
})
})
done(null, config)
})
}
BookMaker.prototype.ensureFolder = function(filepath, done){
var folder = path.dirname(filepath)
mkdirp(folder, done)
}
BookMaker.prototype.copyFiles = function(glob, dest, done){
var self = this;
if(!fs.existsSync(dest)){
return done(dest + ' does not exist')
}
self.fileCopies(glob, dest, function(err, files){
if(err) return done(err)
async.forEach(files, function(file, next){
self.ensureFolder(file.dest, function(){
if(err) return next(err)
var source = fs.createReadStream(file.source)
var dest = fs.createWriteStream(file.dest)
dest.on('error', next)
dest.on('close', next)
source.pipe(dest)
})
}, done)
})
}
BookMaker.prototype.resizeImages = function(glob, dest, size, done){
var self = this;
if(!fs.existsSync(dest)){
return done(dest + ' does not exist')
}
if(typeof(size)==='string'){
var parts = size.replace(/\s/g, '').split('x')
size = {
width:parts[0],
height:parts[1]
}
}
self.fileCopies(glob, dest, function(err, files){
if(err) return done(err)
async.forEach(files, function(file, next){
self.ensureFolder(file.dest, function(){
if(err) return next(err)
resize.image(file.source, file.dest, size, next)
})
}, done)
})
}
BookMaker.prototype.write = function(dest, config, done){
var self = this;
if(!fs.existsSync(dest)){
return done(dest + ' does not exist')
}
if(!config.config){
return done('config setting does not exist')
}
if(!config.pages){
return done('pages setting does not exist')
}
var bookjson = config.bookjson || 'book.json'
async.parallel({
book:function(next){
self.load(config.config, config.pages, function(err, book){
fs.writeFile(dest + '/' + bookjson, JSON.stringify(book, null, 4), 'utf8', next)
})
},
files:function(next){
if(!config.files){
return next()
}
self.copyFiles(config.files, dest, next)
},
images:function(next){
if(!config.images){
return next()
}
if(config.imageSize){
self.resizeImages(config.images, dest, config.imageSize, next)
}
else{
self.copyFiles(config.images, dest, next)
}
}
}, done)
}
module.exports = function(source, options){
return new BookMaker(source, options)
}