mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-26 12:18:01 +00:00
Compare commits
213 Commits
2.0.0-beta.4
...
2.4.3
| Author | SHA1 | Date | |
|---|---|---|---|
| f0c97756de | |||
| f14c57d4c2 | |||
| b6f60cc8ad | |||
| 170516d343 | |||
| c931e91bef | |||
| db9151ae36 | |||
| f2d86b6d69 | |||
| 534cdd091b | |||
| fdf4a48638 | |||
| 58a3c5634e | |||
| f0ddb6e89f | |||
| 2b408db7cb | |||
| 4e4bd23e2b | |||
| 62d0eb606e | |||
| 0e9beeae14 | |||
| 5e4dbdee24 | |||
| 9435dae6cd | |||
| 813210bf47 | |||
| c372dfc544 | |||
| ac77200683 | |||
| f5afbc90a4 | |||
| b24760c819 | |||
| 6732957e42 | |||
| 1fd0006859 | |||
| 5a205346e3 | |||
| e193004659 | |||
| fe3a9c33d0 | |||
| d83116b88f | |||
| 5f56098ae7 | |||
| 0f935f4372 | |||
| df5c5a8c01 | |||
| 57a0c3bad8 | |||
| 2a52326a45 | |||
| 87e4ee1eeb | |||
| 45833eea9a | |||
| baf10e9b15 | |||
| 463a947df1 | |||
| cceebb4fd9 | |||
| afd5465a61 | |||
| fe604733de | |||
| da22f9f39f | |||
| 284ebbf75d | |||
| 2c08ccad87 | |||
| fa4e234bab | |||
| 35c4179ca1 | |||
| c14ba7ce34 | |||
| 601ce3add3 | |||
| 08ff39fd0b | |||
| 82a6ee3875 | |||
| 3bf592f870 | |||
| 241e205717 | |||
| fb2720ea8d | |||
| 14853befda | |||
| e7f2803d11 | |||
| 320015dd8f | |||
| 471c124a98 | |||
| aa44a109c6 | |||
| 41e5ca8422 | |||
| 53af8b2bb6 | |||
| bd885b135f | |||
| 62a6dde81a | |||
| 14b72b344f | |||
| ac03668063 | |||
| 2512e176c9 | |||
| b613393866 | |||
| 607a6e95de | |||
| 871f574ff7 | |||
| 377e470b15 | |||
| e0a3f0cea5 | |||
| 66727b2736 | |||
| 8215ad977f | |||
| a611dffbdf | |||
| 39f378d8cf | |||
| f00da58153 | |||
| 09b038b16d | |||
| cf601d97ad | |||
| 80edad53d4 | |||
| a972b32769 | |||
| efcaae3e60 | |||
| e0ee591638 | |||
| 310a6d909f | |||
| cefd8fc50f | |||
| a4422593f2 | |||
| e2c5d22756 | |||
| 84385d9dc3 | |||
| 6cfa4c8ab7 | |||
| c76ccc53e1 | |||
| d48eafb1a2 | |||
| 302d9b4b91 | |||
| a091a7f68b | |||
| 53621f6de3 | |||
| 288b965db3 | |||
| 578c9dcfc6 | |||
| 64de17b248 | |||
| d699dff872 | |||
| 501843d624 | |||
| 508ce4283e | |||
| 4aa8e66549 | |||
| 917a6f70d8 | |||
| 2f5e53da55 | |||
| c3cdefc90a | |||
| 9c580e4989 | |||
| dbc75f7352 | |||
| db841a8578 | |||
| 50c902c4e4 | |||
| 84b6e0bde6 | |||
| abc29c3fba | |||
| 8356c534f5 | |||
| 78ff2687b4 | |||
| 9b6586fb36 | |||
| ea133219d0 | |||
| c9b5d81a3c | |||
| d895c63211 | |||
| 8c86da67ca | |||
| a9a230dfc3 | |||
| 6d53e52771 | |||
| 52e10b7cfe | |||
| addac85696 | |||
| 13fb949d68 | |||
| f1b593cfbd | |||
| f998e2dec9 | |||
| 2c0bd53a91 | |||
| 6dd7f6162f | |||
| 475b0bb2b3 | |||
| e6343935e3 | |||
| 946883fd4c | |||
| b1b7e3148b | |||
| 32203f6438 | |||
| f2d3c5d2ed | |||
| 7e2f0ed4bc | |||
| a877acf905 | |||
| ac756ddf7e | |||
| 36e2236e6b | |||
| 72df665520 | |||
| 07785528a5 | |||
| bb579dd581 | |||
| a7d3064340 | |||
| 1a60778248 | |||
| cda4bb5bda | |||
| 7543781046 | |||
| 82f3ffad06 | |||
| dc4d2a1731 | |||
| 2523925915 | |||
| d5bbd8475c | |||
| 54d195268a | |||
| a0deb9509f | |||
| b0b1433594 | |||
| 17a2c0a37c | |||
| fd68ea1cc2 | |||
| 784874c776 | |||
| f45c6d3466 | |||
| 98af70902f | |||
| 41a67b5c4c | |||
| 83ee903af4 | |||
| e591b0e485 | |||
| bcc9a34742 | |||
| 6b56c7de7f | |||
| 7b42719699 | |||
| 64b8343f67 | |||
| fcc5089fa6 | |||
| 334f782aa8 | |||
| 1776f2bf77 | |||
| e259412f79 | |||
| 999a02f110 | |||
| 604d0fba65 | |||
| 0e8f7b870b | |||
| 1eebc9a46e | |||
| 0a94c80c44 | |||
| c70bb2fbd8 | |||
| 12c06b384f | |||
| 82a58e7ac3 | |||
| dcb56ef433 | |||
| 8da91de514 | |||
| 4b20685562 | |||
| fff7c5cc3a | |||
| 3065742b73 | |||
| ffd69ad1f2 | |||
| 9004011479 | |||
| 1046bb4133 | |||
| 5578505213 | |||
| a61f56662a | |||
| f42e48be03 | |||
| f640e101ba | |||
| b7da276ae0 | |||
| bf7c0989f5 | |||
| 65fdbe7f55 | |||
| c9af209a93 | |||
| 48ab44a776 | |||
| b2e684af8f | |||
| b38a2c11a4 | |||
| c31acef47a | |||
| e9a573e18a | |||
| c07ddc77f7 | |||
| 303fc992d4 | |||
| 4b7797f61b | |||
| f775d41933 | |||
| 7722e7bdfa | |||
| 346c05f0de | |||
| 73c69923b3 | |||
| afbca4bdf5 | |||
| b61371942b | |||
| 61c07e8876 | |||
| d317741144 | |||
| 275739a778 | |||
| cb3a43ed8a | |||
| ec8d1d8fc9 | |||
| 868a3186dd | |||
| 17b546181c | |||
| 2d1aa15af8 | |||
| 63ee94ff89 | |||
| 7d9dc08a38 | |||
| a836bdedb4 | |||
| 15ebae8cb1 |
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"strict": false,
|
||||
"browser": false,
|
||||
"quotmark": true,
|
||||
"node": true,
|
||||
"mocha": true
|
||||
}
|
||||
+27
-10
@@ -21,22 +21,39 @@ Translators
|
||||
------------
|
||||
|
||||
- German
|
||||
- Winnie (@winniehell)
|
||||
- Winnie (@winniehell)
|
||||
- Italian
|
||||
- Giulio Bonanome (@gbonanome)
|
||||
- Giulio Bonanome (@gbonanome)
|
||||
- Russian
|
||||
- Andrey Akinshin (@AndreyAkinshin)
|
||||
- Andrey Akinshin (@AndreyAkinshin)
|
||||
- Norwegian
|
||||
- Knut Melvær (@kmelve)
|
||||
- Knut Melvær (@kmelve)
|
||||
- Persian (Farsi)
|
||||
- Mohammad Hossein Mojtahedi (@mhm5000)
|
||||
- Mohammad Hossein Mojtahedi (@mhm5000)
|
||||
- Polish
|
||||
- Łukasz Szeremeta (@lszeremeta)
|
||||
- Portuguese
|
||||
- Ryan O'Mullan (@RyanOM)
|
||||
- Ryan O'Mullan (@RyanOM)
|
||||
- Spanish
|
||||
- Ryan O'Mullan (@RyanOM)
|
||||
- Ryan O'Mullan (@RyanOM)
|
||||
- Simplifiled Chinese
|
||||
- Hu Hao (@howiehu)
|
||||
- Hu Hao (@howiehu)
|
||||
- Traditional Chinese
|
||||
- Hu Hao (@howiehu)
|
||||
- Hu Hao (@howiehu)
|
||||
- French
|
||||
- Samy Pessé (@SamyPesse)
|
||||
- Samy Pessé (@SamyPesse)
|
||||
- Romanian
|
||||
- Iancu Aurel (@awrelll)
|
||||
- Finnish
|
||||
- Tommi Savikko (@savikko)
|
||||
- Japanese
|
||||
- Iancu Aurel (@awrelll)
|
||||
- Korean
|
||||
- Iancu Aurel (@awrelll)
|
||||
- SangYeob Yu (@deminoth)
|
||||
- Vietnamese
|
||||
- Hong Nguyen (@nghong)
|
||||
- Hebrew
|
||||
- @a-moses
|
||||
- Ukrainian
|
||||
- @Karnaukhov
|
||||
|
||||
+95
@@ -2,6 +2,101 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 2.4.3
|
||||
- Add ukrainian translation (`uk`)
|
||||
- Add `book.json` configuration for maximum size of search index
|
||||
- Improve reliability of summary parser
|
||||
|
||||
## 2.4.2
|
||||
- Default plugins should not be installed by `gitbook install`
|
||||
- Limit search index size to avoid crash during generation
|
||||
- Fix code highlighting for html without language specified
|
||||
- Fix warning message for gitbook version when building a multilingual book
|
||||
|
||||
## 2.4.1
|
||||
- Fix disabling of default plugins, ex: `-highlight`
|
||||
|
||||
## 2.4.0
|
||||
- Fix page being updated when user wants to open a link in a new tab
|
||||
- Plugins can now replaced default code highlighter
|
||||
- Add semantic information for screen readers (web version)
|
||||
- Content references accept absolute paths, resolved to book folder
|
||||
- Improve overall reliability
|
||||
|
||||
## 2.3.3
|
||||
- Fix bug in SUMMARY parsing preventing multiple entries without filenames
|
||||
|
||||
## 2.3.2
|
||||
- Fix blocks (like maths) in Asciidoc
|
||||
- Fix error when checking gitbook version
|
||||
|
||||
## 2.3.1
|
||||
- Fix black font color for ebooks (mobi, pdf and epub)
|
||||
- Fix ISO code for korean language
|
||||
- Fix korean translation
|
||||
- Fix syntax highlighting for asciidoc
|
||||
- Fix inline html escaping in markdown
|
||||
- Add warning for file outside SUMMARY
|
||||
- Force SUMMARY entries to be unique by filename
|
||||
|
||||
## 2.3.0
|
||||
- Fix nunjucks issue with multiple `{% raw %}` blocks
|
||||
- Fix crash when git conref failed
|
||||
- Fix crash when failed to download remote image (better error message)
|
||||
- Fix flicking effect when changing page (big UX improvement)
|
||||
- Add Hebrew translation (`he`)
|
||||
- Add utility method `book.config.get` for plugins
|
||||
- Hooks `page:before` and `page` are no longer deprecated
|
||||
- Remove webfonts to make website lighter
|
||||
- Make glossary's order case insensitive
|
||||
|
||||
## 2.2.0
|
||||
- Fix direction in code blocks (always LTR)
|
||||
- Add options `chapterMark` and `pageBreaksBefore` for PDF
|
||||
- Update code highlighting library
|
||||
- `book.json` accessible as `config` in templating syntax
|
||||
- Add Vietnamese translation (`vi`)
|
||||
|
||||
## 2.1.0
|
||||
- Fix error in calcul of `levels` in table of contents, error introduced a few versions ago
|
||||
- Add optional `styles/print.css` to replace `print.css` used in ebook
|
||||
|
||||
## 2.0.4
|
||||
- Fix `{% raw %}`, got confused with "fake" variable declarations
|
||||
- Fix title of language chooser
|
||||
- Fix the X-UA-Compatible meta tag
|
||||
- Move style sheets to the <head> section
|
||||
|
||||
## 2.0.3
|
||||
- Fix `gitbook init` for SUMMARY with empty entries
|
||||
- Fix escaping of code blocks in markdown
|
||||
|
||||
## 2.0.2
|
||||
- Fix relative links in windows
|
||||
- Improve watcher in serve command (switch to chokidar)
|
||||
- Add Romanian translation (`ro`)
|
||||
- Add Finish translation (`fi`)
|
||||
- Add Japanese translation (`jp`)
|
||||
- Add Korean translation (`kr`)
|
||||
|
||||
## 2.0.1
|
||||
- Improve error logging (display file, line and column)
|
||||
- Add back support for `options.originalInput`
|
||||
- Don't process math in markdown parser (delegated to `mathjax` plugin)
|
||||
- Fix some cases of code blocks escaping
|
||||
- Fix i18n for introduction title in json format
|
||||
- Fix reload when book configuration is updated
|
||||
- Fix backslashes in url when building on windows
|
||||
|
||||
## 2.0.0
|
||||
- Fix page title of introduction
|
||||
- Ignore codeblocks when replacing glossary terms
|
||||
- Improve Unit Tests
|
||||
- Fix scrolling position in website when preparing page
|
||||
|
||||
## 2.0.0-beta.5
|
||||
- Fix progress order in json format
|
||||
|
||||
## 2.0.0-beta.4
|
||||
- Fix default generator for use programmatically
|
||||
- Add option "author" for html meta tags
|
||||
|
||||
@@ -2,14 +2,15 @@ GitBook
|
||||
=======
|
||||
|
||||
[](http://badge.fury.io/js/gitbook)
|
||||
[](https://travis-ci.org/GitbookIO/gitbook)
|
||||
[](https://ci.appveyor.com/project/SamyPesse/gitbook)
|
||||
[](https://travis-ci.org/GitbookIO/gitbook)
|
||||
[](https://ci.appveyor.com/project/GitBook/gitbook)
|
||||
[](https://slack.gitbook.com)
|
||||
|
||||
GitBook is a command line tool (and Node.js library) for building beautiful books using GitHub/Git and Markdown (or AsciiDoc). Here is an example: [Learn Javascript](https://www.gitbook.com/book/GitBookIO/javascript).
|
||||
|
||||
You can publish and host book easily online using [gitbook.com](https://www.gitbook.com), a web-editor is [also available](https://www.gitbook.com/editor).
|
||||
You can publish and host book easily online using [gitbook.com](https://www.gitbook.com), a desktop editor is [also available](https://www.gitbook.com/editor).
|
||||
|
||||
Stay updated by following [@GitBookIO](https://twitter.com/GitBookIO) on Twitter or [GitBook](https://www.facebook.com/gitbookcom) on Facebook.
|
||||
Check out the [GitBook Community Slack Channel](https://slack.gitbook.com), Stay updated by following [@GitBookIO](https://twitter.com/GitBookIO) on Twitter or [GitBook](https://www.facebook.com/gitbookcom) on Facebook.
|
||||
|
||||
Complete documentation is available at [help.gitbook.com](http://help.gitbook.com/).
|
||||
|
||||
@@ -32,13 +33,13 @@ $ gitbook init
|
||||
You can serve a repository as a book using:
|
||||
|
||||
```
|
||||
$ gitbook serve ./repository
|
||||
$ gitbook serve
|
||||
```
|
||||
|
||||
Or simply build the static website using:
|
||||
|
||||
```
|
||||
$ gitbook build ./repository ./outputFolder
|
||||
$ gitbook build
|
||||
```
|
||||
|
||||
## Features
|
||||
@@ -50,7 +51,7 @@ $ gitbook build ./repository ./outputFolder
|
||||
* [AsciiDoc Support](http://help.gitbook.com/format/asciidoc.html)
|
||||
* [Variables and Templating](http://help.gitbook.com/format/templating.html)
|
||||
* [Content References](http://help.gitbook.com/format/conrefs.html)
|
||||
* [Plugins](http://help.gitbook.com/plugins/index.html)
|
||||
* [Plugins](http://help.gitbook.com/format/plugins.html)
|
||||
* Interractive reader website:
|
||||
* Search
|
||||
* Font Settings (Serif, Sans Serif)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = {
|
||||
// Return non-parsed html
|
||||
// since blocks are by default non-parsable, a simple identity method works fine
|
||||
html: _.identity,
|
||||
|
||||
// Highlight a code block
|
||||
// This block can be extent by plugins
|
||||
code: _.identity
|
||||
};
|
||||
+178
-80
@@ -1,29 +1,25 @@
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var util = require("util");
|
||||
var lunr = require('lunr');
|
||||
var lunr = require("lunr");
|
||||
var parsers = require("gitbook-parsers");
|
||||
var color = require('bash-color');
|
||||
|
||||
var fs = require("./utils/fs");
|
||||
var parseNavigation = require("./utils/navigation");
|
||||
var parseProgress = require("./utils/progress");
|
||||
var pageUtil = require("./utils/page");
|
||||
var batch = require("./utils/batch");
|
||||
var pathUtil = require("./utils/path");
|
||||
var links = require("./utils/links");
|
||||
var i18n = require("./utils/i18n");
|
||||
var logger = require("./utils/logger");
|
||||
|
||||
var Configuration = require("./configuration");
|
||||
var TemplateEngine = require("./template");
|
||||
var Plugin = require("./plugin");
|
||||
var PluginsList = require("./pluginslist");
|
||||
|
||||
var generators = require("./generators");
|
||||
|
||||
var Book = function(root, context, parent) {
|
||||
var that = this;
|
||||
|
||||
this.context = _.defaults(context || {}, {
|
||||
// Extend book configuration
|
||||
config: {},
|
||||
@@ -83,17 +79,24 @@ var Book = function(root, context, parent) {
|
||||
this.langsFile = null;
|
||||
|
||||
// Search Index
|
||||
this.searchIndexEnabled = true;
|
||||
this.searchIndexSize = 0;
|
||||
this.searchIndex = lunr(function () {
|
||||
this.ref('url');
|
||||
this.ref("url");
|
||||
|
||||
this.field('title', { boost: 10 });
|
||||
this.field('body');
|
||||
this.field("title", { boost: 10 });
|
||||
this.field("body");
|
||||
});
|
||||
|
||||
// Bind methods
|
||||
_.bindAll(this);
|
||||
};
|
||||
|
||||
// Return string representation
|
||||
Book.prototype.toString = function() {
|
||||
return "[Book "+this.root+"]";
|
||||
};
|
||||
|
||||
// Initialize and parse the book: config, summary, glossary
|
||||
Book.prototype.parse = function() {
|
||||
var that = this;
|
||||
@@ -109,7 +112,7 @@ Book.prototype.parse = function() {
|
||||
return that.parseLangs()
|
||||
.then(function() {
|
||||
multilingual = that.langs.length > 0;
|
||||
if (multilingual) that.log.info.ln("Parsing multilingual book, with", that.langs.length, "lanuages");
|
||||
if (multilingual) that.log.info.ln("Parsing multilingual book, with", that.langs.length, "languages");
|
||||
|
||||
// Sub-books that inherit from the current book configuration
|
||||
that.books = _.map(that.langs, function(lang) {
|
||||
@@ -118,12 +121,12 @@ Book.prototype.parse = function() {
|
||||
path.join(that.root, lang.path),
|
||||
_.merge({}, that.context, {
|
||||
config: _.extend({}, that.options, {
|
||||
'output': path.join(that.options.output, lang.lang),
|
||||
'language': lang.lang
|
||||
"output": path.join(that.options.output, lang.lang),
|
||||
"language": lang.lang
|
||||
})
|
||||
}),
|
||||
that
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -180,7 +183,7 @@ Book.prototype.generate = function(generator) {
|
||||
// Create generator
|
||||
.then(function() {
|
||||
var Generator = generators[generator];
|
||||
if (!Generator) throw "Generator '"+that.options.generator+"' doesn't exist";
|
||||
if (!Generator) throw "Generator \""+that.options.generator+"\" doesn't exist";
|
||||
generator = new Generator(that);
|
||||
|
||||
return generator.prepare();
|
||||
@@ -205,9 +208,9 @@ Book.prototype.generate = function(generator) {
|
||||
|
||||
return Q()
|
||||
|
||||
// First, let's create folder
|
||||
// First, let"s create folder
|
||||
.then(function() {
|
||||
return _.reduce(ops["directories"] || [], function(prev, folder) {
|
||||
return _.reduce(ops.directories || [], function(prev, folder) {
|
||||
return prev.then(function() {
|
||||
that.log.debug.ln("transferring folder", folder);
|
||||
return Q(generator.transferFolder(folder));
|
||||
@@ -215,22 +218,29 @@ Book.prototype.generate = function(generator) {
|
||||
}, Q());
|
||||
})
|
||||
|
||||
// Then, let's copy other files
|
||||
// Then, let"s copy other files
|
||||
.then(function() {
|
||||
return Q.all(_.map(ops["files"] || [], function(file) {
|
||||
return Q.all(_.map(ops.files || [], function(file) {
|
||||
that.log.debug.ln("transferring file", file);
|
||||
return Q(generator.transferFile(file));
|
||||
}));
|
||||
})
|
||||
|
||||
// Finally let's generate content
|
||||
// Finally let"s generate content
|
||||
.then(function() {
|
||||
var nFiles = (ops["content"] || []).length;
|
||||
return _.reduce(ops["content"] || [], function(prev, file, i) {
|
||||
var nFiles = (ops.content || []).length;
|
||||
return _.reduce(ops.content || [], function(prev, file, i) {
|
||||
return prev.then(function() {
|
||||
var p = ((i*100)/nFiles).toFixed(0)+"%";
|
||||
that.log.info.ln("processing", file, p);
|
||||
return Q(generator.convertFile(file));
|
||||
that.log.debug.ln("processing", file, p);
|
||||
|
||||
return Q(generator.convertFile(file))
|
||||
.fail(function(err) {
|
||||
// Transform error message to signal file
|
||||
throw that.normError(err, {
|
||||
fileName: file
|
||||
});
|
||||
});
|
||||
});
|
||||
}, Q());
|
||||
});
|
||||
@@ -253,7 +263,7 @@ Book.prototype.generate = function(generator) {
|
||||
};
|
||||
|
||||
// Generate the output for a multilingual book
|
||||
Book.prototype.generateMultiLingual = function(generator) {
|
||||
Book.prototype.generateMultiLingual = function() {
|
||||
var that = this;
|
||||
|
||||
return Q()
|
||||
@@ -281,7 +291,7 @@ Book.prototype.generateFile = function(output, options) {
|
||||
book.setOutput(tmpDir);
|
||||
|
||||
return book.generate(options.ebookFormat)
|
||||
.then(function(_options) {
|
||||
.then(function() {
|
||||
var copyFile = function(lang) {
|
||||
var _outputFile = output;
|
||||
var _tmpDir = tmpDir;
|
||||
@@ -325,7 +335,7 @@ Book.prototype.generateFile = function(output, options) {
|
||||
Book.prototype.parseConfig = function() {
|
||||
var that = this;
|
||||
|
||||
that.log.info("loading book configuration....")
|
||||
that.log.info("loading book configuration....");
|
||||
return that.config.load()
|
||||
.then(function() {
|
||||
that.log.info.ok();
|
||||
@@ -335,12 +345,11 @@ Book.prototype.parseConfig = function() {
|
||||
// Parse list of plugins
|
||||
Book.prototype.parsePlugins = function() {
|
||||
var that = this;
|
||||
var failed = [];
|
||||
|
||||
// Load plugins
|
||||
return that.plugins.load(that.options.plugins)
|
||||
.then(function() {
|
||||
if (_.size(that.plugins.failed) > 0) return Q.reject(new Error("Error loading plugins: "+that.plugins.failed.join(",")+". Run 'gitbook install' to install plugins from NPM."));
|
||||
if (_.size(that.plugins.failed) > 0) return Q.reject(new Error("Error loading plugins: "+that.plugins.failed.join(",")+". Run \"gitbook install\" to install plugins from NPM."));
|
||||
|
||||
that.log.info.ok(that.plugins.count()+" plugins loaded");
|
||||
that.log.debug.ln("normalize plugins list");
|
||||
@@ -364,7 +373,13 @@ Book.prototype.parseReadme = function() {
|
||||
that.log.debug.ln("readme located at", that.readmeFile);
|
||||
return that.template.renderFile(that.readmeFile)
|
||||
.then(function(content) {
|
||||
return readme.parser.readme(content);
|
||||
return readme.parser.readme(content)
|
||||
.fail(function(err) {
|
||||
throw that.normError(err, {
|
||||
name: err.name || "Readme Parse Error",
|
||||
fileName: that.readmeFile
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function(readme) {
|
||||
@@ -391,7 +406,13 @@ Book.prototype.parseLangs = function() {
|
||||
that.log.debug.ln("languages index located at", that.langsFile);
|
||||
return that.template.renderFile(that.langsFile)
|
||||
.then(function(content) {
|
||||
return langs.parser.langs(content);
|
||||
return langs.parser.langs(content)
|
||||
.fail(function(err) {
|
||||
throw that.normError(err, {
|
||||
name: err.name || "Langs Parse Error",
|
||||
fileName: that.langsFile
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function(langs) {
|
||||
@@ -420,8 +441,14 @@ Book.prototype.parseSummary = function() {
|
||||
.then(function(content) {
|
||||
return summary.parser.summary(content, {
|
||||
entryPoint: that.readmeFile,
|
||||
entryPointTitle: "",
|
||||
entryPointTitle: that.i18n("SUMMARY_INTRODUCTION"),
|
||||
files: that.files
|
||||
})
|
||||
.fail(function(err) {
|
||||
throw that.normError(err, {
|
||||
name: err.name || "Summary Parse Error",
|
||||
fileName: that.summaryFile
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -450,7 +477,13 @@ Book.prototype.parseGlossary = function() {
|
||||
that.log.debug.ln("glossary located at", that.glossaryFile);
|
||||
return that.template.renderFile(that.glossaryFile)
|
||||
.then(function(content) {
|
||||
return glossary.parser.glossary(content);
|
||||
return glossary.parser.glossary(content)
|
||||
.fail(function(err) {
|
||||
throw that.normError(err, {
|
||||
name: err.name || "Glossary Parse Error",
|
||||
fileName: that.glossaryFile
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function(glossary) {
|
||||
@@ -475,7 +508,7 @@ Book.prototype.parsePage = function(filename, options) {
|
||||
var interpolate = function(fn) {
|
||||
return Q(fn(page))
|
||||
.then(function(_page) {
|
||||
page = _page;
|
||||
page = _page || page;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -537,7 +570,7 @@ Book.prototype.parsePage = function(filename, options) {
|
||||
.then(function(content) {
|
||||
section.content = content;
|
||||
return _sections.concat([section]);
|
||||
})
|
||||
});
|
||||
});
|
||||
}, Q([]));
|
||||
})
|
||||
@@ -549,8 +582,8 @@ Book.prototype.parsePage = function(filename, options) {
|
||||
convertImages: options.convertImages,
|
||||
input: filename,
|
||||
navigation: that.navigation,
|
||||
base: path.dirname(filename) || './',
|
||||
output: path.dirname(filename) || './',
|
||||
base: path.dirname(filename) || "./",
|
||||
output: path.dirname(filename) || "./",
|
||||
glossary: that.glossary
|
||||
});
|
||||
})
|
||||
@@ -585,7 +618,7 @@ Book.prototype.findFile = function(filename) {
|
||||
parser: parsers.get(ext),
|
||||
path: filepath
|
||||
};
|
||||
})
|
||||
});
|
||||
});
|
||||
}, Q(null));
|
||||
};
|
||||
@@ -593,21 +626,26 @@ Book.prototype.findFile = function(filename) {
|
||||
// Check if a file exists in the book
|
||||
Book.prototype.fileExists = function(filename) {
|
||||
return fs.exists(
|
||||
path.join(this.root, filename)
|
||||
this.resolve(filename)
|
||||
);
|
||||
};
|
||||
|
||||
// Check if a file path is inside the book
|
||||
Book.prototype.fileIsInBook = function(filename) {
|
||||
return pathUtil.isInRoot(this.root, filename);
|
||||
};
|
||||
|
||||
// Read a file
|
||||
Book.prototype.readFile = function(filename) {
|
||||
return fs.readFile(
|
||||
path.join(this.root, filename),
|
||||
this.resolve(filename),
|
||||
{ encoding: "utf8" }
|
||||
);
|
||||
};
|
||||
|
||||
// Return stat for a file
|
||||
Book.prototype.statFile = function(filename) {
|
||||
return fs.stat(path.join(this.root, filename));
|
||||
return fs.stat(this.resolve(filename));
|
||||
};
|
||||
|
||||
// List all files in the book
|
||||
@@ -615,28 +653,28 @@ Book.prototype.listAllFiles = function() {
|
||||
var that = this;
|
||||
|
||||
return fs.list(this.root, {
|
||||
ignoreFiles: ['.ignore', '.gitignore', '.bookignore'],
|
||||
ignoreFiles: [".ignore", ".gitignore", ".bookignore"],
|
||||
ignoreRules: [
|
||||
// Skip Git stuff
|
||||
'.git/',
|
||||
'.gitignore',
|
||||
".git/",
|
||||
".gitignore",
|
||||
|
||||
// Skip OS X meta data
|
||||
'.DS_Store',
|
||||
".DS_Store",
|
||||
|
||||
// Skip stuff installed by plugins
|
||||
'node_modules',
|
||||
"node_modules",
|
||||
|
||||
// Skip book outputs
|
||||
'_book',
|
||||
'*.pdf',
|
||||
'*.epub',
|
||||
'*.mobi',
|
||||
"_book",
|
||||
"*.pdf",
|
||||
"*.epub",
|
||||
"*.mobi",
|
||||
|
||||
// Skip config files
|
||||
'.ignore',
|
||||
'.bookignore',
|
||||
'book.json',
|
||||
".ignore",
|
||||
".bookignore",
|
||||
"book.json",
|
||||
]
|
||||
})
|
||||
.then(function(_files) {
|
||||
@@ -645,7 +683,7 @@ Book.prototype.listAllFiles = function() {
|
||||
};
|
||||
|
||||
// Return true if the book is a multilingual book
|
||||
Book.prototype.isMultilingual = function(filename) {
|
||||
Book.prototype.isMultilingual = function() {
|
||||
return this.books.length > 0;
|
||||
};
|
||||
|
||||
@@ -655,7 +693,7 @@ Book.prototype.parentRoot = function() {
|
||||
return this.root;
|
||||
};
|
||||
|
||||
// Return true if it's a sub-book
|
||||
// Return true if it"s a sub-book
|
||||
Book.prototype.isSubBook = function() {
|
||||
return !!this.parent;
|
||||
};
|
||||
@@ -665,34 +703,62 @@ Book.prototype.isEntryPoint = function(fp) {
|
||||
return fp == this.readmeFile;
|
||||
};
|
||||
|
||||
// Resolve a path in book
|
||||
Book.prototype.resolve = function(p) {
|
||||
return path.resolve(this.root, p);
|
||||
// Alias to book.config.get
|
||||
Book.prototype.getConfig = function(key, def) {
|
||||
return this.config.get(key, def);
|
||||
};
|
||||
|
||||
// Normalize a link to .html and convert README -> index
|
||||
Book.prototype.contentLink = function(link) {
|
||||
// Resolve a path in the book source
|
||||
// Enforce that the output path in the root folder
|
||||
Book.prototype.resolve = function() {
|
||||
return pathUtil.resolveInRoot.apply(null, [this.root].concat(_.toArray(arguments)));
|
||||
};
|
||||
|
||||
// Convert an abslute path into a relative path to this
|
||||
Book.prototype.relative = function(p) {
|
||||
return path.relative(this.root, p);
|
||||
};
|
||||
|
||||
// Normalize a path to .html and convert README -> index
|
||||
Book.prototype.contentPath = function(link) {
|
||||
if (
|
||||
path.basename(link, path.extname(link)) == "README"
|
||||
|| link == this.readmeFile
|
||||
path.basename(link, path.extname(link)) == "README" ||
|
||||
link == this.readmeFile
|
||||
) {
|
||||
link = path.join(path.dirname(link), "index"+path.extname(link));
|
||||
}
|
||||
|
||||
link = links.changeExtension(link, ".html");
|
||||
return link;
|
||||
}
|
||||
};
|
||||
|
||||
// Normalize a link to .html and convert README -> index
|
||||
Book.prototype.contentLink = function(link) {
|
||||
return links.normalize(this.contentPath(link));
|
||||
};
|
||||
|
||||
// Index a page into the search index
|
||||
Book.prototype.indexPage = function(page) {
|
||||
var nav = this.navigation[page.path];
|
||||
if (!nav) return;
|
||||
if (!nav || !this.searchIndexEnabled) return;
|
||||
|
||||
this.log.debug.ln("index page", page.path);
|
||||
|
||||
// Extract text from the page
|
||||
var text = pageUtil.extractText(page.sections);
|
||||
|
||||
// Limit size of index (to avoid #941)
|
||||
this.searchIndexSize = this.searchIndexSize + text.length;
|
||||
if (this.searchIndexSize > this.config.get('search.maxIndexSize')) {
|
||||
this.log.warn.ln("search index is too big, indexing is now disabled");
|
||||
this.searchIndexEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.searchIndex.add({
|
||||
url: this.contentLink(page.path),
|
||||
title: nav.title,
|
||||
body: pageUtil.extractText(page.sections),
|
||||
body: text
|
||||
});
|
||||
};
|
||||
|
||||
@@ -705,7 +771,7 @@ Book.prototype._defaultsStructure = function(filename) {
|
||||
that.summaryFile = that.summaryFile || that.config.getStructure("summary")+extension;
|
||||
that.glossaryFile = that.glossaryFile || that.config.getStructure("glossary")+extension;
|
||||
that.langsFile = that.langsFile || that.config.getStructure("langs")+extension;
|
||||
}
|
||||
};
|
||||
|
||||
// Change output path
|
||||
Book.prototype.setOutput = function(p) {
|
||||
@@ -717,22 +783,49 @@ Book.prototype.setOutput = function(p) {
|
||||
});
|
||||
};
|
||||
|
||||
// Translate a strign according to the book language
|
||||
Book.prototype.i18n = function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
return i18n.__.apply({}, [this.config.normalizeLanguage()].concat(args));
|
||||
};
|
||||
|
||||
// Normalize error
|
||||
Book.prototype.normError = function(err, opts, defs) {
|
||||
if (_.isString(err)) err = new Error(err);
|
||||
|
||||
// Extend err
|
||||
_.extend(err, opts || {});
|
||||
_.defaults(err, defs || {});
|
||||
|
||||
err.lineNumber = err.lineNumber || err.lineno;
|
||||
err.columnNumber = err.columnNumber || err.colno;
|
||||
|
||||
err.toString = function() {
|
||||
var attributes = [];
|
||||
|
||||
if (this.fileName) attributes.push("In file \""+this.fileName+"\"");
|
||||
if (this.lineNumber) attributes.push("Line "+this.lineNumber);
|
||||
if (this.columnNumber) attributes.push("Column "+this.columnNumber);
|
||||
return (this.name || "Error")+": "+this.message+((attributes.length > 0)? " ("+attributes.join(", ")+")" : "");
|
||||
};
|
||||
|
||||
return err;
|
||||
};
|
||||
|
||||
// Init and return a book
|
||||
Book.init = function(root) {
|
||||
var book = new Book(root);
|
||||
Book.init = function(root, opts) {
|
||||
var book = new Book(root, opts);
|
||||
var extensionToUse = ".md";
|
||||
|
||||
var chaptersPaths = function(chapters) {
|
||||
return _.reduce(chapters || [], function(accu, chapter) {
|
||||
if (!chapter.path) return accu;
|
||||
var o = {
|
||||
title: chapter.title
|
||||
};
|
||||
if (chapter.path) o.path = chapter.path;
|
||||
|
||||
return accu.concat(
|
||||
_.filter([
|
||||
{
|
||||
title: chapter.title,
|
||||
path: chapter.path
|
||||
}
|
||||
].concat(chaptersPaths(chapter.articles)))
|
||||
[o].concat(chaptersPaths(chapter.articles))
|
||||
);
|
||||
}, []);
|
||||
};
|
||||
@@ -751,7 +844,7 @@ Book.init = function(root) {
|
||||
var chapters = book.summary.chapters || [];
|
||||
extensionToUse = path.extname(summary);
|
||||
|
||||
if (chapters.length == 0) {
|
||||
if (chapters.length === 0) {
|
||||
chapters = [
|
||||
{
|
||||
title: "Summary",
|
||||
@@ -767,18 +860,23 @@ Book.init = function(root) {
|
||||
return Q(chaptersPaths(chapters));
|
||||
})
|
||||
.then(function(chapters) {
|
||||
// Create files that don't exist
|
||||
// Create files that don"t exist
|
||||
return Q.all(_.map(chapters, function(chapter) {
|
||||
if (!chapter.path) return Q();
|
||||
var absolutePath = path.resolve(book.root, chapter.path);
|
||||
|
||||
return fs.exists(absolutePath)
|
||||
.then(function(exists) {
|
||||
book.log.info.ln("create", chapter.path);
|
||||
if(exists) return;
|
||||
if(exists) {
|
||||
book.log.info.ln("found", chapter.path);
|
||||
return;
|
||||
} else {
|
||||
book.log.info.ln("create", chapter.path);
|
||||
}
|
||||
|
||||
return fs.mkdirp(path.dirname(absolutePath))
|
||||
.then(function() {
|
||||
return fs.writeFile(absolutePath, '# '+chapter.title+'\n');
|
||||
return fs.writeFile(absolutePath, "# "+chapter.title+"\n");
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
+96
-34
@@ -1,17 +1,30 @@
|
||||
var _ = require("lodash");
|
||||
var Q = require("q");
|
||||
var path = require("path");
|
||||
var semver = require('semver');
|
||||
var semver = require("semver");
|
||||
|
||||
var pkg = require('../package.json');
|
||||
var fs = require("./utils/fs");
|
||||
var pkg = require("../package.json");
|
||||
var i18n = require("./utils/i18n");
|
||||
|
||||
// Default plugins added to each books
|
||||
var defaultsPlugins = [];
|
||||
var DEFAULT_PLUGINS = ["highlight"];
|
||||
|
||||
// Check if a plugin is a default plugin
|
||||
// Plugin should be in the list
|
||||
// And version from book.json specified for this plugin should be satisfied
|
||||
function isDefaultPlugin(name, version) {
|
||||
if (!_.contains(DEFAULT_PLUGINS, name)) return false;
|
||||
|
||||
try {
|
||||
var pluginPkg = require('gitbook-plugin-'+name+'/package.json');
|
||||
return semver.satisfies(pluginPkg.version, version || '*');
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize a list of plugins to use
|
||||
function normalizePluginsList(plugins) {
|
||||
function normalizePluginsList(plugins, addDefaults) {
|
||||
// Normalize list to an array
|
||||
plugins = _.isString(plugins) ? plugins.split(",") : (plugins || []);
|
||||
|
||||
@@ -23,10 +36,13 @@ function normalizePluginsList(plugins) {
|
||||
if (plugin.name) return plugin;
|
||||
|
||||
var parts = plugin.split("@");
|
||||
var name = parts[0];
|
||||
var version = parts[1];
|
||||
return {
|
||||
'name': parts[0],
|
||||
'version': parts[1] // optional
|
||||
}
|
||||
"name": name,
|
||||
"version": version, // optional
|
||||
"isDefault": isDefaultPlugin(name, version)
|
||||
};
|
||||
});
|
||||
|
||||
// List plugins to remove
|
||||
@@ -40,27 +56,30 @@ function normalizePluginsList(plugins) {
|
||||
.value();
|
||||
|
||||
// Merge with defaults
|
||||
plugins = _.chain(plugins)
|
||||
.concat(_.map(defaultsPlugins, function(plugin) {
|
||||
return { 'name': plugin }
|
||||
}))
|
||||
.uniq()
|
||||
.value();
|
||||
if (addDefaults !== false) {
|
||||
_.each(DEFAULT_PLUGINS, function(plugin) {
|
||||
if (_.find(plugins, { name: plugin })) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build final list
|
||||
plugins.push({
|
||||
"name": plugin,
|
||||
"isDefault": true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Remove plugin that start with '-'
|
||||
plugins = _.filter(plugins, function(plugin) {
|
||||
return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == "-");
|
||||
});
|
||||
|
||||
// Remove duplicates
|
||||
plugins = _.uniq(plugins, 'name');
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
// Normalize a list of plugin name to use
|
||||
function normalizePluginsNames(plugins) {
|
||||
return _.pluck(normalizePluginsList(plugins), "name");
|
||||
};
|
||||
|
||||
|
||||
var Configuration = function(book, options) {
|
||||
var that = this;
|
||||
|
||||
@@ -75,6 +94,20 @@ var Configuration = function(book, options) {
|
||||
return that.book.root;
|
||||
}
|
||||
});
|
||||
|
||||
// options.originalInput == book.parent.root
|
||||
Object.defineProperty(this.options, "originalInput", {
|
||||
get: function () {
|
||||
return that.book.parent? that.book.parent.root : undefined;
|
||||
}
|
||||
});
|
||||
|
||||
// options.originalOutput == book.parent.options.output
|
||||
Object.defineProperty(this.options, "originalOutput", {
|
||||
get: function () {
|
||||
return that.book.parent? that.book.parent.options.output : undefined;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Read and parse the configuration
|
||||
@@ -83,11 +116,20 @@ Configuration.prototype.load = function() {
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
var configPath, _config;
|
||||
|
||||
try {
|
||||
var _config = require(path.resolve(that.book.root, that.options.configFile));
|
||||
configPath = require.resolve(
|
||||
that.book.resolve(that.options.configFile)
|
||||
);
|
||||
|
||||
// Invalidate node.js cache for livreloading
|
||||
delete require.cache[configPath];
|
||||
|
||||
_config = require(configPath);
|
||||
that.options = _.merge(
|
||||
that.options,
|
||||
_.omit(_config, 'configFile', 'defaultsPlugins', 'generator', 'extension')
|
||||
_.omit(_config, "configFile", "defaultsPlugins", "generator", "extension")
|
||||
);
|
||||
}
|
||||
catch(err) {
|
||||
@@ -96,22 +138,24 @@ Configuration.prototype.load = function() {
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
if (!semver.satisfies(pkg.version, that.options.gitbook)) {
|
||||
throw "GitBook version doesn't satisfy version required by the book: "+that.options.gitbook;
|
||||
}
|
||||
if (that.options.gitbook == "*") {
|
||||
that.book.log.warn.ln("you should specify a gitbook version to use in your book.json, for example: "+(_.first(pkg.version.split("."))+".x.x"));
|
||||
if (!that.book.isSubBook()) {
|
||||
if (!semver.satisfies(pkg.version, that.options.gitbook)) {
|
||||
throw new Error("GitBook version doesn't satisfy version required by the book: "+that.options.gitbook);
|
||||
}
|
||||
if (that.options.gitbook != "*" && !semver.satisfies(semver.inc(pkg.version, "patch"), that.options.gitbook)) {
|
||||
that.book.log.warn.ln("gitbook version specified in your book.json might be too strict for future patches, \""+(_.first(pkg.version.split("."))+".x.x")+"\" is more adequate");
|
||||
}
|
||||
}
|
||||
|
||||
that.options.output = path.resolve(that.options.output || path.join(that.book.root, "_book"));
|
||||
that.options.output = path.resolve(that.options.output || that.book.resolve("_book"));
|
||||
that.options.plugins = normalizePluginsList(that.options.plugins);
|
||||
that.options.defaultsPlugins = normalizePluginsList(that.options.defaultsPlugins || "");
|
||||
that.options.defaultsPlugins = normalizePluginsList(that.options.defaultsPlugins || "", false);
|
||||
that.options.plugins = _.union(that.options.plugins, that.options.defaultsPlugins);
|
||||
that.options.plugins = _.uniq(that.options.plugins, "name");
|
||||
|
||||
// Default value for text direction (from language)
|
||||
if (!that.options.direction) {
|
||||
var lang = i18n.getByLanguage(that.options.language);
|
||||
var lang = i18n.getCatalog(that.options.language);
|
||||
if (lang) that.options.direction = lang.direction;
|
||||
}
|
||||
|
||||
@@ -134,10 +178,14 @@ Configuration.prototype.normalizeLanguage = function() {
|
||||
return i18n.normalizeLanguage(this.options.language);
|
||||
};
|
||||
|
||||
// Return a configuration
|
||||
Configuration.prototype.get = function(key, def) {
|
||||
return _.get(this.options, key, def);
|
||||
};
|
||||
|
||||
// Default configuration
|
||||
Configuration.DEFAULT = {
|
||||
// Options that can't be extend
|
||||
// Options that can"t be extend
|
||||
"configFile": "book",
|
||||
"generator": "website",
|
||||
"extension": null,
|
||||
@@ -153,6 +201,11 @@ Configuration.DEFAULT = {
|
||||
// version of gitbook to use
|
||||
"gitbook": "*",
|
||||
|
||||
// Search index
|
||||
"search": {
|
||||
"maxIndexSize": 1000000
|
||||
},
|
||||
|
||||
// Structure
|
||||
"structure": {
|
||||
"langs": "LANGS.md",
|
||||
@@ -164,6 +217,7 @@ Configuration.DEFAULT = {
|
||||
// CSS Styles
|
||||
"styles": {
|
||||
"website": "styles/website.css",
|
||||
"print": "styles/print.css",
|
||||
"ebook": "styles/ebook.css",
|
||||
"pdf": "styles/pdf.css",
|
||||
"mobi": "styles/mobi.css",
|
||||
@@ -186,9 +240,9 @@ Configuration.DEFAULT = {
|
||||
"variables": {},
|
||||
|
||||
// Set another theme with your own layout
|
||||
// It's recommended to use plugins or add more options for default theme, though
|
||||
// It"s recommended to use plugins or add more options for default theme, though
|
||||
// See https://github.com/GitbookIO/gitbook/issues/209
|
||||
"theme": path.resolve(__dirname, '../theme'),
|
||||
"theme": path.resolve(__dirname, "../theme"),
|
||||
|
||||
// Links in template (null: default, false: remove, string: new value)
|
||||
"links": {
|
||||
@@ -223,6 +277,14 @@ Configuration.DEFAULT = {
|
||||
// Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’]
|
||||
"paperSize": "a4",
|
||||
|
||||
// How to mark detected chapters.
|
||||
// Choices are “pagebreak”, “rule”, "both" or “none”.
|
||||
"chapterMark" : "pagebreak",
|
||||
|
||||
// An XPath expression. Page breaks are inserted before the specified elements.
|
||||
// To disable use the expression: "/"
|
||||
"pageBreaksBefore": "/",
|
||||
|
||||
// Margin (in pts)
|
||||
// Note: 72 pts equals 1 inch
|
||||
"margin": {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
var path = require("path");
|
||||
var nunjucks = require("nunjucks");
|
||||
|
||||
var git = require("./utils/git");
|
||||
var fs = require("./utils/fs");
|
||||
var pathUtil = require("./utils/path");
|
||||
|
||||
// The loader should handle relative and git url
|
||||
var BookLoader = nunjucks.Loader.extend({
|
||||
async: true,
|
||||
|
||||
init: function(book) {
|
||||
this.book = book;
|
||||
},
|
||||
|
||||
getSource: function(fileurl, callback) {
|
||||
var that = this;
|
||||
|
||||
git.resolveFile(fileurl)
|
||||
.then(function(filepath) {
|
||||
// Is local file
|
||||
if (!filepath) filepath = path.resolve(fileurl);
|
||||
else that.book.log.debug.ln("resolve from git", fileurl, "to", filepath);
|
||||
|
||||
// Read file from absolute path
|
||||
return fs.readFile(filepath)
|
||||
.then(function(source) {
|
||||
return {
|
||||
src: source.toString(),
|
||||
path: filepath
|
||||
};
|
||||
});
|
||||
})
|
||||
.nodeify(callback);
|
||||
},
|
||||
|
||||
resolve: function(from, to) {
|
||||
// If origin is in the book, we enforce result file to be in the book
|
||||
if (this.book.fileIsInBook(from)) {
|
||||
return this.book.resolve(
|
||||
this.book.relative(path.dirname(from)),
|
||||
to
|
||||
);
|
||||
}
|
||||
|
||||
// If origin is in a git repository, we resolve file in the git repository
|
||||
var gitRoot = git.resolveRoot(from);
|
||||
if (gitRoot) {
|
||||
return pathUtil.resolveInRoot(gitRoot, to);
|
||||
}
|
||||
|
||||
// If origin is not in the book (include from a git content ref)
|
||||
return path.resolve(path.dirname(from), to);
|
||||
},
|
||||
|
||||
// Handle all files as relative, so that nunjucks pass responsability to "resolve"
|
||||
// Only git urls are considered as absolute
|
||||
isRelative: function(filename) {
|
||||
return !git.checkUrl(filename);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = BookLoader;
|
||||
+4
-6
@@ -3,8 +3,6 @@ var path = require("path");
|
||||
var Q = require("q");
|
||||
var fs = require("./utils/fs");
|
||||
|
||||
var Plugin = require("./plugin");
|
||||
|
||||
var BaseGenerator = function(book) {
|
||||
this.book = book;
|
||||
|
||||
@@ -36,7 +34,7 @@ BaseGenerator.prototype.convertFile = function(input) {
|
||||
// Copy file to the output (non parsable)
|
||||
BaseGenerator.prototype.transferFile = function(input) {
|
||||
return fs.copy(
|
||||
path.join(this.book.root, input),
|
||||
this.book.resolve(input),
|
||||
path.join(this.options.output, input)
|
||||
);
|
||||
};
|
||||
@@ -53,8 +51,8 @@ BaseGenerator.prototype.copyCover = function() {
|
||||
var that = this;
|
||||
|
||||
return Q.all([
|
||||
fs.copy(path.join(that.book.root, "cover.jpg"), path.join(that.options.output, "cover.jpg")),
|
||||
fs.copy(path.join(that.book.root, "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg"))
|
||||
fs.copy(that.book.resolve("cover.jpg"), path.join(that.options.output, "cover.jpg")),
|
||||
fs.copy(that.book.resolve("cover_small.jpg"), path.join(that.options.output, "cover_small.jpg"))
|
||||
])
|
||||
.fail(function() {
|
||||
// If orignaly from multi-lang, try copy from parent
|
||||
@@ -65,7 +63,7 @@ BaseGenerator.prototype.copyCover = function() {
|
||||
fs.copy(path.join(that.book.parentRoot(), "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg"))
|
||||
]);
|
||||
})
|
||||
.fail(function(err) {
|
||||
.fail(function() {
|
||||
return Q();
|
||||
});
|
||||
};
|
||||
|
||||
+13
-13
@@ -2,7 +2,7 @@ var util = require("util");
|
||||
var path = require("path");
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var exec = require('child_process').exec;
|
||||
var exec = require("child_process").exec;
|
||||
|
||||
var fs = require("../utils/fs");
|
||||
var stringUtils = require("../utils/string");
|
||||
@@ -18,7 +18,7 @@ var Generator = function(book, format) {
|
||||
this.namespace = "ebook";
|
||||
|
||||
// Styles to use
|
||||
this.styles = _.compact(["ebook", this.ebookFormat]);
|
||||
this.styles = _.compact(["print", "ebook", this.ebookFormat]);
|
||||
|
||||
// Convert images (svg -> png)
|
||||
this.convertImages = true;
|
||||
@@ -26,9 +26,9 @@ var Generator = function(book, format) {
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
Generator.prototype.prepareTemplates = function() {
|
||||
this.templates["page"] = this.book.plugins.template("ebook:page") || path.resolve(this.options.theme, 'templates/ebook/page.html');
|
||||
this.templates["summary"] = this.book.plugins.template("ebook:summary") || path.resolve(this.options.theme, 'templates/ebook/summary.html');
|
||||
this.templates["glossary"] = this.book.plugins.template("ebook:glossary") || path.resolve(this.options.theme, 'templates/ebook/glossary.html');
|
||||
this.templates.page = this.book.plugins.template("ebook:page") || path.resolve(this.options.theme, "templates/ebook/page.html");
|
||||
this.templates.summary = this.book.plugins.template("ebook:summary") || path.resolve(this.options.theme, "templates/ebook/summary.html");
|
||||
this.templates.glossary = this.book.plugins.template("ebook:glossary") || path.resolve(this.options.theme, "templates/ebook/glossary.html");
|
||||
|
||||
return Q();
|
||||
};
|
||||
@@ -38,7 +38,7 @@ Generator.prototype.writeSummary = function() {
|
||||
var that = this;
|
||||
|
||||
that.book.log.info.ln("write SUMMARY.html");
|
||||
return this._writeTemplate(this.templates["summary"], {}, path.join(this.options.output, "SUMMARY.html"));
|
||||
return this._writeTemplate(this.templates.summary, {}, path.join(this.options.output, "SUMMARY.html"));
|
||||
};
|
||||
|
||||
Generator.prototype.finish = function() {
|
||||
@@ -52,12 +52,12 @@ Generator.prototype.finish = function() {
|
||||
.then(function() {
|
||||
if (!that.ebookFormat) return Q();
|
||||
|
||||
var d = Q.defer();
|
||||
|
||||
if (!that.options.cover && fs.existsSync(path.join(that.options.output, "cover.jpg"))) {
|
||||
that.options.cover = path.join(that.options.output, "cover.jpg");
|
||||
}
|
||||
|
||||
var d = Q.defer();
|
||||
|
||||
var _options = {
|
||||
"--cover": that.options.cover,
|
||||
"--title": that.options.title,
|
||||
@@ -68,8 +68,6 @@ Generator.prototype.finish = function() {
|
||||
"--book-producer": "GitBook",
|
||||
"--publisher": "GitBook",
|
||||
"--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]",
|
||||
"--chapter-mark": "pagebreak",
|
||||
"--page-breaks-before": "/",
|
||||
"--level1-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-1 ')]",
|
||||
"--level2-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-2 ')]",
|
||||
"--level3-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-3 ')]",
|
||||
@@ -82,6 +80,8 @@ Generator.prototype.finish = function() {
|
||||
var pdfOptions = that.options.pdf;
|
||||
|
||||
_.extend(_options, {
|
||||
"--chapter-mark": String(pdfOptions.chapterMark),
|
||||
"--page-breaks-before": String(pdfOptions.pageBreaksBefore),
|
||||
"--margin-left": String(pdfOptions.margin.left),
|
||||
"--margin-right": String(pdfOptions.margin.right),
|
||||
"--margin-top": String(pdfOptions.margin.top),
|
||||
@@ -108,7 +108,7 @@ Generator.prototype.finish = function() {
|
||||
|
||||
that.book.log.info("start conversion to", that.ebookFormat, "....");
|
||||
|
||||
var child = exec(command, function (error, stdout, stderr) {
|
||||
var child = exec(command, function (error, stdout) {
|
||||
if (error) {
|
||||
that.book.log.info.fail();
|
||||
|
||||
@@ -124,11 +124,11 @@ Generator.prototype.finish = function() {
|
||||
d.resolve();
|
||||
});
|
||||
|
||||
child.stdout.on('data', function (data) {
|
||||
child.stdout.on("data", function (data) {
|
||||
that.book.log.debug(data);
|
||||
});
|
||||
|
||||
child.stderr.on('data', function (data) {
|
||||
child.stderr.on("data", function (data) {
|
||||
that.book.log.debug(data);
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ var Generator = function() {
|
||||
util.inherits(Generator, BaseGenerator);
|
||||
|
||||
// Ignore some methods
|
||||
Generator.prototype.transferFile = function(input) { };
|
||||
Generator.prototype.transferFile = function() { };
|
||||
|
||||
// Convert an input file
|
||||
Generator.prototype.convertFile = function(input) {
|
||||
@@ -44,7 +44,7 @@ Generator.prototype.finish = function() {
|
||||
// Write README.json
|
||||
Generator.prototype.writeReadme = function() {
|
||||
var that = this;
|
||||
var mainlang, langs;
|
||||
var mainLang, langs, readme;
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
|
||||
+24
-32
@@ -4,14 +4,12 @@ var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
|
||||
var nunjucks = require("nunjucks");
|
||||
var AutoEscapeExtension = require("nunjucks-autoescape");
|
||||
var FilterExtension = require("nunjucks-filter");
|
||||
var I18nExtension = require("nunjucks-i18n");
|
||||
var AutoEscapeExtension = require("nunjucks-autoescape")(nunjucks);
|
||||
var FilterExtension = require("nunjucks-filter")(nunjucks);
|
||||
|
||||
var fs = require("../utils/fs");
|
||||
var BaseGenerator = require("../generator");
|
||||
var links = require("../utils/links");
|
||||
var pageUtil = require("../utils/page");
|
||||
var i18n = require("../utils/i18n");
|
||||
|
||||
var pkg = require("../../package.json");
|
||||
@@ -51,12 +49,13 @@ Generator.prototype.prepareStyles = function() {
|
||||
this.styles = _.chain(this.styles)
|
||||
.map(function(style) {
|
||||
var stylePath = that.options.styles[style];
|
||||
if (fs.existsSync(path.resolve(that.book.root, stylePath))) {
|
||||
return stylePath;
|
||||
if (stylePath && fs.existsSync(that.book.resolve(stylePath))) {
|
||||
return [style, stylePath];
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.compact()
|
||||
.object()
|
||||
.value();
|
||||
|
||||
return Q();
|
||||
@@ -64,9 +63,9 @@ Generator.prototype.prepareStyles = function() {
|
||||
|
||||
// Prepare templates
|
||||
Generator.prototype.prepareTemplates = function() {
|
||||
this.templates["page"] = this.book.plugins.template("site:page") || path.resolve(this.options.theme, 'templates/website/page.html');
|
||||
this.templates["langs"] = this.book.plugins.template("site:langs") || path.resolve(this.options.theme, 'templates/website/langs.html');
|
||||
this.templates["glossary"] = this.book.plugins.template("site:glossary") || path.resolve(this.options.theme, 'templates/website/glossary.html');
|
||||
this.templates.page = this.book.plugins.template("site:page") || path.resolve(this.options.theme, "templates/website/page.html");
|
||||
this.templates.langs = this.book.plugins.template("site:langs") || path.resolve(this.options.theme, "templates/website/langs.html");
|
||||
this.templates.glossary = this.book.plugins.template("site:glossary") || path.resolve(this.options.theme, "templates/website/glossary.html");
|
||||
|
||||
return Q();
|
||||
};
|
||||
@@ -79,8 +78,8 @@ Generator.prototype.prepareTemplateEngine = function() {
|
||||
.then(function() {
|
||||
var language = that.book.config.normalizeLanguage();
|
||||
|
||||
if (!i18n.getByLanguage(language) && language != "en") {
|
||||
that.book.log.warn.ln("Language '"+language+"' is not available as a layout locales (en, "+i18n.getLanguages().join(", ")+")");
|
||||
if (!i18n.hasLocale(language)) {
|
||||
that.book.log.warn.ln("Language '"+language+"' is not available as a layout locales (en, "+i18n.getLocales().join(", ")+")");
|
||||
}
|
||||
|
||||
var folders = _.chain(that.templates)
|
||||
@@ -98,18 +97,13 @@ Generator.prototype.prepareTemplateEngine = function() {
|
||||
|
||||
// Add filter
|
||||
that.env.addFilter("contentLink", that.book.contentLink.bind(that.book));
|
||||
that.env.addFilter('lvl', function(lvl) {
|
||||
that.env.addFilter("lvl", function(lvl) {
|
||||
return lvl.split(".").length;
|
||||
});
|
||||
|
||||
// Add extension
|
||||
that.env.addExtension('AutoEscapeExtension', new AutoEscapeExtension(that.env));
|
||||
that.env.addExtension('FilterExtension', new FilterExtension(that.env));
|
||||
that.env.addExtension('I18nExtension', new I18nExtension({
|
||||
env: that.env,
|
||||
translations: i18n.getLocales(),
|
||||
locale: "language"
|
||||
}));
|
||||
that.env.addExtension("AutoEscapeExtension", new AutoEscapeExtension(that.env));
|
||||
that.env.addExtension("FilterExtension", new FilterExtension(that.env));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -119,7 +113,7 @@ Generator.prototype.finish = function() {
|
||||
.then(this.copyCover)
|
||||
.then(this.writeGlossary)
|
||||
.then(this.writeSearchIndex)
|
||||
.then(this.writeLangsIndex)
|
||||
.then(this.writeLangsIndex);
|
||||
};
|
||||
|
||||
// Convert an input file
|
||||
@@ -136,15 +130,15 @@ Generator.prototype.convertFile = function(input) {
|
||||
}
|
||||
})
|
||||
.then(function(page) {
|
||||
var relativeOutput = that.book.contentLink(page.path);
|
||||
var relativeOutput = that.book.contentPath(page.path);
|
||||
var output = path.join(that.options.output, relativeOutput);
|
||||
|
||||
var basePath = path.relative(path.dirname(output), that.options.output) || ".";
|
||||
if (process.platform === 'win32') basePath = basePath.replace(/\\/g, '/');
|
||||
if (process.platform === "win32") basePath = basePath.replace(/\\/g, "/");
|
||||
|
||||
that.book.log.debug.ln("write parsed file", page.path, "to", relativeOutput);
|
||||
|
||||
return that._writeTemplate(that.templates["page"], {
|
||||
return that._writeTemplate(that.templates.page, {
|
||||
progress: page.progress,
|
||||
|
||||
_input: page.path,
|
||||
@@ -158,27 +152,23 @@ Generator.prototype.convertFile = function(input) {
|
||||
|
||||
// Write the index for langs
|
||||
Generator.prototype.writeLangsIndex = function() {
|
||||
var that = this;
|
||||
if (!this.book.langs.length) return Q();
|
||||
return this._writeTemplate(this.templates["langs"], {
|
||||
|
||||
return this._writeTemplate(this.templates.langs, {
|
||||
langs: this.book.langs
|
||||
}, path.join(this.options.output, "index.html"));
|
||||
};
|
||||
|
||||
// Write glossary
|
||||
Generator.prototype.writeGlossary = function() {
|
||||
var that = this;
|
||||
|
||||
// No glossary
|
||||
if (this.book.glossary.length == 0) return Q();
|
||||
if (this.book.glossary.length === 0) return Q();
|
||||
|
||||
return this._writeTemplate(this.templates["glossary"], {}, path.join(this.options.output, "GLOSSARY.html"));
|
||||
return this._writeTemplate(this.templates.glossary, {}, path.join(this.options.output, "GLOSSARY.html"));
|
||||
};
|
||||
|
||||
// Write the search index
|
||||
Generator.prototype.writeSearchIndex = function() {
|
||||
var that = this;
|
||||
|
||||
return fs.writeFile(
|
||||
path.join(this.options.output, "search_index.json"),
|
||||
JSON.stringify(this.book.searchIndex)
|
||||
@@ -212,7 +202,7 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
|
||||
|
||||
interpolate = interpolate || _.identity;
|
||||
return Q()
|
||||
.then(function(sections) {
|
||||
.then(function() {
|
||||
return that.env.render(
|
||||
tpl,
|
||||
_.extend({
|
||||
@@ -243,6 +233,8 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
|
||||
|
||||
basePath: ".",
|
||||
staticBase: path.join(".", "gitbook"),
|
||||
|
||||
"__": that.book.i18n.bind(that.book)
|
||||
}, options)
|
||||
);
|
||||
})
|
||||
|
||||
+21
-22
@@ -1,11 +1,10 @@
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var tinylr = require('tiny-lr');
|
||||
var color = require('bash-color');
|
||||
var tinylr = require("tiny-lr");
|
||||
var color = require("bash-color");
|
||||
|
||||
var Book = require("./book");
|
||||
var Plugin = require("./plugin");
|
||||
var Server = require("./utils/server");
|
||||
var stringUtils = require("./utils/string");
|
||||
var watch = require("./utils/watch");
|
||||
@@ -42,10 +41,10 @@ module.exports = {
|
||||
var output = args[1] || path.join(input, "_book");
|
||||
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'config': {
|
||||
'output': output
|
||||
"config": {
|
||||
"output": output
|
||||
},
|
||||
'logLevel': kwargs.log
|
||||
"logLevel": kwargs.log
|
||||
}));
|
||||
|
||||
return book.parse()
|
||||
@@ -71,7 +70,7 @@ module.exports = {
|
||||
var output = args[1];
|
||||
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'logLevel': kwargs.log
|
||||
"logLevel": kwargs.log
|
||||
}));
|
||||
|
||||
return book.parse()
|
||||
@@ -116,7 +115,7 @@ module.exports = {
|
||||
|
||||
// Init livereload server
|
||||
var lrServer = tinylr({});
|
||||
var lrPath = undefined;
|
||||
var lrPath;
|
||||
|
||||
var generate = function() {
|
||||
if (server.isRunning()) console.log("Stopping server");
|
||||
@@ -124,10 +123,10 @@ module.exports = {
|
||||
return server.stop()
|
||||
.then(function() {
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'config': {
|
||||
'defaultsPlugins': ["livereload"]
|
||||
"config": {
|
||||
"defaultsPlugins": ["livereload"]
|
||||
},
|
||||
'logLevel': kwargs.log
|
||||
"logLevel": kwargs.log
|
||||
}));
|
||||
|
||||
return book.parse()
|
||||
@@ -138,10 +137,10 @@ module.exports = {
|
||||
})
|
||||
.then(function(book) {
|
||||
console.log();
|
||||
console.log('Starting server ...');
|
||||
console.log("Starting server ...");
|
||||
return server.start(book.options.output, kwargs.port)
|
||||
.then(function() {
|
||||
console.log('Serving book on http://localhost:'+kwargs.port);
|
||||
console.log("Serving book on http://localhost:"+kwargs.port);
|
||||
|
||||
if (lrPath) {
|
||||
// trigger livereload
|
||||
@@ -158,19 +157,19 @@ module.exports = {
|
||||
.then(function(filepath) {
|
||||
// set livereload path
|
||||
lrPath = filepath;
|
||||
console.log("Restart after change in files");
|
||||
console.log('');
|
||||
console.log("Restart after change in file", filepath);
|
||||
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('')
|
||||
console.log("Live reload server started on port:", kwargs.lrport);
|
||||
console.log("Press CTRL+C to quit ...");
|
||||
console.log("");
|
||||
return generate();
|
||||
});
|
||||
}
|
||||
@@ -179,7 +178,7 @@ module.exports = {
|
||||
{
|
||||
name: "install [book]",
|
||||
description: "install plugins dependencies",
|
||||
exec: function(args, kwargs) {
|
||||
exec: function(args) {
|
||||
var input = args[0] || process.cwd();
|
||||
|
||||
var book = new Book(input);
|
||||
@@ -198,7 +197,7 @@ module.exports = {
|
||||
{
|
||||
name: "init [directory]",
|
||||
description: "create files and folders based on contents of SUMMARY.md",
|
||||
exec: function(args, kwargs) {
|
||||
exec: function(args) {
|
||||
return Book.init(args[0] || process.cwd())
|
||||
.then(function(){
|
||||
console.log("");
|
||||
|
||||
+6
-6
@@ -4,7 +4,7 @@ var semver = require("semver");
|
||||
var path = require("path");
|
||||
var url = require("url");
|
||||
var fs = require("./utils/fs");
|
||||
var resolve = require('resolve');
|
||||
var resolve = require("resolve");
|
||||
|
||||
var pkg = require("../package.json");
|
||||
|
||||
@@ -33,8 +33,8 @@ var Plugin = function(book, name) {
|
||||
// Type of plugins resources
|
||||
Plugin.RESOURCES = ["js", "css"];
|
||||
Plugin.HOOKS = [
|
||||
"init", "finish", "finish:before"
|
||||
]
|
||||
"init", "finish", "finish:before", "page", "page:before"
|
||||
];
|
||||
|
||||
// Load from a name
|
||||
Plugin.prototype.load = function(name, baseDir) {
|
||||
@@ -77,7 +77,7 @@ Plugin.prototype._getResources = function(base) {
|
||||
var book = this.infos[base];
|
||||
|
||||
// Compatibility with version 1.x.x
|
||||
if (base == "website") book = book || this.infos["book"];
|
||||
if (base == "website") book = book || this.infos.book;
|
||||
|
||||
// Nothing specified, fallback to default
|
||||
if (!book) {
|
||||
@@ -155,7 +155,7 @@ Plugin.prototype.callHook = function(name, data) {
|
||||
if (!hookFunc) return Q(data);
|
||||
|
||||
this.book.log.debug.ln("call hook", name);
|
||||
if (!_.contains(Plugin.HOOKS, name)) this.book.log.warn.ln("hook '"+name+"' used by plugin '"+this.name+"' is deprecated, and will be remove in the coming versions");
|
||||
if (!_.contains(Plugin.HOOKS, name)) this.book.log.warn.ln("hook '"+name+"' used by plugin '"+this.name+"' is deprecated, and will be removed in the coming versions");
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
@@ -168,7 +168,7 @@ Plugin.prototype.copyAssets = function(out, base) {
|
||||
var that = this;
|
||||
|
||||
return this.getResources(base)
|
||||
.get('assets')
|
||||
.get("assets")
|
||||
.then(function(assets) {
|
||||
// Assets are undefined
|
||||
if(!assets) return false;
|
||||
|
||||
+19
-26
@@ -1,14 +1,14 @@
|
||||
var _ = require("lodash");
|
||||
var Q = require("q");
|
||||
var npmi = require('npmi');
|
||||
var npm = require('npm');
|
||||
var semver = require('semver');
|
||||
var npmi = require("npmi");
|
||||
var npm = require("npm");
|
||||
var semver = require("semver");
|
||||
|
||||
var Plugin = require("./plugin");
|
||||
var pkg = require("../package.json");
|
||||
|
||||
var initNPM = _.memoize(function() {
|
||||
return Q.nfcall(npm.load, { silent: true, loglevel: 'silent' });
|
||||
return Q.nfcall(npm.load, { silent: true, loglevel: "silent" });
|
||||
});
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ PluginsList.prototype.count = function() {
|
||||
};
|
||||
|
||||
// Add and load a plugin
|
||||
PluginsList.prototype.load = function(plugin, options) {
|
||||
PluginsList.prototype.load = function(plugin) {
|
||||
var that = this;
|
||||
|
||||
if (_.isArray(plugin)) {
|
||||
@@ -79,14 +79,10 @@ PluginsList.prototype.load = function(plugin, options) {
|
||||
}
|
||||
|
||||
// Extract filters
|
||||
_.each(plugin.getFilters(), function(filterFunc, filterName) {
|
||||
that.book.template.addFilter(filterName, filterFunc);
|
||||
});
|
||||
that.book.template.addFilters(plugin.getFilters());
|
||||
|
||||
// Extract blocks
|
||||
_.each(plugin.getBlocks(), function(block, blockName) {
|
||||
that.book.template.addBlock(blockName, block);
|
||||
});
|
||||
that.book.template.addBlocks(plugin.getBlocks());
|
||||
|
||||
return _.reduce(_.keys(that.namespaces), function(prev, namespaceName) {
|
||||
return prev.then(function() {
|
||||
@@ -107,7 +103,7 @@ PluginsList.prototype.load = function(plugin, options) {
|
||||
namespace.html[tag] = namespace.html[tag] || [];
|
||||
namespace.html[tag].push(value);
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
}, Q());
|
||||
};
|
||||
@@ -150,11 +146,10 @@ PluginsList.prototype.resources = function(namespace) {
|
||||
// Install plugins from a book
|
||||
PluginsList.prototype.install = function() {
|
||||
var that = this;
|
||||
var defaultsPlugins = _.pluck(that.book.options.defaultsPlugins)
|
||||
|
||||
// Remove defaults (no need to install)
|
||||
var plugins = _.filter(that.book.options.plugins, function(plugin) {
|
||||
return !_.contains(defaultsPlugins, plugin.name);
|
||||
var plugins = _.reject(that.book.options.plugins, {
|
||||
isDefault: true
|
||||
});
|
||||
|
||||
// Install plugins one by one
|
||||
@@ -180,8 +175,8 @@ PluginsList.prototype.install = function() {
|
||||
.map(function(v) {
|
||||
return {
|
||||
version: v[0],
|
||||
gitbook: (v[1].engines || {})["gitbook"]
|
||||
}
|
||||
gitbook: (v[1].engines || {}).gitbook
|
||||
};
|
||||
})
|
||||
.filter(function(v) {
|
||||
return v.gitbook && semver.satisfies(pkg.version, v.gitbook);
|
||||
@@ -203,13 +198,13 @@ PluginsList.prototype.install = function() {
|
||||
|
||||
that.book.log.info.ln("install plugin", plugin.name, "from npm ("+fullname+") with version", version);
|
||||
return Q.nfcall(npmi, {
|
||||
'name': fullname,
|
||||
'version': version,
|
||||
'path': that.book.root,
|
||||
'npmLoad': {
|
||||
'loglevel': 'silent',
|
||||
'loaded': true,
|
||||
'prefix': that.book.root
|
||||
"name": fullname,
|
||||
"version": version,
|
||||
"path": that.book.root,
|
||||
"npmLoad": {
|
||||
"loglevel": "silent",
|
||||
"loaded": true,
|
||||
"prefix": that.book.root
|
||||
}
|
||||
});
|
||||
})
|
||||
@@ -220,6 +215,4 @@ PluginsList.prototype.install = function() {
|
||||
}, Q());
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports = PluginsList;
|
||||
|
||||
+136
-90
@@ -1,48 +1,18 @@
|
||||
var _ = require("lodash");
|
||||
var Q = require("q");
|
||||
var path = require("path");
|
||||
var nunjucks = require("nunjucks");
|
||||
var escapeStringRegexp = require("escape-string-regexp");
|
||||
|
||||
var git = require("./utils/git");
|
||||
var stringUtils = require("./utils/string");
|
||||
var fs = require("./utils/fs");
|
||||
var batch = require("./utils/batch");
|
||||
var pkg = require("../package.json");
|
||||
var defaultBlocks = require("./blocks");
|
||||
var BookLoader = require("./conrefs_loader");
|
||||
|
||||
|
||||
// The loader should handle relative and git url
|
||||
var BookLoader = nunjucks.Loader.extend({
|
||||
async: true,
|
||||
|
||||
init: function(book) {
|
||||
this.book = book;
|
||||
},
|
||||
|
||||
getSource: function(fileurl, callback) {
|
||||
var that = this;
|
||||
|
||||
git.resolveFile(fileurl)
|
||||
.then(function(filepath) {
|
||||
// Is local file
|
||||
if (!filepath) filepath = path.resolve(that.book.root, fileurl);
|
||||
else that.book.log.debug.ln("resolve from git", fileurl, "to", filepath)
|
||||
|
||||
// Read file from absolute path
|
||||
return fs.readFile(filepath)
|
||||
.then(function(source) {
|
||||
return {
|
||||
src: source.toString(),
|
||||
path: filepath
|
||||
}
|
||||
});
|
||||
})
|
||||
.nodeify(callback);
|
||||
},
|
||||
|
||||
resolve: function(from, to) {
|
||||
return path.resolve(path.dirname(from), to);
|
||||
}
|
||||
});
|
||||
// Normalize result from a block
|
||||
function normBlockResult(blk) {
|
||||
if (_.isString(blk)) blk = { body: blk };
|
||||
return blk;
|
||||
}
|
||||
|
||||
|
||||
var TemplateEngine = function(book) {
|
||||
@@ -58,12 +28,12 @@ var TemplateEngine = function(book) {
|
||||
|
||||
// Tags
|
||||
tags: {
|
||||
blockStart: '{%',
|
||||
blockEnd: '%}',
|
||||
variableStart: '{{',
|
||||
variableEnd: '}}',
|
||||
commentStart: '{###',
|
||||
commentEnd: '###}'
|
||||
blockStart: "{%",
|
||||
blockEnd: "%}",
|
||||
variableStart: "{{",
|
||||
variableEnd: "}}",
|
||||
commentStart: "{###",
|
||||
commentEnd: "###}"
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -71,49 +41,48 @@ var TemplateEngine = function(book) {
|
||||
// List of tags shortcuts
|
||||
this.shortcuts = [];
|
||||
|
||||
// Map of blocks
|
||||
// Map of blocks bodies (that requires post-processing)
|
||||
this.blockBodies = {};
|
||||
|
||||
// Map of added blocks
|
||||
this.blocks = {};
|
||||
|
||||
// Bind methods
|
||||
_.bindAll(this);
|
||||
|
||||
// Default block "html" that return html not parsed
|
||||
this.addBlock("html", {
|
||||
process: _.identity
|
||||
});
|
||||
// Add default blocks
|
||||
this.addBlocks(defaultBlocks);
|
||||
};
|
||||
|
||||
// Process a block in a context
|
||||
// Process the result of block in a context
|
||||
TemplateEngine.prototype.processBlock = function(blk) {
|
||||
if (_.isString(blk)) blk = { body: blk };
|
||||
|
||||
blk = _.defaults(blk, {
|
||||
parse: false,
|
||||
post: undefined
|
||||
});
|
||||
blk.id = _.uniqueId("blk");
|
||||
|
||||
var toAdd = (!blk.parse) || (blk.post != undefined);
|
||||
var toAdd = (!blk.parse) || (blk.post !== undefined);
|
||||
|
||||
// Add to global map
|
||||
if (toAdd) this.blocks[blk.id] = blk;
|
||||
if (toAdd) this.blockBodies[blk.id] = blk;
|
||||
|
||||
//Parsable block, just return it
|
||||
if (blk.parse) {
|
||||
return blk.body;
|
||||
}
|
||||
|
||||
// Return it as a macro
|
||||
return "%+%"+blk.id+"%+%";
|
||||
// Return it as a position marker
|
||||
return "@%@"+blk.id+"@%@";
|
||||
};
|
||||
|
||||
// Replace blocks by body after processing
|
||||
// This is done to avoid that markdown processer parse the block content
|
||||
// Replace position markers of blocks by body after processing
|
||||
// This is done to avoid that markdown/asciidoc processer parse the block content
|
||||
TemplateEngine.prototype.replaceBlocks = function(content) {
|
||||
var that = this;
|
||||
|
||||
return content.replace(/\%\+\%([\s\S]+?)\%\+\%/g, function(match, key) {
|
||||
var blk = that.blocks[key];
|
||||
return content.replace(/\@\%\@([\s\S]+?)\@\%\@/g, function(match, key) {
|
||||
var blk = that.blockBodies[key];
|
||||
if (!blk) return match;
|
||||
|
||||
var body = blk.body;
|
||||
@@ -141,11 +110,11 @@ TemplateEngine.prototype.bindContext = function(func) {
|
||||
TemplateEngine.prototype.addFilter = function(filterName, func) {
|
||||
try {
|
||||
this.env.getFilter(filterName);
|
||||
this.log.warn.ln("conflict in filters, '"+filterName+"' is already set");
|
||||
this.log.warn.ln("conflict in filters, \""+filterName+"\" is already set");
|
||||
return false;
|
||||
} catch(e) {}
|
||||
|
||||
this.log.debug.ln("add filter '"+filterName+"'");
|
||||
this.log.debug.ln("add filter \""+filterName+"\"");
|
||||
this.env.addFilter(filterName, this.bindContext(function() {
|
||||
var ctx = this;
|
||||
var args = Array.prototype.slice.apply(arguments);
|
||||
@@ -160,9 +129,41 @@ TemplateEngine.prototype.addFilter = function(filterName, func) {
|
||||
return true;
|
||||
};
|
||||
|
||||
// Add multiple filters
|
||||
TemplateEngine.prototype.addFilters = function(filters) {
|
||||
_.each(filters, function(filter, name) {
|
||||
this.addFilter(name, filter);
|
||||
}, this);
|
||||
};
|
||||
|
||||
// Return nunjucks extension name of a block
|
||||
TemplateEngine.prototype.blockExtName = function(name) {
|
||||
return "Block"+name+"Extension";
|
||||
};
|
||||
|
||||
// Test if a block is defined
|
||||
TemplateEngine.prototype.hasBlock = function(name) {
|
||||
return this.env.hasExtension(this.blockExtName(name));
|
||||
};
|
||||
|
||||
// Remove a block
|
||||
TemplateEngine.prototype.removeBlock = function(name) {
|
||||
if (!this.hasBlock(name)) return;
|
||||
|
||||
// Remove nunjucks extension
|
||||
this.env.removeExtension(this.blockExtName(name));
|
||||
|
||||
// Cleanup shortcuts
|
||||
this.shortcuts = _.reject(this.shortcuts, {
|
||||
block: name
|
||||
});
|
||||
};
|
||||
|
||||
// Add a block
|
||||
TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
var that = this;
|
||||
var that = this, Ext, extName;
|
||||
|
||||
if (_.isFunction(block)) block = { process: block };
|
||||
|
||||
block = _.defaults(block || {}, {
|
||||
shortcuts: [],
|
||||
@@ -171,18 +172,22 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
blocks: []
|
||||
});
|
||||
|
||||
var extName = 'Block'+name+'Extension';
|
||||
if (this.env.getExtension(extName)) {
|
||||
this.log.warn.ln("conflict in blocks, '"+name+"' is already defined");
|
||||
return false;
|
||||
extName = this.blockExtName(name);
|
||||
|
||||
if (this.hasBlock(name) && !defaultBlocks[name]) {
|
||||
this.log.warn.ln("conflict in blocks, \""+name+"\" is already defined");
|
||||
}
|
||||
|
||||
this.log.debug.ln("add block '"+name+"'");
|
||||
// Cleanup previous block
|
||||
this.removeBlock(name);
|
||||
|
||||
var Ext = function () {
|
||||
this.log.debug.ln("add block \""+name+"\"");
|
||||
this.blocks[name] = block;
|
||||
|
||||
Ext = function () {
|
||||
this.tags = [name];
|
||||
|
||||
this.parse = function(parser, nodes, lexer) {
|
||||
this.parse = function(parser, nodes) {
|
||||
var body = null;
|
||||
var lastBlockName = null;
|
||||
var lastBlockArgs = null;
|
||||
@@ -223,7 +228,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
var bodies = [body];
|
||||
_.each(block.blocks, function(blockName) {
|
||||
subbodies[blockName] = subbodies[blockName] || [];
|
||||
if (subbodies[blockName].length == 0) {
|
||||
if (subbodies[blockName].length === 0) {
|
||||
subbodies[blockName].push({
|
||||
args: new nodes.NodeList(),
|
||||
body: new nodes.NodeList()
|
||||
@@ -233,7 +238,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
bodies.push(subbodies[blockName][0].body);
|
||||
});
|
||||
|
||||
return new nodes.CallExtensionAsync(this, 'run', args, bodies);
|
||||
return new nodes.CallExtensionAsync(this, "run", args, bodies);
|
||||
};
|
||||
|
||||
this.run = function(context) {
|
||||
@@ -250,7 +255,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
|
||||
// Extract main body and kwargs
|
||||
var body = args.pop();
|
||||
var kwargs = args.pop() || {};
|
||||
var kwargs = _.isObject(_.last(args))? args.pop() : {};
|
||||
|
||||
// Extract blocks body
|
||||
var _blocks = _.map(block.blocks, function(blockName, i){
|
||||
@@ -260,33 +265,31 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
};
|
||||
});
|
||||
|
||||
var func = that.bindContext(block.process);
|
||||
|
||||
Q()
|
||||
.then(function() {
|
||||
return func.call(context, {
|
||||
return that.applyBlock(name, {
|
||||
body: body(),
|
||||
args: args,
|
||||
kwargs: kwargs,
|
||||
blocks: _blocks
|
||||
});
|
||||
}, context);
|
||||
})
|
||||
|
||||
// process the block returned
|
||||
.then(that.processBlock)
|
||||
.nodeify(callback)
|
||||
.nodeify(callback);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// Add the Extension
|
||||
this.env.addExtension(extName, new Ext());
|
||||
|
||||
// Add shortcuts
|
||||
if (!_.isArray(block.shortcuts)) block.shortcuts = [block.shortcuts];
|
||||
_.each(block.shortcuts, function(shortcut) {
|
||||
this.log.debug.ln("add template shortcut from '"+shortcut.start+"' to block '"+name+"' for parsers ", shortcut.parsers);
|
||||
this.log.debug.ln("add template shortcut from \""+shortcut.start+"\" to block \""+name+"\" for parsers ", shortcut.parsers);
|
||||
this.shortcuts.push({
|
||||
block: name,
|
||||
parsers: shortcut.parsers,
|
||||
start: shortcut.start,
|
||||
end: shortcut.end,
|
||||
@@ -298,12 +301,46 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
}, this);
|
||||
};
|
||||
|
||||
// Add multiple blocks
|
||||
TemplateEngine.prototype.addBlocks = function(blocks) {
|
||||
_.each(blocks, function(block, name) {
|
||||
this.addBlock(name, block);
|
||||
}, this);
|
||||
};
|
||||
|
||||
// Apply a block to some content
|
||||
// This method result depends on the type of block (async or sync)
|
||||
TemplateEngine.prototype.applyBlock = function(name, blk, ctx) {
|
||||
var func, block, r;
|
||||
|
||||
block = this.blocks[name];
|
||||
if (!block) throw new Error("Block not found \""+name+"\"");
|
||||
if (_.isString(blk)) {
|
||||
blk = {
|
||||
body: blk
|
||||
};
|
||||
}
|
||||
|
||||
blk = _.defaults(blk, {
|
||||
args: [],
|
||||
kwargs: {},
|
||||
blocks: []
|
||||
});
|
||||
|
||||
// Bind and call block processor
|
||||
func = this.bindContext(block.process);
|
||||
r = func.call(ctx || {}, blk);
|
||||
|
||||
if (Q.isPromise(r)) return r.then(normBlockResult);
|
||||
else return normBlockResult(r);
|
||||
};
|
||||
|
||||
// Apply a shortcut to a string
|
||||
TemplateEngine.prototype._applyShortcut = function(parser, content, shortcut) {
|
||||
if (!_.contains(shortcut.parsers, parser)) return content;
|
||||
var regex = new RegExp(
|
||||
stringUtils.escapeRegex(shortcut.start) + "([\\s\\S]*?[^\\$])" + stringUtils.escapeRegex(shortcut.end),
|
||||
'g'
|
||||
escapeStringRegexp(shortcut.start) + "([\\s\\S]*?[^\\$])" + escapeStringRegexp(shortcut.end),
|
||||
"g"
|
||||
);
|
||||
return content.replace(regex, function(all, match) {
|
||||
return "{% "+shortcut.tag.start+" %}"+ match + "{% "+shortcut.tag.end+" %}";
|
||||
@@ -312,10 +349,13 @@ TemplateEngine.prototype._applyShortcut = function(parser, content, shortcut) {
|
||||
|
||||
// Render a string from the book
|
||||
TemplateEngine.prototype.renderString = function(content, context, options) {
|
||||
var context = _.extend({}, context, {
|
||||
context = _.extend({}, context, {
|
||||
// Variables from book.json
|
||||
book: this.book.options.variables,
|
||||
|
||||
// Complete book.json
|
||||
config: this.book.options,
|
||||
|
||||
// infos about gitbook
|
||||
gitbook: {
|
||||
version: pkg.version,
|
||||
@@ -331,12 +371,18 @@ TemplateEngine.prototype.renderString = function(content, context, options) {
|
||||
// Replace shortcuts
|
||||
content = _.reduce(this.shortcuts, _.partial(this._applyShortcut.bind(this), options.type), content);
|
||||
|
||||
return Q.nfcall(this.env.renderString.bind(this.env), content, context, options);
|
||||
return Q.nfcall(this.env.renderString.bind(this.env), content, context, options)
|
||||
.fail(function(err) {
|
||||
if (_.isString(err)) err = new Error(err);
|
||||
err.message = err.message.replace(/^Error: /, "");
|
||||
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
|
||||
// Render a file from the book
|
||||
TemplateEngine.prototype.renderFile = function(filename, options) {
|
||||
var that = this, context;
|
||||
TemplateEngine.prototype.renderFile = function(filename) {
|
||||
var that = this;
|
||||
|
||||
return that.book.readFile(filename)
|
||||
.then(function(content) {
|
||||
@@ -348,11 +394,11 @@ TemplateEngine.prototype.renderFile = function(filename, options) {
|
||||
|
||||
// Render a page from the book
|
||||
TemplateEngine.prototype.renderPage = function(page) {
|
||||
var that = this, context;
|
||||
var that = this;
|
||||
|
||||
return that.book.statFile(page.path)
|
||||
.then(function(stat) {
|
||||
context = {
|
||||
var context = {
|
||||
// infos about the file
|
||||
file: {
|
||||
path: page.path,
|
||||
@@ -374,7 +420,7 @@ TemplateEngine.prototype.postProcess = function(content) {
|
||||
return Q(content)
|
||||
.then(that.replaceBlocks)
|
||||
.then(function(_content) {
|
||||
return batch.execEach(that.blocks, {
|
||||
return batch.execEach(that.blockBodies, {
|
||||
max: 20,
|
||||
fn: function(blk, blkId) {
|
||||
return Q()
|
||||
@@ -383,7 +429,7 @@ TemplateEngine.prototype.postProcess = function(content) {
|
||||
return blk.post();
|
||||
})
|
||||
.then(function() {
|
||||
delete that.blocks[blkId];
|
||||
delete that.blockBodies[blkId];
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
+4
-4
@@ -3,12 +3,12 @@ var _ = require("lodash");
|
||||
|
||||
// Execute a method for all element
|
||||
function execEach(items, options) {
|
||||
if (_.size(items) == 0) return Q();
|
||||
if (_.size(items) === 0) return Q();
|
||||
var concurrents = 0, d = Q.defer(), pending = [];
|
||||
|
||||
options = _.defaults(options || {}, {
|
||||
max: 100,
|
||||
fn: function(item) {}
|
||||
fn: function() {}
|
||||
});
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ function execEach(items, options) {
|
||||
// Next pending
|
||||
var next = pending.shift();
|
||||
|
||||
if (concurrents == 0 && !next) {
|
||||
if (concurrents === 0 && !next) {
|
||||
d.resolve();
|
||||
} else if (next) {
|
||||
startItem.apply(null, next);
|
||||
@@ -38,7 +38,7 @@ function execEach(items, options) {
|
||||
.fail(function(err) {
|
||||
pending = [];
|
||||
d.reject(err);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
_.each(items, startItem);
|
||||
|
||||
+19
-15
@@ -9,10 +9,10 @@ var Ignore = require("fstream-ignore");
|
||||
var fsUtils = {
|
||||
tmp: {
|
||||
file: function(opt) {
|
||||
return Q.nfcall(tmp.file.bind(tmp), opt).get(0)
|
||||
return Q.nfcall(tmp.file.bind(tmp), opt).get(0);
|
||||
},
|
||||
dir: function() {
|
||||
return Q.nfcall(tmp.dir.bind(tmp)).get(0)
|
||||
return Q.nfcall(tmp.dir.bind(tmp)).get(0);
|
||||
}
|
||||
},
|
||||
list: listFiles,
|
||||
@@ -34,14 +34,14 @@ var fsUtils = {
|
||||
readFileSync: fs.readFileSync.bind(fs),
|
||||
clean: cleanFolder,
|
||||
getUniqueFilename: getUniqueFilename,
|
||||
}
|
||||
};
|
||||
|
||||
// Write a file
|
||||
function writeFile(filename, data, options) {
|
||||
var d = Q.defer();
|
||||
|
||||
try {
|
||||
fs.writeFileSync(filename, data, options)
|
||||
fs.writeFileSync(filename, data, options);
|
||||
} catch(err) {
|
||||
d.reject(err);
|
||||
}
|
||||
@@ -57,10 +57,14 @@ function writeStream(filename, st) {
|
||||
|
||||
var wstream = fs.createWriteStream(filename);
|
||||
|
||||
wstream.on('finish', function () {
|
||||
wstream.on("finish", function () {
|
||||
d.resolve();
|
||||
});
|
||||
wstream.on('error', function (err) {
|
||||
wstream.on("error", function (err) {
|
||||
d.reject(err);
|
||||
});
|
||||
|
||||
st.on("error", function(err) {
|
||||
d.reject(err);
|
||||
});
|
||||
|
||||
@@ -111,20 +115,20 @@ function listFiles(root, options) {
|
||||
});
|
||||
|
||||
// Add extra rules to ignore common folders
|
||||
ig.addIgnoreRules(options.ignoreRules, '__custom_stuff');
|
||||
ig.addIgnoreRules(options.ignoreRules, "__custom_stuff");
|
||||
|
||||
// Push each file to our list
|
||||
ig.on('child', function (c) {
|
||||
ig.on("child", function (c) {
|
||||
files.push(
|
||||
c.path.substr(c.root.path.length + 1) + (c.props.Directory === true ? '/' : '')
|
||||
c.path.substr(c.root.path.length + 1) + (c.props.Directory === true ? "/" : "")
|
||||
);
|
||||
});
|
||||
|
||||
ig.on('end', function() {
|
||||
ig.on("end", function() {
|
||||
// Normalize paths on Windows
|
||||
if(process.platform === 'win32') {
|
||||
if(process.platform === "win32") {
|
||||
return d.resolve(files.map(function(file) {
|
||||
return file.replace(/\\/g, '/');
|
||||
return file.replace(/\\/g, "/");
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -132,7 +136,7 @@ function listFiles(root, options) {
|
||||
return d.resolve(files);
|
||||
});
|
||||
|
||||
ig.on('error', d.reject);
|
||||
ig.on("error", d.reject);
|
||||
|
||||
return d.promise;
|
||||
}
|
||||
@@ -146,8 +150,8 @@ function cleanFolder(root) {
|
||||
ignoreFiles: [],
|
||||
ignoreRules: [
|
||||
// Skip Git and SVN stuff
|
||||
'.git/',
|
||||
'.svn/'
|
||||
".git/",
|
||||
".svn/"
|
||||
]
|
||||
})
|
||||
.then(function(files) {
|
||||
|
||||
+21
-6
@@ -1,11 +1,10 @@
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var url = require("url");
|
||||
var tmp = require("tmp");
|
||||
var path = require("path");
|
||||
var crc = require("crc");
|
||||
var exec = Q.denodeify(require("child_process").exec);
|
||||
var URI = require("URIjs");
|
||||
var pathUtil = require("./path");
|
||||
|
||||
var fs = require("./fs");
|
||||
|
||||
@@ -83,13 +82,12 @@ function cloneGitRepo(host, ref) {
|
||||
return exec("git clone "+host+" "+repoPath)
|
||||
.then(function() {
|
||||
return exec("git checkout "+ref, { cwd: repoPath });
|
||||
})
|
||||
});
|
||||
})
|
||||
.thenResolve(repoPath);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Get file from a git repo
|
||||
function resolveFileFromGit(giturl) {
|
||||
if (_.isString(giturl)) giturl = parseGitUrl(giturl);
|
||||
@@ -102,11 +100,28 @@ function resolveFileFromGit(giturl) {
|
||||
// Resolve relative path
|
||||
return path.resolve(repo, giturl.filepath);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Return root of git repo from a filepath
|
||||
function resolveGitRoot(filepath) {
|
||||
var relativeToGit, repoId;
|
||||
|
||||
// No git repo cloned, or file is not in a git repository
|
||||
if (!GIT_TMP || !pathUtil.isInRoot(GIT_TMP, filepath)) return null;
|
||||
|
||||
// Extract first directory (is the repo id)
|
||||
relativeToGit = path.relative(GIT_TMP, filepath);
|
||||
repoId = _.first(relativeToGit.split(path.sep));
|
||||
if (!repoId) return;
|
||||
|
||||
// Return an absolute file
|
||||
return path.resolve(GIT_TMP, repoId);
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
checkUrl: checkGitUrl,
|
||||
parseUrl: parseGitUrl,
|
||||
resolveFile: resolveFileFromGit
|
||||
resolveFile: resolveFileFromGit,
|
||||
resolveRoot: resolveGitRoot
|
||||
};
|
||||
|
||||
+41
-33
@@ -2,34 +2,22 @@ var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
|
||||
var I18N_PATH = path.resolve(__dirname, "../../theme/i18n/")
|
||||
var i18n = require("i18n");
|
||||
|
||||
var getLocales = _.memoize(function() {
|
||||
var locales = fs.readdirSync(I18N_PATH);
|
||||
return _.chain(locales)
|
||||
.map(function(local) {
|
||||
local = path.basename(local, ".json");
|
||||
return [local, _.extend({
|
||||
direction: "ltr"
|
||||
}, require(path.join(I18N_PATH, local)), {
|
||||
id: local
|
||||
})];
|
||||
})
|
||||
.object()
|
||||
.value();
|
||||
var I18N_PATH = path.resolve(__dirname, "../../theme/i18n/");
|
||||
var DEFAULT_LANGUAGE = "en";
|
||||
var LOCALES = _.map(fs.readdirSync(I18N_PATH), function(lang) {
|
||||
return path.basename(lang, ".json");
|
||||
});
|
||||
|
||||
var getLanguages = function() {
|
||||
return _.keys(getLocales());
|
||||
};
|
||||
i18n.configure({
|
||||
locales: LOCALES,
|
||||
directory: I18N_PATH,
|
||||
defaultLocale: DEFAULT_LANGUAGE,
|
||||
updateFiles: false
|
||||
});
|
||||
|
||||
var getByLanguage = function(lang) {
|
||||
lang = normalizeLanguage(lang);
|
||||
var locales = getLocales();
|
||||
return locales[lang];
|
||||
};
|
||||
|
||||
var compareLocales = function(lang, locale) {
|
||||
function compareLocales(lang, locale) {
|
||||
var langMain = _.first(lang.split("-"));
|
||||
var langSecond = _.last(lang.split("-"));
|
||||
|
||||
@@ -40,19 +28,16 @@ var compareLocales = function(lang, locale) {
|
||||
if (localeMain == langMain) return 50;
|
||||
if (localeSecond == langSecond) return 20;
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
var normalizeLanguage = _.memoize(function(lang) {
|
||||
var locales = getLocales();
|
||||
var language = _.chain(locales)
|
||||
var language = _.chain(LOCALES)
|
||||
.values()
|
||||
.map(function(locale) {
|
||||
locale = locale.id;
|
||||
|
||||
return {
|
||||
locale: locale,
|
||||
score: compareLocales(lang, locale)
|
||||
}
|
||||
};
|
||||
})
|
||||
.filter(function(lang) {
|
||||
return lang.score > 0;
|
||||
@@ -64,9 +49,32 @@ var normalizeLanguage = _.memoize(function(lang) {
|
||||
return language || lang;
|
||||
});
|
||||
|
||||
function translate(locale, phrase) {
|
||||
var args = Array.prototype.slice.call(arguments, 2);
|
||||
|
||||
return i18n.__.apply({}, [{
|
||||
locale: locale,
|
||||
phrase: phrase
|
||||
}].concat(args));
|
||||
}
|
||||
|
||||
function getCatalog(locale) {
|
||||
locale = normalizeLanguage(locale);
|
||||
return i18n.getCatalog(locale);
|
||||
}
|
||||
|
||||
function getLocales() {
|
||||
return LOCALES;
|
||||
}
|
||||
|
||||
function hasLocale(locale) {
|
||||
return _.contains(LOCALES, locale);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
__: translate,
|
||||
normalizeLanguage: normalizeLanguage,
|
||||
getCatalog: getCatalog,
|
||||
getLocales: getLocales,
|
||||
getLanguages: getLanguages,
|
||||
getByLanguage: getByLanguage,
|
||||
normalizeLanguage: normalizeLanguage
|
||||
hasLocale: hasLocale
|
||||
};
|
||||
|
||||
+5
-7
@@ -1,9 +1,7 @@
|
||||
var _ = require("lodash");
|
||||
var Q = require("q");
|
||||
var fs = require("./fs");
|
||||
var spawn = require('spawn-cmd').spawn;
|
||||
|
||||
var links = require("./links");
|
||||
var spawn = require("spawn-cmd").spawn;
|
||||
|
||||
// Convert a svg file
|
||||
var convertSVG = function(source, dest, options) {
|
||||
@@ -14,16 +12,16 @@ var convertSVG = function(source, dest, options) {
|
||||
|
||||
});
|
||||
|
||||
//var command = shellescape(['svgexport', source, dest]);
|
||||
var child = spawn('svgexport', [source, dest]);
|
||||
//var command = shellescape(["svgexport", source, dest]);
|
||||
var child = spawn("svgexport", [source, dest]);
|
||||
|
||||
child.on("error", function(error) {
|
||||
if (error.code == "ENOENT") error = new Error("Need to install 'svgexport' using 'npm install svgexport -g'");
|
||||
if (error.code == "ENOENT") error = new Error("Need to install \"svgexport\" using \"npm install svgexport -g\"");
|
||||
return d.reject(error);
|
||||
});
|
||||
|
||||
child.on("close", function(code) {
|
||||
if (code == 0 && fs.existsSync(dest)) {
|
||||
if (code === 0 && fs.existsSync(dest)) {
|
||||
d.resolve();
|
||||
} else {
|
||||
d.reject(new Error("Error converting "+source+" into "+dest));
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
var url = require('url');
|
||||
var path = require('path');
|
||||
var url = require("url");
|
||||
var path = require("path");
|
||||
|
||||
// Is the link an external link
|
||||
var isExternal = function(href) {
|
||||
@@ -33,7 +33,7 @@ var isAnchor = function(href) {
|
||||
|
||||
// Normalize a path to be a link
|
||||
var normalizeLink = function(s) {
|
||||
return s.replace(/\\/g, '/');
|
||||
return s.replace(/\\/g, "/");
|
||||
};
|
||||
|
||||
// Relative to absolute path
|
||||
@@ -43,7 +43,7 @@ var normalizeLink = function(s) {
|
||||
var toAbsolute = function(_href, dir, outdir) {
|
||||
if (isExternal(_href)) return _href;
|
||||
|
||||
// Path '_href' inside the base folder
|
||||
// Path "_href" inside the base folder
|
||||
var hrefInRoot = path.normalize(path.join(dir, _href));
|
||||
if (_href[0] == "/") hrefInRoot = path.normalize(_href.slice(1));
|
||||
|
||||
|
||||
+9
-9
@@ -1,6 +1,6 @@
|
||||
var _ = require('lodash');
|
||||
var util = require('util');
|
||||
var color = require('bash-color');
|
||||
var _ = require("lodash");
|
||||
var util = require("util");
|
||||
var color = require("bash-color");
|
||||
|
||||
var LEVELS = {
|
||||
DEBUG: 0,
|
||||
@@ -19,7 +19,7 @@ var COLORS = {
|
||||
|
||||
module.exports = function(_write, logLevel) {
|
||||
var logger = {};
|
||||
var lastChar = '\n';
|
||||
var lastChar = "\n";
|
||||
if (_.isString(logLevel)) logLevel = LEVELS[logLevel.toUpperCase()];
|
||||
|
||||
// Write a simple message
|
||||
@@ -47,14 +47,14 @@ module.exports = function(_write, logLevel) {
|
||||
var args = Array.prototype.slice.apply(arguments, [1]);
|
||||
var msg = logger.format.apply(logger, args);
|
||||
|
||||
if (lastChar == '\n') {
|
||||
if (lastChar == "\n") {
|
||||
msg = COLORS[levelKey](levelKey.toLowerCase()+":")+" "+msg;
|
||||
}
|
||||
|
||||
return logger.write(msg);
|
||||
};
|
||||
logger.logLn = function() {
|
||||
if (lastChar != '\n') logger.write("\n");
|
||||
if (lastChar != "\n") logger.write("\n");
|
||||
|
||||
var args = Array.prototype.slice.apply(arguments);
|
||||
args.push("\n");
|
||||
@@ -66,7 +66,7 @@ module.exports = function(_write, logLevel) {
|
||||
var args = Array.prototype.slice.apply(arguments, [1]);
|
||||
var msg = logger.format.apply(logger, args);
|
||||
if (arguments.length > 1) {
|
||||
logger.logLn(level, color.green('>> ') + msg.trim().replace(/\n/g, color.green('\n>> ')));
|
||||
logger.logLn(level, color.green(">> ") + msg.trim().replace(/\n/g, color.green("\n>> ")));
|
||||
} else {
|
||||
logger.log(level, color.green("OK"), "\n");
|
||||
}
|
||||
@@ -77,7 +77,7 @@ module.exports = function(_write, logLevel) {
|
||||
return logger.log(level, color.red("ERROR")+"\n");
|
||||
};
|
||||
|
||||
_.each(_.omit(LEVELS, 'DISABLED'), function(level, levelKey) {
|
||||
_.each(_.omit(LEVELS, "DISABLED"), function(level, levelKey) {
|
||||
levelKey = levelKey.toLowerCase();
|
||||
|
||||
logger[levelKey] = _.partial(logger.log, level);
|
||||
@@ -93,7 +93,7 @@ module.exports = function(_write, logLevel) {
|
||||
logger[levelKey].fail();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return logger;
|
||||
|
||||
+9
-10
@@ -1,9 +1,9 @@
|
||||
var _ = require('lodash');
|
||||
var _ = require("lodash");
|
||||
|
||||
// Cleans up an article/chapter object
|
||||
// remove 'articles' attributes
|
||||
// remove "articles" attributes
|
||||
function clean(obj) {
|
||||
return obj && _.omit(obj, ['articles']);
|
||||
return obj && _.omit(obj, ["articles"]);
|
||||
}
|
||||
|
||||
function flattenChapters(chapters) {
|
||||
@@ -27,7 +27,7 @@ function navigation(summary, files) {
|
||||
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
|
||||
|
||||
// List of all navNodes
|
||||
// Flatten chapters, then add in default README node if needed etc ...
|
||||
// Flatten chapters
|
||||
var navNodes = flattenChapters(summary.chapters);
|
||||
|
||||
// Mapping of prev/next for a give path
|
||||
@@ -36,11 +36,10 @@ function navigation(summary, files) {
|
||||
var prev = null, next = null;
|
||||
|
||||
// Skip if no path
|
||||
if(!current.path) return null;
|
||||
if(!current.exists) return null;
|
||||
|
||||
// Find prev
|
||||
prev = _.chain(navNodes)
|
||||
.slice(0, i)
|
||||
prev = _.chain(navNodes.slice(0, i))
|
||||
.reverse()
|
||||
.find(function(node) {
|
||||
return node.exists && !node.external;
|
||||
@@ -48,16 +47,16 @@ function navigation(summary, files) {
|
||||
.value();
|
||||
|
||||
// Find next
|
||||
next = _.chain(navNodes)
|
||||
.slice(i+1)
|
||||
next = _.chain(navNodes.slice(i+1))
|
||||
.find(function(node) {
|
||||
return node.exists && !node.external;
|
||||
})
|
||||
.value();
|
||||
|
||||
|
||||
return [current.path, {
|
||||
index: i,
|
||||
title: current.title,
|
||||
introduction: current.introduction,
|
||||
prev: prev,
|
||||
next: next,
|
||||
level: current.level,
|
||||
|
||||
+91
-34
@@ -1,16 +1,18 @@
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
var url = require('url');
|
||||
var path = require('path');
|
||||
var cheerio = require('cheerio');
|
||||
var domSerializer = require('dom-serializer');
|
||||
var request = require('request');
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var url = require("url");
|
||||
var path = require("path");
|
||||
var cheerio = require("cheerio");
|
||||
var domSerializer = require("dom-serializer");
|
||||
var request = require("request");
|
||||
var crc = require("crc");
|
||||
|
||||
var links = require('./links');
|
||||
var imgUtils = require('./images');
|
||||
var fs = require('./fs');
|
||||
var batch = require('./batch');
|
||||
var links = require("./links");
|
||||
var imgUtils = require("./images");
|
||||
var fs = require("./fs");
|
||||
var batch = require("./batch");
|
||||
|
||||
var parsableExtensions = require("gitbook-parsers").extensions;
|
||||
|
||||
// Render a cheerio dom as html
|
||||
var renderDom = function($, dom, options) {
|
||||
@@ -38,7 +40,8 @@ function replaceText($, el, search, replace, text_only ) {
|
||||
if ( node ) {
|
||||
|
||||
// Loop over all childNodes.
|
||||
do {
|
||||
while (node) {
|
||||
|
||||
// Only process text nodes.
|
||||
if ( node.nodeType === 3 ) {
|
||||
|
||||
@@ -56,7 +59,7 @@ function replaceText($, el, search, replace, text_only ) {
|
||||
// robust way.
|
||||
$(node).before( new_val );
|
||||
|
||||
// Don't remove the node yet, or the loop will lose its place.
|
||||
// Don"t remove the node yet, or the loop will lose its place.
|
||||
remove.push( node );
|
||||
} else {
|
||||
// The new value contains no HTML, so it can be set in this
|
||||
@@ -66,17 +69,18 @@ function replaceText($, el, search, replace, text_only ) {
|
||||
}
|
||||
}
|
||||
|
||||
} while ( node = node.nextSibling );
|
||||
node = node.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
// Time to remove those elements!
|
||||
remove.length && $(remove).remove();
|
||||
if (remove.length) $(remove).remove();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function pregQuote( str ) {
|
||||
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
|
||||
};
|
||||
return (str+"").replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
|
||||
}
|
||||
|
||||
|
||||
// Adapt an html snippet to be relative to a base folder
|
||||
@@ -105,15 +109,16 @@ function normalizeHtml(src, options) {
|
||||
// Generate filename
|
||||
dest = "/"+fs.getUniqueFilename(outputRoot, dest);
|
||||
|
||||
svgContent[dest] = '<?xml version="1.0" encoding="UTF-8"?>'+content;
|
||||
svgContent[dest] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+content;
|
||||
$(this).replaceWith($("<img>").attr("src", dest));
|
||||
});
|
||||
}
|
||||
|
||||
// Find images to normalize
|
||||
$("img").each(function() {
|
||||
var origin = undefined;
|
||||
var origin;
|
||||
var src = $(this).attr("src");
|
||||
|
||||
if (!src) return;
|
||||
var isExternal = links.isExternal(src);
|
||||
|
||||
@@ -148,7 +153,7 @@ function normalizeHtml(src, options) {
|
||||
dest = links.join(path.dirname(srcAbs), path.basename(srcAbs, ext)+".png");
|
||||
dest = dest[0] == "/"? dest.slice(1) : dest;
|
||||
|
||||
// Get a name that doesn't exists
|
||||
// Get a name that doesn"t exists
|
||||
dest = fs.getUniqueFilename(outputRoot, dest);
|
||||
|
||||
options.book.log.debug.ln("detect invalid image (will be converted to png):", srcAbs);
|
||||
@@ -183,6 +188,7 @@ function normalizeHtml(src, options) {
|
||||
$(this).attr("src", src);
|
||||
});
|
||||
|
||||
// Normalize links
|
||||
$("a").each(function() {
|
||||
var href = $(this).attr("href");
|
||||
if (!href) return;
|
||||
@@ -191,14 +197,22 @@ function normalizeHtml(src, options) {
|
||||
// Keep it as it is
|
||||
} else if (links.isRelative(href)) {
|
||||
var parts = url.parse(href);
|
||||
var absolutePath = path.join(options.base, parts.pathname);
|
||||
var absolutePath = links.join(options.base, parts.pathname);
|
||||
var anchor = parts.hash || "";
|
||||
|
||||
|
||||
// If is in navigation relative: transform as content
|
||||
if (options.navigation[absolutePath]) {
|
||||
absolutePath = options.book.contentLink(absolutePath);
|
||||
}
|
||||
|
||||
// If md/adoc/rst files is not in summary
|
||||
// or for ebook, signal all files that are outside the summary
|
||||
else if (_.contains(parsableExtensions, path.extname(absolutePath)) ||
|
||||
_.contains(["epub", "pdf", "mobi"], options.book.options.generator)) {
|
||||
options.book.log.warn.ln("page", options.input, "contains an hyperlink to resource outside spine \""+href+"\"");
|
||||
}
|
||||
|
||||
// Transform as absolute
|
||||
href = links.toAbsolute("/"+absolutePath, options.base, options.output)+anchor;
|
||||
} else {
|
||||
@@ -210,12 +224,50 @@ function normalizeHtml(src, options) {
|
||||
$(this).attr("href", href);
|
||||
});
|
||||
|
||||
// Replace glossayr terms
|
||||
_.each(options.glossary, function(term) {
|
||||
var r = new RegExp( "\\b(" + pregQuote(term.name.toLowerCase()) + ")\\b" , 'gi' );
|
||||
// Highlight code blocks
|
||||
$("code").each(function() {
|
||||
// Normalize language
|
||||
var lang = _.chain(
|
||||
($(this).attr("class") || "").split(" ")
|
||||
)
|
||||
.map(function(cl) {
|
||||
// Markdown
|
||||
if (cl.search("lang-") === 0) return cl.slice("lang-".length);
|
||||
|
||||
// Asciidoc
|
||||
if (cl.search("language-") === 0) return cl.slice("language-".length);
|
||||
|
||||
return null;
|
||||
})
|
||||
.compact()
|
||||
.first()
|
||||
.value();
|
||||
|
||||
var source = $(this).text();
|
||||
var blk = options.book.template.applyBlock("code", {
|
||||
body: source,
|
||||
kwargs: {
|
||||
language: lang
|
||||
}
|
||||
});
|
||||
|
||||
if (blk.html === false) $(this).text(blk.body);
|
||||
else $(this).html(blk.body);
|
||||
});
|
||||
|
||||
// Replace glossary terms
|
||||
var glossary = _.sortBy(options.glossary, function(term) {
|
||||
return -term.name.length;
|
||||
});
|
||||
|
||||
_.each(glossary, function(term) {
|
||||
var r = new RegExp( "\\b(" + pregQuote(term.name.toLowerCase()) + ")\\b" , "gi" );
|
||||
var includedInFiles = false;
|
||||
|
||||
$("*").each(function() {
|
||||
// Ignore codeblocks
|
||||
if (_.contains(["code", "pre", "a"], this.name.toLowerCase())) return;
|
||||
|
||||
replaceText($, this, r, function(match) {
|
||||
// Add to files index in glossary
|
||||
if (!includedInFiles) {
|
||||
@@ -223,7 +275,7 @@ function normalizeHtml(src, options) {
|
||||
term.files = term.files || [];
|
||||
term.files.push(options.navigation[options.input]);
|
||||
}
|
||||
return "<a href='"+links.toAbsolute("/GLOSSARY.html", options.base, options.output)+"#"+term.id+"' class='glossary-term' title='"+_.escape(term.description)+"'>"+match+"</a>";
|
||||
return "<a href=\""+links.toAbsolute("/GLOSSARY.html", options.base, options.output) + "#" + term.id+"\" class=\"glossary-term\" title=\""+_.escape(term.description)+"\">"+match+"</a>";
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -232,7 +284,7 @@ function normalizeHtml(src, options) {
|
||||
html: renderDom($),
|
||||
images: toConvert
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Convert svg images to png
|
||||
function convertImages(images, options) {
|
||||
@@ -253,7 +305,13 @@ function convertImages(images, options) {
|
||||
if (!image.origin && !_.contains(downloaded, image.origin)) return;
|
||||
options.book.log.debug("download image", image.origin, "...");
|
||||
downloaded.push(image.origin);
|
||||
return options.book.log.debug.promise(fs.writeStream(imgin, request(image.origin)));
|
||||
return options.book.log.debug.promise(fs.writeStream(imgin, request(image.origin)))
|
||||
.fail(function(err) {
|
||||
if (!_.isError(err)) err = new Error(err);
|
||||
|
||||
err.message = "Fail downloading "+image.origin+": "+err.message;
|
||||
throw err;
|
||||
});
|
||||
})
|
||||
|
||||
// Write svg if content
|
||||
@@ -274,8 +332,7 @@ function convertImages(images, options) {
|
||||
.then(function() {
|
||||
options.book.log.debug.ok(images.length+" images converted with success");
|
||||
});
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Adapt page content to be relative to a base folder
|
||||
function normalizePage(sections, options) {
|
||||
@@ -308,7 +365,7 @@ function normalizePage(sections, options) {
|
||||
sections = _.map(sections, function(section) {
|
||||
if (section.type != "normal") return section;
|
||||
|
||||
var out = normalizeHtml(section.content, options);;
|
||||
var out = normalizeHtml(section.content, options);
|
||||
|
||||
toConvert = toConvert.concat(out.images);
|
||||
section.content = out.html;
|
||||
@@ -317,11 +374,11 @@ function normalizePage(sections, options) {
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
toConvert = _.uniq(toConvert, 'source');
|
||||
toConvert = _.uniq(toConvert, "source");
|
||||
return convertImages(toConvert, options);
|
||||
})
|
||||
.thenResolve(sections);
|
||||
};
|
||||
}
|
||||
|
||||
// Extract text from sections
|
||||
function extractText(sections) {
|
||||
@@ -335,7 +392,7 @@ function extractText(sections) {
|
||||
|
||||
return prev;
|
||||
}, "");
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
normalize: normalizePage,
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
|
||||
// Return true if file path is inside a folder
|
||||
function isInRoot(root, filename) {
|
||||
filename = path.normalize(filename);
|
||||
return (filename.substr(0, root.length) === root);
|
||||
}
|
||||
|
||||
// Resolve paths in a specific folder
|
||||
// Throw error if file is outside this folder
|
||||
function resolveInRoot(root) {
|
||||
var input, result, err;
|
||||
|
||||
input = _.chain(arguments)
|
||||
.toArray()
|
||||
.slice(1)
|
||||
.reduce(function(current, p) {
|
||||
// Handle path relative to book root ("/README.md")
|
||||
if (p[0] == "/" || p[0] == "\\") return p.slice(1);
|
||||
|
||||
return current? path.join(current, p) : path.normalize(p);
|
||||
}, "")
|
||||
.value();
|
||||
|
||||
result = path.resolve(root, input);
|
||||
|
||||
if (!isInRoot(root, result)) {
|
||||
err = new Error("EACCESS: \"" + result + "\" not in \"" + root + "\"");
|
||||
err.code = "EACCESS";
|
||||
throw err;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isInRoot: isInRoot,
|
||||
resolveInRoot: resolveInRoot
|
||||
};
|
||||
@@ -1,16 +1,24 @@
|
||||
var _ = require("lodash");
|
||||
|
||||
// Returns from a navigation and a current file, a snapshot of current detailed state
|
||||
var calculProgress = function(navigation, current) {
|
||||
function calculProgress(navigation, current) {
|
||||
var n = _.size(navigation);
|
||||
var percent = 0, prevPercent = 0, currentChapter = null;
|
||||
var done = true;
|
||||
|
||||
var chapters = _.chain(navigation)
|
||||
|
||||
// Transform as array
|
||||
.map(function(nav, path) {
|
||||
nav.path = path;
|
||||
return nav;
|
||||
})
|
||||
|
||||
// Sort entries
|
||||
.sortBy(function(nav) {
|
||||
return nav.index;
|
||||
})
|
||||
|
||||
.map(function(nav, i) {
|
||||
// Calcul percent
|
||||
nav.percent = (i * 100) / Math.max((n - 1), 1);
|
||||
|
||||
+15
-17
@@ -1,11 +1,9 @@
|
||||
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 Q = require("q");
|
||||
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;
|
||||
@@ -17,7 +15,7 @@ util.inherits(Server, events.EventEmitter);
|
||||
|
||||
// Return true if the server is running
|
||||
Server.prototype.isRunning = function() {
|
||||
return this.running != null;
|
||||
return !!this.running;
|
||||
};
|
||||
|
||||
// Stop the server
|
||||
@@ -57,25 +55,25 @@ Server.prototype.start = function(dir, port) {
|
||||
res.end(err.message);
|
||||
}
|
||||
|
||||
// Redirect to directory's index.html
|
||||
// Redirect to directory"s index.html
|
||||
function redirect() {
|
||||
res.statusCode = 301;
|
||||
res.setHeader('Location', req.url + '/');
|
||||
res.end('Redirecting to ' + req.url + '/');
|
||||
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)
|
||||
.on("error", error)
|
||||
.on("directory", redirect)
|
||||
.pipe(res);
|
||||
});
|
||||
|
||||
that.running.on('connection', function (socket) {
|
||||
that.running.on("connection", function (socket) {
|
||||
that.sockets.push(socket);
|
||||
socket.setTimeout(4000);
|
||||
socket.on('close', function () {
|
||||
socket.on("close", function () {
|
||||
that.sockets.splice(that.sockets.indexOf(socket), 1);
|
||||
});
|
||||
});
|
||||
@@ -91,6 +89,6 @@ Server.prototype.start = function(dir, port) {
|
||||
|
||||
return d.promise;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Server;
|
||||
|
||||
+2
-7
@@ -1,7 +1,7 @@
|
||||
var _ = require("lodash");
|
||||
|
||||
function escapeShellArg(arg) {
|
||||
var ret = '';
|
||||
var ret = "";
|
||||
|
||||
ret = arg.replace(/"/g, '\\"');
|
||||
|
||||
@@ -11,7 +11,7 @@ function escapeShellArg(arg) {
|
||||
function optionsToShellArgs(options) {
|
||||
return _.chain(options)
|
||||
.map(function(value, key) {
|
||||
if (value == null || value === false) return null;
|
||||
if (value === null || value === undefined || value === false) return null;
|
||||
if (value === true) return key;
|
||||
return key+"="+escapeShellArg(value);
|
||||
})
|
||||
@@ -20,12 +20,7 @@ function optionsToShellArgs(options) {
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
function escapeRegex(str) {
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
escapeRegex: escapeRegex,
|
||||
escapeShellArg: escapeShellArg,
|
||||
optionsToShellArgs: optionsToShellArgs,
|
||||
toLowerCase: String.prototype.toLowerCase.call.bind(String.prototype.toLowerCase)
|
||||
|
||||
+13
-11
@@ -1,9 +1,9 @@
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
var Gaze = require('gaze').Gaze;
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
var path = require("path");
|
||||
var chokidar = require("chokidar");
|
||||
|
||||
var parsers = require('gitbook-parsers')
|
||||
var parsers = require("gitbook-parsers");
|
||||
|
||||
function watch(dir) {
|
||||
var d = Q.defer();
|
||||
@@ -17,17 +17,19 @@ function watch(dir) {
|
||||
toWatch.push("**/*"+ext);
|
||||
});
|
||||
|
||||
var gaze = new Gaze(toWatch, {
|
||||
cwd: dir
|
||||
var watcher = chokidar.watch(toWatch, {
|
||||
cwd: dir,
|
||||
ignored: "_book/**",
|
||||
ignoreInitial: true
|
||||
});
|
||||
|
||||
gaze.once("all", function(e, filepath) {
|
||||
gaze.close();
|
||||
watcher.once("all", function(e, filepath) {
|
||||
watcher.close();
|
||||
|
||||
d.resolve(filepath);
|
||||
});
|
||||
gaze.once("error", function(err) {
|
||||
gaze.close();
|
||||
watcher.once("error", function(err) {
|
||||
watcher.close();
|
||||
|
||||
d.reject(err);
|
||||
});
|
||||
|
||||
+18
-15
@@ -1,27 +1,28 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "2.0.0-beta.4",
|
||||
"version": "2.4.3",
|
||||
"homepage": "https://www.gitbook.com",
|
||||
"description": "Library and cmd utility to generate GitBooks",
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
"q": "1.0.1",
|
||||
"lunr": "0.5.7",
|
||||
"lodash": "3.5.0",
|
||||
"lunr": "0.5.12",
|
||||
"lodash": "3.10.1",
|
||||
"graceful-fs": "3.0.5",
|
||||
"resolve": "0.6.3",
|
||||
"fs-extra": "0.16.5",
|
||||
"fstream-ignore": "1.0.2",
|
||||
"gitbook-parsers": "0.5.4",
|
||||
"nunjucks": "git+https://github.com/mozilla/nunjucks.git#0f8b21b8df7e8e852b2e1889388653b7075f0d09",
|
||||
"nunjucks-autoescape": "0.1.1",
|
||||
"nunjucks-filter": "0.1.0",
|
||||
"nunjucks-i18n": "1.0.0",
|
||||
"semver": "2.2.1",
|
||||
"gitbook-parsers": "0.8.3",
|
||||
"gitbook-plugin-highlight": "1.0.3",
|
||||
"nunjucks": "2.1.0",
|
||||
"nunjucks-autoescape": "1.0.0",
|
||||
"nunjucks-filter": "1.0.0",
|
||||
"i18n": "0.5.0",
|
||||
"semver": "5.0.1",
|
||||
"npmi": "0.1.1",
|
||||
"cheerio": "0.18.0",
|
||||
"cheerio": "0.19.0",
|
||||
"gitbook-plugin-livereload": "0.0.1",
|
||||
"gaze": "~0.5.1",
|
||||
"chokidar": "~1.0.5",
|
||||
"send": "0.2.0",
|
||||
"tiny-lr": "0.1.5",
|
||||
"tmp": "0.0.24",
|
||||
@@ -31,10 +32,12 @@
|
||||
"request": "2.51.0",
|
||||
"npm": "2.4.1",
|
||||
"dom-serializer": "0.1.0",
|
||||
"spawn-cmd": "0.0.2"
|
||||
"spawn-cmd": "0.0.2",
|
||||
"escape-string-regexp": "1.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "2.2.1",
|
||||
"mocha": "2.3.2",
|
||||
"should": "7.1.0",
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-cli": "0.1.11",
|
||||
"grunt-contrib-copy": "0.5.0",
|
||||
@@ -43,7 +46,7 @@
|
||||
"grunt-bower-install-simple": "0.9.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node_modules/.bin/mocha --reporter list --timeout 15000"
|
||||
"test": "node_modules/.bin/mocha --reporter spec --timeout 15000"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -58,7 +61,7 @@
|
||||
"gitbook"
|
||||
],
|
||||
"author": "FriendCode Inc. <contact@gitbook.com>",
|
||||
"license": "Apache 2",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/GitbookIO/gitbook/issues"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
var _ = require("lodash");
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var should = require("should");
|
||||
var cheerio = require("cheerio");
|
||||
|
||||
should.Assertion.add("file", function(file, description) {
|
||||
this.params = { actual: this.obj.toString(), operator: "have file " + file, message: description };
|
||||
|
||||
this.obj.should.have.property("options").which.is.an.Object();
|
||||
this.obj.options.should.have.property("output").which.is.a.String();
|
||||
this.assert(fs.existsSync(path.resolve(this.obj.options.output, file)));
|
||||
});
|
||||
|
||||
should.Assertion.add("jsonfile", function(file, description) {
|
||||
this.params = { actual: this.obj.toString(), operator: "have valid jsonfile " + file, message: description };
|
||||
|
||||
this.obj.should.have.property("options").which.is.an.Object();
|
||||
this.obj.options.should.have.property("output").which.is.a.String();
|
||||
this.assert(JSON.parse(fs.readFileSync(path.resolve(this.obj.options.output, file), { encoding: "utf-8" })));
|
||||
});
|
||||
|
||||
should.Assertion.add("html", function(rules, description) {
|
||||
this.params = { actual: "HTML string", operator: "valid html", message: description };
|
||||
var $ = cheerio.load(this.obj);
|
||||
|
||||
_.each(rules, function(validations, query) {
|
||||
validations = _.defaults(validations || {}, {
|
||||
// Select a specific element in the list of matched elements
|
||||
index: null,
|
||||
|
||||
// Check that there is the correct count of elements
|
||||
count: 1,
|
||||
|
||||
// Check attribute values
|
||||
attributes: {},
|
||||
|
||||
// Trim inner text
|
||||
trim: false,
|
||||
|
||||
// Check inner text
|
||||
text: undefined
|
||||
});
|
||||
|
||||
var $el = $(query);
|
||||
|
||||
// Select correct element
|
||||
if (_.isNumber(validations.index)) $el = $($el.get(validations.index));
|
||||
|
||||
// Test number of elements
|
||||
$el.length.should.be.equal(validations.count);
|
||||
|
||||
// Test text
|
||||
if (validations.text !== undefined) {
|
||||
var text = $el.text();
|
||||
if (validations.trim) text = text.trim();
|
||||
text.should.be.equal(validations.text);
|
||||
}
|
||||
|
||||
// Test attributes
|
||||
_.each(validations.attributes, function(value, name) {
|
||||
var attr = $el.attr(name);
|
||||
should(attr).be.ok();
|
||||
attr.should.be.equal(value);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
# Readme
|
||||
|
||||
Default description for the book.
|
||||
@@ -0,0 +1 @@
|
||||
# Readme
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
"title": "js-config"
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
# Readme
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "json-config"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# Readme
|
||||
|
||||
### Relative
|
||||
|
||||
<p id="t1">{% include "./hello.md" %}</p>
|
||||
<p id="t2">{% include "/hello.md" %}</p>
|
||||
|
||||
### Git
|
||||
|
||||
<p id="t3">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md" %}</p>
|
||||
<p id="t4">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test2.md" %}</p>
|
||||
<p id="t5">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test3.md" %}</p>
|
||||
@@ -0,0 +1 @@
|
||||
# Summary
|
||||
@@ -0,0 +1 @@
|
||||
Hello World
|
||||
@@ -0,0 +1,13 @@
|
||||
# Glossary
|
||||
|
||||
## test
|
||||
|
||||
Just a simple and easy to understand test.
|
||||
|
||||
## hakunamatata
|
||||
|
||||
This word is not present in the content.
|
||||
|
||||
## test long
|
||||
|
||||
This is a test with a longer text that the first entry to test order.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Readme
|
||||
|
||||
The word test should be replaced by a glossary link.
|
||||
|
||||
```
|
||||
But the word test should not be replaced in code blocks
|
||||
```
|
||||
|
||||
The two words test long should be replaced by the correct glossary links.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Summary
|
||||
|
||||
* [Page](folder/PAGE.md)
|
||||
@@ -0,0 +1,3 @@
|
||||
# Page
|
||||
|
||||
Just a page in a sub-directory to test relative link to glossary.
|
||||
@@ -0,0 +1,16 @@
|
||||
# Readme
|
||||
|
||||
Block without language
|
||||
|
||||
```
|
||||
test 1
|
||||
```
|
||||
|
||||
Block with a language
|
||||
|
||||
```lang
|
||||
test 2
|
||||
```
|
||||
|
||||
Inline code: `test 3`
|
||||
Inline code with html: `<test>`
|
||||
@@ -0,0 +1 @@
|
||||
# Summary
|
||||
@@ -0,0 +1,18 @@
|
||||
# Tests for Images
|
||||
|
||||
# SVG relative image
|
||||
|
||||
|
||||
### Convert SVG to PNG
|
||||

|
||||
|
||||
### Should only convert it once
|
||||

|
||||
|
||||
### Download remote images
|
||||

|
||||
|
||||
### Remote images with same filename
|
||||

|
||||

|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Summary
|
||||
|
||||
* [Page](./folder/PAGE.md)
|
||||
@@ -0,0 +1,3 @@
|
||||
### Images from other folder
|
||||

|
||||
|
||||
|
Before Width: | Height: | Size: 730 B After Width: | Height: | Size: 730 B |
@@ -0,0 +1,3 @@
|
||||
*
|
||||
!SUMMARY.md
|
||||
!.gitignore
|
||||
@@ -0,0 +1,9 @@
|
||||
# Summary
|
||||
|
||||
* [Hello](hello.md)
|
||||
* [Hello 2](hello2.md)
|
||||
* Hello 3
|
||||
* [Hello 4](hello3/hello4.md)
|
||||
* Hello 5
|
||||
* [Hello 6](hello3/hello5/hello6.md)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# Language
|
||||
|
||||
* [English](en)
|
||||
* [French](fr)
|
||||
@@ -0,0 +1 @@
|
||||
# Readme
|
||||
@@ -0,0 +1 @@
|
||||
Hello
|
||||
@@ -0,0 +1 @@
|
||||
# Summary
|
||||
@@ -0,0 +1 @@
|
||||
Bonjour
|
||||
@@ -0,0 +1 @@
|
||||
# Summary
|
||||
@@ -0,0 +1 @@
|
||||
# Readme
|
||||
@@ -0,0 +1,4 @@
|
||||
# Summary
|
||||
|
||||
* [Folder1](folder1/README.md)
|
||||
* [Folder2](folder2/README.md)
|
||||
@@ -0,0 +1,3 @@
|
||||
# Folder 1
|
||||
|
||||
[Link to folder2](../folder2/README.md)
|
||||
@@ -0,0 +1 @@
|
||||
# Folder 2
|
||||
@@ -0,0 +1,3 @@
|
||||
# Readme
|
||||
|
||||
Default description for the book.
|
||||
@@ -0,0 +1 @@
|
||||
# Summary
|
||||
@@ -0,0 +1,3 @@
|
||||
body {
|
||||
color: red;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
# Page 1
|
||||
@@ -0,0 +1 @@
|
||||
# Readme
|
||||
@@ -0,0 +1,5 @@
|
||||
# Summary
|
||||
|
||||
* [Page 1](./PAGE1.md)
|
||||
* [Page 2](./folder/PAGE2.md)
|
||||
* [Don't exists](./NOTFOUND.md)
|
||||
@@ -0,0 +1 @@
|
||||
# Page 2
|
||||
@@ -0,0 +1,65 @@
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
|
||||
var Plugin = require("../lib/plugin");
|
||||
var PLUGINS_ROOT = path.resolve(__dirname, "plugins");
|
||||
|
||||
describe("Code Highlighting", function () {
|
||||
var book, PAGE;
|
||||
|
||||
before(function() {
|
||||
return books.generate("highlight", "website", {
|
||||
prepare: function(_book) {
|
||||
book = _book;
|
||||
|
||||
var plugin = new Plugin(book, "replace_highlight");
|
||||
plugin.load("./replace_highlight", PLUGINS_ROOT);
|
||||
|
||||
book.plugins.load(plugin);
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
PAGE = fs.readFileSync(
|
||||
path.join(book.options.output, "index.html"),
|
||||
{ encoding: "utf-8" }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly replace highlighting", function() {
|
||||
PAGE.should.be.html({
|
||||
"code": {
|
||||
index: 0,
|
||||
text: "code_test 1\n_code"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly replace highlighting with language", function() {
|
||||
PAGE.should.be.html({
|
||||
"code": {
|
||||
index: 1,
|
||||
text: "lang_test 2\n_lang"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly replace highlighting for inline code", function() {
|
||||
PAGE.should.be.html({
|
||||
"code": {
|
||||
index: 2,
|
||||
text: "code_test 3_code"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly replace highlighting for inline code with html tags", function() {
|
||||
PAGE.should.be.html({
|
||||
"code": {
|
||||
index: 3,
|
||||
text: "code_<test>_code"
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+33
-10
@@ -1,14 +1,37 @@
|
||||
var path = require('path');
|
||||
var assert = require('assert');
|
||||
|
||||
var Book = require('../').Book;
|
||||
|
||||
describe('Configuration parsing', function () {
|
||||
it('should correctly load from json', function() {
|
||||
assert(books[0].options.title == "Test");
|
||||
describe("Configuration", function () {
|
||||
it("should extract default title from README", function() {
|
||||
return books.parse("basic")
|
||||
.then(function(book) {
|
||||
book.options.title.should.be.equal("Readme");
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly load from javascript', function() {
|
||||
assert(books[4].options.title == "Test 2");
|
||||
it("should extract default description from README", function() {
|
||||
return books.parse("basic")
|
||||
.then(function(book) {
|
||||
book.options.description.should.be.equal("Default description for the book.");
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly load from json (book.json)", function() {
|
||||
return books.parse("config-json")
|
||||
.then(function(book) {
|
||||
book.options.title.should.be.equal("json-config");
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly load from JavaScript (book.js)", function() {
|
||||
return books.parse("config-js")
|
||||
.then(function(book) {
|
||||
book.options.title.should.be.equal("js-config");
|
||||
});
|
||||
});
|
||||
|
||||
it("should provide configuration on book.config.get", function() {
|
||||
return books.parse("basic")
|
||||
.then(function(book) {
|
||||
book.config.get("description").should.be.equal("Default description for the book.");
|
||||
book.getConfig("description").should.be.equal("Default description for the book.");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
describe("ConRefs", function () {
|
||||
var book, readme;
|
||||
|
||||
before(function() {
|
||||
return books.generate("conrefs", "website")
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
|
||||
readme = fs.readFileSync(
|
||||
path.join(book.options.output, "index.html"),
|
||||
{ encoding: "utf-8" }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle local references", function() {
|
||||
readme.should.be.html({
|
||||
".page-inner p#t1": {
|
||||
count: 1,
|
||||
text: "Hello World",
|
||||
trim: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle local references with absolute paths", function() {
|
||||
readme.should.be.html({
|
||||
".page-inner p#t2": {
|
||||
count: 1,
|
||||
text: "Hello World",
|
||||
trim: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly include file from git reference", function() {
|
||||
readme.should.be.html({
|
||||
".page-inner p#t3": {
|
||||
count: 1,
|
||||
text: "Hello from git",
|
||||
trim: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly handle deep include in git reference", function() {
|
||||
readme.should.be.html({
|
||||
".page-inner p#t4": {
|
||||
count: 1,
|
||||
text: "First Hello. Hello from git",
|
||||
trim: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly handle absolute include in git reference", function() {
|
||||
readme.should.be.html({
|
||||
".page-inner p#t5": {
|
||||
count: 1,
|
||||
text: "First Hello. Hello from git",
|
||||
trim: true
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
+57
-27
@@ -1,36 +1,66 @@
|
||||
var path = require('path');
|
||||
var _ = require('lodash');
|
||||
var assert = require('assert');
|
||||
var cheerio = require('cheerio');
|
||||
|
||||
var fs = require("fs");
|
||||
var fsUtil = require("../lib/utils/fs");
|
||||
var path = require("path");
|
||||
|
||||
describe("eBook generator", function () {
|
||||
describe("Basic Book", function() {
|
||||
var book;
|
||||
|
||||
describe('eBook Generator', function () {
|
||||
it('should correctly generate ebook pages', function(done) {
|
||||
testGeneration(books[1], "ebook", function(output) {
|
||||
assert(fs.existsSync(path.join(output, "SUMMARY.html")));
|
||||
}, done);
|
||||
before(function() {
|
||||
return books.generate("basic", "ebook")
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly output a SUMMARY.html", function() {
|
||||
book.should.have.file("SUMMARY.html");
|
||||
});
|
||||
|
||||
it("should correctly copy assets", function() {
|
||||
book.should.have.file("gitbook");
|
||||
book.should.have.file("gitbook/style.css");
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly convert svg images to png', function(done) {
|
||||
testGeneration(books[4], "ebook", function(output) {
|
||||
// Check that all images exists
|
||||
_.each([
|
||||
"index.html",
|
||||
"sub/PAGE.html"
|
||||
], function(pageName) {
|
||||
var pageFile = path.join(output, pageName);
|
||||
var pageFolder = path.dirname(pageFile);
|
||||
var pageContent = fs.readFileSync(pageFile, {encoding: "utf8"});
|
||||
var $ = cheerio.load(pageContent);
|
||||
describe("Custom styles", function() {
|
||||
var book;
|
||||
|
||||
$("img").each(function() {
|
||||
var src = $(this).attr("src");
|
||||
assert(fs.existsSync(path.resolve(pageFolder, src)), src+" not found for page "+pageName);
|
||||
})
|
||||
before(function() {
|
||||
return books.generate("style-print", "ebook")
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
});
|
||||
});
|
||||
|
||||
it("should remove default print.css", function() {
|
||||
var PAGE = fs.readFileSync(
|
||||
path.join(book.options.output, "index.html"),
|
||||
{ encoding: "utf-8" }
|
||||
);
|
||||
|
||||
// There are 2 styles (one from plugin-highlight and the new style)
|
||||
PAGE.should.be.html({
|
||||
"link": {
|
||||
count: 2
|
||||
}
|
||||
});
|
||||
}, done);
|
||||
|
||||
PAGE.should.be.html({
|
||||
"link[href=\"./styles/print.css\"]": {
|
||||
count: 1
|
||||
}
|
||||
});
|
||||
|
||||
PAGE.should.be.html({
|
||||
"link[href=\"gitbook/plugins/gitbook-plugin-highlight/ebook.css\"]": {
|
||||
count: 1
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should correctly print.css", function() {
|
||||
book.should.have.file("styles");
|
||||
book.should.have.file("styles/print.css");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
# Glossary
|
||||
|
||||
## Description
|
||||
|
||||
This is the dexcription of a description.
|
||||
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
# Other Title
|
||||
|
||||
This is a description, it's simple, no ?
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
# Summary
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"title": "Test"
|
||||
}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
body {
|
||||
background: red;
|
||||
}
|
||||
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
|
||||
# Test
|
||||
|
||||
a test text
|
||||
|
||||
# Test 2
|
||||
|
||||
a second test
|
||||
|
||||
Vendored
-4
@@ -1,4 +0,0 @@
|
||||
# Test
|
||||
|
||||
This file is not parsed by gitbook because the structure is defined in book.json.
|
||||
|
||||
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
# Summary
|
||||
|
||||
* [Chapter 1](test.md)
|
||||
* [Article 1](sub/test1.md)
|
||||
* [Chapter 2](test2.md)
|
||||
* [Google](https://www.google.com)
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"structure": {
|
||||
"readme": "intro.md"
|
||||
}
|
||||
}
|
||||
Vendored
-4
@@ -1,4 +0,0 @@
|
||||
# My Book
|
||||
|
||||
Test description
|
||||
|
||||
Vendored
-8
@@ -1,8 +0,0 @@
|
||||
# This file is used to etst links transformations:
|
||||
|
||||
This is a relative link [test](../intro.md).
|
||||
|
||||

|
||||
|
||||
This is a glossary link.
|
||||
|
||||
Vendored
-4
@@ -1,4 +0,0 @@
|
||||
# Languages
|
||||
|
||||
* [English](en/)
|
||||
* [French](fr/)
|
||||
Vendored
-2
@@ -1,2 +0,0 @@
|
||||
# Multi-Languages test
|
||||
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
# English Book
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
# French Book
|
||||
Vendored
-4
@@ -1,4 +0,0 @@
|
||||
= My Book
|
||||
|
||||
Test description
|
||||
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
= Summary
|
||||
|
||||
. link:test.adoc[Chapter 1]
|
||||
.. link:test1.adoc[Article 1]
|
||||
. link:test2.adoc[Chapter 2]
|
||||
Vendored
Vendored
Vendored
Vendored
-11
@@ -1,11 +0,0 @@
|
||||
# Other Title
|
||||
|
||||
A description
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
# Test with youtube videos that have the same filename:
|
||||

|
||||

|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
# Summary
|
||||
|
||||
* [Page](sub/PAGE.md)
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
title: "Test 2"
|
||||
};
|
||||
Vendored
-18
@@ -1,18 +0,0 @@
|
||||
##
|
||||
|
||||
## Image from root page
|
||||
|
||||

|
||||

|
||||
|
||||
## Inline svg
|
||||
|
||||
{% html %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M97.008198003228,6.103238498249268A97.2,97.2 0 0,1 87.9491894996971,41.385747140125076L54.289623147961166,25.546757493904366A60,60 0 0,0 59.88160370569629,3.7674311717588074Z"></path>
|
||||
</svg>
|
||||
{% endhtml %}
|
||||
|
||||
## Remote image
|
||||
|
||||

|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user