summaryrefslogtreecommitdiffstats
path: root/lib/cli
diff options
context:
space:
mode:
authorSamy Pessé <samypesse@gmail.com>2016-02-18 15:08:34 +0100
committerSamy Pessé <samypesse@gmail.com>2016-02-18 15:08:34 +0100
commitb99c499d2ea67268760a7c8265a0669b016a56f1 (patch)
treef7e95ab1096abaf9579bf670205b1fb5b8b848b8 /lib/cli
parent709b388dfcc641fab25d297618b6ffe49f5cd677 (diff)
downloadgitbook-b99c499d2ea67268760a7c8265a0669b016a56f1.zip
gitbook-b99c499d2ea67268760a7c8265a0669b016a56f1.tar.gz
gitbook-b99c499d2ea67268760a7c8265a0669b016a56f1.tar.bz2
Add back serve command
Diffstat (limited to 'lib/cli')
-rw-r--r--lib/cli/helper.js4
-rw-r--r--lib/cli/index.js109
-rw-r--r--lib/cli/server.js95
-rw-r--r--lib/cli/watch.js42
4 files changed, 248 insertions, 2 deletions
diff --git a/lib/cli/helper.js b/lib/cli/helper.js
index e8a049f..1086b4d 100644
--- a/lib/cli/helper.js
+++ b/lib/cli/helper.js
@@ -63,5 +63,7 @@ module.exports = {
options: {
log: LOG_OPTION,
format: FORMAT_OPTION
- }
+ },
+
+ FORMATS: FORMATS
};
diff --git a/lib/cli/index.js b/lib/cli/index.js
index c9a2e8f..0492c29 100644
--- a/lib/cli/index.js
+++ b/lib/cli/index.js
@@ -1,7 +1,16 @@
+/* eslint-disable no-console */
+
var path = require('path');
+var tinylr = require('tiny-lr');
+var Promise = require('../utils/promise');
var PluginsManager = require('../plugins');
+var Book = require('../book');
+var NodeFS = require('../fs/node');
+
var helper = require('./helper');
+var Server = require('./server');
+var watch = require('./watch');
module.exports = {
commands: [
@@ -60,8 +69,106 @@ module.exports = {
return output.generate();
});
})
- }
+ },
+
+ {
+ name: 'serve [book]',
+ description: 'Build then serve a book 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
+ },
+ helper.options.format,
+ helper.options.log
+ ],
+ exec: function(args, kwargs) {
+ var input = args[0] || process.cwd();
+ var server = new Server();
+
+ // Init livereload server
+ var lrServer = tinylr({});
+ var port = kwargs.port;
+ var lrPath;
+
+ var generate = function() {
+ // Stop server if running
+ if (server.isRunning()) console.log('Stopping server');
+ return server.stop()
+
+ // Generate the book
+ .then(function() {
+ var book = new Book({
+ fs: new NodeFS(),
+ root: input,
+ 'config': {
+ 'defaultsPlugins': ['livereload']
+ },
+ 'logLevel': kwargs.log
+ });
+
+ return book.parse()
+ .then(function() {
+ var Out = helper.FORMATS[kwargs.format];
+ var output = new Out(book);
+
+ return output.generate()
+ .thenResolve(output);
+ });
+ })
+
+ // Start server and watch changes
+ .then(function(output) {
+ console.log();
+ console.log('Starting server ...');
+ return server.start(output.root(), port)
+ .then(function() {
+ console.log('Serving book on http://localhost:'+port);
+
+ if (lrPath) {
+ // trigger livereload
+ lrServer.changed({
+ body: {
+ files: [lrPath]
+ }
+ });
+ }
+
+ if (!kwargs.watch) return;
+
+ return watch(output.book.root)
+ .then(function(filepath) {
+ // set livereload path
+ lrPath = filepath;
+ console.log('Restart after change in file', filepath);
+ console.log('');
+ return generate();
+ });
+ });
+ });
+ };
+
+ return Promise.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();
+ });
+ }
+ }
]
};
diff --git a/lib/cli/server.js b/lib/cli/server.js
new file mode 100644
index 0000000..3bd5d18
--- /dev/null
+++ b/lib/cli/server.js
@@ -0,0 +1,95 @@
+var events = require('events');
+var http = require('http');
+var send = require('send');
+var util = require('util');
+var url = require('url');
+
+var Promise = require('../utils/promise');
+
+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;
+};
+
+// Stop the server
+Server.prototype.stop = function() {
+ var that = this;
+ if (!this.isRunning()) return Promise();
+
+ var d = Promise.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 = Promise();
+ port = port || 8004;
+
+ if (that.isRunning()) pre = this.stop();
+ return pre
+ .then(function() {
+ var d = Promise.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/cli/watch.js b/lib/cli/watch.js
new file mode 100644
index 0000000..36c42cc
--- /dev/null
+++ b/lib/cli/watch.js
@@ -0,0 +1,42 @@
+var _ = require('lodash');
+var path = require('path');
+var chokidar = require('chokidar');
+var parsers = require('gitbook-parsers');
+
+var Promise = require('../utils/promise');
+
+// Watch a folder and resolve promise once a file is modified
+function watch(dir) {
+ var d = Promise.defer();
+ dir = path.resolve(dir);
+
+ var toWatch = [
+ 'book.json', 'book.js'
+ ];
+
+ // Watch all parsable files
+ _.each(parsers.extensions, function(ext) {
+ toWatch.push('**/*'+ext);
+ });
+
+ var watcher = chokidar.watch(toWatch, {
+ cwd: dir,
+ ignored: '_book/**',
+ ignoreInitial: true
+ });
+
+ watcher.once('all', function(e, filepath) {
+ watcher.close();
+
+ d.resolve(filepath);
+ });
+ watcher.once('error', function(err) {
+ watcher.close();
+
+ d.reject(err);
+ });
+
+ return d.promise;
+}
+
+module.exports = watch;