diff options
author | Samy Pesse <samypesse@gmail.com> | 2016-04-30 20:15:08 +0200 |
---|---|---|
committer | Samy Pesse <samypesse@gmail.com> | 2016-04-30 20:15:08 +0200 |
commit | 36b49c66c6b75515bc84dd678fd52121a313e8d2 (patch) | |
tree | bc7e0f703d4557869943ec7f9495cac7a5027d4f /lib/cli | |
parent | 87db7cf1d412fa6fbd18e9a7e4f4755f2c0c5547 (diff) | |
parent | 80b8e340dadc54377ff40500f86b1de631395806 (diff) | |
download | gitbook-36b49c66c6b75515bc84dd678fd52121a313e8d2.zip gitbook-36b49c66c6b75515bc84dd678fd52121a313e8d2.tar.gz gitbook-36b49c66c6b75515bc84dd678fd52121a313e8d2.tar.bz2 |
Merge branch 'fixes'
Diffstat (limited to 'lib/cli')
-rw-r--r-- | lib/cli/build.js | 34 | ||||
-rw-r--r-- | lib/cli/buildEbook.js | 76 | ||||
-rw-r--r-- | lib/cli/getBook.js | 23 | ||||
-rw-r--r-- | lib/cli/getOutputFolder.js | 17 | ||||
-rw-r--r-- | lib/cli/helper.js | 140 | ||||
-rw-r--r-- | lib/cli/index.js | 211 | ||||
-rw-r--r-- | lib/cli/init.js | 17 | ||||
-rw-r--r-- | lib/cli/install.js | 21 | ||||
-rw-r--r-- | lib/cli/options.js | 30 | ||||
-rw-r--r-- | lib/cli/parse.js | 79 | ||||
-rw-r--r-- | lib/cli/serve.js | 93 | ||||
-rw-r--r-- | lib/cli/server.js | 21 | ||||
-rw-r--r-- | lib/cli/watch.js | 7 |
13 files changed, 425 insertions, 344 deletions
diff --git a/lib/cli/build.js b/lib/cli/build.js new file mode 100644 index 0000000..023901e --- /dev/null +++ b/lib/cli/build.js @@ -0,0 +1,34 @@ +var Parse = require('../parse'); +var Output = require('../output'); +var timing = require('../utils/timing'); + +var options = require('./options'); +var getBook = require('./getBook'); +var getOutputFolder = require('./getOutputFolder'); + + +module.exports = { + name: 'build [book] [output]', + description: 'build a book', + options: [ + options.log, + options.format, + options.timing + ], + exec: function(args, kwargs) { + var book = getBook(args, kwargs); + var outputFolder = getOutputFolder(args); + + var Generator = Output.getGenerator(kwargs.format); + + return Parse.parseBook(book) + .then(function(resultBook) { + return Output.generate(Generator, resultBook, { + root: outputFolder + }); + }) + .fin(function() { + if (kwargs.timing) timing.dump(book.getLogger()); + }); + } +}; diff --git a/lib/cli/buildEbook.js b/lib/cli/buildEbook.js new file mode 100644 index 0000000..405d838 --- /dev/null +++ b/lib/cli/buildEbook.js @@ -0,0 +1,76 @@ +var path = require('path'); +var tmp = require('tmp'); + +var Promise = require('../utils/promise'); +var fs = require('../utils/fs'); +var Parse = require('../parse'); +var Output = require('../output'); + +var options = require('./options'); +var getBook = require('./getBook'); + + +module.exports = function(format) { + return { + name: (format + ' [book] [output]'), + description: 'build a book into an ebook file', + options: [ + options.log + ], + exec: function(args, kwargs) { + // Output file will be stored in + var outputFile = args[1] || ('book.' + format); + + // Create temporary directory + var outputFolder = tmp.dirSync().name; + + var book = getBook(args, kwargs); + var logger = book.getLogger(); + var Generator = Output.getGenerator('ebook'); + + return Parse.parseBook(book) + .then(function(resultBook) { + return Output.generate(Generator, resultBook, { + root: outputFolder, + format: format + }); + }) + + // Extract ebook file + .then(function(output) { + var book = output.getBook(); + var languages = book.getLanguages(); + + if (book.isMultilingual()) { + return Promise.ForEach(languages, function(lang) { + var langID = lang.getID(); + + var langOutputFile = path.join( + path.dirname(outputFile), + path.basename(outputFile, format) + '_' + langID + '.' + format + ); + + return fs.copy( + path.resolve(outputFolder, langID, 'index.' + format), + langOutputFile + ); + }) + .thenResolve(languages.getCount()); + } else { + return fs.copy( + path.resolve(outputFolder, 'index.' + format), + outputFile + ).thenResolve(1); + } + }) + + // Log end + .then(function(count) { + logger.info.ok(count + ' file(s) generated'); + + logger.debug('cleaning up... '); + return logger.debug.promise(fs.rmDir(outputFolder)); + }); + } + }; +}; diff --git a/lib/cli/getBook.js b/lib/cli/getBook.js new file mode 100644 index 0000000..ac82187 --- /dev/null +++ b/lib/cli/getBook.js @@ -0,0 +1,23 @@ +var path = require('path'); +var Book = require('../models/book'); +var createNodeFS = require('../fs/node'); + +/** + Return a book instance to work on from + command line args/kwargs + + @param {Array} args + @param {Object} kwargs + @return {Book} +*/ +function getBook(args, kwargs) { + var input = path.resolve(args[0] || process.cwd()); + var logLevel = kwargs.log; + + var fs = createNodeFS(input); + var book = Book.createForFS(fs); + + return book.setLogLevel(logLevel); +} + +module.exports = getBook; diff --git a/lib/cli/getOutputFolder.js b/lib/cli/getOutputFolder.js new file mode 100644 index 0000000..272dff9 --- /dev/null +++ b/lib/cli/getOutputFolder.js @@ -0,0 +1,17 @@ +var path = require('path'); + +/** + Return path to output folder + + @param {Array} args + @return {String} +*/ +function getOutputFolder(args) { + var bookRoot = path.resolve(args[0] || process.cwd()); + var defaultOutputRoot = path.join(bookRoot, '_book'); + var outputFolder = args[1]? path.resolve(process.cwd(), args[1]) : defaultOutputRoot; + + return outputFolder; +} + +module.exports = getOutputFolder; diff --git a/lib/cli/helper.js b/lib/cli/helper.js deleted file mode 100644 index 02cede6..0000000 --- a/lib/cli/helper.js +++ /dev/null @@ -1,140 +0,0 @@ -var _ = require('lodash'); -var path = require('path'); - -var Book = require('../book'); -var NodeFS = require('../fs/node'); -var Logger = require('../utils/logger'); -var Promise = require('../utils/promise'); -var fs = require('../utils/fs'); -var JSONOutput = require('../output/json'); -var WebsiteOutput = require('../output/website'); -var EBookOutput = require('../output/ebook'); - -var nodeFS = new NodeFS(); - -var LOG_OPTION = { - name: 'log', - description: 'Minimum log level to display', - values: _.chain(Logger.LEVELS) - .keys() - .map(function(s) { - return s.toLowerCase(); - }) - .value(), - defaults: 'info' -}; - -var FORMAT_OPTION = { - name: 'format', - description: 'Format to build to', - values: ['website', 'json', 'ebook'], - defaults: 'website' -}; - -var FORMATS = { - json: JSONOutput, - website: WebsiteOutput, - ebook: EBookOutput -}; - -// Commands which is processing a book -// the root of the book is the first argument (or current directory) -function bookCmd(fn) { - return function(args, kwargs) { - var input = path.resolve(args[0] || process.cwd()); - var book = new Book({ - fs: nodeFS, - root: input, - logLevel: kwargs.log - }); - - return fn(book, args.slice(1), kwargs); - }; -} - -// Commands which is working on a Output instance -function outputCmd(fn) { - return bookCmd(function(book, args, kwargs) { - var Out = FORMATS[kwargs.format]; - var outputFolder = undefined; - - // Set output folder - if (args[0]) { - outputFolder = path.resolve(process.cwd(), args[0]); - } - - return fn(new Out(book, { - root: outputFolder - }), args); - }); -} - -// Command to generate an ebook -function ebookCmd(format) { - return { - name: format + ' [book] [output] [file]', - description: 'generates ebook '+format, - options: [ - LOG_OPTION - ], - exec: bookCmd(function(book, args, kwargs) { - return fs.tmpDir() - .then(function(dir) { - var ext = '.'+format; - var outputFile = path.resolve(process.cwd(), args[0] || ('book' + ext)); - var output = new EBookOutput(book, { - root: dir, - format: format - }); - - return output.book.parse() - .then(function() { - return output.generate(); - }) - - // Copy the ebook files - .then(function() { - if (output.book.isMultilingual()) { - return Promise.serie(output.book.langs.list(), function(lang) { - var _outputFile = path.join( - path.dirname(outputFile), - path.basename(outputFile, ext) + '_' + lang.id + ext - ); - - return fs.copy( - path.resolve(dir, lang.id, 'index' + ext), - _outputFile - ); - }) - .thenResolve(output.book.langs.count()); - } else { - return fs.copy( - path.resolve(dir, 'index' + ext), - outputFile - ).thenResolve(1); - } - }) - .then(function(n) { - output.book.log.info.ok(n+' file(s) generated'); - - output.book.log.info('cleaning up... '); - return output.book.log.info.promise(fs.rmDir(dir)); - }); - }); - }) - }; -} - -module.exports = { - nodeFS: nodeFS, - bookCmd: bookCmd, - outputCmd: outputCmd, - ebookCmd: ebookCmd, - - options: { - log: LOG_OPTION, - format: FORMAT_OPTION - }, - - FORMATS: FORMATS -}; diff --git a/lib/cli/index.js b/lib/cli/index.js index eea707f..f1fca1d 100644 --- a/lib/cli/index.js +++ b/lib/cli/index.js @@ -1,199 +1,12 @@ -/* eslint-disable no-console */ - -var _ = require('lodash'); -var path = require('path'); -var tinylr = require('tiny-lr'); - -var Promise = require('../utils/promise'); -var PluginsManager = require('../plugins'); -var Book = require('../book'); - -var helper = require('./helper'); -var Server = require('./server'); -var watch = require('./watch'); - -module.exports = { - commands: [ - { - name: 'init [book]', - description: 'setup and create files for chapters', - options: [ - helper.options.log - ], - exec: function(args) { - var input = path.resolve(args[0] || process.cwd()); - return Book.init(helper.nodeFS, input); - } - }, - - { - name: 'parse [book]', - description: 'parse and returns debug information for a book', - options: [ - helper.options.log - ], - exec: helper.bookCmd(function(book) { - return book.parse() - .then(function() { - book.log.info.ln('Book located in:', book.root); - book.log.info.ln(''); - - if (book.config.exists()) book.log.info.ln('Configuration:', book.config.path); - - if (book.isMultilingual()) { - book.log.info.ln('Multilingual book detected:', book.langs.path); - } else { - book.log.info.ln('Readme:', book.readme.path); - book.log.info.ln('Summary:', book.summary.path); - if (book.glossary.exists()) book.log.info.ln('Glossary:', book.glossary.path); - - book.log.info.ln('Pages:'); - _.each(book.pages, function(page) { - book.log.info.ln('\t-', page.path); - }); - } - }); - }) - }, - - { - name: 'install [book]', - description: 'install all plugins dependencies', - options: [ - helper.options.log - ], - exec: helper.bookCmd(function(book, args) { - var plugins = new PluginsManager(book); - - return book.config.load() - .then(function() { - return plugins.install(); - }); - }) - }, - - { - name: 'build [book] [output]', - description: 'build a book', - options: [ - helper.options.log, - helper.options.format - ], - exec: helper.outputCmd(function(output, args, kwargs) { - return output.book.parse() - .then(function() { - return output.generate(); - }); - }) - }, - - helper.ebookCmd('pdf'), - helper.ebookCmd('epub'), - helper.ebookCmd('mobi'), - - { - 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 = path.resolve(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: helper.nodeFS, - root: input, - logLevel: kwargs.log - }); - - return book.parse() - .then(function() { - // Add livereload plugin - book.config.set('plugins', - book.config.get('plugins') - .concat([ - { name: 'livereload' } - ]) - ); - - 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(); - }); - } - } - - ] -}; +var buildEbook = require('./buildEbook'); + +module.exports = [ + require('./build'), + require('./serve'), + require('./install'), + require('./parse'), + require('./init'), + buildEbook('pdf'), + buildEbook('epub'), + buildEbook('mobi') +]; diff --git a/lib/cli/init.js b/lib/cli/init.js new file mode 100644 index 0000000..9a1bff8 --- /dev/null +++ b/lib/cli/init.js @@ -0,0 +1,17 @@ +var path = require('path'); + +var options = require('./options'); +var initBook = require('../init'); + +module.exports = { + name: 'install [book]', + description: 'setup and create files for chapters', + options: [ + options.log + ], + exec: function(args, kwargs) { + var bookRoot = path.resolve(process.cwd(), args[0] || './'); + + return initBook(bookRoot); + } +}; diff --git a/lib/cli/install.js b/lib/cli/install.js new file mode 100644 index 0000000..c001711 --- /dev/null +++ b/lib/cli/install.js @@ -0,0 +1,21 @@ +var options = require('./options'); +var getBook = require('./getBook'); + +var Parse = require('../parse'); +var Plugins = require('../plugins'); + +module.exports = { + name: 'install [book]', + description: 'install all plugins dependencies', + options: [ + options.log + ], + exec: function(args, kwargs) { + var book = getBook(args, kwargs); + + return Parse.parseConfig(book) + .then(function(resultBook) { + return Plugins.installPlugins(resultBook); + }); + } +}; diff --git a/lib/cli/options.js b/lib/cli/options.js new file mode 100644 index 0000000..ddcb5c5 --- /dev/null +++ b/lib/cli/options.js @@ -0,0 +1,30 @@ +var Logger = require('../utils/logger'); + +var logOptions = { + name: 'log', + description: 'Minimum log level to display', + values: Object.keys(Logger.LEVELS) + .map(function(s) { + return s.toLowerCase(); + }), + defaults: 'info' +}; + +var formatOption = { + name: 'format', + description: 'Format to build to', + values: ['website', 'json', 'ebook'], + defaults: 'website' +}; + +var timingOption = { + name: 'timing', + description: 'Print timing debug information', + defaults: false +}; + +module.exports = { + log: logOptions, + format: formatOption, + timing: timingOption +}; diff --git a/lib/cli/parse.js b/lib/cli/parse.js new file mode 100644 index 0000000..0fa509a --- /dev/null +++ b/lib/cli/parse.js @@ -0,0 +1,79 @@ +var options = require('./options'); +var getBook = require('./getBook'); + +var Parse = require('../parse'); + +function printBook(book) { + var logger = book.getLogger(); + + var config = book.getConfig(); + var configFile = config.getFile(); + + var summary = book.getSummary(); + var summaryFile = summary.getFile(); + + var readme = book.getReadme(); + var readmeFile = readme.getFile(); + + var glossary = book.getGlossary(); + var glossaryFile = glossary.getFile(); + + if (configFile.exists()) { + logger.info.ln('Configuration file is', configFile.getPath()); + } + + if (readmeFile.exists()) { + logger.info.ln('Introduction file is', readmeFile.getPath()); + } + + if (glossaryFile.exists()) { + logger.info.ln('Glossary file is', glossaryFile.getPath()); + } + + if (summaryFile.exists()) { + logger.info.ln('Table of Contents file is', summaryFile.getPath()); + } +} + +function printMultingualBook(book) { + var logger = book.getLogger(); + var languages = book.getLanguages(); + var books = book.getBooks(); + + logger.info.ln(languages.size + ' languages'); + + languages.forEach(function(lang) { + logger.info.ln('Language:', lang.getTitle()); + printBook(books.get(lang.getID())); + logger.info.ln(''); + }); +} + +module.exports = { + name: 'parse [book]', + description: 'parse and print debug information about a book', + options: [ + options.log + ], + exec: function(args, kwargs) { + var book = getBook(args, kwargs); + var logger = book.getLogger(); + + return Parse.parseBook(book) + .then(function(resultBook) { + var rootFolder = book.getRoot(); + var contentFolder = book.getContentRoot(); + + logger.info.ln('Book located in:', rootFolder); + if (contentFolder != rootFolder) { + logger.info.ln('Content located in:', contentFolder); + } + + if (resultBook.isMultilingual()) { + printMultingualBook(resultBook); + } else { + printBook(resultBook); + } + }); + } +}; diff --git a/lib/cli/serve.js b/lib/cli/serve.js new file mode 100644 index 0000000..628f591 --- /dev/null +++ b/lib/cli/serve.js @@ -0,0 +1,93 @@ +/* eslint-disable no-console */ + +var tinylr = require('tiny-lr'); + +var Parse = require('../parse'); +var Output = require('../output'); + +var options = require('./options'); +var getBook = require('./getBook'); +var getOutputFolder = require('./getOutputFolder'); +var Server = require('./server'); +var watch = require('./watch'); + +var server, lrServer, lrPath; + +function generateBook(args, kwargs) { + var port = kwargs.port; + var outputFolder = getOutputFolder(args); + var book = getBook(args, kwargs); + var Generator = Output.getGenerator(kwargs.format); + + // Stop server if running + if (server.isRunning()) console.log('Stopping server'); + + return server.stop() + .then(function() { + return Parse.parseBook(book) + .then(function(resultBook) { + return Output.generate(Generator, resultBook, { + root: outputFolder + }); + }); + }) + .then(function() { + console.log(); + console.log('Starting server ...'); + return server.start(outputFolder, port); + }) + .then(function() { + console.log('Serving book on http://localhost:'+port); + + if (lrPath) { + // trigger livereload + lrServer.changed({ + body: { + files: [lrPath] + } + }); + } + }) + .then(function() { + if (!kwargs.watch) return; + + return watch(book.getRoot()) + .then(function(filepath) { + // set livereload path + lrPath = filepath; + console.log('Restart after change in file', filepath); + console.log(''); + return generateBook(args, kwargs); + }); + }); +} + +module.exports = { + name: 'serve [book] [output]', + description: 'serve the book as a website for testing', + 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 + }, + options.log, + options.format + ], + exec: function(args, kwargs) { + server = new Server(); + lrServer = tinylr({}); + + return generateBook(args, kwargs); + } +}; diff --git a/lib/cli/server.js b/lib/cli/server.js index 8d3d7ce..555bbb7 100644 --- a/lib/cli/server.js +++ b/lib/cli/server.js @@ -6,20 +6,28 @@ var url = require('url'); var Promise = require('../utils/promise'); -var Server = function() { +function Server() { this.running = null; this.dir = null; this.port = 0; this.sockets = []; -}; +} util.inherits(Server, events.EventEmitter); -// Return true if the server is running +/** + Return true if the server is running + + @return {Boolean} +*/ Server.prototype.isRunning = function() { return !!this.running; }; -// Stop the server +/** + Stop the server + + @return {Promise} +*/ Server.prototype.stop = function() { var that = this; if (!this.isRunning()) return Promise(); @@ -40,6 +48,11 @@ Server.prototype.stop = function() { return d.promise; }; +/** + Start the server + + @return {Promise} +*/ Server.prototype.start = function(dir, port) { var that = this, pre = Promise(); port = port || 8004; diff --git a/lib/cli/watch.js b/lib/cli/watch.js index 130b0d4..0d1ab17 100644 --- a/lib/cli/watch.js +++ b/lib/cli/watch.js @@ -5,7 +5,12 @@ var chokidar = require('chokidar'); var Promise = require('../utils/promise'); var parsers = require('../parsers'); -// Watch a folder and resolve promise once a file is modified +/** + Watch a folder and resolve promise once a file is modified + + @param {String} dir + @return {Promise} +*/ function watch(dir) { var d = Promise.defer(); dir = path.resolve(dir); |