399 lines
11 KiB
JavaScript
Executable File
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;
|
|
}
|
|
});
|