summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/index.js120
-rw-r--r--lib/utils/server.js96
-rw-r--r--lib/utils/string.js3
-rw-r--r--lib/utils/watch.js27
4 files changed, 239 insertions, 7 deletions
diff --git a/lib/index.js b/lib/index.js
index a2c8edf..a03f39d 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,9 +1,27 @@
var Q = require("q");
var _ = require("lodash");
var path = require("path");
+var tinylr = require('tiny-lr-fork');
var Book = require("./book");
var Plugin = require("./plugin");
+var Server = require("./utils/server");
+var stringUtils = require("./utils/string");
+var watch = require("./utils/watch");
+
+var LOG_OPTION = {
+ name: "log",
+ description: "Minimum log level to display",
+ values: _.chain(Book.LOG_LEVELS).keys().map(stringUtils.toLowerCase).value(),
+ defaults: "info"
+};
+
+var FORMAT_OPTION = {
+ name: "format",
+ description: "Format to build to",
+ values: ["website", "json"],
+ defaults: "website"
+};
module.exports = {
Book: Book,
@@ -11,20 +29,21 @@ module.exports = {
commands: [
// Build command that simply build a book into an output folder
{
- name: "build",
+ name: "build [book] [output]",
description: "build a book",
+ options: [
+ FORMAT_OPTION,
+ LOG_OPTION
+ ],
exec: function(args, kwargs) {
var input = args[0] || process.cwd();
var output = args[1] || path.join(input, "_book");
- kwargs = _.defaults(kwargs || {}, {
- format: "website"
- });
var book = new Book(input, _.extend({}, {
'config': {
'output': output
},
- 'logLevel': Book.LOG_LEVELS[(kwargs.log || "info").toUpperCase()]
+ 'logLevel': Book.LOG_LEVELS[(kwargs.log).toUpperCase()]
}));
return book.parse()
@@ -34,9 +53,98 @@ module.exports = {
}
},
+ // Build and serve a book
+ {
+ name: "serve [book]",
+ description: "Build then serve a gitbook from a directory",
+ options: [
+ {
+ name: "port",
+ description: "Port for server to listen on",
+ defaults: 4000
+ },
+ {
+ name: "lrport",
+ description: "Port for livereload server to listen on",
+ defaults: 35729
+ },
+ {
+ name: "watch",
+ description: "Enable/disable file watcher",
+ defaults: true
+ },
+ FORMAT_OPTION,
+ LOG_OPTION
+ ],
+ exec: function(args, kwargs) {
+ var input = args[0] || process.cwd();
+ var server = new Server();
+
+ // Init livereload server
+ var lrServer = tinylr({});
+ var lrPath = undefined;
+
+ var generate = function() {
+ if (server.isRunning()) console.log("Stopping server");
+
+ return server.stop()
+ .then(function() {
+ var book = new Book(input, _.extend({}, {
+ 'config': {
+ 'defaultsPlugins': ["livereload"]
+ },
+ 'logLevel': Book.LOG_LEVELS[(kwargs.log).toUpperCase()]
+ }));
+
+ return book.parse()
+ .then(function() {
+ return book.generate(kwargs.format);
+ })
+ .thenResolve(book);
+ })
+ .then(function(book) {
+ console.log();
+ console.log('Starting server ...');
+ return server.start(book.options.output, kwargs.port)
+ .then(function() {
+ console.log('Serving book on http://localhost:'+kwargs.port);
+
+ if (lrPath) {
+ // trigger livereload
+ lrServer.changed({
+ body: {
+ files: [lrPath]
+ }
+ });
+ }
+
+ if (!kwargs.watch) return;
+
+ return watch(book.root)
+ .then(function(filepath) {
+ // set livereload path
+ lrPath = filepath;
+ console.log("Restart after change in files");
+ console.log('');
+ return generate();
+ })
+ })
+ });
+ };
+
+ return Q.nfcall(lrServer.listen.bind(lrServer), kwargs.lrport)
+ .then(function() {
+ console.log('Live reload server started on port:', kwargs.lrport);
+ console.log('Press CTRL+C to quit ...');
+ console.log('')
+ return generate();
+ });
+ }
+ },
+
// Install command that install plugins needed by a book
{
- name: "install",
+ name: "install [book]",
description: "install plugins dependencies",
exec: function(args, kwargs) {
var input = args[0] || process.cwd();
diff --git a/lib/utils/server.js b/lib/utils/server.js
new file mode 100644
index 0000000..2b97fe8
--- /dev/null
+++ b/lib/utils/server.js
@@ -0,0 +1,96 @@
+var Q = require('q');
+var _ = require('lodash');
+
+var events = require('events');
+var http = require('http');
+var send = require('send');
+var util = require('util');
+var url = require('url');
+
+var Server = function() {
+ this.running = null;
+ this.dir = null;
+ this.port = 0;
+ this.sockets = [];
+};
+util.inherits(Server, events.EventEmitter);
+
+// Return true if the server is running
+Server.prototype.isRunning = function() {
+ return this.running != null;
+};
+
+// Stop the server
+Server.prototype.stop = function() {
+ var that = this;
+ if (!this.isRunning()) return Q();
+
+ var d = Q.defer();
+ this.running.close(function(err) {
+ that.running = null;
+ that.emit("state", false);
+
+ if (err) d.reject(err);
+ else d.resolve();
+ });
+
+ for (var i = 0; i < this.sockets.length; i++) {
+ this.sockets[i].destroy();
+ }
+
+ return d.promise;
+};
+
+Server.prototype.start = function(dir, port) {
+ var that = this, pre = Q();
+ port = port || 8004;
+
+ if (that.isRunning()) pre = this.stop();
+ return pre
+ .then(function() {
+ var d = Q.defer();
+
+ that.running = http.createServer(function(req, res){
+ // Render error
+ function error(err) {
+ res.statusCode = err.status || 500;
+ res.end(err.message);
+ }
+
+ // Redirect to directory's index.html
+ function redirect() {
+ res.statusCode = 301;
+ res.setHeader('Location', req.url + '/');
+ res.end('Redirecting to ' + req.url + '/');
+ }
+
+ // Send file
+ send(req, url.parse(req.url).pathname)
+ .root(dir)
+ .on('error', error)
+ .on('directory', redirect)
+ .pipe(res);
+ });
+
+ that.running.on('connection', function (socket) {
+ that.sockets.push(socket);
+ socket.setTimeout(4000);
+ socket.on('close', function () {
+ that.sockets.splice(that.sockets.indexOf(socket), 1);
+ });
+ });
+
+ that.running.listen(port, function(err) {
+ if (err) return d.reject(err);
+
+ that.port = port;
+ that.dir = dir;
+ that.emit("state", true);
+ d.resolve();
+ });
+
+ return d.promise;
+ });
+}
+
+module.exports = Server;
diff --git a/lib/utils/string.js b/lib/utils/string.js
index 54c4c66..72a9ca0 100644
--- a/lib/utils/string.js
+++ b/lib/utils/string.js
@@ -22,5 +22,6 @@ function optionsToShellArgs(options) {
module.exports = {
escapeShellArg: escapeShellArg,
- optionsToShellArgs: optionsToShellArgs
+ optionsToShellArgs: optionsToShellArgs,
+ toLowerCase: String.prototype.toLowerCase.call.bind(String.prototype.toLowerCase)
};
diff --git a/lib/utils/watch.js b/lib/utils/watch.js
new file mode 100644
index 0000000..715179d
--- /dev/null
+++ b/lib/utils/watch.js
@@ -0,0 +1,27 @@
+var Q = require('q');
+var path = require('path');
+var Gaze = require('gaze').Gaze;
+
+function watch(dir) {
+ var d = Q.defer();
+ dir = path.resolve(dir);
+
+ var gaze = new Gaze("**/*.md", {
+ cwd: dir
+ });
+
+ gaze.once("all", function(e, filepath) {
+ gaze.close();
+
+ d.resolve(filepath);
+ });
+ gaze.once("error", function(err) {
+ gaze.close();
+
+ d.reject(err);
+ });
+
+ return d.promise;
+}
+
+module.exports = watch;