Compare commits

...

14 Commits

Author SHA1 Message Date
Aaron O'Mullan 7fd7732345 Bump version to 1.4.2 2014-12-10 23:27:01 +01:00
Aaron O'Mullan 960ef4d945 Fix descriptor and me leak with bad write queues
Node.js for some reason queues up writes from fs.writeFile causing
memory and descriptor leaks, preventing building large books. This is
now fixed

Fixes #336, fixes #454
2014-12-10 16:32:49 +01:00
Aaron O'Mullan 92523fb4b5 Force process.exit at end of build 2014-12-09 17:31:23 +01:00
Samy Pessé 66bb46df22 Merge pull request #516 from jr0cket/patch-1
Add gitbook init command to "how to use" section
2014-12-08 00:46:04 +01:00
John Stevenson 0454710d3b Add gitbook init command to "how to use" section
Highlight that Gitbook can generate the files and directories for a book from its SUMMARY.md file.
2014-12-07 23:44:41 +00:00
Samy Pessé c9a31cc987 Add option isbn for books 2014-12-03 23:19:47 +01:00
Samy Pessé f03c316274 Bump version to 1.4.1 2014-11-30 17:01:47 +01:00
Samy Pessé 001a4f91be Fix command 'install' without arguments 2014-11-30 17:00:12 +01:00
Samy Pessé 44a3bfe296 Bump version to 1.4.0 2014-11-30 14:59:08 +01:00
Samy Pessé a9ce6f808d Add doc about gitbook install 2014-11-30 14:54:34 +01:00
Samy Pessé e5c667e0fa Add test for parsing of plugins list with version 2014-11-30 14:50:47 +01:00
Samy Pessé e36f274dbe Handle specific version for plugins 2014-11-30 14:46:21 +01:00
Samy Pessé 2a88427e74 Improve error message when plugins not found 2014-11-30 13:13:00 +01:00
Samy Pessé ed48f5f3ee Add command "install" to install plugins from book.json 2014-11-30 13:04:38 +01:00
12 changed files with 174 additions and 45 deletions
+7
View File
@@ -1,5 +1,12 @@
# Release notes
## 1.4.1
- Fix command 'install' without arguments
## 1.4.0
- Add command `gitbook install` to install plugins from book.json
- `package.json` is no longer necessary
## 1.3.4
- Add glossary to ebooks
- Fix autocover with new hook "finish:before"
+9 -1
View File
@@ -16,6 +16,11 @@ GitBook can be installed from **NPM** using:
$ npm install gitbook -g
```
Create the directories and files for a book from its [SUMMARY.md](https://github.com/GitbookIO/gitbook#book-format) file using
```
$ gitbook init
```
You can serve a repository as a book using:
```
@@ -52,9 +57,10 @@ Here are the options that can be stored in this file:
// It's not advised this option in the book.json
"generator": "site",
// Book title and description (defaults are extracted from the README)
// Book metadats (somes are extracted from the README by default)
"title": null,
"description": null,
"isbn": null,
// For ebook format, the extension to use for generation (default is detected from output extension)
// "epub", "pdf", "mobi"
@@ -214,6 +220,8 @@ The platform [GitBook.io](https://www.gitbook.io/) is like an "Heroku for books"
Plugins can be used to extend your book's functionality. Read [GitbookIO/plugin](https://github.com/GitbookIO/plugin) for more information about how to build a plugin for GitBook.
Plugins needed to build a book can be installed using: `gitbook install ./`.
##### Official plugins:
| Name | Description |
+5
View File
@@ -51,6 +51,11 @@ var makeBuildFunc = function(converter) {
// Exit process with failure code
process.exit(-1);
})
.then(function() {
// Prevent badly behaving plugins
// from making the process hang
process.exit(0);
});
};
};
+25 -1
View File
@@ -7,7 +7,7 @@ var prog = require('commander');
var tinylr = require('tiny-lr-fork');
var pkg = require('../package.json');
var generators = require("../lib/generate").generators;
var genbook = require("../lib/generate");
var initDir = require("../lib/generate/init");
var fs = require('../lib/generate/fs');
@@ -80,6 +80,30 @@ build.command(prog.command('serve [source_dir]'))
generate();
});
build.commandEbook(prog.command('install [source_dir]'))
.description('Install plugins for a book')
.action(function(dir, options) {
dir = dir || process.cwd();
console.log("Install plugins in", dir);
genbook.config.read({
input: dir
})
.then(function(options) {
return genbook.Plugin.install(options);
})
.then(function() {
console.log("Successfully installed plugins!");
})
.fail(function(err) {
// Log error
utils.logError(err);
// Exit process with failure code
process.exit(-1);
});
});
build.commandEbook(prog.command('pdf [source_dir]'))
.description('Build a gitbook as a PDF')
.action(function(dir, options) {
+28 -4
View File
@@ -1,3 +1,4 @@
var Q = require('q');
var _ = require('lodash');
var path = require('path');
@@ -16,9 +17,10 @@ var CONFIG = {
// Configuration file to use
"configFile": "book",
// Book title and description (defaults are extracted from the README)
// Book metadats (somes are extracted from the README by default)
"title": null,
"description": null,
"isbn": null,
// For ebook format, the extension to use for generation (default is detected from output extension)
// "epub", "pdf", "mobi"
@@ -96,10 +98,32 @@ var CONFIG = {
}
};
// Return complete configuration
var defaultsConfig = function(options) {
return _.merge(options || {}, CONFIG, _.defaults);
};
// Read configuration from book.json
var readConfig = function(options) {
options = defaultsConfig(options);
return Q()
.then(function() {
try {
var _config = require(path.resolve(options.input, options.configFile));
options = _.merge(options, _.omit(_config, 'input', 'configFile', 'defaultsPlugins', 'generator'));
}
catch(err) {
// No config file: not a big deal
return Q();
}
})
.thenResolve(options);
};
module.exports = {
CONFIG: CONFIG,
defaults: function(options) {
return _.merge(options || {}, CONFIG, _.defaults);
}
defaults: defaultsConfig,
read: readConfig
}
+1
View File
@@ -30,6 +30,7 @@ Generator.prototype.finish = function() {
"--cover": that.options.cover,
"--title": that.options.title,
"--comments": that.options.description,
"--isbn": that.options.isbn,
"--authors": that.options.author,
"--publisher": "GitBook",
"--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]",
+14 -1
View File
@@ -64,7 +64,20 @@ var getFiles = function(path) {
module.exports = {
list: getFiles,
readFile: Q.denodeify(fs.readFile),
writeFile: Q.denodeify(fs.writeFile),
//writeFile: Q.denodeify(fs.writeFile),
writeFile: function(filename, data, options) {
var d = Q.defer();
try {
fs.writeFileSync(filename, data, options)
} catch(err) {
d.reject(err);
}
d.resolve();
return d.promise;
},
mkdirp: Q.denodeify(fsExtra.mkdirp),
copy: Q.denodeify(fsExtra.copy),
remove: Q.denodeify(fsExtra.remove),
+13 -20
View File
@@ -48,28 +48,18 @@ var loadGenerator = function(options) {
var generate = function(options) {
// Set defaults to options
options = defaultConfig.defaults(options);
// Validate options
if (!options.input) {
return Q.reject(new Error("Need option input (book input directory)"));
}
// Check files to get folder type (book, multilanguage book or neither)
return checkGenerator(options)
// Read config file
.then(function() {
try {
var _config = require(path.resolve(options.input, options.configFile));
return defaultConfig.read(options)
.then(function(_options) {
options = _options;
options = _.merge(options, _.omit(_config, 'input', 'configFile', 'defaultsPlugins', 'generator'));
}
catch(err) {
// No config file: not a big deal
return Q();
// Validate options
if (!options.input) {
return Q.reject(new Error("Need option input (book input directory)"));
}
// Check files to get folder type (book, multilanguage book or neither)
return checkGenerator(options);
})
// Read readme
@@ -99,7 +89,9 @@ var generate = function(options) {
});
};
/*
* Generate a multilanguage book by generating a book for each folder.
*/
var generateMultiLang = function(options) {
var langsSummary;
options.output = options.output || path.join(options.input, "_book");
@@ -378,4 +370,5 @@ module.exports = {
file: generateFile,
book: generateBook,
Plugin: Plugin,
config: defaultConfig
};
+57 -16
View File
@@ -4,6 +4,7 @@ var semver = require("semver");
var path = require("path");
var url = require("url");
var fs = require("./fs");
var npmi = require('npmi');
var resolve = require('resolve');
var pkg = require("../../package.json");
@@ -152,33 +153,73 @@ Plugin.prototype.copyAssets = function(out, options) {
};
// Normalize a list of plugin name to use
Plugin.normalizeNames = function(names) {
// Install a list of plugin
Plugin.install = function(options) {
// Normalize list of plugins
var plugins = Plugin.normalizeList(options.plugins);
// Install plugins one by one
return _.reduce(plugins, function(prev, plugin) {
return prev.then(function() {
var fullname = "gitbook-plugin-"+plugin.name;
console.log("Install plugin", plugin.name, "from npm ("+fullname+") with version", (plugin.version || "*"));
return Q.nfcall(npmi, {
'name': fullname,
'version': plugin.version,
'path': options.input,
'npmLoad': {
'loglevel': 'silent',
'loaded': false,
'prefix': options.input
}
});
});
}, Q());
};
// Normalize a list of plugins to use
Plugin.normalizeList = function(plugins) {
// Normalize list to an array
names = _.isString(names) ? names.split(",") : (names || []);
plugins = _.isString(plugins) ? plugins.split(",") : (plugins || []);
// Divide as {name, version} to handle format like "myplugin@1.0.0"
plugins = _.map(plugins, function(plugin) {
var parts = plugin.split("@");
return {
'name': parts[0],
'version': parts[1] // optional
}
});
// List plugins to remove
var toremove = _.chain(names)
.filter(function(name) {
return name.length > 0 && name[0] == "-";
var toremove = _.chain(plugins)
.filter(function(plugin) {
return plugin.name.length > 0 && plugin.name[0] == "-";
})
.map(function(name) {
return name.slice(1);
.map(function(plugin) {
return plugin.name.slice(1);
})
.value();
// Merge with defaults
names = _.chain(names)
.concat(Plugin.defaults)
plugins = _.chain(plugins)
.concat(_.map(Plugin.defaults, function(plugin) {
return { 'name': plugin }
}))
.uniq()
.value();
// Remove plugins starting with
names = _.filter(names, function(name) {
return !_.contains(toremove, name) && !(name.length > 0 && name[0] == "-");
// Build final list
plugins = _.filter(plugins, function(plugin) {
return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == "-");
});
return names;
return plugins;
};
// Normalize a list of plugin name to use
Plugin.normalizeNames = function(plugins) {
return _.pluck(Plugin.normalizeList(plugins), "name");
};
// Extract data from a list of plugin
@@ -196,7 +237,7 @@ Plugin.fromList = function(names, root, generator, options) {
return plugin;
});
if (_.size(failed) > 0) return Q.reject(new Error("Error loading plugins: "+failed.join(",")));
if (_.size(failed) > 0) return Q.reject(new Error("Error loading plugins: "+failed.join(",")+". Run 'gitbook install' to install plugins from NPM."));
// The raw resources extracted from each plugin
var pluginResources;
@@ -276,7 +317,7 @@ Plugin.fromList = function(names, root, generator, options) {
});
};
// Default plugins
// Default plugins added to each books
Plugin.defaults = [
"mathjax"
];
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "gitbook",
"version": "1.3.4",
"version": "1.4.2",
"homepage": "http://www.gitbook.io/",
"description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js",
@@ -19,6 +19,7 @@
"highlight.js": "8.3.0",
"tmp": "0.0.23",
"semver": "2.2.1",
"npmi": "0.1.1",
"gaze": "~0.5.1",
"resolve": "0.6.3",
"tiny-lr-fork": "0.0.5",
+10 -1
View File
@@ -12,7 +12,7 @@ describe('Plugin validation', function () {
});
});
describe('Plugin list of names', function () {
describe('Plugins list', function () {
var firstDefault = _.first(Plugin.defaults);
it('should convert string to array', function() {
@@ -27,6 +27,15 @@ describe('Plugin list of names', function () {
it('should remove name starting with -', function() {
assert(!_.contains(Plugin.normalizeNames(["-"+firstDefault]), firstDefault));
});
it('should accept version', function() {
var _name = "test@0.3.0,exercises@1.2.0,test2";
var plugins = Plugin.normalizeList(_name);
assert(_.find(plugins, {'name': "test"}).version = "0.3.0");
assert(_.find(plugins, {'name': "exercises"}).version = "1.2.0");
assert(!_.find(plugins, {'name': "test2"}).version);
});
});
describe('Plugin defaults loading', function () {
+3
View File
@@ -15,6 +15,9 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="{{ staticBase }}/images/apple-touch-icon-precomposed-152.png">
<link rel="shortcut icon" href="{{ staticBase }}/images/favicon.ico" type="image/x-icon">
{% if options.isbn %}
<meta name="identifier" content="{{ options.isbn }}" scheme="ISBN">
{% endif %}
{% block head %}{% endblock %}
{{ htmlSnippet("head:end")|default("") }}
</head>