mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-26 12:18:01 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7fd7732345 | |||
| 960ef4d945 | |||
| 92523fb4b5 | |||
| 66bb46df22 | |||
| 0454710d3b | |||
| c9a31cc987 | |||
| f03c316274 | |||
| 001a4f91be | |||
| 44a3bfe296 | |||
| a9ce6f808d | |||
| e5c667e0fa | |||
| e36f274dbe | |||
| 2a88427e74 | |||
| ed48f5f3ee |
@@ -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"
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 () {
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user