deardesi.js/bin/deardesi.js

399 lines
11 KiB
JavaScript
Executable File

#!/usr/bin/env node
'use strict';
var PromiseA = require('bluebird')
, fs = PromiseA.promisifyAll(require('fs'))
, fsx = PromiseA.promisifyAll(require('fs.extra'))
, tar = require('tar')
//, requestAsync = PromiseA.promisify(require('request'))
, request = PromiseA.promisifyAll(require('request'))
, forEachAsync = require('foreachasync').forEachAsync
//, spawn = require('child_process').spawn
, path = require('path')
, cli = require('cli')
, UUID = require('node-uuid')
, Desi
, zlib = require('zlib')
;
cli.parse({
blogdir: ['d', 'Where your blog is, i.e. ~/path/to/blog', 'string', './']
//, output: ['o', 'name of output directory within ~/path/to/blog', 'string', './compiled']
});
function init() {
Desi = require('desirae').Desirae;
//
// 1. Transform (yml, slug, etc)
//
Desi.registerTransform(
'lint'
, require('desirae/lib/transform-core').DesiraeTransformCore.lint
, { collections: true }
);
Desi.registerTransform(
'root'
, require('desirae/lib/transform-core').DesiraeTransformCore.root
, { root: true }
);
Desi.registerTransform(
'normalize'
, require('desirae/lib/transform-core').DesiraeTransformCore.normalize
, { root: true, collections: true }
);
Desi.registerTransform(
'disqus'
, require('desirae/lib/transform-core').DesiraeTransformCore.disqus
, { collections: true }
);
//
// 2. Aggregate (rss, categories, tags, etc)
//
Desi.registerAggregator(require('desirae/lib/aggregate-core').DesiraeAggregateCore.collate);
//
// 3. Datamap (ruhoh, desirae, jade, mustache, liquid)
//
Desi.registerDataMapper('desirae', require('desirae/lib/datamap-core').DesiraeDatamapCore);
Desi.registerDataMapper('desirae@1.0', require('desirae/lib/datamap-core').DesiraeDatamapCore);
// TODO ruhoh versions are ruhoh-twitter (1.0) and ruhoh-boostrap-2 (2.6)
Desi.registerDataMapper('ruhoh', require('desirae-datamap-ruhoh').DesiraeDatamapRuhoh);
Desi.registerDataMapper('ruhoh@1.0', require('desirae-datamap-ruhoh').DesiraeDatamapRuhoh);
Desi.registerDataMapper('ruhoh@2.6', require('desirae-datamap-ruhoh').DesiraeDatamapRuhoh);
//
// 4. Render (md -> html, less -> css, etc)
//
Desi.registerRenderer(
'js'
, require('desirae/lib/render-core').DesiraeRenderCss
, { themes: true, assets: true }
);
Desi.registerRenderer(
'css'
, require('desirae/lib/render-core').DesiraeRenderCss
, { themes: true, assets: true }
);
['html', 'htm', 'xhtm', 'xhtml'].forEach(function (ext) {
Desi.registerRenderer(
ext
, require('desirae/lib/render-core').DesiraeRenderHtml
, { root: true, collections: true, themes: true, assets: true }
);
});
['md', 'markdown', 'mdown', 'mkdn', 'mkd', 'mdwn', 'mdtxt', 'mdtext'].forEach(function (ext) {
Desi.registerRenderer(
ext
, require('desirae/lib/render-core').DesiraeRenderMarkdown
, { root: true, collections: true }
);
});
Desi.registerRenderer(
'jade'
, require('desirae/lib/render-core').DesiraeRenderJade
// TODO how to support jade in place of Mustache for layouts?
, { root: true, collections: true, themes: true }
);
}
function serve(displayDir, blogdir) {
var http = require('http')
//, https = require('https')
, app = require('../server').create({ blogdir: blogdir })
, server
;
server = http.createServer(app).listen(65080, function () {
console.info("Listening from " + displayDir);
console.info("Listening on http://local.dear.desi:" + server.address().port);
}).on('error', function (err) {
if (/EADDRINUSE/.test(err.message)) {
console.error("");
console.error("You're already running desi in another tab.");
console.error("");
console.error("Go to the other tab and press <control> + c to stop her. Then you can come back here to try again.");
console.error("");
console.error("");
return;
}
throw err;
});
//secureServer = https.createServer(app).listen(65043);
}
function build(blogdir) {
var desi = {}
, env = {}
;
env.working_path = env.blogdir = blogdir;
Desi.init(desi, env).then(function () {
env.url = desi.site.base_url + desi.site.base_path.replace(/^\/$/, '');
env.base_url = desi.site.base_url;
env.base_path = desi.site.base_path;
env.compiled_path = 'compiled';
//env.since = 0;
Desi.buildAll(desi, env).then(function () {
Desi.write(desi, env).then(function (info) {
console.info(
'wrote', info.numFiles
, 'files'
, '(' + (info.size && (info.size / (1024 * 1024)).toFixed(2) || 'unkown'), 'MiB)'
, 'in'
, ((info.end - info.start) / 1000).toFixed(3) + 's'
);
console.info('Built and saved to ' + path.join(env.working_path, env.compiled_path));
});
});
});
}
function createPost(originalDir, blogdir, title, extra) {
if (!title) {
console.error("Usage desi post \"My First Post\"");
console.error("(you didn't specify a title)");
process.exit(1);
}
if (extra) {
console.error("Usage desi post \"My First Post\"");
console.error("(too many arguments - maybe you didn't put your title in quotes?)");
process.exit(1);
}
var desi = {}
, env = {}
, post = {}
, slug
, filepath
, displaypath
;
env.working_path = env.blogdir = blogdir;
Desi.init(desi, env).then(function () {
// TODO move this 'create new post' logic to desirae proper
var collectionname = Object.keys(desi.config.collections)[0]
, collection = desi.config.collections[collectionname]
, entity = {}
;
/*
Desi.init(desi, env).then(function () {
env.url = desi.site.base_url + desi.site.base_path.replace(/^\/$/, '');
env.base_url = desi.site.base_url;
env.base_path = desi.site.base_path;
env.compiled_path = 'compiled';
//env.since = 0;
*/
post.title = title;
post.description = "";
post.date = Desi.toLocaleDate(new Date());
// TODO use site.permalink or collection.permalink or something like that
slug = Desi.slugify(post.title);
// TODO as per config
post.uuid = UUID.v4();
// TODO as per config for default collection and default format (jade, md, etc)
filepath = path.join(blogdir, collectionname, slug + '.md');
displaypath = path.join(originalDir, 'posts', slug + '.md').replace(/^\/(Users|home)\/[^\/]+\//, '~/').replace(/ /g, '\\ ');
['updated', 'theme', 'layout', 'swatch'].forEach(function (key) {
if (!post[key]) {
delete post[key];
}
});
Object.keys(post).forEach(function (key) {
entity[key] = post[key];
});
entity.slug = slug;
post.permalink = Desi.permalinkify(
desi
, collection.fallback_permalink || collection.permalink
, entity);
return Desi.fsapi.putFiles([{
path: filepath
, contents:
'---\n'
+ Desi.YAML.stringify(post).trim()
+ '\n'
+ '---\n'
+ '\n'
+ '\n'
}], { overwrite: false }).then(function (r) {
var err
;
if (r.error || r.errors.length) {
err = r.error || r.errors[0];
if (/exists/i.test(err.message)) {
console.error('');
console.error("Looks like that post already exists. Try a different name?");
console.error('');
console.error('');
} else {
throw err;
}
}
console.log('');
console.log(displaypath);
console.log('');
console.log('Markdown: [' + post.title + ']('
+ desi.site.base_url
+ path.join(desi.site.base_path, post.permalink)
+ ')'
);
console.log('HTML: <a href="'
+ desi.site.base_url
+ path.join(desi.site.base_path, post.permalink)
+ '">' + post.title + '</a>'
);
console.log('');
console.log('');
console.log('vim ' + displaypath);
console.log('(or emacs ' + displaypath + ', if you swing that way)');
console.log('');
console.log('');
});
/*
});
*/
});
}
function initialize(displayPath, blogdir) {
console.info("\nCreating new blog", displayPath);
return fs.readdirAsync(blogdir).then(function (nodes) {
// ignore dotfiles (.DS_Store, etc)
nodes = nodes.filter(function (node) {
return !/^\./.test(node);
});
if (nodes.length) {
console.error("\n\tOops! It looks like that directory is already being used");
console.error("\nIf you want you can DELETE it and start from scratch:");
console.error("\n\trm -r '" + blogdir.replace("'", "'\"'\"'") + "'");
console.error("\nOr you can specify a different directory.");
console.error("\n");
process.exit(1);
}
}).catch(function (/*err*/) {
// doesn't exist? No problamo (all the better, actually)
return;
}).then(function () {
return fsx.mkdirp(blogdir);
}).then(function () {
return new PromiseA(function (resolve, reject) {
var t = tar.Extract({ path: blogdir, strip: 1 })
, gunzip = zlib.createGunzip()
;
console.info("Downloading blog template...", displayPath);
request.get("https://github.com/DearDesi/desirae-blog-template/archive/v1.1.0.tar.gz")
.pipe(gunzip)
.pipe(t)
.on('end', resolve)
.on('error', reject)
;
});
}).then(function () {
var themes
;
themes = [
{ name: 'ruhoh-twitter'
, url: "https://github.com/DearDesi/ruhoh-twitter/archive/v1.0.0.tar.gz"
}
, { name: 'ruhoh-bootstrap-2'
, url: "https://github.com/DearDesi/ruhoh-bootstrap-2/archive/v1.0.1.tar.gz"
}
];
return forEachAsync(themes, function (theme) {
return new PromiseA(function (resolve, reject) {
var t = tar.Extract({ path: path.join(blogdir, 'themes', theme.name), strip: 1 })
, gunzip = zlib.createGunzip()
;
console.info("Downloading theme '" + theme.name + "'");
request.get(theme.url)
.pipe(gunzip)
.pipe(t)
.on('end', resolve)
.on('error', reject)
;
});
});
}).then(function () {
console.info("Done.");
console.info("\nTo start the web editor run this:");
console.info("\n\tdesi serve -d '" + blogdir.replace("'", "'\"'\"'") + "'");
})
;
}
cli.main(function (args, options) {
init();
var command = args[0]
, blogdir = options.blogdir
, originalDir = blogdir
, displayPath
;
if (!blogdir) {
blogdir = path.resolve('./');
originalDir = './';
}
displayPath = path.resolve(originalDir)
.replace(/^\/(Users|home)\/[^\/]+\//, '~/')
.replace(/ /g, '\\ ')
;
if ('init' === command) {
initialize(displayPath, blogdir);
return;
}
if (!fs.existsSync(path.join(blogdir, 'site.yml'))) {
console.error("Usage: desi [serve|init|post] -d ~/path/to/blog");
console.error("(if ~/path/to/blog doesn't yet exist or doesn't have config.yml, site.yml, etc, "
+ "try `deardesi init -d ~/path/to/blog'");
process.exit(1);
return;
}
else if ('build' === command) {
build(blogdir);
return;
}
else if ('post' === command) {
createPost(originalDir, blogdir, args[1], args[2]);
return;
}
else if ('serve' === command) {
serve(displayPath, blogdir);
return;
}
else {
console.error("Usage: desi [serve|init|post] -d ~/path/to/blog");
return;
}
});