mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-16 07:35:16 +00:00
Compare commits
179 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 315556310f | |||
| 9c0b26304b | |||
| acdaea9f6c | |||
| e481deb5bb | |||
| 6d3ac6074c | |||
| 5e611bde48 | |||
| 9448562156 | |||
| e7e8fe4d08 | |||
| 8320484b88 | |||
| 0e243e6f23 | |||
| 5cd25b0d21 | |||
| 3218ee7455 | |||
| 0552007b8a | |||
| 50bc89d9d9 | |||
| 743a88f53a | |||
| ee92c6dd99 | |||
| b6c54c17d1 | |||
| 992ef1ab74 | |||
| 960848ba34 | |||
| d9aca7a9df | |||
| 05a0de2d86 | |||
| 19e4f498cf | |||
| 3055139297 | |||
| 3d6b099dea | |||
| dd970e33b8 | |||
| 59ba479465 | |||
| 54c9ecf3c2 | |||
| 8308ae69a2 | |||
| bd6c3287ed | |||
| 24fc36deb1 | |||
| 2f44b61cec | |||
| 45ee503db1 | |||
| dc826da99b | |||
| 3ea659d582 | |||
| 34fc2831e0 | |||
| 1bf68a5aa0 | |||
| d72c397f23 | |||
| 2c69525148 | |||
| ee3a0ba801 | |||
| 7a4179a2a2 | |||
| 1d0083c5f4 | |||
| 33a6111a30 | |||
| a5d7a87e31 | |||
| c7f7c5b5ab | |||
| 55a7b99b4d | |||
| 8bfba604b3 | |||
| 2a5fa1c5fb | |||
| a3e95feb26 | |||
| 145b00f590 | |||
| 5f2785f0a8 | |||
| 412529c548 | |||
| 547ec6ade8 | |||
| eac513cfa3 | |||
| 5ec8a1f18c | |||
| c923a7e01a | |||
| 366e6ed105 | |||
| 52078eb666 | |||
| d9ddbff443 | |||
| 01b160f9a1 | |||
| 23fcb817a4 | |||
| f531a3f984 | |||
| c777023e9f | |||
| 62c99759c1 | |||
| 6951c21d19 | |||
| 3a8289483e | |||
| 4079de9587 | |||
| b86d171082 | |||
| 959b272e65 | |||
| 684440e2d1 | |||
| 4740cd3b06 | |||
| b3131d99d2 | |||
| b0fd2a67ea | |||
| 7c83869869 | |||
| 57d5c23eaf | |||
| 8d92b74e61 | |||
| 4e2cda99cc | |||
| 12a7a4c4cf | |||
| e1594cde2c |
@@ -0,0 +1,10 @@
|
||||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
+12
@@ -28,6 +28,10 @@ Translators
|
||||
- Andrey Akinshin (@AndreyAkinshin)
|
||||
- Norwegian
|
||||
- Knut Melvær (@kmelve)
|
||||
- Persian (Farsi)
|
||||
- Mohammad Hossein Mojtahedi (@mhm5000)
|
||||
- Polish
|
||||
- Łukasz Szeremeta (@lszeremeta)
|
||||
- Portuguese
|
||||
- Ryan O'Mullan (@RyanOM)
|
||||
- Spanish
|
||||
@@ -38,3 +42,11 @@ Translators
|
||||
- Hu Hao (@howiehu)
|
||||
- French
|
||||
- Samy Pessé (@SamyPesse)
|
||||
- Romanian
|
||||
- Iancu Aurel (@awrelll)
|
||||
- Finnish
|
||||
- Tommi Savikko (@savikko)
|
||||
- Japanese
|
||||
- Iancu Aurel (@awrelll)
|
||||
- Korean
|
||||
- Iancu Aurel (@awrelll)
|
||||
|
||||
+80
@@ -1,4 +1,84 @@
|
||||
# Release notes
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 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
|
||||
- Fix links normalization (content and hash)
|
||||
|
||||
## 2.0.0-beta.3
|
||||
- Fix odd cases with code blocks escaping
|
||||
|
||||
## 2.0.0-beta.2
|
||||
- Fix definition of entry point title using SUMMARY.md
|
||||
|
||||
## 2.0.0-beta.1
|
||||
- Fix windows incompatibility
|
||||
- Add support for rtl (enabled by default for `ar` and `fa`)
|
||||
- Escape code blocks in markdown parser
|
||||
- Add Persian/Farsi translation (`fa`)
|
||||
- Add Arabic translation (`ar`)
|
||||
- Add Bengali translation (`bn`)
|
||||
- Provide generator name in template context
|
||||
|
||||
## 2.0.0-alpha.9
|
||||
- Fix links in sidebar
|
||||
- Fix normalization of html link (README to index)
|
||||
- Fix html snippets escaping
|
||||
|
||||
## 2.0.0-alpha.8
|
||||
- Improve locale detection for i18n
|
||||
- Fix chapter name for Glossary in pdf
|
||||
- Don't escape html in glossary items
|
||||
- Fix generation of multilingual book as ebook
|
||||
- Add "post" block attribute to post-process
|
||||
|
||||
## 2.0.0-alpha.7
|
||||
- Fix display of glossary in ebook formats
|
||||
- Add default footer and header to pdf
|
||||
- Fix generation of json format compatible with 1.x.x
|
||||
- Add Simplifiled Chinese and Traditional Chinese translations
|
||||
|
||||
## 2.0.0-alpha.6
|
||||
- Add es and pt translations
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
GitBook
|
||||
=======
|
||||
|
||||
:warning: This branch contains the version 2.0.
|
||||
|
||||
[](https://travis-ci.org/GitbookIO/gitbook)
|
||||
[](http://badge.fury.io/js/gitbook)
|
||||
[](https://travis-ci.org/GitbookIO/gitbook)
|
||||
[](https://ci.appveyor.com/project/SamyPesse/gitbook)
|
||||
|
||||
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 follow [@GitBookIO](https://twitter.com/GitBookIO) on Twitter. Complete documentation is available at [help.gitbook.io](http://help.gitbook.io/).
|
||||
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).
|
||||
|
||||
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/).
|
||||
|
||||

|
||||
|
||||
@@ -27,25 +32,25 @@ $ 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
|
||||
|
||||
* [Output as a website or ebook (pdf, epub, mobi)](#output-formats)
|
||||
* [Multi-Languages](#multi-languages)
|
||||
* [Glossary](#glossary)
|
||||
* [Cover](#cover)
|
||||
* [AsciiDoc Support](#asciidoc)
|
||||
* [Variables and Templating](#variables-and-templating)
|
||||
* [Content References](#content-references)
|
||||
* [Plugins](#plugins)
|
||||
* [Output as a website or ebook (pdf, epub, mobi)](http://help.gitbook.com/format/output.html)
|
||||
* [Multi-Languages](http://help.gitbook.com/format/languages.html)
|
||||
* [Glossary](http://help.gitbook.com/format/glossary.html)
|
||||
* [Cover](http://help.gitbook.com/format/cover.html)
|
||||
* [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)
|
||||
* Interractive reader website:
|
||||
* Search
|
||||
* Font Settings (Serif, Sans Serif)
|
||||
@@ -199,7 +204,7 @@ The platform [GitBook.com](https://www.gitbook.com/) is like an "Heroku for book
|
||||
|
||||
Plugins can be used to extend your book's functionality. Read [GitbookIO/plugin](https://github.com/GitbookIO/plugin) for more information about how to build a plugin for GitBook.
|
||||
|
||||
Plugins needed to build a book can be installed using: `gitbook install ./`. You can find plugins at [plugins.gitbook.com](plugins.gitbook.com).
|
||||
Plugins needed to build a book can be installed using: `gitbook install ./`. You can find plugins at [plugins.gitbook.com](http://plugins.gitbook.com).
|
||||
|
||||
|
||||
## Debugging
|
||||
@@ -209,3 +214,16 @@ You can use the options `--log=debug` and `--debug` to get better error messages
|
||||
```
|
||||
$ gitbook build ./ -- log=debug --debug
|
||||
```
|
||||
|
||||
#### How to use the latest commit from GitBook in gitbook-cli
|
||||
|
||||
To use the latest commit from `GitBook/gitbook` with `gitbook-cli`:
|
||||
|
||||
```
|
||||
$ git clone https://github.com/GitbookIO/gitbook.git ./gitbook
|
||||
$ gitbook versions:link 2.0.0 ./gitbook
|
||||
```
|
||||
|
||||
Now the version tag `2.0.0` wil be associated with the `./gitbook` folder.
|
||||
|
||||
You can uninstall it using: `gitbook versions:uninstall 2.0.0`.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Fix line endings in Windows. (runs before repo cloning)
|
||||
init:
|
||||
- git config --global core.autocrlf input
|
||||
|
||||
# Test against these versions of Node.js.
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "0.10"
|
||||
- nodejs_version: "0.11"
|
||||
|
||||
# Install scripts. (runs after repo cloning)
|
||||
install:
|
||||
# Get the latest stable version of Node.js or io.js
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
# install svgexport
|
||||
- npm install svgexport -g
|
||||
# install modules
|
||||
- npm install
|
||||
|
||||
# Post-install test scripts.
|
||||
test_script:
|
||||
# Output useful info for debugging.
|
||||
- node --version
|
||||
- npm --version
|
||||
# run tests
|
||||
- npm test
|
||||
|
||||
# Don't actually build.
|
||||
build: off
|
||||
+1
-1
@@ -3,5 +3,5 @@
|
||||
var color = require('bash-color');
|
||||
|
||||
console.log(color.red("You need to install 'gitbook-cli' to have access to the gitbook command anywhere on your system."));
|
||||
console.log(color.red("If you've installe this package globally, you need to uninstall it."));
|
||||
console.log(color.red("If you've installed this package globally, you need to uninstall it."));
|
||||
console.log(color.red(">> Run 'npm uninstall -g gitbook' then 'npm install -g gitbook-cli'"));
|
||||
|
||||
+420
-334
File diff suppressed because it is too large
Load Diff
+45
-14
@@ -60,14 +60,13 @@ function normalizePluginsNames(plugins) {
|
||||
return _.pluck(normalizePluginsList(plugins), "name");
|
||||
};
|
||||
|
||||
|
||||
var Configuration = function(book, options) {
|
||||
var that = this;
|
||||
|
||||
this.book = book;
|
||||
this.book = book;
|
||||
|
||||
this.options = _.cloneDeep(Configuration.DEFAULT);
|
||||
this.options = _.merge(this.options, options || {});
|
||||
this.options = _.merge(this.options, options || {});
|
||||
|
||||
// options.input == book.root
|
||||
Object.defineProperty(this.options, "input", {
|
||||
@@ -75,19 +74,42 @@ 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
|
||||
Configuration.prototype.load = function() {
|
||||
var that = this;
|
||||
var that = this;
|
||||
|
||||
return Q()
|
||||
return Q()
|
||||
.then(function() {
|
||||
var configPath, config;
|
||||
|
||||
try {
|
||||
var _config = require(path.resolve(that.book.root, that.options.configFile));
|
||||
configPath = require.resolve(
|
||||
path.resolve(that.book.root, 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')
|
||||
that.options,
|
||||
_.omit(_config, 'configFile', 'defaultsPlugins', 'generator', 'extension')
|
||||
);
|
||||
}
|
||||
catch(err) {
|
||||
@@ -96,17 +118,23 @@ 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"));
|
||||
}
|
||||
|
||||
that.options.output = path.resolve(that.options.output || path.join(that.book.root, "_book"));
|
||||
that.options.plugins = normalizePluginsList(that.options.plugins);
|
||||
that.options.defaultsPlugins = normalizePluginsList(that.options.defaultsPlugins || "");
|
||||
that.options.plugins = _.union(that.options.plugins, that.options.defaultsPlugins);
|
||||
that.options.plugins = _.uniq(that.options.plugins, "name");
|
||||
|
||||
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"));
|
||||
// Default value for text direction (from language)
|
||||
if (!that.options.direction) {
|
||||
var lang = i18n.getCatalog(that.options.language);
|
||||
if (lang) that.options.direction = lang.direction;
|
||||
}
|
||||
|
||||
that.options.gitbook = pkg.version;
|
||||
@@ -133,7 +161,7 @@ Configuration.prototype.normalizeLanguage = function() {
|
||||
Configuration.DEFAULT = {
|
||||
// Options that can't be extend
|
||||
"configFile": "book",
|
||||
"generator": "site",
|
||||
"generator": "website",
|
||||
"extension": null,
|
||||
|
||||
// Book metadats (somes are extracted from the README by default)
|
||||
@@ -141,6 +169,8 @@ Configuration.DEFAULT = {
|
||||
"description": null,
|
||||
"isbn": null,
|
||||
"language": "en",
|
||||
"direction": null,
|
||||
"author": null,
|
||||
|
||||
// version of gitbook to use
|
||||
"gitbook": "*",
|
||||
@@ -156,6 +186,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",
|
||||
|
||||
+13
-6
@@ -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;
|
||||
@@ -30,7 +30,7 @@ Generator.prototype.prepareTemplates = function() {
|
||||
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();
|
||||
return Q();
|
||||
};
|
||||
|
||||
// Generate table of contents
|
||||
@@ -44,10 +44,11 @@ Generator.prototype.writeSummary = function() {
|
||||
Generator.prototype.finish = function() {
|
||||
var that = this;
|
||||
|
||||
return BaseGenerator.prototype.finish.apply(this)
|
||||
.then(function() {
|
||||
return that.writeSummary();
|
||||
})
|
||||
return Q()
|
||||
.then(this.copyAssets)
|
||||
.then(this.copyCover)
|
||||
.then(this.writeGlossary)
|
||||
.then(this.writeSummary)
|
||||
.then(function() {
|
||||
if (!that.ebookFormat) return Q();
|
||||
|
||||
@@ -63,6 +64,8 @@ Generator.prototype.finish = function() {
|
||||
"--comments": that.options.description,
|
||||
"--isbn": that.options.isbn,
|
||||
"--authors": that.options.author,
|
||||
"--language": that.options.language,
|
||||
"--book-producer": "GitBook",
|
||||
"--publisher": "GitBook",
|
||||
"--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]",
|
||||
"--chapter-mark": "pagebreak",
|
||||
@@ -90,6 +93,10 @@ Generator.prototype.finish = function() {
|
||||
"--pdf-header-template": String(pdfOptions.headerTemplate) || "<p class='header'><span>"+that.options.title+"</span></p>",
|
||||
"--pdf-footer-template": String(pdfOptions.footerTemplate) || "<p class='footer'><span>_SECTION_</span> <span style='float:right;'>_PAGENUM_</span></p>"
|
||||
});
|
||||
} else if (that.ebookFormat == "epub") {
|
||||
_.extend(_options, {
|
||||
"--dont-split-on-page-breaks": true
|
||||
});
|
||||
}
|
||||
|
||||
var command = [
|
||||
|
||||
@@ -2,10 +2,10 @@ var _ = require("lodash");
|
||||
var EbookGenerator = require("./ebook");
|
||||
|
||||
module.exports = {
|
||||
json: require("./json"),
|
||||
website: require("./website"),
|
||||
ebook: EbookGenerator,
|
||||
pdf: _.partialRight(EbookGenerator, "pdf"),
|
||||
mobi: _.partialRight(EbookGenerator, "mobi"),
|
||||
epub: _.partialRight(EbookGenerator, "epub")
|
||||
json: require("./json"),
|
||||
website: require("./website"),
|
||||
ebook: EbookGenerator,
|
||||
pdf: _.partialRight(EbookGenerator, "pdf"),
|
||||
mobi: _.partialRight(EbookGenerator, "mobi"),
|
||||
epub: _.partialRight(EbookGenerator, "epub")
|
||||
};
|
||||
|
||||
+29
-32
@@ -4,9 +4,8 @@ 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");
|
||||
@@ -51,12 +50,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(path.resolve(that.book.root, stylePath))) {
|
||||
return [style, stylePath];
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.compact()
|
||||
.object()
|
||||
.value();
|
||||
|
||||
return Q();
|
||||
@@ -64,11 +64,11 @@ 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["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();
|
||||
return Q();
|
||||
};
|
||||
|
||||
// Prepare template engine
|
||||
@@ -79,8 +79,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)
|
||||
@@ -105,11 +105,6 @@ Generator.prototype.prepareTemplateEngine = function() {
|
||||
// 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"
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -136,13 +131,13 @@ 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, '/');
|
||||
|
||||
that.book.log.info.ln("write parsed file", page.path, "to", relativeOutput);
|
||||
that.book.log.debug.ln("write parsed file", page.path, "to", relativeOutput);
|
||||
|
||||
return that._writeTemplate(that.templates["page"], {
|
||||
progress: page.progress,
|
||||
@@ -213,38 +208,40 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
|
||||
interpolate = interpolate || _.identity;
|
||||
return Q()
|
||||
.then(function(sections) {
|
||||
return that.env.render(
|
||||
tpl,
|
||||
_.extend({
|
||||
return that.env.render(
|
||||
tpl,
|
||||
_.extend({
|
||||
gitbook: {
|
||||
version: pkg.version
|
||||
},
|
||||
|
||||
styles: that.styles,
|
||||
styles: that.styles,
|
||||
|
||||
revision: that.revision,
|
||||
revision: that.revision,
|
||||
|
||||
title: that.options.title,
|
||||
description: that.options.description,
|
||||
title: that.options.title,
|
||||
description: that.options.description,
|
||||
language: that.book.config.normalizeLanguage(),
|
||||
|
||||
glossary: that.book.glossary,
|
||||
glossary: that.book.glossary,
|
||||
|
||||
summary: that.book.summary,
|
||||
allNavigation: that.book.navigation,
|
||||
summary: that.book.summary,
|
||||
allNavigation: that.book.navigation,
|
||||
|
||||
plugins: {
|
||||
plugins: {
|
||||
resources: that.book.plugins.resources(that.namespace)
|
||||
},
|
||||
pluginsConfig: JSON.stringify(that.options.pluginsConfig),
|
||||
htmlSnippet: _.partial(_.partialRight(that.book.plugins.html, that, options), that.namespace),
|
||||
pluginsConfig: JSON.stringify(that.options.pluginsConfig),
|
||||
htmlSnippet: _.partial(_.partialRight(that.book.plugins.html, that, options), that.namespace),
|
||||
|
||||
options: that.options,
|
||||
options: that.options,
|
||||
|
||||
basePath: ".",
|
||||
staticBase: path.join(".", "gitbook"),
|
||||
}, options)
|
||||
);
|
||||
|
||||
'__': that.book.i18n.bind(that.book)
|
||||
}, options)
|
||||
);
|
||||
})
|
||||
.then(interpolate)
|
||||
.then(function(html) {
|
||||
|
||||
+170
-170
@@ -12,199 +12,199 @@ var watch = require("./utils/watch");
|
||||
var logger = require("./utils/logger");
|
||||
|
||||
var LOG_OPTION = {
|
||||
name: "log",
|
||||
description: "Minimum log level to display",
|
||||
values: _.chain(logger.LEVELS).keys().map(stringUtils.toLowerCase).value(),
|
||||
defaults: "info"
|
||||
name: "log",
|
||||
description: "Minimum log level to display",
|
||||
values: _.chain(logger.LEVELS).keys().map(stringUtils.toLowerCase).value(),
|
||||
defaults: "info"
|
||||
};
|
||||
|
||||
var FORMAT_OPTION = {
|
||||
name: "format",
|
||||
description: "Format to build to",
|
||||
values: ["website", "json", "ebook"],
|
||||
defaults: "website"
|
||||
name: "format",
|
||||
description: "Format to build to",
|
||||
values: ["website", "json", "ebook"],
|
||||
defaults: "website"
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Book: Book,
|
||||
LOG_LEVELS: logger.LEVELS,
|
||||
Book: Book,
|
||||
LOG_LEVELS: logger.LEVELS,
|
||||
|
||||
commands: _.flatten([
|
||||
{
|
||||
name: "build [book] [output]",
|
||||
description: "build a book",
|
||||
options: [
|
||||
FORMAT_OPTION,
|
||||
LOG_OPTION
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
var input = args[0] || process.cwd();
|
||||
var output = args[1] || path.join(input, "_book");
|
||||
commands: _.flatten([
|
||||
{
|
||||
name: "build [book] [output]",
|
||||
description: "build a book",
|
||||
options: [
|
||||
FORMAT_OPTION,
|
||||
LOG_OPTION
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
var input = args[0] || process.cwd();
|
||||
var output = args[1] || path.join(input, "_book");
|
||||
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'config': {
|
||||
'output': output
|
||||
},
|
||||
'logLevel': kwargs.log
|
||||
}));
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'config': {
|
||||
'output': output
|
||||
},
|
||||
'logLevel': kwargs.log
|
||||
}));
|
||||
|
||||
return book.parse()
|
||||
.then(function() {
|
||||
return book.generate(kwargs.format);
|
||||
})
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
});
|
||||
}
|
||||
},
|
||||
return book.parse()
|
||||
.then(function() {
|
||||
return book.generate(kwargs.format);
|
||||
})
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_.map(["pdf", "epub", "mobi"], function(ebookType) {
|
||||
return {
|
||||
name: ebookType+" [book] [output]",
|
||||
description: "build a book to "+ebookType,
|
||||
options: [
|
||||
LOG_OPTION
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
var input = args[0] || process.cwd();
|
||||
var output = args[1];
|
||||
_.map(["pdf", "epub", "mobi"], function(ebookType) {
|
||||
return {
|
||||
name: ebookType+" [book] [output]",
|
||||
description: "build a book to "+ebookType,
|
||||
options: [
|
||||
LOG_OPTION
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
var input = args[0] || process.cwd();
|
||||
var output = args[1];
|
||||
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'logLevel': kwargs.log
|
||||
}));
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'logLevel': kwargs.log
|
||||
}));
|
||||
|
||||
return book.parse()
|
||||
.then(function() {
|
||||
return book.generateFile(output, {
|
||||
ebookFormat: ebookType
|
||||
});
|
||||
})
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
});
|
||||
}
|
||||
};
|
||||
}),
|
||||
return book.parse()
|
||||
.then(function() {
|
||||
return book.generateFile(output, {
|
||||
ebookFormat: ebookType
|
||||
});
|
||||
})
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
});
|
||||
}
|
||||
};
|
||||
}),
|
||||
|
||||
{
|
||||
name: "serve [book]",
|
||||
description: "Build then serve a gitbook from a directory",
|
||||
options: [
|
||||
{
|
||||
name: "port",
|
||||
description: "Port for server to listen on",
|
||||
defaults: 4000
|
||||
},
|
||||
{
|
||||
name: "lrport",
|
||||
description: "Port for livereload server to listen on",
|
||||
defaults: 35729
|
||||
},
|
||||
{
|
||||
name: "watch",
|
||||
description: "Enable/disable file watcher",
|
||||
defaults: true
|
||||
},
|
||||
FORMAT_OPTION,
|
||||
LOG_OPTION
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
var input = args[0] || process.cwd();
|
||||
var server = new Server();
|
||||
{
|
||||
name: "serve [book]",
|
||||
description: "Build then serve a gitbook from a directory",
|
||||
options: [
|
||||
{
|
||||
name: "port",
|
||||
description: "Port for server to listen on",
|
||||
defaults: 4000
|
||||
},
|
||||
{
|
||||
name: "lrport",
|
||||
description: "Port for livereload server to listen on",
|
||||
defaults: 35729
|
||||
},
|
||||
{
|
||||
name: "watch",
|
||||
description: "Enable/disable file watcher",
|
||||
defaults: true
|
||||
},
|
||||
FORMAT_OPTION,
|
||||
LOG_OPTION
|
||||
],
|
||||
exec: function(args, kwargs) {
|
||||
var input = args[0] || process.cwd();
|
||||
var server = new Server();
|
||||
|
||||
// Init livereload server
|
||||
var lrServer = tinylr({});
|
||||
var lrPath = undefined;
|
||||
// Init livereload server
|
||||
var lrServer = tinylr({});
|
||||
var lrPath = undefined;
|
||||
|
||||
var generate = function() {
|
||||
if (server.isRunning()) console.log("Stopping server");
|
||||
var generate = function() {
|
||||
if (server.isRunning()) console.log("Stopping server");
|
||||
|
||||
return server.stop()
|
||||
.then(function() {
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'config': {
|
||||
'defaultsPlugins': ["livereload"]
|
||||
},
|
||||
'logLevel': kwargs.log
|
||||
}));
|
||||
return server.stop()
|
||||
.then(function() {
|
||||
var book = new Book(input, _.extend({}, {
|
||||
'config': {
|
||||
'defaultsPlugins': ["livereload"]
|
||||
},
|
||||
'logLevel': kwargs.log
|
||||
}));
|
||||
|
||||
return book.parse()
|
||||
.then(function() {
|
||||
return book.generate(kwargs.format);
|
||||
})
|
||||
.thenResolve(book);
|
||||
})
|
||||
.then(function(book) {
|
||||
console.log();
|
||||
console.log('Starting server ...');
|
||||
return server.start(book.options.output, kwargs.port)
|
||||
.then(function() {
|
||||
console.log('Serving book on http://localhost:'+kwargs.port);
|
||||
return book.parse()
|
||||
.then(function() {
|
||||
return book.generate(kwargs.format);
|
||||
})
|
||||
.thenResolve(book);
|
||||
})
|
||||
.then(function(book) {
|
||||
console.log();
|
||||
console.log('Starting server ...');
|
||||
return server.start(book.options.output, kwargs.port)
|
||||
.then(function() {
|
||||
console.log('Serving book on http://localhost:'+kwargs.port);
|
||||
|
||||
if (lrPath) {
|
||||
// trigger livereload
|
||||
lrServer.changed({
|
||||
body: {
|
||||
files: [lrPath]
|
||||
}
|
||||
});
|
||||
}
|
||||
if (lrPath) {
|
||||
// trigger livereload
|
||||
lrServer.changed({
|
||||
body: {
|
||||
files: [lrPath]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!kwargs.watch) return;
|
||||
if (!kwargs.watch) return;
|
||||
|
||||
return watch(book.root)
|
||||
.then(function(filepath) {
|
||||
// set livereload path
|
||||
lrPath = filepath;
|
||||
console.log("Restart after change in files");
|
||||
console.log('');
|
||||
return generate();
|
||||
})
|
||||
})
|
||||
});
|
||||
};
|
||||
return watch(book.root)
|
||||
.then(function(filepath) {
|
||||
// set livereload path
|
||||
lrPath = filepath;
|
||||
console.log("Restart after change in files");
|
||||
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('')
|
||||
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('')
|
||||
return generate();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: "install [book]",
|
||||
description: "install plugins dependencies",
|
||||
exec: function(args, kwargs) {
|
||||
var input = args[0] || process.cwd();
|
||||
{
|
||||
name: "install [book]",
|
||||
description: "install plugins dependencies",
|
||||
exec: function(args, kwargs) {
|
||||
var input = args[0] || process.cwd();
|
||||
|
||||
var book = new Book(input);
|
||||
var book = new Book(input);
|
||||
|
||||
return book.config.load()
|
||||
.then(function() {
|
||||
return book.plugins.install();
|
||||
})
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
});
|
||||
}
|
||||
},
|
||||
return book.config.load()
|
||||
.then(function() {
|
||||
return book.plugins.install();
|
||||
})
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: "init [directory]",
|
||||
description: "create files and folders based on contents of SUMMARY.md",
|
||||
exec: function(args, kwargs) {
|
||||
return Book.init(args[0] || process.cwd())
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
});
|
||||
}
|
||||
}
|
||||
])
|
||||
{
|
||||
name: "init [directory]",
|
||||
description: "create files and folders based on contents of SUMMARY.md",
|
||||
exec: function(args, kwargs) {
|
||||
return Book.init(args[0] || process.cwd())
|
||||
.then(function(){
|
||||
console.log("");
|
||||
console.log(color.green("Done, without error"));
|
||||
});
|
||||
}
|
||||
}
|
||||
])
|
||||
};
|
||||
|
||||
+1
-1
@@ -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() {
|
||||
|
||||
+114
-114
@@ -8,113 +8,113 @@ 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' });
|
||||
});
|
||||
|
||||
|
||||
var PluginsList = function(book, plugins) {
|
||||
this.book = book;
|
||||
this.log = this.book.log;
|
||||
this.book = book;
|
||||
this.log = this.book.log;
|
||||
|
||||
// List of Plugin objects
|
||||
this.list = [];
|
||||
// List of Plugin objects
|
||||
this.list = [];
|
||||
|
||||
// List of names of failed plugins
|
||||
this.failed = [];
|
||||
// List of names of failed plugins
|
||||
this.failed = [];
|
||||
|
||||
// Namespaces
|
||||
this.namespaces = _.chain(["website", "ebook"])
|
||||
.map(function(namespace) {
|
||||
return [
|
||||
namespace,
|
||||
{
|
||||
html: {},
|
||||
resources: _.chain(Plugin.RESOURCES)
|
||||
.map(function(type) {
|
||||
return [type, []];
|
||||
})
|
||||
.object()
|
||||
.value()
|
||||
}
|
||||
];
|
||||
})
|
||||
.object()
|
||||
.value();
|
||||
// Namespaces
|
||||
this.namespaces = _.chain(["website", "ebook"])
|
||||
.map(function(namespace) {
|
||||
return [
|
||||
namespace,
|
||||
{
|
||||
html: {},
|
||||
resources: _.chain(Plugin.RESOURCES)
|
||||
.map(function(type) {
|
||||
return [type, []];
|
||||
})
|
||||
.object()
|
||||
.value()
|
||||
}
|
||||
];
|
||||
})
|
||||
.object()
|
||||
.value();
|
||||
|
||||
// Bind methods
|
||||
_.bindAll(this);
|
||||
// Bind methods
|
||||
_.bindAll(this);
|
||||
|
||||
if (plugins) this.load(plugins);
|
||||
if (plugins) this.load(plugins);
|
||||
};
|
||||
|
||||
// return count of plugins
|
||||
PluginsList.prototype.count = function() {
|
||||
return this.list.length;
|
||||
return this.list.length;
|
||||
};
|
||||
|
||||
// Add and load a plugin
|
||||
PluginsList.prototype.load = function(plugin, options) {
|
||||
var that = this;
|
||||
var that = this;
|
||||
|
||||
if (_.isArray(plugin)) {
|
||||
return _.reduce(plugin, function(prev, p) {
|
||||
return prev.then(function() {
|
||||
return that.load(p);
|
||||
});
|
||||
}, Q());
|
||||
}
|
||||
if (_.isObject(plugin) && !(plugin instanceof Plugin)) plugin = plugin.name;
|
||||
if (_.isString(plugin)) plugin = new Plugin(this.book, plugin);
|
||||
if (_.isArray(plugin)) {
|
||||
return _.reduce(plugin, function(prev, p) {
|
||||
return prev.then(function() {
|
||||
return that.load(p);
|
||||
});
|
||||
}, Q());
|
||||
}
|
||||
if (_.isObject(plugin) && !(plugin instanceof Plugin)) plugin = plugin.name;
|
||||
if (_.isString(plugin)) plugin = new Plugin(this.book, plugin);
|
||||
|
||||
that.log.info("load plugin", plugin.name, "....");
|
||||
if (!plugin.isValid()) {
|
||||
that.log.info.fail();
|
||||
that.failed.push(plugin.name);
|
||||
return Q();
|
||||
that.log.info("load plugin", plugin.name, "....");
|
||||
if (!plugin.isValid()) {
|
||||
that.log.info.fail();
|
||||
that.failed.push(plugin.name);
|
||||
return Q();
|
||||
} else {
|
||||
that.log.info.ok();
|
||||
that.log.info.ok();
|
||||
|
||||
// Push in the list
|
||||
that.list.push(plugin);
|
||||
// Push in the list
|
||||
that.list.push(plugin);
|
||||
}
|
||||
|
||||
// Extract filters
|
||||
_.each(plugin.getFilters(), function(filterFunc, filterName) {
|
||||
// Extract filters
|
||||
_.each(plugin.getFilters(), function(filterFunc, filterName) {
|
||||
that.book.template.addFilter(filterName, filterFunc);
|
||||
});
|
||||
|
||||
// Extract blocks
|
||||
_.each(plugin.getBlocks(), function(block, blockName) {
|
||||
_.each(plugin.getBlocks(), function(block, blockName) {
|
||||
that.book.template.addBlock(blockName, block);
|
||||
});
|
||||
|
||||
return _.reduce(_.keys(that.namespaces), function(prev, namespaceName) {
|
||||
return prev.then(function() {
|
||||
return plugin.getResources(namespaceName)
|
||||
.then(function(plResources) {
|
||||
var namespace = that.namespaces[namespaceName];
|
||||
return _.reduce(_.keys(that.namespaces), function(prev, namespaceName) {
|
||||
return prev.then(function() {
|
||||
return plugin.getResources(namespaceName)
|
||||
.then(function(plResources) {
|
||||
var namespace = that.namespaces[namespaceName];
|
||||
|
||||
// Extract js and css
|
||||
_.each(Plugin.RESOURCES, function(resourceType) {
|
||||
namespace.resources[resourceType] = (namespace.resources[resourceType] || []).concat(plResources[resourceType] || []);
|
||||
});
|
||||
// Extract js and css
|
||||
_.each(Plugin.RESOURCES, function(resourceType) {
|
||||
namespace.resources[resourceType] = (namespace.resources[resourceType] || []).concat(plResources[resourceType] || []);
|
||||
});
|
||||
|
||||
// Map of html resources by name added by each plugin
|
||||
_.each(plResources.html || {}, function(value, tag) {
|
||||
// Turn into function if not one already
|
||||
if (!_.isFunction(value)) value = _.constant(value);
|
||||
// Map of html resources by name added by each plugin
|
||||
_.each(plResources.html || {}, function(value, tag) {
|
||||
// Turn into function if not one already
|
||||
if (!_.isFunction(value)) value = _.constant(value);
|
||||
|
||||
namespace.html[tag] = namespace.html[tag] || [];
|
||||
namespace.html[tag].push(value);
|
||||
});
|
||||
})
|
||||
});
|
||||
}, Q());
|
||||
namespace.html[tag] = namespace.html[tag] || [];
|
||||
namespace.html[tag].push(value);
|
||||
});
|
||||
})
|
||||
});
|
||||
}, Q());
|
||||
};
|
||||
|
||||
// Call a hook
|
||||
PluginsList.prototype.hook = function(name, data) {
|
||||
return _.reduce(this.list, function(prev, plugin) {
|
||||
return _.reduce(this.list, function(prev, plugin) {
|
||||
return prev.then(function(ret) {
|
||||
return plugin.callHook(name, ret);
|
||||
});
|
||||
@@ -123,7 +123,7 @@ PluginsList.prototype.hook = function(name, data) {
|
||||
|
||||
// Return a template from a plugin
|
||||
PluginsList.prototype.template = function(name) {
|
||||
var withTpl = _.find(this.list, function(plugin) {
|
||||
var withTpl = _.find(this.list, function(plugin) {
|
||||
return (
|
||||
plugin.infos.templates &&
|
||||
plugin.infos.templates[name]
|
||||
@@ -136,7 +136,7 @@ PluginsList.prototype.template = function(name) {
|
||||
|
||||
// Return an html snippet
|
||||
PluginsList.prototype.html = function(namespace, tag, context, options) {
|
||||
var htmlSnippets = this.namespaces[namespace].html[tag];
|
||||
var htmlSnippets = this.namespaces[namespace].html[tag];
|
||||
return _.map(htmlSnippets || [], function(code) {
|
||||
return code.call(context, options);
|
||||
}).join("\n");
|
||||
@@ -144,13 +144,13 @@ PluginsList.prototype.html = function(namespace, tag, context, options) {
|
||||
|
||||
// Return a resources map for a namespace
|
||||
PluginsList.prototype.resources = function(namespace) {
|
||||
return this.namespaces[namespace].resources;
|
||||
return this.namespaces[namespace].resources;
|
||||
};
|
||||
|
||||
// Install plugins from a book
|
||||
PluginsList.prototype.install = function() {
|
||||
var that = this;
|
||||
var defaultsPlugins = _.pluck(that.book.options.defaultsPlugins)
|
||||
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) {
|
||||
@@ -167,51 +167,51 @@ PluginsList.prototype.install = function() {
|
||||
|
||||
// Resolve version if needed
|
||||
.then(function() {
|
||||
if (plugin.version) return plugin.version;
|
||||
if (plugin.version) return plugin.version;
|
||||
|
||||
that.book.log.info.ln("No version specified, resolve plugin", plugin.name);
|
||||
return initNPM()
|
||||
.then(function() {
|
||||
return Q.nfcall(npm.commands.view, [fullname+"@*", "engines"], true);
|
||||
})
|
||||
.then(function(versions) {
|
||||
return _.chain(versions)
|
||||
.pairs()
|
||||
.map(function(v) {
|
||||
return {
|
||||
version: v[0],
|
||||
gitbook: (v[1].engines || {})["gitbook"]
|
||||
}
|
||||
})
|
||||
.filter(function(v) {
|
||||
return v.gitbook && semver.satisfies(pkg.version, v.gitbook);
|
||||
})
|
||||
.sort(function(v1, v2) {
|
||||
return semver.lt(v1.version, v2.version)? 1 : -1;
|
||||
})
|
||||
.pluck("version")
|
||||
.first()
|
||||
.value();
|
||||
});
|
||||
that.book.log.info.ln("No version specified, resolve plugin", plugin.name);
|
||||
return initNPM()
|
||||
.then(function() {
|
||||
return Q.nfcall(npm.commands.view, [fullname+"@*", "engines"], true);
|
||||
})
|
||||
.then(function(versions) {
|
||||
return _.chain(versions)
|
||||
.pairs()
|
||||
.map(function(v) {
|
||||
return {
|
||||
version: v[0],
|
||||
gitbook: (v[1].engines || {})["gitbook"]
|
||||
}
|
||||
})
|
||||
.filter(function(v) {
|
||||
return v.gitbook && semver.satisfies(pkg.version, v.gitbook);
|
||||
})
|
||||
.sort(function(v1, v2) {
|
||||
return semver.lt(v1.version, v2.version)? 1 : -1;
|
||||
})
|
||||
.pluck("version")
|
||||
.first()
|
||||
.value();
|
||||
});
|
||||
})
|
||||
|
||||
// Install the plugin with the resolved version
|
||||
.then(function(version) {
|
||||
if (!version) {
|
||||
throw "Found no satisfactory version for plugin "+plugin.name;
|
||||
}
|
||||
if (!version) {
|
||||
throw "Found no satisfactory version for plugin "+plugin.name;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
});
|
||||
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
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
that.book.log.info.ok("plugin", plugin.name, "installed with success");
|
||||
|
||||
+90
-60
@@ -2,40 +2,41 @@ 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");
|
||||
|
||||
|
||||
// The loader should handle relative and git url
|
||||
var BookLoader = nunjucks.Loader.extend({
|
||||
async: true,
|
||||
async: true,
|
||||
|
||||
init: function(book) {
|
||||
this.book = book;
|
||||
this.book = book;
|
||||
},
|
||||
|
||||
getSource: function(fileurl, callback) {
|
||||
var that = this;
|
||||
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)
|
||||
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);
|
||||
// Read file from absolute path
|
||||
return fs.readFile(filepath)
|
||||
.then(function(source) {
|
||||
return {
|
||||
src: source.toString(),
|
||||
path: filepath
|
||||
}
|
||||
});
|
||||
})
|
||||
.nodeify(callback);
|
||||
},
|
||||
|
||||
resolve: function(from, to) {
|
||||
@@ -45,27 +46,27 @@ var BookLoader = nunjucks.Loader.extend({
|
||||
|
||||
|
||||
var TemplateEngine = function(book) {
|
||||
this.book = book;
|
||||
this.book = book;
|
||||
this.log = this.book.log;
|
||||
|
||||
// Nunjucks env
|
||||
this.env = new nunjucks.Environment(
|
||||
new BookLoader(book),
|
||||
{
|
||||
// Escaping is done after by the markdown parser
|
||||
autoescape: false,
|
||||
// Nunjucks env
|
||||
this.env = new nunjucks.Environment(
|
||||
new BookLoader(book),
|
||||
{
|
||||
// Escaping is done after by the markdown parser
|
||||
autoescape: false,
|
||||
|
||||
// Tags
|
||||
tags: {
|
||||
blockStart: '{%',
|
||||
blockEnd: '%}',
|
||||
variableStart: '{{',
|
||||
variableEnd: '}}',
|
||||
commentStart: '{###',
|
||||
commentEnd: '###}'
|
||||
}
|
||||
}
|
||||
);
|
||||
// Tags
|
||||
tags: {
|
||||
blockStart: '{%',
|
||||
blockEnd: '%}',
|
||||
variableStart: '{{',
|
||||
variableEnd: '}}',
|
||||
commentStart: '{###',
|
||||
commentEnd: '###}'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// List of tags shortcuts
|
||||
this.shortcuts = [];
|
||||
@@ -87,18 +88,21 @@ TemplateEngine.prototype.processBlock = function(blk) {
|
||||
if (_.isString(blk)) blk = { body: blk };
|
||||
|
||||
blk = _.defaults(blk, {
|
||||
parse: false
|
||||
parse: false,
|
||||
post: undefined
|
||||
});
|
||||
blk.id = _.uniqueId("blk");
|
||||
|
||||
var toAdd = (!blk.parse) || (blk.post != undefined);
|
||||
|
||||
// Add to global map
|
||||
if (toAdd) this.blocks[blk.id] = blk;
|
||||
|
||||
//Parsable block, just return it
|
||||
if (blk.parse) {
|
||||
return blk.body;
|
||||
}
|
||||
|
||||
// Add to global map
|
||||
this.blocks[blk.id] = blk;
|
||||
|
||||
// Return it as a macro
|
||||
return "%+%"+blk.id+"%+%";
|
||||
};
|
||||
@@ -114,9 +118,6 @@ TemplateEngine.prototype.replaceBlocks = function(content) {
|
||||
|
||||
var body = blk.body;
|
||||
|
||||
// Remove it from map
|
||||
delete that.blocks[key];
|
||||
|
||||
return body;
|
||||
});
|
||||
};
|
||||
@@ -239,21 +240,25 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
var callback = args.pop();
|
||||
|
||||
// Extract blocks body
|
||||
var _blocks = _.chain(block.blocks)
|
||||
.reverse()
|
||||
.map(function(blockName){
|
||||
return {
|
||||
name: blockName,
|
||||
body: args.pop()()
|
||||
};
|
||||
})
|
||||
.reverse()
|
||||
.value();
|
||||
// Extract blocks
|
||||
var blocks = args
|
||||
.concat([])
|
||||
.slice(-block.blocks.length);
|
||||
|
||||
// Eliminate blocks from list
|
||||
if (block.blocks.length > 0) args = args.slice(0, -block.blocks.length);
|
||||
|
||||
// Extract main body and kwargs
|
||||
var body = args.pop();
|
||||
var kwargs = args.pop() || {};
|
||||
|
||||
// Extract blocks body
|
||||
var _blocks = _.map(block.blocks, function(blockName, i){
|
||||
return {
|
||||
name: blockName,
|
||||
body: blocks[i]()
|
||||
};
|
||||
});
|
||||
|
||||
var func = that.bindContext(block.process);
|
||||
|
||||
@@ -297,7 +302,7 @@ TemplateEngine.prototype.addBlock = function(name, block) {
|
||||
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),
|
||||
escapeStringRegexp(shortcut.start) + "([\\s\\S]*?[^\\$])" + escapeStringRegexp(shortcut.end),
|
||||
'g'
|
||||
);
|
||||
return content.replace(regex, function(all, match) {
|
||||
@@ -313,7 +318,8 @@ TemplateEngine.prototype.renderString = function(content, context, options) {
|
||||
|
||||
// infos about gitbook
|
||||
gitbook: {
|
||||
version: pkg.version
|
||||
version: pkg.version,
|
||||
generator: this.book.options.generator
|
||||
}
|
||||
});
|
||||
options = _.defaults(options || {}, {
|
||||
@@ -325,12 +331,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;
|
||||
var that = this, context;
|
||||
|
||||
return that.book.readFile(filename)
|
||||
.then(function(content) {
|
||||
@@ -363,8 +375,26 @@ TemplateEngine.prototype.renderPage = function(page) {
|
||||
|
||||
// Post process content
|
||||
TemplateEngine.prototype.postProcess = function(content) {
|
||||
var that = this;
|
||||
|
||||
return Q(content)
|
||||
.then(this.replaceBlocks);
|
||||
.then(that.replaceBlocks)
|
||||
.then(function(_content) {
|
||||
return batch.execEach(that.blocks, {
|
||||
max: 20,
|
||||
fn: function(blk, blkId) {
|
||||
return Q()
|
||||
.then(function() {
|
||||
if (!blk.post) return Q();
|
||||
return blk.post();
|
||||
})
|
||||
.then(function() {
|
||||
delete that.blocks[blkId];
|
||||
});
|
||||
}
|
||||
})
|
||||
.thenResolve(_content);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = TemplateEngine;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
var Q = require("q");
|
||||
var _ = require("lodash");
|
||||
|
||||
// Execute a method for all element
|
||||
function execEach(items, options) {
|
||||
if (_.size(items) == 0) return Q();
|
||||
var concurrents = 0, d = Q.defer(), pending = [];
|
||||
|
||||
options = _.defaults(options || {}, {
|
||||
max: 100,
|
||||
fn: function(item) {}
|
||||
});
|
||||
|
||||
|
||||
function startItem(item, i) {
|
||||
if (concurrents >= options.max) {
|
||||
pending.push([item, i]);
|
||||
return;
|
||||
}
|
||||
|
||||
concurrents++;
|
||||
Q()
|
||||
.then(function() {
|
||||
return options.fn(item, i);
|
||||
})
|
||||
.then(function() {
|
||||
concurrents--;
|
||||
|
||||
// Next pending
|
||||
var next = pending.shift();
|
||||
|
||||
if (concurrents == 0 && !next) {
|
||||
d.resolve();
|
||||
} else if (next) {
|
||||
startItem.apply(null, next);
|
||||
}
|
||||
})
|
||||
.fail(function(err) {
|
||||
pending = [];
|
||||
d.reject(err);
|
||||
})
|
||||
}
|
||||
|
||||
_.each(items, startItem);
|
||||
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
execEach: execEach
|
||||
};
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
var hljs = require('highlight.js');
|
||||
|
||||
var MAP = {
|
||||
'py': 'python',
|
||||
'js': 'javascript',
|
||||
'json': 'javascript',
|
||||
'rb': 'ruby',
|
||||
'csharp': 'cs',
|
||||
};
|
||||
|
||||
function normalize(lang) {
|
||||
if(!lang) { return null; }
|
||||
|
||||
var lower = lang.toLowerCase();
|
||||
return MAP[lower] || lower;
|
||||
}
|
||||
|
||||
function highlight(lang, code) {
|
||||
if(!lang) return code;
|
||||
|
||||
// Normalize lang
|
||||
lang = normalize(lang);
|
||||
|
||||
try {
|
||||
return hljs.highlight(lang, code).value;
|
||||
} catch(e) { }
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// Exports
|
||||
module.exports = {
|
||||
highlight: highlight,
|
||||
normalize: normalize,
|
||||
MAP: MAP
|
||||
};
|
||||
+58
-58
@@ -15,7 +15,7 @@ var GIT_TMP = null;
|
||||
|
||||
// Check if an url is a git dependency url
|
||||
function checkGitUrl(giturl) {
|
||||
return (giturl.indexOf(GIT_PREFIX) === 0);
|
||||
return (giturl.indexOf(GIT_PREFIX) === 0);
|
||||
}
|
||||
|
||||
// Validates a SHA in hexadecimal
|
||||
@@ -25,88 +25,88 @@ function validateSha(str) {
|
||||
|
||||
// Parse and extract infos
|
||||
function parseGitUrl(giturl) {
|
||||
var ref, uri, fileParts, filepath;
|
||||
var ref, uri, fileParts, filepath;
|
||||
|
||||
if (!checkGitUrl(giturl)) return null;
|
||||
giturl = giturl.slice(GIT_PREFIX.length);
|
||||
if (!checkGitUrl(giturl)) return null;
|
||||
giturl = giturl.slice(GIT_PREFIX.length);
|
||||
|
||||
uri = new URI(giturl);
|
||||
ref = uri.fragment() || "master";
|
||||
uri.fragment(null);
|
||||
uri = new URI(giturl);
|
||||
ref = uri.fragment() || "master";
|
||||
uri.fragment(null);
|
||||
|
||||
// Extract file inside the repo (after the .git)
|
||||
fileParts =uri.path().split(".git");
|
||||
filepath = fileParts.length > 1? fileParts.slice(1).join(".git") : "";
|
||||
if (filepath[0] == "/") filepath = filepath.slice(1);
|
||||
// Extract file inside the repo (after the .git)
|
||||
fileParts =uri.path().split(".git");
|
||||
filepath = fileParts.length > 1? fileParts.slice(1).join(".git") : "";
|
||||
if (filepath[0] == "/") filepath = filepath.slice(1);
|
||||
|
||||
// Recreate pathname without the real filename
|
||||
uri.path(_.first(fileParts)+".git");
|
||||
// Recreate pathname without the real filename
|
||||
uri.path(_.first(fileParts)+".git");
|
||||
|
||||
return {
|
||||
host: uri.toString(),
|
||||
ref: ref || "master",
|
||||
filepath: filepath
|
||||
};
|
||||
return {
|
||||
host: uri.toString(),
|
||||
ref: ref || "master",
|
||||
filepath: filepath
|
||||
};
|
||||
}
|
||||
|
||||
// Clone a git repo from a specific ref
|
||||
function cloneGitRepo(host, ref) {
|
||||
var isBranch = false;
|
||||
var isBranch = false;
|
||||
|
||||
ref = ref || "master";
|
||||
if (!validateSha(ref)) isBranch = true;
|
||||
ref = ref || "master";
|
||||
if (!validateSha(ref)) isBranch = true;
|
||||
|
||||
return Q()
|
||||
return Q()
|
||||
|
||||
// Create temporary folder to store git repos
|
||||
.then(function() {
|
||||
if (GIT_TMP) return;
|
||||
return fs.tmp.dir()
|
||||
.then(function(_tmp) {
|
||||
GIT_TMP = _tmp;
|
||||
});
|
||||
})
|
||||
// Create temporary folder to store git repos
|
||||
.then(function() {
|
||||
if (GIT_TMP) return;
|
||||
return fs.tmp.dir()
|
||||
.then(function(_tmp) {
|
||||
GIT_TMP = _tmp;
|
||||
});
|
||||
})
|
||||
|
||||
// Return or clone the git repo
|
||||
.then(function() {
|
||||
// Unique ID for repo/ref combinaison
|
||||
var repoId = crc.crc32(host+"#"+ref).toString(16);
|
||||
// Return or clone the git repo
|
||||
.then(function() {
|
||||
// Unique ID for repo/ref combinaison
|
||||
var repoId = crc.crc32(host+"#"+ref).toString(16);
|
||||
|
||||
// Absolute path to the folder
|
||||
var repoPath = path.resolve(GIT_TMP, repoId);
|
||||
// Absolute path to the folder
|
||||
var repoPath = path.resolve(GIT_TMP, repoId);
|
||||
|
||||
return fs.exists(repoPath)
|
||||
.then(function(doExists) {
|
||||
if (doExists) return;
|
||||
return fs.exists(repoPath)
|
||||
.then(function(doExists) {
|
||||
if (doExists) return;
|
||||
|
||||
// Clone repo
|
||||
return exec("git clone "+host+" "+repoPath)
|
||||
.then(function() {
|
||||
return exec("git checkout "+ref, { cwd: repoPath });
|
||||
})
|
||||
})
|
||||
.thenResolve(repoPath);
|
||||
});
|
||||
// Clone repo
|
||||
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);
|
||||
if (!giturl) return Q(null);
|
||||
if (_.isString(giturl)) giturl = parseGitUrl(giturl);
|
||||
if (!giturl) return Q(null);
|
||||
|
||||
// Clone or get from cache
|
||||
return cloneGitRepo(giturl.host, giturl.ref)
|
||||
.then(function(repo) {
|
||||
// Clone or get from cache
|
||||
return cloneGitRepo(giturl.host, giturl.ref)
|
||||
.then(function(repo) {
|
||||
|
||||
// Resolve relative path
|
||||
return path.resolve(repo, giturl.filepath);
|
||||
});
|
||||
// Resolve relative path
|
||||
return path.resolve(repo, giturl.filepath);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
checkUrl: checkGitUrl,
|
||||
parseUrl: parseGitUrl,
|
||||
resolveFile: resolveFileFromGit
|
||||
checkUrl: checkGitUrl,
|
||||
parseUrl: parseGitUrl,
|
||||
resolveFile: resolveFileFromGit
|
||||
};
|
||||
|
||||
+64
-50
@@ -2,65 +2,79 @@ 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(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());
|
||||
};
|
||||
|
||||
var getByLanguage = function(lang) {
|
||||
lang = normalizeLanguage(lang);
|
||||
var locales = getLocales();
|
||||
return locales[lang];
|
||||
};
|
||||
i18n.configure({
|
||||
locales: LOCALES,
|
||||
directory: I18N_PATH,
|
||||
defaultLocale: DEFAULT_LANGUAGE,
|
||||
updateFiles: false
|
||||
});
|
||||
|
||||
var compareLocales = function(lang, locale) {
|
||||
var langMain = _.first(lang.split("-"));
|
||||
var localeMain = _.first(locale.split("-"));
|
||||
var langMain = _.first(lang.split("-"));
|
||||
var langSecond = _.last(lang.split("-"));
|
||||
|
||||
if (locale == lang) return 100;
|
||||
if (localeMain == langMain) return 50;
|
||||
return 0;
|
||||
var localeMain = _.first(locale.split("-"));
|
||||
var localeSecond = _.last(locale.split("-"));
|
||||
|
||||
if (locale == lang) return 100;
|
||||
if (localeMain == langMain) return 50;
|
||||
if (localeSecond == langSecond) return 20;
|
||||
return 0;
|
||||
};
|
||||
|
||||
var normalizeLanguage = _.memoize(function(lang) {
|
||||
var locales = getLocales();
|
||||
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;
|
||||
})
|
||||
.sortBy("score")
|
||||
.pluck("locale")
|
||||
.last()
|
||||
.value();
|
||||
return language || lang;
|
||||
var language = _.chain(LOCALES)
|
||||
.values()
|
||||
.map(function(locale) {
|
||||
return {
|
||||
locale: locale,
|
||||
score: compareLocales(lang, locale)
|
||||
}
|
||||
})
|
||||
.filter(function(lang) {
|
||||
return lang.score > 0;
|
||||
})
|
||||
.sortBy("score")
|
||||
.pluck("locale")
|
||||
.last()
|
||||
.value();
|
||||
return language || lang;
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
getLocales: getLocales,
|
||||
getLanguages: getLanguages,
|
||||
getByLanguage: getByLanguage,
|
||||
normalizeLanguage: normalizeLanguage
|
||||
var translate = function(locale, phrase) {
|
||||
var args = Array.prototype.slice.call(arguments, 2);
|
||||
|
||||
return i18n.__.apply({}, [{
|
||||
locale: locale,
|
||||
phrase: phrase
|
||||
}].concat(args));
|
||||
};
|
||||
|
||||
var getCatalog = function(locale) {
|
||||
locale = normalizeLanguage(locale);
|
||||
return i18n.getCatalog(locale);
|
||||
};
|
||||
|
||||
var getLocales = function() {
|
||||
return LOCALES;
|
||||
};
|
||||
|
||||
var hasLocale = function(locale) {
|
||||
return _.contains(LOCALES, locale);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
__: translate,
|
||||
normalizeLanguage: normalizeLanguage,
|
||||
getCatalog: getCatalog,
|
||||
getLocales: getLocales,
|
||||
hasLocale: hasLocale
|
||||
};
|
||||
|
||||
+17
-16
@@ -1,38 +1,39 @@
|
||||
var _ = require("lodash");
|
||||
var Q = require("q");
|
||||
var fs = require("./fs");
|
||||
var exec = require('child_process').exec;
|
||||
var spawn = require('spawn-cmd').spawn;
|
||||
|
||||
var links = require("./links");
|
||||
|
||||
// Convert a svg file
|
||||
var convertSVG = function(source, dest, options) {
|
||||
if (!fs.existsSync(source)) return Q.reject(new Error("File doesn't exist: "+source));
|
||||
if (!fs.existsSync(source)) return Q.reject(new Error("File doesn't exist: "+source));
|
||||
var d = Q.defer();
|
||||
|
||||
var d = Q.defer();
|
||||
options = _.defaults(options || {}, {
|
||||
|
||||
options = _.defaults(options || {}, {
|
||||
});
|
||||
|
||||
});
|
||||
//var command = shellescape(['svgexport', source, dest]);
|
||||
var child = spawn('svgexport', [source, dest]);
|
||||
|
||||
var command = "svgexport "+source+" "+dest;
|
||||
child.on("error", function(error) {
|
||||
if (error.code == "ENOENT") error = new Error("Need to install 'svgexport' using 'npm install svgexport -g'");
|
||||
return d.reject(error);
|
||||
});
|
||||
|
||||
var child = exec(command, function (error, stdout, stderr) {
|
||||
if (error) {
|
||||
if (error.code == 127) error = new Error("Need to install 'svgexport' using 'npm install svgexport -g'");
|
||||
return d.reject(error);
|
||||
}
|
||||
if (fs.existsSync(dest)) {
|
||||
child.on("close", function(code) {
|
||||
if (code == 0 && fs.existsSync(dest)) {
|
||||
d.resolve();
|
||||
} else {
|
||||
d.reject(new Error("Error converting "+source));
|
||||
d.reject(new Error("Error converting "+source+" into "+dest));
|
||||
}
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
convertSVG: convertSVG,
|
||||
INVALID: [".svg"]
|
||||
convertSVG: convertSVG,
|
||||
INVALID: [".svg"]
|
||||
};
|
||||
|
||||
+21
-8
@@ -15,12 +15,27 @@ var isRelative = function(href) {
|
||||
try {
|
||||
var parsed = url.parse(href);
|
||||
|
||||
return !!(!parsed.protocol && parsed.path);// && parsed.path[0] != '/';
|
||||
return !!(!parsed.protocol && parsed.path);
|
||||
} catch(err) {}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Return true if the link is an achor
|
||||
var isAnchor = function(href) {
|
||||
try {
|
||||
var parsed = url.parse(href);
|
||||
return !!(!parsed.protocol && !parsed.path && parsed.hash);
|
||||
} catch(err) {}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Normalize a path to be a link
|
||||
var normalizeLink = function(s) {
|
||||
return s.replace(/\\/g, '/');
|
||||
};
|
||||
|
||||
// Relative to absolute path
|
||||
// dir: directory parent of the file currently in rendering process
|
||||
// outdir: directory parent from the html output
|
||||
@@ -36,7 +51,7 @@ var toAbsolute = function(_href, dir, outdir) {
|
||||
_href = path.relative(outdir, hrefInRoot);
|
||||
|
||||
// Normalize windows paths
|
||||
_href = _href.replace(/\\/g, '/');
|
||||
_href = normalizeLink(_href);
|
||||
|
||||
return _href;
|
||||
};
|
||||
@@ -45,11 +60,7 @@ var toAbsolute = function(_href, dir, outdir) {
|
||||
var join = function() {
|
||||
var _href = path.join.apply(path, arguments);
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
_href = _href.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
return _href;
|
||||
return normalizeLink(_href);
|
||||
};
|
||||
|
||||
// Change extension
|
||||
@@ -61,9 +72,11 @@ var changeExtension = function(filename, newext) {
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
isAnchor: isAnchor,
|
||||
isRelative: isRelative,
|
||||
isExternal: isExternal,
|
||||
toAbsolute: toAbsolute,
|
||||
join: join,
|
||||
changeExtension: changeExtension
|
||||
changeExtension: changeExtension,
|
||||
normalize: normalizeLink
|
||||
};
|
||||
|
||||
+75
-75
@@ -3,100 +3,100 @@ var util = require('util');
|
||||
var color = require('bash-color');
|
||||
|
||||
var LEVELS = {
|
||||
DEBUG: 0,
|
||||
INFO: 1,
|
||||
WARN: 2,
|
||||
ERROR: 3,
|
||||
DISABLED: 10
|
||||
DEBUG: 0,
|
||||
INFO: 1,
|
||||
WARN: 2,
|
||||
ERROR: 3,
|
||||
DISABLED: 10
|
||||
};
|
||||
|
||||
var COLORS = {
|
||||
DEBUG: color.purple,
|
||||
INFO: color.cyan,
|
||||
WARN: color.yellow,
|
||||
ERROR: color.red
|
||||
DEBUG: color.purple,
|
||||
INFO: color.cyan,
|
||||
WARN: color.yellow,
|
||||
ERROR: color.red
|
||||
};
|
||||
|
||||
module.exports = function(_write, logLevel) {
|
||||
var logger = {};
|
||||
var lastChar = '\n';
|
||||
if (_.isString(logLevel)) logLevel = LEVELS[logLevel.toUpperCase()];
|
||||
var logger = {};
|
||||
var lastChar = '\n';
|
||||
if (_.isString(logLevel)) logLevel = LEVELS[logLevel.toUpperCase()];
|
||||
|
||||
// Write a simple message
|
||||
logger.write = function(msg) {
|
||||
msg = msg.toString();
|
||||
lastChar = _.last(msg);
|
||||
return _write(msg);
|
||||
};
|
||||
// Write a simple message
|
||||
logger.write = function(msg) {
|
||||
msg = msg.toString();
|
||||
lastChar = _.last(msg);
|
||||
return _write(msg);
|
||||
};
|
||||
|
||||
// Format a message
|
||||
logger.format = function() {
|
||||
return util.format.apply(util, arguments);
|
||||
};
|
||||
// Format a message
|
||||
logger.format = function() {
|
||||
return util.format.apply(util, arguments);
|
||||
};
|
||||
|
||||
// Write a line
|
||||
logger.writeLn = function(msg) {
|
||||
return this.write((msg || "")+"\n");
|
||||
};
|
||||
// Write a line
|
||||
logger.writeLn = function(msg) {
|
||||
return this.write((msg || "")+"\n");
|
||||
};
|
||||
|
||||
// Write a message with a certain level
|
||||
logger.log = function(level) {
|
||||
if (level < logLevel) return;
|
||||
// Write a message with a certain level
|
||||
logger.log = function(level) {
|
||||
if (level < logLevel) return;
|
||||
|
||||
var levelKey = _.findKey(LEVELS, function(v) { return v == level; });
|
||||
var args = Array.prototype.slice.apply(arguments, [1]);
|
||||
var msg = logger.format.apply(logger, args);
|
||||
var levelKey = _.findKey(LEVELS, function(v) { return v == level; });
|
||||
var args = Array.prototype.slice.apply(arguments, [1]);
|
||||
var msg = logger.format.apply(logger, args);
|
||||
|
||||
if (lastChar == '\n') {
|
||||
msg = COLORS[levelKey](levelKey.toLowerCase()+":")+" "+msg;
|
||||
}
|
||||
if (lastChar == '\n') {
|
||||
msg = COLORS[levelKey](levelKey.toLowerCase()+":")+" "+msg;
|
||||
}
|
||||
|
||||
return logger.write(msg);
|
||||
};
|
||||
logger.logLn = function() {
|
||||
if (lastChar != '\n') logger.write("\n");
|
||||
return logger.write(msg);
|
||||
};
|
||||
logger.logLn = function() {
|
||||
if (lastChar != '\n') logger.write("\n");
|
||||
|
||||
var args = Array.prototype.slice.apply(arguments);
|
||||
args.push("\n");
|
||||
logger.log.apply(logger, args);
|
||||
};
|
||||
var args = Array.prototype.slice.apply(arguments);
|
||||
args.push("\n");
|
||||
logger.log.apply(logger, args);
|
||||
};
|
||||
|
||||
// Write a OK
|
||||
logger.ok = function(level) {
|
||||
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>> ')));
|
||||
} else {
|
||||
logger.log(level, color.green("OK"), "\n");
|
||||
}
|
||||
};
|
||||
// Write a OK
|
||||
logger.ok = function(level) {
|
||||
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>> ')));
|
||||
} else {
|
||||
logger.log(level, color.green("OK"), "\n");
|
||||
}
|
||||
};
|
||||
|
||||
// Write an "FAIL"
|
||||
logger.fail = function(level) {
|
||||
return logger.log(level, color.red("ERROR")+"\n");
|
||||
};
|
||||
// Write an "FAIL"
|
||||
logger.fail = function(level) {
|
||||
return logger.log(level, color.red("ERROR")+"\n");
|
||||
};
|
||||
|
||||
_.each(_.omit(LEVELS, 'DISABLED'), function(level, levelKey) {
|
||||
levelKey = levelKey.toLowerCase();
|
||||
_.each(_.omit(LEVELS, 'DISABLED'), function(level, levelKey) {
|
||||
levelKey = levelKey.toLowerCase();
|
||||
|
||||
logger[levelKey] = _.partial(logger.log, level);
|
||||
logger[levelKey].ln = _.partial(logger.logLn, level);
|
||||
logger[levelKey].ok = _.partial(logger.ok, level);
|
||||
logger[levelKey].fail = _.partial(logger.fail, level);
|
||||
logger[levelKey].promise = function(p) {
|
||||
return p.
|
||||
then(function(st) {
|
||||
logger[levelKey].ok();
|
||||
return st;
|
||||
}, function(err) {
|
||||
logger[levelKey].fail();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
});
|
||||
logger[levelKey] = _.partial(logger.log, level);
|
||||
logger[levelKey].ln = _.partial(logger.logLn, level);
|
||||
logger[levelKey].ok = _.partial(logger.ok, level);
|
||||
logger[levelKey].fail = _.partial(logger.fail, level);
|
||||
logger[levelKey].promise = function(p) {
|
||||
return p.
|
||||
then(function(st) {
|
||||
logger[levelKey].ok();
|
||||
return st;
|
||||
}, function(err) {
|
||||
logger[levelKey].fail();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return logger;
|
||||
return logger;
|
||||
};
|
||||
module.exports.LEVELS = LEVELS;
|
||||
module.exports.COLORS = COLORS;
|
||||
|
||||
@@ -36,7 +36,7 @@ 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)
|
||||
@@ -57,7 +57,9 @@ function navigation(summary, files) {
|
||||
|
||||
|
||||
return [current.path, {
|
||||
index: i,
|
||||
title: current.title,
|
||||
introduction: current.introduction,
|
||||
prev: prev,
|
||||
next: next,
|
||||
level: current.level,
|
||||
|
||||
+81
-35
@@ -1,5 +1,6 @@
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
var url = require('url');
|
||||
var path = require('path');
|
||||
var cheerio = require('cheerio');
|
||||
var domSerializer = require('dom-serializer');
|
||||
@@ -9,6 +10,8 @@ var crc = require("crc");
|
||||
var links = require('./links');
|
||||
var imgUtils = require('./images');
|
||||
var fs = require('./fs');
|
||||
var batch = require('./batch');
|
||||
var code = require('./code');
|
||||
|
||||
// Render a cheerio dom as html
|
||||
var renderDom = function($, dom, options) {
|
||||
@@ -103,7 +106,7 @@ function normalizeHtml(src, options) {
|
||||
// Generate filename
|
||||
dest = "/"+fs.getUniqueFilename(outputRoot, dest);
|
||||
|
||||
svgContent[dest] = content;
|
||||
svgContent[dest] = '<?xml version="1.0" encoding="UTF-8"?>'+content;
|
||||
$(this).replaceWith($("<img>").attr("src", dest));
|
||||
});
|
||||
}
|
||||
@@ -112,6 +115,7 @@ function normalizeHtml(src, options) {
|
||||
$("img").each(function() {
|
||||
var origin = undefined;
|
||||
var src = $(this).attr("src");
|
||||
if (!src) return;
|
||||
var isExternal = links.isExternal(src);
|
||||
|
||||
// Transform as relative to the bases
|
||||
@@ -130,7 +134,7 @@ function normalizeHtml(src, options) {
|
||||
}
|
||||
|
||||
var ext = path.extname(src);
|
||||
var srcAbs = path.join("/", options.base, src);
|
||||
var srcAbs = links.join("/", options.base, src);
|
||||
|
||||
// Test image extension
|
||||
if (_.contains(imgUtils.INVALID, ext)) {
|
||||
@@ -142,7 +146,7 @@ function normalizeHtml(src, options) {
|
||||
var dest = "";
|
||||
|
||||
// Replace extension
|
||||
dest = path.join(path.dirname(srcAbs), path.basename(srcAbs, ext)+".png");
|
||||
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
|
||||
@@ -161,7 +165,7 @@ function normalizeHtml(src, options) {
|
||||
dest: path.join("./", dest)
|
||||
});
|
||||
|
||||
src = path.join("/", dest);
|
||||
src = links.join("/", dest);
|
||||
}
|
||||
|
||||
// Reset as relative to output
|
||||
@@ -180,18 +184,27 @@ function normalizeHtml(src, options) {
|
||||
$(this).attr("src", src);
|
||||
});
|
||||
|
||||
// Normalize links
|
||||
$("a").each(function() {
|
||||
var href = $(this).attr("href");
|
||||
if (!href) return;
|
||||
|
||||
if (links.isRelative(href)) {
|
||||
var absolutePath = path.join(options.base, href);
|
||||
if (links.isAnchor(href)) {
|
||||
// Keep it as it is
|
||||
} else if (links.isRelative(href)) {
|
||||
var parts = url.parse(href);
|
||||
var absolutePath = links.join(options.base, parts.pathname);
|
||||
var anchor = parts.hash || "";
|
||||
|
||||
// If is in navigation relative: change extsnio nto be .html
|
||||
if (options.navigation[absolutePath]) href = links.changeExtension(href, ".html");
|
||||
// If is in navigation relative: transform as content
|
||||
if (options.navigation[absolutePath]) {
|
||||
absolutePath = options.book.contentLink(absolutePath);
|
||||
}
|
||||
|
||||
// Transform as absolute
|
||||
href = links.toAbsolute(href, options.base, options.output);
|
||||
href = links.toAbsolute("/"+absolutePath, options.base, options.output)+anchor;
|
||||
} else {
|
||||
// External links
|
||||
$(this).attr("target", "_blank");
|
||||
}
|
||||
|
||||
@@ -199,12 +212,42 @@ function normalizeHtml(src, options) {
|
||||
$(this).attr("href", href);
|
||||
});
|
||||
|
||||
// Replace glossayr terms
|
||||
_.each(options.glossary, function(term) {
|
||||
// Highlight code blocks
|
||||
$("code").each(function() {
|
||||
// Extract language
|
||||
var lang = _.chain(
|
||||
($(this).attr("class") || "").split(" ")
|
||||
)
|
||||
.map(function(cl) {
|
||||
if (cl.search("lang-") === 0) return cl.slice("lang-".length);
|
||||
return null;
|
||||
})
|
||||
.compact()
|
||||
.first()
|
||||
.value();
|
||||
|
||||
if (lang) {
|
||||
var html = code.highlight(
|
||||
lang,
|
||||
$(this).text()
|
||||
);
|
||||
|
||||
$(this).html(html);
|
||||
}
|
||||
});
|
||||
|
||||
// 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) {
|
||||
@@ -212,7 +255,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='"+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>";
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -230,33 +273,36 @@ function convertImages(images, options) {
|
||||
var downloaded = [];
|
||||
options.book.log.debug.ln("convert ", images.length, "images to png");
|
||||
|
||||
return _.reduce(images, function(prev, image) {
|
||||
var imgin = path.resolve(options.book.options.output, image.source);
|
||||
return batch.execEach(images, {
|
||||
max: 100,
|
||||
fn: function(image) {
|
||||
var imgin = path.resolve(options.book.options.output, image.source);
|
||||
|
||||
return prev
|
||||
return Q()
|
||||
|
||||
// Write image if need to be download
|
||||
.then(function() {
|
||||
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)));
|
||||
})
|
||||
// Write image if need to be download
|
||||
.then(function() {
|
||||
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)));
|
||||
})
|
||||
|
||||
// Write svg if content
|
||||
.then(function() {
|
||||
if (!image.content) return;
|
||||
return fs.writeFile(imgin, image.content);
|
||||
})
|
||||
// Write svg if content
|
||||
.then(function() {
|
||||
if (!image.content) return;
|
||||
return fs.writeFile(imgin, image.content);
|
||||
})
|
||||
|
||||
// Convert
|
||||
.then(function() {
|
||||
if (!image.dest) return;
|
||||
var imgout = path.resolve(options.book.options.output, image.dest);
|
||||
options.book.log.debug("convert image", image.source, "to", image.dest, "...");
|
||||
return options.book.log.debug.promise(imgUtils.convertSVG(imgin, imgout));
|
||||
});
|
||||
}, Q())
|
||||
// Convert
|
||||
.then(function() {
|
||||
if (!image.dest) return;
|
||||
var imgout = path.resolve(options.book.options.output, image.dest);
|
||||
options.book.log.debug("convert image", image.source, "to", image.dest, "...");
|
||||
return options.book.log.debug.promise(imgUtils.convertSVG(imgin, imgout));
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
options.book.log.debug.ok(images.length+" images converted with success");
|
||||
});
|
||||
|
||||
@@ -7,10 +7,18 @@ var calculProgress = function(navigation, current) {
|
||||
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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
+8
-7
@@ -1,7 +1,7 @@
|
||||
var Q = require('q');
|
||||
var _ = require('lodash');
|
||||
var path = require('path');
|
||||
var Gaze = require('gaze').Gaze;
|
||||
var chokidar = require('chokidar');
|
||||
|
||||
var parsers = require('gitbook-parsers')
|
||||
|
||||
@@ -17,17 +17,18 @@ function watch(dir) {
|
||||
toWatch.push("**/*"+ext);
|
||||
});
|
||||
|
||||
var gaze = new Gaze(toWatch, {
|
||||
cwd: dir
|
||||
var watcher = chokidar.watch(toWatch, {
|
||||
cwd: dir,
|
||||
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
-14
@@ -1,27 +1,27 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "2.0.0-alpha.7",
|
||||
"version": "2.1.0",
|
||||
"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": "2.4.1",
|
||||
"lodash": "3.5.0",
|
||||
"graceful-fs": "3.0.5",
|
||||
"resolve": "0.6.3",
|
||||
"fs-extra": "0.16.3",
|
||||
"fs-extra": "0.16.5",
|
||||
"fstream-ignore": "1.0.2",
|
||||
"gitbook-parsers": "0.4.0",
|
||||
"nunjucks": "git+https://github.com/SamyPesse/nunjucks.git#4019d1b7379372336b86ce1b0bf84352a2029747",
|
||||
"nunjucks-autoescape": "0.1.0",
|
||||
"nunjucks-filter": "0.1.0",
|
||||
"nunjucks-i18n": "1.0.0",
|
||||
"gitbook-parsers": "0.7.6",
|
||||
"nunjucks": "mozilla/nunjucks#103513c294835bcbe64222de6db494e2555e294e",
|
||||
"nunjucks-autoescape": "1.0.0",
|
||||
"nunjucks-filter": "1.0.0",
|
||||
"i18n": "0.5.0",
|
||||
"semver": "2.2.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.1",
|
||||
"send": "0.2.0",
|
||||
"tiny-lr": "0.1.5",
|
||||
"tmp": "0.0.24",
|
||||
@@ -30,10 +30,14 @@
|
||||
"URIjs": "1.14.1",
|
||||
"request": "2.51.0",
|
||||
"npm": "2.4.1",
|
||||
"dom-serializer": "0.1.0"
|
||||
"dom-serializer": "0.1.0",
|
||||
"spawn-cmd": "0.0.2",
|
||||
"highlight.js": "8.4.0",
|
||||
"escape-string-regexp": "1.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "1.18.2",
|
||||
"mocha": "2.2.1",
|
||||
"should": "5.2.0",
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-cli": "0.1.11",
|
||||
"grunt-contrib-copy": "0.5.0",
|
||||
@@ -42,7 +46,7 @@
|
||||
"grunt-bower-install-simple": "0.9.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "export TESTING=true; mocha --reporter list --timeout 15000"
|
||||
"test": "node_modules/.bin/mocha --reporter spec --timeout 15000"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -57,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,54 @@
|
||||
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 || {}, {
|
||||
count: 1,
|
||||
attributes: {},
|
||||
trim: false,
|
||||
text: undefined
|
||||
});
|
||||
|
||||
var $el = $(query);
|
||||
|
||||
// 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,10 @@
|
||||
# Readme
|
||||
|
||||
### Relative
|
||||
|
||||
<p id="t1">{% include "./hello.md" %}</p>
|
||||
|
||||
### Git
|
||||
|
||||
<p id="t2">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md" %}</p>
|
||||
<p id="t3">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test2.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,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
|
||||

|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
|
||||
<svg version="1.1" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="svg-root" width="100%" height="100%" viewBox="0 0 480 360">
|
||||
<title id="test-title">basic SVG tiny doc</title>
|
||||
<g id="test-body-content">
|
||||
<text font-family="Arial" font-size="14" text-anchor="middle" x="225" y="25">hello world</text>
|
||||
</g>
|
||||
<text id="revision" x="10" y="340" font-size="40" stroke="none" fill="black">Revision: 1.1</text>
|
||||
<rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000000"/>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 730 B |
@@ -0,0 +1,3 @@
|
||||
*
|
||||
!SUMMARY.md
|
||||
!.gitignore
|
||||
@@ -0,0 +1,6 @@
|
||||
# Summary
|
||||
|
||||
* [Hello](hello.md)
|
||||
* [Hello 2](hello2.md)
|
||||
* Hello 3
|
||||
* [Hello 4](hello3/hello4.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
|
||||
+26
-8
@@ -1,14 +1,32 @@
|
||||
var fs = require('fs');
|
||||
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");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
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 git references', function() {
|
||||
readme.should.be.html({
|
||||
".page-inner p#t2": {
|
||||
count: 1,
|
||||
text: "Hello from git",
|
||||
trim: true
|
||||
},
|
||||
".page-inner p#t3": {
|
||||
count: 1,
|
||||
text: "First Hello. Hello from git",
|
||||
trim: true
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
+46
-27
@@ -1,36 +1,55 @@
|
||||
var fs = require('fs');
|
||||
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");
|
||||
describe('eBook generator', function () {
|
||||
describe('Basic Book', function() {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
return books.generate("basic", "ebook")
|
||||
.then(function(_book) {
|
||||
book = _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);
|
||||
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" }
|
||||
);
|
||||
PAGE.should.be.html({
|
||||
"link": {
|
||||
count: 1,
|
||||
attributes: {
|
||||
href: "./styles/print.css"
|
||||
}
|
||||
}
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
});
|
||||
|
||||
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
|
||||
|
||||

|
||||
Vendored
-11
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
|
||||
<svg version="1.1" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="svg-root" width="100%" height="100%" viewBox="0 0 480 360">
|
||||
<title id="test-title">basic SVG tiny doc</title>
|
||||
<g id="test-body-content">
|
||||
<text font-family="Arial" font-size="14" text-anchor="middle" x="225" y="25">hello world</text>
|
||||
</g>
|
||||
<text id="revision" x="10" y="340" font-size="40" stroke="none" fill="black">Revision: 1.1</text>
|
||||
<rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000000"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 709 B |
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
# Test Content inclusion
|
||||
|
||||
{% include "./test.md" %}
|
||||
Git1:{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md" %}
|
||||
Git2:{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test2.md" %}
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
Hello World
|
||||
+17
-18
@@ -1,32 +1,31 @@
|
||||
var path = require('path');
|
||||
var _ = require('lodash');
|
||||
var assert = require('assert');
|
||||
|
||||
var fs = require("fs");
|
||||
var git = require("../lib/utils/git");
|
||||
var should = require('should');
|
||||
var git = require('../lib/utils/git');
|
||||
|
||||
describe('GIT parser and getter', function () {
|
||||
it('should correctly parse an https url', function() {
|
||||
var parts = git.parseUrl("git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md");
|
||||
assert(parts);
|
||||
assert.equal(parts.host, "https://gist.github.com/69ea4542e4c8967d2fa7.git");
|
||||
assert.equal(parts.ref, "master");
|
||||
assert.equal(parts.filepath, "test.md");
|
||||
|
||||
should.exist(parts);
|
||||
parts.host.should.be.equal("https://gist.github.com/69ea4542e4c8967d2fa7.git");
|
||||
parts.ref.should.be.equal("master");
|
||||
parts.filepath.should.be.equal("test.md");
|
||||
});
|
||||
|
||||
it('should correctly parse an https url with a reference', function() {
|
||||
var parts = git.parseUrl("git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md#0.1.2");
|
||||
assert(parts);
|
||||
assert.equal(parts.host, "https://gist.github.com/69ea4542e4c8967d2fa7.git");
|
||||
assert.equal(parts.ref, "0.1.2");
|
||||
assert.equal(parts.filepath, "test.md");
|
||||
|
||||
should.exist(parts);
|
||||
parts.host.should.be.equal("https://gist.github.com/69ea4542e4c8967d2fa7.git");
|
||||
parts.ref.should.be.equal("0.1.2");
|
||||
parts.filepath.should.be.equal("test.md");
|
||||
});
|
||||
|
||||
it('should correctly parse an ssh url', function() {
|
||||
var parts = git.parseUrl("git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1");
|
||||
assert(parts);
|
||||
assert.equal(parts.host, "git@github.com:GitbookIO/gitbook.git");
|
||||
assert.equal(parts.ref, "e1594cde2c32e4ff48f6c4eff3d3d461743d74e1");
|
||||
assert.equal(parts.filepath, "directory/README.md");
|
||||
|
||||
should.exist(parts);
|
||||
parts.host.should.be.equal("git@github.com:GitbookIO/gitbook.git");
|
||||
parts.ref.should.be.equal("e1594cde2c32e4ff48f6c4eff3d3d461743d74e1");
|
||||
parts.filepath.should.be.equal("directory/README.md");
|
||||
});
|
||||
});
|
||||
|
||||
+78
-26
@@ -1,35 +1,87 @@
|
||||
var fs = require('fs');
|
||||
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");
|
||||
describe('Glossary', function () {
|
||||
describe('Parsing', function() {
|
||||
var book;
|
||||
|
||||
before(function() {
|
||||
return books.parse("glossary")
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Glossary Generation', function () {
|
||||
it('should correctly replace glossary terms', function(done) {
|
||||
testGeneration(books[0], "website", function(output) {
|
||||
var content = fs.readFileSync(path.join(output, "index.html"), { encoding: "utf8" });
|
||||
var $ = cheerio.load(content);
|
||||
|
||||
var $body = $(".page-inner");
|
||||
var $a = $("a[href='GLOSSARY.html#description']");
|
||||
assert($a.length == 1);
|
||||
assert($a.text() == "description");
|
||||
}, done);
|
||||
it('should correctly list items', function() {
|
||||
book.should.have.property("glossary");
|
||||
book.glossary.should.have.lengthOf(3);
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly replace glossary terms in sub pages', function(done) {
|
||||
testGeneration(books[1], "website", function(output) {
|
||||
var content = fs.readFileSync(path.join(output, "sub/test1.html"), { encoding: "utf8" });
|
||||
var $ = cheerio.load(content);
|
||||
describe('Generation', function() {
|
||||
var book;
|
||||
|
||||
var $body = $(".page-inner");
|
||||
var $a = $("a[href='../GLOSSARY.html#test']");
|
||||
assert($a.length == 1);
|
||||
assert($a.text() == "test");
|
||||
assert($a.attr("title") == "a test text");
|
||||
}, done);
|
||||
before(function() {
|
||||
return books.generate("glossary", "website")
|
||||
.then(function(_book) {
|
||||
book = _book;
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly generate a GLOSSARY.html', function() {
|
||||
book.should.have.file("GLOSSARY.html");
|
||||
});
|
||||
|
||||
describe('Page Integration', function() {
|
||||
var readme, page;
|
||||
|
||||
before(function() {
|
||||
readme = fs.readFileSync(
|
||||
path.join(book.options.output, "index.html"),
|
||||
{ encoding: "utf-8" }
|
||||
);
|
||||
page = fs.readFileSync(
|
||||
path.join(book.options.output, "folder/PAGE.html"),
|
||||
{ encoding: "utf-8" }
|
||||
);
|
||||
});
|
||||
|
||||
it('should correctly replaced terms by links', function() {
|
||||
readme.should.be.html({
|
||||
".page-inner a[href='GLOSSARY.html#test']": {
|
||||
count: 1,
|
||||
text: "test",
|
||||
attributes: {
|
||||
title: "Just a simple and easy to understand test."
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly replaced terms by links (relative)', function() {
|
||||
page.should.be.html({
|
||||
".page-inner a[href='../GLOSSARY.html#test']": {
|
||||
count: 1
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should not replace terms in codeblocks', function() {
|
||||
readme.should.be.html({
|
||||
".page-inner code a": {
|
||||
count: 0
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly select the longest term', function() {
|
||||
readme.should.be.html({
|
||||
".page-inner a[href='GLOSSARY.html#test_long']": {
|
||||
count: 1,
|
||||
text: "test long"
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+55
-49
@@ -1,62 +1,68 @@
|
||||
var os = require('os');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var fs = require('fs');
|
||||
var _ = require('lodash');
|
||||
var should = require('should');
|
||||
|
||||
var fsUtil = require("../lib/utils/fs");
|
||||
var fsUtil = require('../lib/utils/fs');
|
||||
var Book = require('../').Book;
|
||||
var LOG_LEVELS = require('../').LOG_LEVELS;
|
||||
|
||||
// Nicety for mocha / Q
|
||||
global.qdone = function qdone(promise, done) {
|
||||
return promise.then(function() {
|
||||
return done();
|
||||
}, function(err) {
|
||||
return done(err);
|
||||
}).done();
|
||||
require("./assertions");
|
||||
|
||||
|
||||
var BOOKS = {};
|
||||
var TMPDIR = os.tmpdir();
|
||||
|
||||
|
||||
// Generate and return a book
|
||||
function generateBook(bookId, test) {
|
||||
return parseBook(bookId, test)
|
||||
.then(function(book) {
|
||||
return book.generate(test)
|
||||
.thenResolve(book);
|
||||
});
|
||||
}
|
||||
|
||||
// Generate and return a book
|
||||
function parseBook(bookId, test) {
|
||||
test = test || "website";
|
||||
BOOKS[bookId] = BOOKS[bookId] || {};
|
||||
if (BOOKS[bookId][test]) return Q(BOOKS[bookId][test]);
|
||||
|
||||
BOOKS[bookId][test] = new Book(path.resolve(__dirname, "books", bookId), {
|
||||
logLevel: LOG_LEVELS.DISABLED,
|
||||
config: {
|
||||
output: path.resolve(TMPDIR, bookId+"-"+test)
|
||||
}
|
||||
});
|
||||
|
||||
return BOOKS[bookId][test].parse()
|
||||
.then(function() {
|
||||
return BOOKS[bookId][test];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
global.books = {
|
||||
parse: parseBook,
|
||||
generate: generateBook
|
||||
};
|
||||
|
||||
// Test generation of a book
|
||||
global.testGeneration = function(book, type, func, done) {
|
||||
var OUTPUT_PATH = book.options.output;
|
||||
|
||||
qdone(
|
||||
book.generate(type)
|
||||
.then(function() {
|
||||
func(OUTPUT_PATH);
|
||||
})
|
||||
.fin(function() {
|
||||
return fsUtil.remove(OUTPUT_PATH);
|
||||
}),
|
||||
done);
|
||||
};
|
||||
|
||||
// Books for testings
|
||||
var books = fs.readdirSync(path.join(__dirname, './fixtures/'));
|
||||
|
||||
global.books = _.chain(books)
|
||||
.sortBy()
|
||||
.map(function(book) {
|
||||
if (book.indexOf("test") !== 0) return null;
|
||||
return new Book(path.join(__dirname, './fixtures/', book), {
|
||||
logLevel: LOG_LEVELS.DISABLED
|
||||
});
|
||||
})
|
||||
.compact()
|
||||
.value();
|
||||
|
||||
// Init before doing tests
|
||||
before(function(done) {
|
||||
|
||||
qdone(
|
||||
_.reduce(global.books, function(prev, book) {
|
||||
// Cleanup all tests
|
||||
after(function() {
|
||||
return _.chain(BOOKS)
|
||||
.map(function(types, bookId) {
|
||||
return _.values(types);
|
||||
})
|
||||
.flatten()
|
||||
.reduce(function(prev, book) {
|
||||
return prev.then(function() {
|
||||
return fsUtil.remove(path.join(book.root, "_book"));
|
||||
return fsUtil.remove(book.options.output);
|
||||
})
|
||||
.then(function() {
|
||||
return book.parse();
|
||||
});
|
||||
}, Q()),
|
||||
done
|
||||
);
|
||||
}, Q())
|
||||
.value();
|
||||
});
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user